plusui-native-core 0.1.102 → 0.1.104

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.
@@ -25,47 +25,47 @@ private:
25
25
  std::unique_ptr<Impl> pImpl;
26
26
  };
27
27
 
28
- class App::Builder {
29
- public:
30
- struct Config {
31
- std::string title = "PlusUI App";
32
- int width = 1200;
33
- int height = 800;
34
- bool resizable = true;
35
- bool devtools = true;
36
- std::string trayIconPath;
37
- std::string trayTooltip;
38
- bool alwaysOnTop = false;
39
- bool centered = true;
40
- bool transparent = false;
41
- bool decorations = true;
42
- bool skipTaskbar = false;
43
- bool scrollbars = true;
44
- bool enableFileDrop = true;
45
- };
46
-
47
- Builder();
48
-
49
- Builder &title(const std::string &t);
50
- Builder &width(int w);
51
- Builder &height(int h);
52
- Builder &resizable(bool r);
53
- Builder &devtools(bool d);
54
- Builder &trayIcon(const std::string &icon);
55
- Builder &trayTooltip(const std::string &tooltip);
56
- Builder &alwaysOnTop(bool top);
57
- Builder &centered(bool center);
58
- Builder &transparent(bool transparent);
59
- Builder &decorations(bool decorations);
60
- Builder &skipTaskbar(bool skip);
61
- Builder &scrollbars(bool show);
62
- Builder &enableFileDrop(bool enable);
63
-
64
- Window build();
65
-
66
- private:
67
- Config config;
68
- };
28
+ class App::Builder {
29
+ public:
30
+ struct Config {
31
+ std::string title = "PlusUI App";
32
+ int width = 1200;
33
+ int height = 800;
34
+ bool resizable = true;
35
+ bool devtools = true;
36
+ std::string trayIconPath;
37
+ std::string trayTooltip;
38
+ bool alwaysOnTop = false;
39
+ bool centered = true;
40
+ bool transparent = false;
41
+ bool decorations = true;
42
+ bool skipTaskbar = false;
43
+ bool scrollbars = true;
44
+ bool fileDrop = true; // Enable OS file drop
45
+ };
46
+
47
+ Builder();
48
+
49
+ Builder &title(const std::string &t);
50
+ Builder &width(int w);
51
+ Builder &height(int h);
52
+ Builder &resizable(bool r);
53
+ Builder &devtools(bool d);
54
+ Builder &trayIcon(const std::string &icon);
55
+ Builder &trayTooltip(const std::string &tooltip);
56
+ Builder &alwaysOnTop(bool top);
57
+ Builder &centered(bool center);
58
+ Builder &transparent(bool transparent);
59
+ Builder &decorations(bool decorations);
60
+ Builder &skipTaskbar(bool skip);
61
+ Builder &scrollbars(bool show);
62
+ Builder &fileDrop(bool enable);
63
+
64
+ Window build();
65
+
66
+ private:
67
+ Config config;
68
+ };
69
69
 
70
70
  App::Builder createApp();
71
71
 
@@ -6,59 +6,69 @@
6
6
  #include <string>
7
7
  #include <vector>
8
8
 
9
- // ============================================
10
- // PlusUI Connect Feature
11
- // ============================================
12
- //
13
- // TWO METHODS. FIVE PRIMITIVES. EVERYTHING YOU NEED.
14
- //
15
- // Mirror API - same methods on both sides:
16
- // Frontend: connect.emit(name, data) → Backend: emit(name, data)
17
- // Frontend: connect.on(name, callback) → Backend: handleMessage(name, data)
18
- //
19
- // ============================================
20
- // THE 5 PRIMITIVES
21
- // ============================================
22
- //
23
- // 1. EVENT - Fire & forget, one way →
24
- // Frontend: connect.emit('notification', { message: 'Done!' })
25
- // Backend: emit("notification", {{"message", "Done!"}});
26
- //
27
- // 2. CALL - Request/response, two ways ↔
28
- // Frontend: connect.emit('getUser', { id: 123 })
29
- // connect.on('userData', (data) => { ... })
30
- // Backend: handleMessage("getUser", payload) emit("userData", {...})
31
- //
32
- // 3. STREAM - Continuous data, one way push →
33
- // Backend: emit("cpu-usage", {{"percent", 45}}); // called repeatedly
34
- // Frontend: connect.on('cpu-usage', (data) => { ... })
35
- //
36
- // 4. QUERY - Request + stream, two way + many ↔↔↔
37
- // Frontend: connect.emit('search', { query: 'test' })
38
- // connect.on('searchResult', (item) => { ... }) // fires multiple times
39
- // Backend: handleMessage("search", payload) emit("searchResult", item1)
40
- // emit("searchResult", item2) ...
41
- //
42
- // 5. STATE - Synced value, both ways ↕
43
- // Both sides can emit and receive the same message:
44
- // connect.emit('theme', { mode: 'dark' })
45
- // connect.on('theme', (data) => { ... })
46
- //
47
- // ============================================
48
- // BASIC USAGE
49
- // ============================================
50
- //
51
- // class MyConnect : public plusui::Connect {
52
- // protected:
53
- // void handleMessage(const std::string& name, const nlohmann::json& data) override {
54
- // if (name == "greet") {
55
- // auto userName = data["name"].get<std::string>();
56
- // emit("greeting", {{"message", "Hello " + userName + "!"}});
57
- // }
58
- // }
59
- // };
60
- //
61
- // ============================================
9
+ // ============================================================================
10
+ // PlusUI Connect
11
+ // ============================================================================
12
+ //
13
+ // SEMANTIC SYNTAX. ZERO CONFIG. ALL 5 PATTERNS.
14
+ //
15
+ // IMPORTANT: This is for CUSTOM user-defined communication only!
16
+ // Built-in features (window, clipboard, app, etc.) use their own APIs:
17
+ // window.minimize();
18
+ // clipboard.setText("hello");
19
+ // app.quit();
20
+ //
21
+ // Just write code using connect::namespace.method() syntax for custom
22
+ // frontend ↔ backend communication, then run `plusui connect`.
23
+ //
24
+ // Patterns are auto-detected from your code:
25
+ //
26
+ // connect::user.handleFetch = [](const json& p) -> json { ... }; // CALL
27
+ // connect::app.notify({{"msg", "Hello!"}}); // FIRE
28
+ // connect::files.onUpload = [](const json& p) { ... }; // EVENT
29
+ //
30
+ // Custom channels are auto-generated by `plusui connect`.
31
+ //
32
+ // ============================================================================
33
+ // THE 5 COMMUNICATION PATTERNS
34
+ // ============================================================================
35
+ // ALL PATTERNS WORK BIDIRECTIONALLY - frontend ↔ backend
36
+ // ============================================================================
37
+ //
38
+ // 1. REQUEST-RESPONSE (Call)
39
+ // FrontendBackend:
40
+ // const user = await connect.user.fetch(123);
41
+ // connect::user.handleFetch = [](const json& p) -> json { ... };
42
+ // Backend Frontend:
43
+ // auto result = connect.call("ui.handlePrompt", {{"msg", "Hi"}});
44
+ // connect.ui.handlePrompt = async (data) => { ... return result; };
45
+ //
46
+ // 2. SIMPLEX (Fire & Forget) — One-way, no response expected
47
+ // Frontend → Backend:
48
+ // connect.files.upload({ file: myFile })
49
+ // connect::files.onUpload = [](const json& p) { ... };
50
+ // Backend → Frontend:
51
+ // connect::app.notify({{"msg", "Update complete!"}});
52
+ // connect.app.onNotify((msg) => toast.success(msg));
53
+ //
54
+ // 3. PUBLISH-SUBSCRIBE (One-to-Many) — Broadcast to multiple listeners
55
+ // Backend Frontend:
56
+ // connect::sensors.publishTemperature({{72.5}});
57
+ // connect.sensors.onTemperature(cb1); connect.sensors.onTemperature(cb2);
58
+ // Frontend → Backend:
59
+ // connect.auth.login({ user: "john" });
60
+ // connect::auth.onLogin = [](const json& p) { ... };
61
+ //
62
+ // 4. FULL-DUPLEX (Bidirectional Stream) — Continuous both ways
63
+ // Frontend sends chunks, backend sends progress
64
+ // connect.files.onChunk(chunk); connect.files.onProgress(cb);
65
+ // connect::files.onChunk = [](json) { ... }; connect::files.progress({{...}});
66
+ //
67
+ // 5. HALF-DUPLEX (State Sync) — Both sides can update, one at a time
68
+ // connect.settings.setTheme("dark"); connect.settings.onThemeChange(cb);
69
+ // connect::settings.onSetTheme = [](json) { ... }; connect::settings.themeChange({{...}});
70
+ //
71
+ // ============================================================================
62
72
 
63
73
  namespace plusui {
64
74
 
@@ -107,7 +117,7 @@ public:
107
117
  };
108
118
 
109
119
  // ============================================================
110
- // Channel — single-name channel object
120
+ // Channel — single-name channel object (legacy / backward compat)
111
121
  //
112
122
  // Mirror of the TypeScript createChannel() object:
113
123
  // download.on([](const json& p) { ... });
@@ -178,10 +188,6 @@ public:
178
188
  Feature feature(const std::string &scope) { return Feature(this, scope); }
179
189
 
180
190
  // Create a named channel object — mirrors TypeScript createChannel('name')
181
- // Returns by value; store as a member or local:
182
- // auto download = connect.channel("download");
183
- // download.on([](const json& p) { ... });
184
- // download.emit({{"progress", 50}});
185
191
  Channel channel(const std::string &name) { return Channel(this, name); }
186
192
 
187
193
  // Request/response helper for call primitive
@@ -205,12 +211,11 @@ public:
205
211
 
206
212
  /**
207
213
  * emit() - Send a message to the frontend
208
- *
209
- * Works for all 5 primitives:
210
- * - EVENT: emit("notification", {{"msg", "Hi!"}})
211
- * - CALL: emit("response", {{"data", 123}})
212
- * - STREAM: emit("updates", {{"value", x}}) // call repeatedly
213
- * - QUERY: emit("result", item) // call for each result
214
+ *
215
+ * Works for all 5 communication patterns:
216
+ * - SIMPLEX: emit("notification", {{"msg", "Hi!"}})
217
+ * - CALL RESPONSE: emitResult(id, name, payload)
218
+ * - STREAM/PUB-SUB: emit("updates", {{"value", x}}) // call repeatedly
214
219
  * - STATE: emit("theme", {{"mode", "dark"}})
215
220
  */
216
221
  void emit(const std::string &name, const nlohmann::json &payload) {
@@ -227,6 +232,32 @@ public:
227
232
  emitEnvelope("error", name, nlohmann::json::object(), id, message);
228
233
  }
229
234
 
235
+ /**
236
+ * call() - Make a call to the frontend and wait for response
237
+ *
238
+ * Enables bidirectional request/response:
239
+ * Backend: auto answer = connect.call("ui.promptUser", {{"msg", "Sure?"}});
240
+ * Frontend: connect.ui.handlePromptUser = async (data) => { ... return result; };
241
+ */
242
+ void call(const std::string &name, const nlohmann::json &payload,
243
+ std::function<void(const nlohmann::json &)> onResult = nullptr,
244
+ std::function<void(const std::string &)> onError = nullptr) {
245
+ auto id = std::to_string(std::chrono::steady_clock::now().time_since_epoch().count());
246
+
247
+ if (onResult || onError) {
248
+ // Store pending callback for when frontend responds
249
+ on(name + ".__result__." + id, [this, id, name, onResult, onError](const nlohmann::json &p) {
250
+ if (p.contains("error") && onError) {
251
+ onError(p["error"].get<std::string>());
252
+ } else if (onResult) {
253
+ onResult(p.value("payload", nlohmann::json::object()));
254
+ }
255
+ });
256
+ }
257
+
258
+ emitEnvelope("call", name, payload, id);
259
+ }
260
+
230
261
  // Parse and dispatch incoming messages from frontend
231
262
  void dispatchMessage(const std::string &message) {
232
263
  try {
@@ -276,6 +307,25 @@ public:
276
307
  return;
277
308
  }
278
309
 
310
+ if (kind == "result" || kind == "error") {
311
+ // Response from a frontend call — dispatch to pending listeners
312
+ std::string resultKey = name + ".__result__." + id;
313
+ auto listenersIt = eventHandlers.find(resultKey);
314
+ if (listenersIt != eventHandlers.end()) {
315
+ nlohmann::json resultPayload;
316
+ if (kind == "error") {
317
+ resultPayload["error"] = envelope.value("error", "Unknown error");
318
+ } else {
319
+ resultPayload["payload"] = payload;
320
+ }
321
+ for (const auto &listener : listenersIt->second) {
322
+ listener(resultPayload);
323
+ }
324
+ eventHandlers.erase(resultKey);
325
+ }
326
+ return;
327
+ }
328
+
279
329
  if (kind == "sub") {
280
330
  auto subIt = subscriptionHandlers.find(name);
281
331
  if (subIt != subscriptionHandlers.end()) {
@@ -325,23 +375,17 @@ protected:
325
375
 
326
376
  /**
327
377
  * handleMessage() - Receive messages from the frontend
328
- *
378
+ *
329
379
  * Override this to handle incoming messages. This is your "on()" equivalent.
330
- *
331
- * Works for all 5 primitives:
332
- * - EVENT: Just receive and act
333
- * - CALL: Receive request, emit() response
334
- * - STREAM: Receive subscription, emit() repeatedly
335
- * - QUERY: Receive query, emit() multiple results
336
- * - STATE: Receive update, optionally emit() back
337
- *
338
- * Example:
339
- * void handleMessage(const std::string& name, const nlohmann::json& data) override {
340
- * if (name == "greet") {
341
- * auto userName = data["name"].get<std::string>();
342
- * emit("greeting", {{"message", "Hello " + userName}});
343
- * }
344
- * }
380
+ *
381
+ * For the new semantic API, prefer using the generated connect:: namespace
382
+ * instead of overriding this method:
383
+ *
384
+ * connect::user.handleFetch = [](const json& p) -> json {
385
+ * return database.getUser(p["id"]);
386
+ * };
387
+ *
388
+ * This method remains for backward compatibility and catch-all handling.
345
389
  */
346
390
  virtual void handleMessage(const std::string &name,
347
391
  const nlohmann::json &payload) {
@@ -352,7 +396,7 @@ protected:
352
396
 
353
397
  void bindConnect(Window &window, Connect &connect);
354
398
 
355
- // Legacy alias for backwards compatibility
399
+ // Legacy aliases for backwards compatibility
356
400
  using Bridge = Connect;
357
401
  using Connection = Connect;
358
402
 
@@ -11,46 +11,44 @@ namespace plusui {
11
11
 
12
12
  class TrayManager;
13
13
 
14
- struct WindowConfig {
15
- // Window properties
16
- std::string title = "PlusUI Window";
17
- int x = -1;
18
- int y = -1;
19
- int width = 800;
20
- int height = 600;
21
- int minWidth = 100;
22
- int minHeight = 100;
23
- int maxWidth = -1;
24
- int maxHeight = -1;
25
- bool resizable = true;
26
- bool minimizable = true;
27
- bool maximizable = true;
28
- bool closable = true;
29
- bool alwaysOnTop = false;
30
- bool center = true;
31
- bool frame = true;
32
- bool transparent = false;
33
- bool decorations = true;
34
- bool skipTaskbar = false;
35
- double opacity = 1.0;
36
- bool fullscreen = false;
37
- bool enableFileDrop =
38
- true; // Enable native FileDrop API (auto-disables webview drag-drop)
39
-
40
- // WebView properties
41
- std::string userAgent = "";
42
- bool devtools = true;
43
- bool contextMenu = true;
44
- bool javascript = true;
45
- bool webSecurity = true;
46
- bool allowFileAccess = false;
47
- bool allowRemoteContent = true;
48
- std::string dataPath = "";
49
- int cacheSize = 100; // MB
50
- bool scrollbars = true; // Show/hide scrollbars
51
- bool disableWebviewDragDrop =
52
- true; // Disable webview drag-drop (usually set by enableFileDrop)
53
- };
14
+ struct WindowConfig {
15
+ // Window properties
16
+ std::string title = "PlusUI Window";
17
+ int x = -1;
18
+ int y = -1;
19
+ int width = 800;
20
+ int height = 600;
21
+ int minWidth = 100;
22
+ int minHeight = 100;
23
+ int maxWidth = -1;
24
+ int maxHeight = -1;
25
+ bool resizable = true;
26
+ bool minimizable = true;
27
+ bool maximizable = true;
28
+ bool closable = true;
29
+ bool alwaysOnTop = false;
30
+ bool center = true;
31
+ bool frame = true;
32
+ bool transparent = false;
33
+ bool decorations = true;
34
+ bool skipTaskbar = false;
35
+ double opacity = 1.0;
36
+ bool fullscreen = false;
37
+ bool fileDrop = true; // Enable OS file drop (drag files from Explorer/Finder into window)
38
+ bool disableWebviewDragDrop = true; // Disable browser HTML drag-drop (auto-set by fileDrop)
39
+
40
+ // WebView properties
41
+ std::string userAgent = "";
42
+ bool devtools = true;
43
+ bool contextMenu = true;
44
+ bool javascript = true;
45
+ bool webSecurity = true;
46
+ bool allowFileAccess = false;
47
+ bool allowRemoteContent = true;
48
+ std::string dataPath = "";
49
+ int cacheSize = 100; // MB
50
+ bool scrollbars = true; // Show/hide scrollbars
51
+ };
54
52
 
55
53
  struct WindowState {
56
54
  int x = 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "plusui-native-core",
3
- "version": "0.1.102",
3
+ "version": "0.1.104",
4
4
  "description": "PlusUI Core framework (frontend + backend implementations)",
5
5
  "type": "module",
6
6
  "main": "./Core/Features/API/index.ts",