plusui-native-core 0.1.57 → 0.1.59
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/Core/Features/Browser/browser.ts +20 -2
- package/Core/Features/Clipboard/clipboard.ts +11 -0
- package/Core/Features/Connection/README.md +29 -2
- package/Core/Features/Display/display.ts +8 -0
- package/Core/Features/FileDrop/filedrop.ts +11 -0
- package/Core/Features/Keyboard/keyboard.ts +8 -0
- package/Core/Features/Menu/menu.ts +8 -0
- package/Core/Features/Tray/tray.ts +8 -0
- package/Core/Features/WebGPU/webgpu.ts +8 -0
- package/Core/Features/Window/window.ts +11 -1
- package/Core/include/plusui/connect.hpp +38 -0
- package/package.json +1 -1
|
@@ -38,6 +38,8 @@ const _loadErrorCallbacks: ErrorCallback[] = [];
|
|
|
38
38
|
let _currentState: BrowserState = { url: '', title: '', canGoBack: false, canGoForward: false, isLoading: false };
|
|
39
39
|
let _routes: Record<string, string> = {};
|
|
40
40
|
let _currentRoute: string = '/';
|
|
41
|
+
const browserFeatureEvents = connect.feature('browser');
|
|
42
|
+
const routerFeatureEvents = connect.feature('router');
|
|
41
43
|
|
|
42
44
|
// Setup event listeners from native backend
|
|
43
45
|
if (typeof window !== 'undefined') {
|
|
@@ -64,7 +66,15 @@ if (typeof window !== 'undefined') {
|
|
|
64
66
|
}
|
|
65
67
|
|
|
66
68
|
export const browser = {
|
|
67
|
-
connect:
|
|
69
|
+
connect: browserFeatureEvents,
|
|
70
|
+
|
|
71
|
+
on: <TData = unknown>(name: string, callback: (payload: TData) => void): (() => void) => {
|
|
72
|
+
return browserFeatureEvents.on<TData>(name, callback);
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
emit: <TIn = Record<string, unknown>>(name: string, payload: TIn): void => {
|
|
76
|
+
browserFeatureEvents.emit<TIn>(name, payload);
|
|
77
|
+
},
|
|
68
78
|
|
|
69
79
|
async navigate(url: string): Promise<void> {
|
|
70
80
|
await invoke('browser.navigate', [url]);
|
|
@@ -164,7 +174,15 @@ export const browser = {
|
|
|
164
174
|
};
|
|
165
175
|
|
|
166
176
|
export const router = {
|
|
167
|
-
connect:
|
|
177
|
+
connect: routerFeatureEvents,
|
|
178
|
+
|
|
179
|
+
on: <TData = unknown>(name: string, callback: (payload: TData) => void): (() => void) => {
|
|
180
|
+
return routerFeatureEvents.on<TData>(name, callback);
|
|
181
|
+
},
|
|
182
|
+
|
|
183
|
+
emit: <TIn = Record<string, unknown>>(name: string, payload: TIn): void => {
|
|
184
|
+
routerFeatureEvents.emit<TIn>(name, payload);
|
|
185
|
+
},
|
|
168
186
|
|
|
169
187
|
setRoutes(routes: Record<string, string>): void {
|
|
170
188
|
_routes = routes;
|
|
@@ -8,6 +8,9 @@ import { connect } from '../Connection/connect';
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
export interface ClipboardAPI {
|
|
11
|
+
on<TData = unknown>(name: string, callback: (payload: TData) => void): () => void;
|
|
12
|
+
emit<TIn = Record<string, unknown>>(name: string, payload: TIn): void;
|
|
13
|
+
|
|
11
14
|
// Text operations
|
|
12
15
|
getText(): Promise<string>;
|
|
13
16
|
setText(text: string): Promise<void>;
|
|
@@ -38,6 +41,14 @@ export class Clipboard implements ClipboardAPI {
|
|
|
38
41
|
this.onFn = onFn;
|
|
39
42
|
}
|
|
40
43
|
|
|
44
|
+
on<TData = unknown>(name: string, callback: (payload: TData) => void): () => void {
|
|
45
|
+
return this.connect.on<TData>(name, callback);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
emit<TIn = Record<string, unknown>>(name: string, payload: TIn): void {
|
|
49
|
+
this.connect.emit<TIn>(name, payload);
|
|
50
|
+
}
|
|
51
|
+
|
|
41
52
|
/**
|
|
42
53
|
* Get text from clipboard
|
|
43
54
|
* @returns Current clipboard text content
|
|
@@ -117,14 +117,41 @@ connect.onSubscription("cpu", [](bool subscribed, const nlohmann::json&) {
|
|
|
117
117
|
|
|
118
118
|
### Feature-Scoped Names
|
|
119
119
|
|
|
120
|
-
Use `connect` for custom global channels, and
|
|
120
|
+
Use `connect` for custom global channels, and feature-scoped APIs when you want automatic namespacing:
|
|
121
121
|
|
|
122
122
|
```typescript
|
|
123
|
+
window.emit('resized', { width: 1200, height: 800 }); // -> "window.resized"
|
|
124
|
+
clipboard.emit('changed', { text: 'hello' }); // -> "clipboard.changed"
|
|
125
|
+
|
|
126
|
+
// Also supported when you need explicit feature connect access:
|
|
123
127
|
window.connect.emit('resized', { width: 1200, height: 800 }); // -> "window.resized"
|
|
124
|
-
clipboard.connect.emit('changed', { text: 'hello' }); // -> "clipboard.changed"
|
|
125
128
|
connect.emit('custom.appEvent', { ok: true }); // custom/global channel
|
|
126
129
|
```
|
|
127
130
|
|
|
131
|
+
Backend can listen using fully scoped names, or use a scoped helper:
|
|
132
|
+
|
|
133
|
+
```cpp
|
|
134
|
+
plusui::Connect connect;
|
|
135
|
+
plusui::bindConnect(mainWindow, connect);
|
|
136
|
+
|
|
137
|
+
auto windowFeature = connect.feature("window");
|
|
138
|
+
windowFeature.on("resized", [](const nlohmann::json& payload) {
|
|
139
|
+
// receives "window.resized"
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
connect.on("window.resized", [](const nlohmann::json& payload) {
|
|
143
|
+
// from window.emit('resized', ...) or window.connect.emit('resized', ...)
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
connect.on("clipboard.changed", [](const nlohmann::json& payload) {
|
|
147
|
+
// from clipboard.emit('changed', ...) or clipboard.connect.emit('changed', ...)
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
connect.on("custom.appEvent", [](const nlohmann::json& payload) {
|
|
151
|
+
// from connect.emit('custom.appEvent', ...)
|
|
152
|
+
});
|
|
153
|
+
```
|
|
154
|
+
|
|
128
155
|
## 💡 Design Your Own Patterns
|
|
129
156
|
|
|
130
157
|
### Request/Response Pattern
|
|
@@ -40,6 +40,14 @@ export class DisplayAPI {
|
|
|
40
40
|
private invokeFn: (name: string, args?: unknown[]) => Promise<unknown>
|
|
41
41
|
) {}
|
|
42
42
|
|
|
43
|
+
on<TData = unknown>(name: string, callback: (payload: TData) => void): () => void {
|
|
44
|
+
return this.connect.on<TData>(name, callback);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
emit<TIn = Record<string, unknown>>(name: string, payload: TIn): void {
|
|
48
|
+
this.connect.emit<TIn>(name, payload);
|
|
49
|
+
}
|
|
50
|
+
|
|
43
51
|
async getAllDisplays(): Promise<Display[]> {
|
|
44
52
|
return await this.invokeFn<Display[]>('display.getAllDisplays', []);
|
|
45
53
|
}
|
|
@@ -32,6 +32,9 @@ export interface DragEvent {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
export interface FileDropAPI {
|
|
35
|
+
on<TData = unknown>(name: string, callback: (payload: TData) => void): () => void;
|
|
36
|
+
emit<TIn = Record<string, unknown>>(name: string, payload: TIn): void;
|
|
37
|
+
|
|
35
38
|
/**
|
|
36
39
|
* Enable or disable file drop into window
|
|
37
40
|
* @param enabled - true to allow files to be dropped into the window
|
|
@@ -91,6 +94,14 @@ export class FileDrop implements FileDropAPI {
|
|
|
91
94
|
this.onFn = onFn;
|
|
92
95
|
}
|
|
93
96
|
|
|
97
|
+
on<TData = unknown>(name: string, callback: (payload: TData) => void): () => void {
|
|
98
|
+
return this.connect.on<TData>(name, callback);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
emit<TIn = Record<string, unknown>>(name: string, payload: TIn): void {
|
|
102
|
+
this.connect.emit<TIn>(name, payload);
|
|
103
|
+
}
|
|
104
|
+
|
|
94
105
|
/**
|
|
95
106
|
* Enable or disable file drop into window
|
|
96
107
|
* @param enabled - true to allow files to be dropped into the window
|
|
@@ -65,6 +65,14 @@ export class KeyboardAPI {
|
|
|
65
65
|
this.setupEventListeners();
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
on<TData = unknown>(name: string, callback: (payload: TData) => void): () => void {
|
|
69
|
+
return this.connect.on<TData>(name, callback);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
emit<TIn = Record<string, unknown>>(name: string, payload: TIn): void {
|
|
73
|
+
this.connect.emit<TIn>(name, payload);
|
|
74
|
+
}
|
|
75
|
+
|
|
68
76
|
private setupEventListeners(): void {
|
|
69
77
|
this.eventFn('keyboard:keydown', (data) => {
|
|
70
78
|
const event = data as KeyEvent;
|
|
@@ -88,6 +88,14 @@ export class MenuAPI {
|
|
|
88
88
|
});
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
+
on<TData = unknown>(name: string, callback: (payload: TData) => void): () => void {
|
|
92
|
+
return this.connect.on<TData>(name, callback);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
emit<TIn = Record<string, unknown>>(name: string, payload: TIn): void {
|
|
96
|
+
this.connect.emit<TIn>(name, payload);
|
|
97
|
+
}
|
|
98
|
+
|
|
91
99
|
// ========================================================================
|
|
92
100
|
// Popup Menus
|
|
93
101
|
// ========================================================================
|
|
@@ -25,6 +25,14 @@ export class TrayAPI {
|
|
|
25
25
|
private eventFn: (event: string, callback: (...args: unknown[]) => void) => () => void
|
|
26
26
|
) {}
|
|
27
27
|
|
|
28
|
+
on<TData = unknown>(name: string, callback: (payload: TData) => void): () => void {
|
|
29
|
+
return this.connect.on<TData>(name, callback);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
emit<TIn = Record<string, unknown>>(name: string, payload: TIn): void {
|
|
33
|
+
this.connect.emit<TIn>(name, payload);
|
|
34
|
+
}
|
|
35
|
+
|
|
28
36
|
async setIcon(iconPath: string): Promise<void> {
|
|
29
37
|
await this.invokeFn('tray.setIcon', [iconPath]);
|
|
30
38
|
}
|
|
@@ -575,6 +575,14 @@ class WebGPUNamespace {
|
|
|
575
575
|
this.onFn = onFn;
|
|
576
576
|
}
|
|
577
577
|
|
|
578
|
+
on<TData = unknown>(name: string, callback: (payload: TData) => void): () => void {
|
|
579
|
+
return this.connect.on<TData>(name, callback);
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
emit<TIn = Record<string, unknown>>(name: string, payload: TIn): void {
|
|
583
|
+
this.connect.emit<TIn>(name, payload);
|
|
584
|
+
}
|
|
585
|
+
|
|
578
586
|
async requestAdapter(options?: GPURequestAdapterOptions): Promise<GPUAdapter | null> {
|
|
579
587
|
const result = await this.invokeFn('webgpu.requestAdapter', [options || {}]);
|
|
580
588
|
if (!result) {
|
|
@@ -43,8 +43,18 @@ async function invoke(method: string, args?: unknown[]): Promise<unknown> {
|
|
|
43
43
|
return _invoke(method, args);
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
const windowFeatureEvents = connect.feature('window');
|
|
47
|
+
|
|
46
48
|
export const window = {
|
|
47
|
-
connect:
|
|
49
|
+
connect: windowFeatureEvents,
|
|
50
|
+
|
|
51
|
+
on: <TData = unknown>(name: string, callback: (payload: TData) => void): (() => void) => {
|
|
52
|
+
return windowFeatureEvents.on<TData>(name, callback);
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
emit: <TIn = Record<string, unknown>>(name: string, payload: TIn): void => {
|
|
56
|
+
windowFeatureEvents.emit<TIn>(name, payload);
|
|
57
|
+
},
|
|
48
58
|
|
|
49
59
|
async minimize(id?: WindowId): Promise<void> {
|
|
50
60
|
await invoke('window.minimize', id ? [id] : []);
|
|
@@ -72,6 +72,40 @@ public:
|
|
|
72
72
|
using SubscriptionHandler =
|
|
73
73
|
std::function<void(bool subscribed, const nlohmann::json &)>;
|
|
74
74
|
|
|
75
|
+
class Feature {
|
|
76
|
+
Connect *owner;
|
|
77
|
+
std::string scope;
|
|
78
|
+
|
|
79
|
+
std::string scopedName(const std::string &name) const {
|
|
80
|
+
const std::string prefix = scope + ".";
|
|
81
|
+
if (name.rfind(prefix, 0) == 0) {
|
|
82
|
+
return name;
|
|
83
|
+
}
|
|
84
|
+
return prefix + name;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
public:
|
|
88
|
+
Feature(Connect *parent, std::string featureScope)
|
|
89
|
+
: owner(parent), scope(std::move(featureScope)) {}
|
|
90
|
+
|
|
91
|
+
void on(const std::string &name, EventHandler handler) {
|
|
92
|
+
owner->on(scopedName(name), std::move(handler));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
void emit(const std::string &name, const nlohmann::json &payload) {
|
|
96
|
+
owner->emit(scopedName(name), payload);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
void onCall(const std::string &name, CallHandler handler) {
|
|
100
|
+
owner->onCall(scopedName(name), std::move(handler));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
void onSubscription(const std::string &name,
|
|
104
|
+
SubscriptionHandler handler) {
|
|
105
|
+
owner->onSubscription(scopedName(name), std::move(handler));
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
|
|
75
109
|
private:
|
|
76
110
|
std::function<void(const std::string &)> outboundHandler;
|
|
77
111
|
std::map<std::string, std::vector<EventHandler>> eventHandlers;
|
|
@@ -100,6 +134,8 @@ private:
|
|
|
100
134
|
outboundHandler(j.dump());
|
|
101
135
|
}
|
|
102
136
|
|
|
137
|
+
public:
|
|
138
|
+
|
|
103
139
|
virtual ~Connect() = default;
|
|
104
140
|
|
|
105
141
|
// Mirror frontend API: register message listener by name
|
|
@@ -107,6 +143,8 @@ private:
|
|
|
107
143
|
eventHandlers[name].push_back(std::move(handler));
|
|
108
144
|
}
|
|
109
145
|
|
|
146
|
+
Feature feature(const std::string &scope) { return Feature(this, scope); }
|
|
147
|
+
|
|
110
148
|
// Request/response helper for call primitive
|
|
111
149
|
void onCall(const std::string &name, CallHandler handler) {
|
|
112
150
|
callHandlers[name] = std::move(handler);
|