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.
@@ -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: connect.feature('browser'),
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: connect.feature('router'),
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 `feature.connect` when you want automatic namespacing:
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: connect.feature('window'),
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);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "plusui-native-core",
3
- "version": "0.1.57",
3
+ "version": "0.1.59",
4
4
  "description": "PlusUI Core framework (frontend + backend implementations)",
5
5
  "type": "module",
6
6
  "files": [