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.
@@ -1,127 +1,92 @@
1
- import { connect } from '../Connection/connect';
2
-
3
- /**
4
- * Clipboard API - Cross-platform clipboard management
5
- *
6
- * Provides access to system clipboard for text and images.
7
- * Supports clipboard change events.
8
- */
9
-
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
-
14
- // Text operations
15
- getText(): Promise<string>;
16
- setText(text: string): Promise<void>;
17
- hasText(): Promise<boolean>;
18
-
19
- // Image operations (base64 encoded)
20
- getImage(): Promise<string>;
21
- setImage(base64Data: string): Promise<void>;
22
- hasImage(): Promise<boolean>;
23
-
24
- // Clear clipboard
25
- clear(): Promise<void>;
26
-
27
- // Events
28
- onClipboardChange(callback: (text: string) => void): () => void;
29
- }
30
-
31
- export class Clipboard implements ClipboardAPI {
32
- public readonly connect = connect.feature('clipboard');
33
- private invokeFn: (name: string, args?: unknown[]) => Promise<unknown>;
34
- private onFn: (event: string, callback: (data: unknown) => void) => () => void;
35
-
36
- constructor(
37
- invokeFn: (name: string, args?: unknown[]) => Promise<unknown>,
38
- onFn: (event: string, callback: (data: unknown) => void) => () => void
39
- ) {
40
- this.invokeFn = invokeFn;
41
- this.onFn = onFn;
42
- }
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
-
52
- /**
53
- * Get text from clipboard
54
- * @returns Current clipboard text content
55
- */
56
- async getText(): Promise<string> {
57
- return (await this.invokeFn('clipboard.getText')) as string;
58
- }
59
-
60
- /**
61
- * Set clipboard text
62
- * @param text - Text to copy to clipboard
63
- */
64
- async setText(text: string): Promise<void> {
65
- await this.invokeFn('clipboard.setText', [text]);
66
- }
67
-
68
- /**
69
- * Check if clipboard has text
70
- * @returns True if clipboard contains text
71
- */
72
- async hasText(): Promise<boolean> {
73
- return (await this.invokeFn('clipboard.hasText')) as boolean;
74
- }
75
-
76
- /**
77
- * Get image from clipboard as base64
78
- * @returns Base64 encoded image data
79
- */
80
- async getImage(): Promise<string> {
81
- return (await this.invokeFn('clipboard.getImage')) as string;
82
- }
83
-
84
- /**
85
- * Set clipboard image from base64 data
86
- * @param base64Data - Base64 encoded image data
87
- */
88
- async setImage(base64Data: string): Promise<void> {
89
- await this.invokeFn('clipboard.setImage', [base64Data]);
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 Feature
2
-
3
- ## 🎯 Overview
4
-
5
- The **Connection Feature** is PlusUI's absurdly simple system for frontend ↔ backend communication. Use **`feature.emit()`** and **`feature.on()`** on frontend, mirrored by scoped channels on backend.
6
-
7
- ## The 5 Primitives That Cover Everything
8
-
9
- Just `emit()` and `on()` handle all communication patterns:
10
-
11
- | Keyword | Pattern | Direction | Use Cases |
12
- | --------- | --------- | ----------- | ----------- |
13
- | **EVENT** | Fire & forget | One way → | Notifications, commands, logging |
14
- | **CALL** | Request/response | Two ways ↔ | Get data, save data, RPC |
15
- | **STREAM** | Continuous data | One way push → | Real-time updates, monitoring |
16
- | **QUERY** | Request + stream | Two way + many ↔↔↔ | Search results, paginated data |
17
- | **STATE** | Synced value | Both ways ↕ | Theme, settings, shared state |
18
-
19
- ## Why It's Simple
20
-
21
- - **Two methods**: `emit()` and `on()` - that's all
22
- - **Mirror API**: Same methods on both frontend and backend
23
- - **Two-way**: Frontend Backend AND Backend → Frontend
24
- - **No schema**: Write code directly, no extra files
25
- - **No bindgen**: No code generation step
26
- - **Total flexibility**: Design your own patterns (RPC, pub/sub, streaming, whatever)
27
-
28
- ## 🚀 Quick Start
29
-
30
- ### Send Message →
31
-
32
- ```typescript
33
- import { connect } from 'plusui-native-core/connect';
34
-
35
- const custom = connect.feature('custom');
36
-
37
- // Listen for response
38
- custom.on('greetResponse', (data) => {
39
- console.log(data.message); // "Hello, World!"
40
- });
41
-
42
- // Send message to backend
43
- custom.emit('greet', { name: 'World' });
44
- ```
45
-
46
- ### Receive & Respond
47
-
48
- ```cpp
49
- #include <plusui/connect.hpp>
50
-
51
- plusui::Connect connect;
52
-
53
- auto customFeature = connect.feature("custom");
54
-
55
- customFeature.on("greet", [&connect](const nlohmann::json& payload) {
56
- std::string user = payload.value("name", "World");
57
- connect.emit("custom.greetResponse", {{"message", "Hello, " + user + "!"}});
58
- });
59
-
60
- // Wire backend connect window bridge once
61
- plusui::bindConnect(mainWindow, connect);
62
-
63
- // Example: backend push to frontend
64
- connect.emit("custom.resize", {{"width", 1280}, {"height", 720}});
65
- ```
66
-
67
- ### Listen on Frontend
68
-
69
- ```typescript
70
- // Listen for messages from backend
71
- custom.on('resize', (data) => {
72
- console.log(`${data.width}x${data.height}`);
73
- updateLayout(data);
74
- });
75
- ```
76
-
77
- ## 📖 Complete API
78
-
79
- ### Frontend
80
-
81
- ```typescript
82
- // Send message
83
- connect.emit('messageName', { key: 'value' });
84
-
85
- // Listen for messages
86
- connect.on('messageName', (data) => {
87
- console.log(data);
88
- });
89
- ```
90
-
91
- ### Backend
92
-
93
- ```cpp
94
- plusui::Connect connect;
95
-
96
- // Listen for messages
97
- connect.on("messageName", [](const nlohmann::json& payload) {
98
- // process payload
99
- });
100
-
101
- // Send messages
102
- connect.emit("messageName", {{"key", "value"}});
103
-
104
- // Bridge window <-> connect runtime
105
- plusui::bindConnect(mainWindow, connect);
106
-
107
- // Optional: call primitive support
108
- connect.onCall("getUser", [](const nlohmann::json& payload) {
109
- return nlohmann::json{{"id", payload.value("id", 0)}, {"name", "Ada"}};
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`.