plusui-native-core 0.1.65 → 0.1.68
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/API/Connect_API.ts +160 -0
- package/Core/API/index.ts +127 -0
- package/Core/Features/App/app.ts +192 -131
- package/Core/Features/Clipboard/clipboard.ts +92 -127
- package/Core/Features/Connection/README.md +111 -214
- package/Core/Features/Connection/connect.ts +190 -272
- package/Core/Features/Display/display.ts +142 -116
- package/Core/Features/FileDrop/filedrop.ts +163 -283
- package/Core/Features/Keyboard/keyboard.ts +194 -186
- package/Core/Features/Menu/menu.ts +233 -420
- package/Core/Features/Router/router.ts +125 -0
- package/Core/Features/Tray/tray.ts +102 -80
- package/Core/Features/WebGPU/webgpu.ts +283 -1024
- package/Core/Features/Window/window.ts +207 -156
- package/package.json +5 -1
|
@@ -1,127 +1,92 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
getText(): Promise<string>;
|
|
16
|
-
setText(text: string): Promise<void>;
|
|
17
|
-
hasText(): Promise<boolean>;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
* Check if clipboard has image
|
|
94
|
-
* @returns True if clipboard contains image
|
|
95
|
-
*/
|
|
96
|
-
async hasImage(): Promise<boolean> {
|
|
97
|
-
return (await this.invokeFn('clipboard.hasImage')) as boolean;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Clear clipboard contents
|
|
102
|
-
*/
|
|
103
|
-
async clear(): Promise<void> {
|
|
104
|
-
await this.invokeFn('clipboard.clear');
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Listen for clipboard changes
|
|
109
|
-
* @param callback - Function called when clipboard changes
|
|
110
|
-
* @returns Cleanup function to remove listener
|
|
111
|
-
*/
|
|
112
|
-
onClipboardChange(callback: (text: string) => void): () => void {
|
|
113
|
-
return this.onFn('clipboard:change', (data) => {
|
|
114
|
-
callback(data as string);
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Factory function to create Clipboard instance
|
|
121
|
-
*/
|
|
122
|
-
export function createClipboard(
|
|
123
|
-
invokeFn: (name: string, args?: unknown[]) => Promise<unknown>,
|
|
124
|
-
onFn: (event: string, callback: (data: unknown) => void) => () => void
|
|
125
|
-
): Clipboard {
|
|
126
|
-
return new Clipboard(invokeFn, onFn);
|
|
127
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Clipboard API - Cross-platform clipboard management
|
|
3
|
+
*
|
|
4
|
+
* Direct method calls - no .on/.emit
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* import { clipboard } from '@plusui/api';
|
|
8
|
+
*
|
|
9
|
+
* const text = await clipboard.getText();
|
|
10
|
+
* clipboard.setText('hello');
|
|
11
|
+
* clipboard.onChange((text) => { ... });
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export interface ClipboardAPI {
|
|
15
|
+
getText(): Promise<string>;
|
|
16
|
+
setText(text: string): Promise<void>;
|
|
17
|
+
hasText(): Promise<boolean>;
|
|
18
|
+
getImage(): Promise<string>;
|
|
19
|
+
setImage(base64Data: string): Promise<void>;
|
|
20
|
+
hasImage(): Promise<boolean>;
|
|
21
|
+
clear(): Promise<void>;
|
|
22
|
+
onChange(callback: (text: string) => void): () => void;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
let _invoke: ((name: string, args?: unknown[]) => Promise<unknown>) | null = null;
|
|
26
|
+
let _event: ((event: string, callback: (data: unknown) => void) => () => void) | null = null;
|
|
27
|
+
|
|
28
|
+
export function setInvokeFn(fn: (name: string, args?: unknown[]) => Promise<unknown>) {
|
|
29
|
+
_invoke = fn;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function setEventFn(fn: (event: string, callback: (data: unknown) => void) => () => void) {
|
|
33
|
+
_event = fn;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async function invoke<T = unknown>(name: string, args?: unknown[]): Promise<T> {
|
|
37
|
+
if (!_invoke) {
|
|
38
|
+
if (typeof window !== 'undefined' && (window as any).__invoke__) {
|
|
39
|
+
_invoke = (window as any).__invoke__;
|
|
40
|
+
} else {
|
|
41
|
+
throw new Error('Clipboard API not initialized');
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return _invoke!(name, args) as Promise<T>;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function eventHandler(event: string, callback: (data: unknown) => void): () => void {
|
|
48
|
+
if (!_event) {
|
|
49
|
+
if (typeof window !== 'undefined' && (window as any).__on__) {
|
|
50
|
+
_event = (window as any).__on__;
|
|
51
|
+
} else {
|
|
52
|
+
return () => {};
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return _event!(event, callback);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export const clipboard: ClipboardAPI = {
|
|
59
|
+
async getText(): Promise<string> {
|
|
60
|
+
return invoke<string>('clipboard.getText');
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
async setText(text: string): Promise<void> {
|
|
64
|
+
await invoke('clipboard.setText', [text]);
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
async hasText(): Promise<boolean> {
|
|
68
|
+
return invoke<boolean>('clipboard.hasText');
|
|
69
|
+
},
|
|
70
|
+
|
|
71
|
+
async getImage(): Promise<string> {
|
|
72
|
+
return invoke<string>('clipboard.getImage');
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
async setImage(base64Data: string): Promise<void> {
|
|
76
|
+
await invoke('clipboard.setImage', [base64Data]);
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
async hasImage(): Promise<boolean> {
|
|
80
|
+
return invoke<boolean>('clipboard.hasImage');
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
async clear(): Promise<void> {
|
|
84
|
+
await invoke('clipboard.clear');
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
onChange(callback: (text: string) => void): () => void {
|
|
88
|
+
return eventHandler('clipboard:change', callback as (data: unknown) => void);
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
export default clipboard;
|
|
@@ -1,214 +1,111 @@
|
|
|
1
|
-
# PlusUI Connection
|
|
2
|
-
|
|
3
|
-
##
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
###
|
|
68
|
-
|
|
69
|
-
```typescript
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
//
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
// Optional: stream/query subscription hooks
|
|
113
|
-
connect.onSubscription("cpu", [](bool subscribed, const nlohmann::json&) {
|
|
114
|
-
if (subscribed) {
|
|
115
|
-
startCpuStream();
|
|
116
|
-
} else {
|
|
117
|
-
stopCpuStream();
|
|
118
|
-
}
|
|
119
|
-
});
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
### Feature-Scoped Names
|
|
123
|
-
|
|
124
|
-
Use `connect` for custom global channels, and feature-scoped APIs when you want automatic namespacing:
|
|
125
|
-
|
|
126
|
-
```typescript
|
|
127
|
-
window.emit('resized', { width: 1200, height: 800 }); // -> "window.resized"
|
|
128
|
-
clipboard.emit('changed', { text: 'hello' }); // -> "clipboard.changed"
|
|
129
|
-
|
|
130
|
-
// Also supported when you need explicit feature connect access:
|
|
131
|
-
window.connect.emit('resized', { width: 1200, height: 800 }); // -> "window.resized"
|
|
132
|
-
connect.emit('custom.appEvent', { ok: true }); // custom/global channel
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
Backend can listen using fully scoped names, or use a scoped helper:
|
|
136
|
-
|
|
137
|
-
```cpp
|
|
138
|
-
plusui::Connect connect;
|
|
139
|
-
plusui::bindConnect(mainWindow, connect);
|
|
140
|
-
|
|
141
|
-
auto windowFeature = connect.feature("window");
|
|
142
|
-
windowFeature.on("resized", [](const nlohmann::json& payload) {
|
|
143
|
-
// receives "window.resized"
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
connect.on("window.resized", [](const nlohmann::json& payload) {
|
|
147
|
-
// from window.emit('resized', ...) or window.connect.emit('resized', ...)
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
connect.on("clipboard.changed", [](const nlohmann::json& payload) {
|
|
151
|
-
// from clipboard.emit('changed', ...) or clipboard.connect.emit('changed', ...)
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
connect.on("custom.appEvent", [](const nlohmann::json& payload) {
|
|
155
|
-
// from connect.emit('custom.appEvent', ...)
|
|
156
|
-
});
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
`plusui connect` scans frontend and backend `.emit()` / `.on()` calls and understands feature aliases, including:
|
|
160
|
-
|
|
161
|
-
- `const custom = createFeatureConnect('custom'); custom.on('event', ...)`
|
|
162
|
-
- `const custom = connect.feature('custom'); custom.emit('event', ...)`
|
|
163
|
-
- `auto custom = connect.feature("custom"); custom.on("event", ...)`
|
|
164
|
-
|
|
165
|
-
## 💡 Design Your Own Patterns
|
|
166
|
-
|
|
167
|
-
### Request/Response Pattern
|
|
168
|
-
|
|
169
|
-
```typescript
|
|
170
|
-
// Frontend
|
|
171
|
-
connect.on('getUserResponse', (data) => {
|
|
172
|
-
console.log(data.user);
|
|
173
|
-
});
|
|
174
|
-
connect.emit('getUser', { id: 123 });
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
```cpp
|
|
178
|
-
plusui::Connect connect;
|
|
179
|
-
connect.onCall("getUser", [](const nlohmann::json& payload) {
|
|
180
|
-
auto user = database.findUser(payload["id"]);
|
|
181
|
-
return nlohmann::json{{"user", user}};
|
|
182
|
-
});
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
### Notifications
|
|
186
|
-
|
|
187
|
-
```cpp
|
|
188
|
-
// Backend sends
|
|
189
|
-
emit("notification", {{"text", "File saved!"}});
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
```typescript
|
|
193
|
-
// Frontend receives
|
|
194
|
-
connect.on('notification', (data) => {
|
|
195
|
-
toast.success(data.text);
|
|
196
|
-
});
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
### Bidirectional Chat
|
|
200
|
-
|
|
201
|
-
```typescript
|
|
202
|
-
// Both sides listen
|
|
203
|
-
connect.on('chat', (data) => {
|
|
204
|
-
addToLog(data.message);
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
// Both sides send
|
|
208
|
-
connect.emit('chat', { message: 'Hello!' });
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
## 🎯 That's It
|
|
212
|
-
|
|
213
|
-
Two methods. Both sides. Use them however you want.
|
|
214
|
-
|
|
1
|
+
# PlusUI Connection
|
|
2
|
+
|
|
3
|
+
## Two methods. Five primitives. Same syntax.
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
// TypeScript
|
|
7
|
+
myEvent.on((data) => { ... });
|
|
8
|
+
myEvent.emit({ value: 123 });
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
```cpp
|
|
12
|
+
// C++ (identical!)
|
|
13
|
+
myEvent.on([&](const json& p) { ... });
|
|
14
|
+
myEvent.emit({{"value", 123}});
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Quick Start
|
|
18
|
+
|
|
19
|
+
### 1. Write your code
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
// frontend/src/app.ts
|
|
23
|
+
import { download } from '../Connections/connections.gen';
|
|
24
|
+
|
|
25
|
+
download.emit({ url: 'https://example.com/file.zip' });
|
|
26
|
+
download.on((data) => {
|
|
27
|
+
progressBar.value = data.progress;
|
|
28
|
+
});
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
```cpp
|
|
32
|
+
// main.cpp
|
|
33
|
+
#include "Connections/connections.gen.hpp"
|
|
34
|
+
|
|
35
|
+
initChannels(connect);
|
|
36
|
+
|
|
37
|
+
download.on([&](const json& p) {
|
|
38
|
+
auto url = p.value("url", "");
|
|
39
|
+
download.emit({{"progress", 50}});
|
|
40
|
+
});
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### 2. Generate channels
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
plusui connect
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Scans your `.ts`, `.tsx`, `.cpp`, `.hpp` files for `.on()` and `.emit()` calls, generates:
|
|
50
|
+
- `Connections/connections.gen.ts`
|
|
51
|
+
- `Connections/connections.gen.hpp`
|
|
52
|
+
|
|
53
|
+
## The 5 Primitives
|
|
54
|
+
|
|
55
|
+
| Pattern | Direction | Use Case |
|
|
56
|
+
|---------|-----------|----------|
|
|
57
|
+
| **EVENT** | One way → | Notifications, commands |
|
|
58
|
+
| **CALL** | Two ways ↔ | Request/response |
|
|
59
|
+
| **STREAM** | Continuous → | Real-time updates |
|
|
60
|
+
| **QUERY** | Many responses ↔↔↔ | Search, pagination |
|
|
61
|
+
| **STATE** | Both ways ↕ | Synced values |
|
|
62
|
+
|
|
63
|
+
All handled with just `.on()` and `.emit()`.
|
|
64
|
+
|
|
65
|
+
## Generated Files
|
|
66
|
+
|
|
67
|
+
### TypeScript (`connections.gen.ts`)
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
import { _client } from 'plusui';
|
|
71
|
+
|
|
72
|
+
export const download = {
|
|
73
|
+
on: <T = unknown>(cb: (data: T) => void) => _client.on('download', cb),
|
|
74
|
+
emit: (data: unknown) => _client.fire('download', data)
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export const CONNECT_CHANNELS = ['download'] as const;
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### C++ (`connections.gen.hpp`)
|
|
81
|
+
|
|
82
|
+
```cpp
|
|
83
|
+
#pragma once
|
|
84
|
+
#include <plusui/plusui.hpp>
|
|
85
|
+
|
|
86
|
+
namespace channels {
|
|
87
|
+
inline plusui::Connect* _connect = nullptr;
|
|
88
|
+
inline plusui::Connect::Channel download;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
inline void initChannels(plusui::Connect& c) {
|
|
92
|
+
channels::_connect = &c;
|
|
93
|
+
channels::download = c.channel("download");
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Direct access (same as TypeScript)
|
|
97
|
+
using channels::download;
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Built-in Channels
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
import { win, clipboard, app } from 'plusui';
|
|
104
|
+
|
|
105
|
+
win.on((data) => console.log(data.width));
|
|
106
|
+
clipboard.emit({ text: 'copied!' });
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## That's it.
|
|
110
|
+
|
|
111
|
+
No schemas. No configuration. Write `.on()` and `.emit()`, run `plusui connect`.
|