tauri-remote-ui 0.27.0 → 1.0.1-alpha.8

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/LICENSE CHANGED
@@ -1,21 +1,39 @@
1
- MIT License
2
-
3
- Copyright (c) 2025 - DraviaVemal
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ # License Information
2
+
3
+ This project is licensed under **AGPL-3.0** starting from version 1.x.
4
+
5
+ ---
6
+
7
+ ## 📜 Open Source (AGPL-3.0)
8
+
9
+ - You are free to use, study, and modify this project under the terms of the **GNU Affero General Public License v3.0 (AGPL-3.0)**.
10
+ - **Important:** If you build on top of this project (derivative works, forks, integrations, etc.) you must also release your work under **AGPL-3.0**.
11
+ - This ensures improvements and extensions remain open to the community.
12
+
13
+ ---
14
+
15
+ ## 💼 Commercial Use (Sponsorware Model)
16
+
17
+ - Commercial use **is not allowed** under AGPL-3.0 without a separate license.
18
+ - To use this project in **commercial products, SaaS, internal tools, or client projects**, your organization must:
19
+ 1. **Sponsor this project** at the required tier on [GitHub Sponsors](https://github.com/sponsors/DraviaVemal).
20
+ 2. Sponsored organizations are granted access to a **private repository**, licensed under a **commercial-friendly license (MIT terms)**.
21
+ 3. This private repo contains the same core code under commercial terms, plus optional add-ons.
22
+
23
+ ---
24
+
25
+ ## 🚀 Sponsor Benefits
26
+
27
+ - Access to a **commercial license** for this project (MIT terms).
28
+ - Access to the **private repo** with code updates.
29
+ - **Priority support** — issues and feature requests in the private repo are handled **before public requests**.
30
+
31
+ ---
32
+
33
+ ## ✅ How to Comply
34
+
35
+ - **Individuals, students, hobbyists** → Use under **AGPL-3.0** (non-commercial OK).
36
+ - **Businesses, organizations, contractors** → Must **sponsor** to obtain commercial rights.
37
+
38
+ For details on AGPL-3.0, see [LICENSE](https://opensource.org/license/agpl-v3).
39
+ For commercial licensing inquiries, contact: **contact@draviavemal.com**.
package/README.md CHANGED
@@ -1,109 +1,48 @@
1
- # Tauri Remote UI
1
+ # tauri-remote-ui (AGPL-3.0)
2
2
 
3
- **Tauri Remote UI** is a plugin that allows you to expose your Tauri application's UI to any web browser, enabling seamless remote interaction for development and end-to-end testing. The plugin bridges your native app and commercial browsers, letting you use standard web automation tools for testing and debugging—without modifying your app's logic.
3
+ **Tauri Remote UI** is a plugin that allows you to expose your Tauri application's UI to any web browser.
4
4
 
5
5
  ## Badges
6
- ![Crates.io Version](https://img.shields.io/crates/v/tauri-remote-ui?style=flat&label=crates.io%20%3A%20tauri-remote-ui) ![NPM Version](https://img.shields.io/npm/v/tauri-remote-ui?label=npm%20%3A%20tauri-remote-ui)
7
-
8
6
 
9
- ## Features
7
+ ![Crates.io Version](https://img.shields.io/crates/v/tauri-remote-ui?style=flat&label=crates.io%20%3A%20tauri-remote-ui) ![NPM Version](https://img.shields.io/npm/v/tauri-remote-ui?label=npm%20%3A%20tauri-remote-ui)
10
8
 
11
- - **Remote UI Exposure:** Interact with your Tauri app from any browser.
12
- - **Seamless Development:** Enable Development debug attachment for fronend debugging.
13
- - **Seamless E2E Testing:** Use existing web automation/testing tools.
14
- - **Automatic Transport Switching:** IPC for WebView, WebSocket for browsers—handled transparently.
15
- - **Customizable Security:** Control and secure remote access as needed.
16
- - **Future Compatibility For Test Migration:** When [CEF-RS](https://github.com/cef-rs/cef) becomes available, the same E2E tests (e.g., written with Playwright or similar tools that use the Chromium debug port) will work seamlessly in debug mode, ensuring long-term support for modern testing workflows.
9
+ ## 📜 License
17
10
 
18
- ## Completed Features
11
+ - Open Source: AGPL-3.0 (see LICENSE)
12
+ - Commercial: Available via sponsorship (see LICENSE)
19
13
 
20
- ### Javascript
21
- - **api/core** - `invoke`
22
- - **api/event** - `listen`
23
- - **api/app**
24
- - `defaultWindowIcon`,`fetchDataStoreIdentifiers`,`getBundleType`,
25
- - `getIdentifier`,`getName`,`getTauriVersion`,
26
- - `getVersion`,`hide`,`removeDataStore`,
27
- - `setDockVisibility`,`setTheme`,`show`
14
+ ## ✨ Features
28
15
 
29
- ### Rust
30
- - `emit` - Emit method is updated to handle in this plugin.
16
+ - Seamless enable/diable integration
17
+ - Network level access control setting
18
+ - Network latency tracking
31
19
 
32
- ## Operation Flow
20
+ ## Supports
33
21
 
34
- - **WebView:** Uses IPC for communication between frontend and backend.
35
- - **Commercial Browser:** Uses WebSocket (WS) for remote frontend-backend communication.
36
- - **Automatic Switching:** The Rust backend plugin and npm frontend wrapper handle transport selection automatically.
37
- - **Security:** The exposure of the web app can be secured and customized by the end user.
22
+ |Environment|Support|
23
+ |-|-|
24
+ |Windows|✅|
25
+ |Mac|✅|
26
+ |Linux|✅|
27
+ |Android|❌|
28
+ |iOS|❌|
38
29
 
39
- ## Usage
30
+ ## Use Case
40
31
 
41
- 1. **Install the Rust plugin** in your Tauri project `cargo add tauri-remote-ui`.
42
- 2. **Initialize the Rust plugin**
43
- ```rust
44
- pub fn run() {
45
- tauri::Builder::default()
46
- .plugin(tauri_remote_ui::init())
47
- .invoke_handler(tauri::generate_handler![
48
- increment,
49
- decrement,
50
- enable_server,
51
- disable_server,
52
- exit_app,
53
- ])
54
- .setup(|app| {
55
- app.manage(Arc::new(RwLock::new(Counter { now: 0 })));
56
- Ok(())
57
- })
58
- .run(tauri::generate_context!())
59
- .expect("error while running tauri application");
60
- }
61
- ```
62
- 3. **Replace Emitter trait**
63
- ```rust
64
- use tauri::Emitter;
65
- webview_window.emit(data)
66
- ```
67
- To
68
- ```rust
69
- use tauri_remote_ui::EmitterExt;
70
- webview_window.emit(data).await
71
- ```
72
- 4. **Start/Stop Server**
73
- ```rust
74
- async fn enable_server(app: AppHandle) -> String {
75
- match app.start_remote_ui(RemoteUiConfig::default().set_port(Some(9090))).await {
76
- Ok(()) => format!("Server Started."),
77
- Err(err) => format!("Server Error {:?}", err),
78
- }
79
- }
80
- async fn disable_server(app: AppHandle) -> String {
81
- match app.stop_remote_ui().await {
82
- Ok(()) => format!("Server Stoped"),
83
- Err(err) => format!("Server Error {:?}", err),
84
- }
85
- }
86
- ```
87
- 5. **Install the NPM plugin** in your frontend `npm i tauri-remote-ui`.
88
- 6. **Replace the NPM package**
89
- ```typescript
90
- import { invoke } from "@tauri-apps/api/core";
91
- import { listen } from "@tauri-apps/api/event";
92
- ```
93
- To
94
- ```typescript
95
- import { invoke } from "tauri-remote-ui/api/core";
96
- import { listen } from "tauri-remote-ui/api/event";
97
- ```
98
- 7. **Development WebSocket Proxy** `/remote_ui_ws` proxy remote_ui url ws to your dev server like vite.
99
- 8. **Enable Source Map and update lauch.json setup in vscode to debug frontend**
32
+ - Enabling seamless remote interaction for development debugging—without modifying your app's logic.
33
+ - Enabling end-to-end testing of tauri application using standard web automation tools like playwright.
34
+ - Note : Based on tauri target OS the webkit will change windows will be 100% match test case as both are chromium rest of OS around maximum 10% UI difference are expected from actual application
35
+ - Enable remote access feature for local close ciruit hardware related application
100
36
 
101
- ## Plugin Development
37
+ ## Planned Features
102
38
 
103
- - Build Rust: `cargo build`
104
- - Build JS: `cd guest-js && pnpm build`
105
- - Example app: See `examples/tauri-app/`
39
+ - Multiple Window of Tauri app support in Remote UI logic
40
+ - SSO Ingration Option
41
+ - Custom Starting window name options
42
+ - Dynamic Port mapping
43
+ - SSL Certificate ingration
44
+ - Authendication system for remote access (User_id,Password)
106
45
 
107
- ## License
46
+ ## [Documents](https://docs.draviavemal.com)
108
47
 
109
- MIT
48
+ Refer document central for detailed information [docs](https://docs.draviavemal.com)
@@ -27,30 +27,20 @@ async function listen(event$1, handler, options) {
27
27
  if (socket.wsReady) {
28
28
  await socket.wsReady;
29
29
  }
30
- if (socket.ws && socket.ws.readyState === WebSocket.OPEN) {
31
- // Handle WebSocket messages for events
32
- const messageHandler = (wsEvent) => {
33
- try {
34
- const data = JSON.parse(wsEvent.data);
35
- if (data.event === event$1) {
36
- handler(data);
37
- }
38
- }
39
- catch (err) {
40
- console.error(err);
41
- throw new Error('Error handling WebSocket event');
42
- }
43
- };
44
- socket.ws.addEventListener('message', messageHandler);
45
- // Return an unlisten function
46
- return () => {
47
- socket.ws === null || socket.ws === void 0 ? void 0 : socket.ws.removeEventListener('message', messageHandler);
48
- };
49
- }
50
- else {
51
- throw new Error("No WebSocket or Tauri IPC available to invoke");
52
- }
30
+ // Handle WebSocket messages for events
31
+ const messageHandler = ({ data }) => {
32
+ handler(data);
33
+ };
34
+ socket.listenEvent.addEventListener(event$1, messageHandler);
35
+ // Return an unlisten function
36
+ return () => {
37
+ socket.listenEvent.removeEventListener(event$1, messageHandler);
38
+ };
53
39
  }
54
40
  }
55
41
 
42
+ Object.defineProperty(exports, "latencyMs", {
43
+ enumerable: true,
44
+ get: function () { return socket.latencyMs; }
45
+ });
56
46
  exports.listen = listen;
@@ -4,6 +4,8 @@
4
4
  * This module handles listening to events from the Tauri application via WebSocket
5
5
  */
6
6
  import { EventCallback, EventName, Options, UnlistenFn } from '@tauri-apps/api/event';
7
+ export type { UnlistenFn } from '@tauri-apps/api/event';
8
+ export { latencyMs } from "../../socket";
7
9
  /**
8
10
  * Listen to events from the Tauri application
9
11
  * Falls back to WebSocket if Tauri Event API is not available
@@ -1,5 +1,6 @@
1
1
  import { listen as listen$1 } from '@tauri-apps/api/event';
2
- import { initWebSocket, wsReady, ws } from '../../socket.js';
2
+ import { initWebSocket, wsReady, listenEvent } from '../../socket.js';
3
+ export { latencyMs } from '../../socket.js';
3
4
 
4
5
  /**
5
6
  * Event API module for Tauri Remote UI
@@ -25,29 +26,15 @@ async function listen(event, handler, options) {
25
26
  if (wsReady) {
26
27
  await wsReady;
27
28
  }
28
- if (ws && ws.readyState === WebSocket.OPEN) {
29
- // Handle WebSocket messages for events
30
- const messageHandler = (wsEvent) => {
31
- try {
32
- const data = JSON.parse(wsEvent.data);
33
- if (data.event === event) {
34
- handler(data);
35
- }
36
- }
37
- catch (err) {
38
- console.error(err);
39
- throw new Error('Error handling WebSocket event');
40
- }
41
- };
42
- ws.addEventListener('message', messageHandler);
43
- // Return an unlisten function
44
- return () => {
45
- ws === null || ws === void 0 ? void 0 : ws.removeEventListener('message', messageHandler);
46
- };
47
- }
48
- else {
49
- throw new Error("No WebSocket or Tauri IPC available to invoke");
50
- }
29
+ // Handle WebSocket messages for events
30
+ const messageHandler = ({ data }) => {
31
+ handler(data);
32
+ };
33
+ listenEvent.addEventListener(event, messageHandler);
34
+ // Return an unlisten function
35
+ return () => {
36
+ listenEvent.removeEventListener(event, messageHandler);
37
+ };
51
38
  }
52
39
  }
53
40
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "tauri-remote-ui",
3
- "license": "MIT",
4
- "version": "0.27.0",
3
+ "license": "AGPL-3.0-only",
4
+ "version": "1.0.1-alpha.8",
5
5
  "author": "DraviaVemal",
6
6
  "description": "A Tauri plugin that exposes the application's UI to a web browser, allowing full interaction while the native app continues running. This enables end-to-end UI testing using existing web-based testing tools without requiring modifications to the app itself.",
7
7
  "type": "module",
@@ -9,8 +9,9 @@
9
9
  "main": "./index.cjs",
10
10
  "module": "./index.js",
11
11
  "repository": {
12
- "url": "https://github.com/DraviaVemal/tauri-remote-ui"
12
+ "url": "git+https://github.com/DraviaVemal/tauri-remote-ui.git"
13
13
  },
14
+ "readme": "./README.md",
14
15
  "exports": {
15
16
  ".": {
16
17
  "types": "./index.d.ts",
@@ -49,4 +50,4 @@
49
50
  "peerDependencies": {
50
51
  "@tauri-apps/api": ">=2.0.0"
51
52
  }
52
- }
53
+ }
package/socket.cjs CHANGED
@@ -8,8 +8,10 @@
8
8
  * with a Tauri application.
9
9
  */
10
10
  exports.ws = null;
11
+ let listenEvent = new EventTarget();
11
12
  exports.wsReady = null;
12
13
  const filterCollection = {};
14
+ exports.latencyMs = 0;
13
15
  /**
14
16
  * Get the WebSocket URL based on the current window location
15
17
  */
@@ -42,37 +44,57 @@ function initWebSocket() {
42
44
  catch {
43
45
  if (exports.ws)
44
46
  return;
45
- console.info("Remote RPC Attempting...");
47
+ console.info("Tauri-Remote-UI : Remote RPC Attempting...");
46
48
  const wsUrl = getWsUrl();
47
49
  try {
50
+ let lastPingTimestamp = Date.now();
51
+ let pingPongTimer;
48
52
  exports.ws = new WebSocket(wsUrl);
49
53
  exports.wsReady = new Promise((resolve, reject) => {
50
54
  exports.ws.onopen = () => {
51
- console.info("Remote Connected.");
55
+ console.info("Tauri-Remote-UI : Remote Connected.");
56
+ lastPingTimestamp = Date.now();
57
+ exports.ws === null || exports.ws === void 0 ? void 0 : exports.ws.send("ping");
58
+ pingPongTimer = setInterval(() => {
59
+ lastPingTimestamp = Date.now();
60
+ exports.ws === null || exports.ws === void 0 ? void 0 : exports.ws.send("ping");
61
+ }, 10000);
52
62
  resolve();
53
63
  };
64
+ exports.ws.onmessage = ({ data }) => {
65
+ if (data === "pong") {
66
+ exports.latencyMs = Date.now() - lastPingTimestamp;
67
+ if (exports.latencyMs > 200) {
68
+ console.warn(`Tauri-Remote-UI : High latency detected - ${exports.latencyMs}ms`);
69
+ }
70
+ return;
71
+ }
72
+ let jsonData = JSON.parse(data);
73
+ if (jsonData.id && filterCollection[jsonData.id]) {
74
+ filterCollection[jsonData.id](JSON.parse(jsonData.payload));
75
+ }
76
+ else {
77
+ listenEvent.dispatchEvent(new MessageEvent(jsonData.event, { data: jsonData }));
78
+ }
79
+ };
54
80
  exports.ws.onclose = () => {
55
81
  exports.ws = null;
56
82
  exports.wsReady = null;
57
- console.info("Remote Dis-Connected.");
58
- window.location.replace(getUrl());
83
+ pingPongTimer && clearInterval(pingPongTimer);
84
+ console.info("Tauri-Remote-UI : Remote DisConnected.");
85
+ window.location.href = getUrl();
59
86
  };
60
87
  exports.ws.onerror = (e) => {
61
88
  reject(e);
62
89
  };
63
- exports.ws.onmessage = ({ data }) => {
64
- let json_data = JSON.parse(data);
65
- json_data.id && filterCollection[json_data.id] && filterCollection[json_data.id](JSON.parse(json_data.payload));
66
- };
67
90
  });
68
91
  }
69
92
  catch (e) {
70
- setTimeout(() => {
71
- initWebSocket();
72
- }, 5000);
93
+ console.error(e);
73
94
  }
74
95
  }
75
96
  }
76
97
 
77
98
  exports.filterCollection = filterCollection;
78
99
  exports.initWebSocket = initWebSocket;
100
+ exports.listenEvent = listenEvent;
package/socket.d.ts CHANGED
@@ -6,10 +6,12 @@
6
6
  * with a Tauri application.
7
7
  */
8
8
  export declare let ws: WebSocket | null;
9
+ export declare let listenEvent: EventTarget;
9
10
  export declare let wsReady: Promise<void> | null;
10
11
  export declare const filterCollection: {
11
12
  [msg_id: string]: (response: any) => any;
12
13
  };
14
+ export declare let latencyMs: number;
13
15
  /**
14
16
  * Initialize the WebSocket connection
15
17
  * This should be called once at the start of your application
package/socket.js CHANGED
@@ -6,8 +6,10 @@
6
6
  * with a Tauri application.
7
7
  */
8
8
  let ws = null;
9
+ let listenEvent = new EventTarget();
9
10
  let wsReady = null;
10
11
  const filterCollection = {};
12
+ let latencyMs = 0;
11
13
  /**
12
14
  * Get the WebSocket URL based on the current window location
13
15
  */
@@ -40,36 +42,55 @@ function initWebSocket() {
40
42
  catch {
41
43
  if (ws)
42
44
  return;
43
- console.info("Remote RPC Attempting...");
45
+ console.info("Tauri-Remote-UI : Remote RPC Attempting...");
44
46
  const wsUrl = getWsUrl();
45
47
  try {
48
+ let lastPingTimestamp = Date.now();
49
+ let pingPongTimer;
46
50
  ws = new WebSocket(wsUrl);
47
51
  wsReady = new Promise((resolve, reject) => {
48
52
  ws.onopen = () => {
49
- console.info("Remote Connected.");
53
+ console.info("Tauri-Remote-UI : Remote Connected.");
54
+ lastPingTimestamp = Date.now();
55
+ ws === null || ws === void 0 ? void 0 : ws.send("ping");
56
+ pingPongTimer = setInterval(() => {
57
+ lastPingTimestamp = Date.now();
58
+ ws === null || ws === void 0 ? void 0 : ws.send("ping");
59
+ }, 10000);
50
60
  resolve();
51
61
  };
62
+ ws.onmessage = ({ data }) => {
63
+ if (data === "pong") {
64
+ latencyMs = Date.now() - lastPingTimestamp;
65
+ if (latencyMs > 200) {
66
+ console.warn(`Tauri-Remote-UI : High latency detected - ${latencyMs}ms`);
67
+ }
68
+ return;
69
+ }
70
+ let jsonData = JSON.parse(data);
71
+ if (jsonData.id && filterCollection[jsonData.id]) {
72
+ filterCollection[jsonData.id](JSON.parse(jsonData.payload));
73
+ }
74
+ else {
75
+ listenEvent.dispatchEvent(new MessageEvent(jsonData.event, { data: jsonData }));
76
+ }
77
+ };
52
78
  ws.onclose = () => {
53
79
  ws = null;
54
80
  wsReady = null;
55
- console.info("Remote Dis-Connected.");
56
- window.location.replace(getUrl());
81
+ pingPongTimer && clearInterval(pingPongTimer);
82
+ console.info("Tauri-Remote-UI : Remote DisConnected.");
83
+ window.location.href = getUrl();
57
84
  };
58
85
  ws.onerror = (e) => {
59
86
  reject(e);
60
87
  };
61
- ws.onmessage = ({ data }) => {
62
- let json_data = JSON.parse(data);
63
- json_data.id && filterCollection[json_data.id] && filterCollection[json_data.id](JSON.parse(json_data.payload));
64
- };
65
88
  });
66
89
  }
67
90
  catch (e) {
68
- setTimeout(() => {
69
- initWebSocket();
70
- }, 5000);
91
+ console.error(e);
71
92
  }
72
93
  }
73
94
  }
74
95
 
75
- export { filterCollection, initWebSocket, ws, wsReady };
96
+ export { filterCollection, initWebSocket, latencyMs, listenEvent, ws, wsReady };