@pipelab/asset-tauri 1.0.0-beta.0
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 +110 -0
- package/package.json +18 -0
- package/template/README.md +7 -0
- package/template/index.html +45 -0
- package/template/package.json +23 -0
- package/template/pnpm-lock.yaml +715 -0
- package/template/src-tauri/Cargo.lock +11191 -0
- package/template/src-tauri/Cargo.toml +40 -0
- package/template/src-tauri/build.rs +3 -0
- package/template/src-tauri/capabilities/default.json +7 -0
- package/template/src-tauri/icons/128x128.png +0 -0
- package/template/src-tauri/icons/128x128@2x.png +0 -0
- package/template/src-tauri/icons/32x32.png +0 -0
- package/template/src-tauri/icons/Square107x107Logo.png +0 -0
- package/template/src-tauri/icons/Square142x142Logo.png +0 -0
- package/template/src-tauri/icons/Square150x150Logo.png +0 -0
- package/template/src-tauri/icons/Square284x284Logo.png +0 -0
- package/template/src-tauri/icons/Square30x30Logo.png +0 -0
- package/template/src-tauri/icons/Square310x310Logo.png +0 -0
- package/template/src-tauri/icons/Square44x44Logo.png +0 -0
- package/template/src-tauri/icons/Square71x71Logo.png +0 -0
- package/template/src-tauri/icons/Square89x89Logo.png +0 -0
- package/template/src-tauri/icons/StoreLogo.png +0 -0
- package/template/src-tauri/icons/icon.icns +0 -0
- package/template/src-tauri/icons/icon.ico +0 -0
- package/template/src-tauri/icons/icon.png +0 -0
- package/template/src-tauri/src/lib copy 2.rs +438 -0
- package/template/src-tauri/src/lib copy.rs +449 -0
- package/template/src-tauri/src/lib.rs +616 -0
- package/template/src-tauri/src/lib_webview_screenshot.rs +430 -0
- package/template/src-tauri/src/main.rs +6 -0
- package/template/src-tauri/tauri.conf.json +36 -0
- package/template/tsconfig.json +23 -0
|
@@ -0,0 +1,616 @@
|
|
|
1
|
+
use futures_util::{
|
|
2
|
+
stream::{SplitSink, SplitStream},
|
|
3
|
+
SinkExt, StreamExt,
|
|
4
|
+
};
|
|
5
|
+
use serde::{Deserialize, Serialize};
|
|
6
|
+
use serde_json::Value; // Using Value for flexibility in body initially
|
|
7
|
+
use std::{net::SocketAddr, sync::Arc};
|
|
8
|
+
use tauri::{
|
|
9
|
+
async_runtime, webview::WebviewWindowBuilder, AppHandle, Manager, Runtime, WebviewUrl,
|
|
10
|
+
};
|
|
11
|
+
use tokio::{
|
|
12
|
+
net::{TcpListener, TcpStream},
|
|
13
|
+
sync::Mutex, // Using Mutex for the writer part
|
|
14
|
+
};
|
|
15
|
+
use tokio_tungstenite::{accept_async, tungstenite::Message, WebSocketStream};
|
|
16
|
+
// Note: Removed `use anyhow::{Error};` as it's unused when using anyhow::Result<()>
|
|
17
|
+
|
|
18
|
+
// --- Message Structures ---
|
|
19
|
+
|
|
20
|
+
/// Generic structure for incoming WebSocket messages
|
|
21
|
+
#[derive(Deserialize, Debug)]
|
|
22
|
+
struct IncomingMessage {
|
|
23
|
+
url: String,
|
|
24
|
+
#[serde(rename = "correlationId")] // Match JS naming
|
|
25
|
+
correlation_id: Option<String>, // Optional correlation ID
|
|
26
|
+
body: Option<Value>, // Use Option<Value> to handle cases where body might be missing
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/// Generic structure for outgoing WebSocket responses
|
|
30
|
+
#[derive(Serialize, Debug)]
|
|
31
|
+
struct ResponseMessage<T: Serialize> {
|
|
32
|
+
url: String,
|
|
33
|
+
#[serde(rename = "correlationId")]
|
|
34
|
+
correlation_id: Option<String>, // Echo back the correlation ID
|
|
35
|
+
body: T, // Generic body for success or error
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/// Example structure for a success response body
|
|
39
|
+
#[derive(Serialize, Debug)]
|
|
40
|
+
struct SuccessBody<T: Serialize> {
|
|
41
|
+
success: bool,
|
|
42
|
+
#[serde(skip_serializing_if = "Option::is_none")] // Don't serialize data if it's None
|
|
43
|
+
data: Option<T>, // Make data optional for responses that don't need it
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/// Example structure for an error response body
|
|
47
|
+
#[derive(Serialize, Debug)]
|
|
48
|
+
struct ErrorBody {
|
|
49
|
+
success: bool,
|
|
50
|
+
error: String,
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// --- WebSocket Handling ---
|
|
54
|
+
|
|
55
|
+
/// Handles an individual WebSocket connection
|
|
56
|
+
async fn handle_websocket<R: Runtime>(
|
|
57
|
+
stream: TcpStream,
|
|
58
|
+
app_handle: AppHandle<R>, // Pass AppHandle for Tauri interaction
|
|
59
|
+
) {
|
|
60
|
+
let addr = stream
|
|
61
|
+
.peer_addr()
|
|
62
|
+
.expect("Connected stream should have peer address");
|
|
63
|
+
println!("New WebSocket connection from: {}", addr);
|
|
64
|
+
|
|
65
|
+
match accept_async(stream).await {
|
|
66
|
+
Ok(ws_stream) => {
|
|
67
|
+
println!("WebSocket connection established: {}", addr);
|
|
68
|
+
let (write, read) = ws_stream.split();
|
|
69
|
+
let writer = Arc::new(Mutex::new(write));
|
|
70
|
+
process_messages(read, writer.clone(), app_handle, addr).await;
|
|
71
|
+
println!("WebSocket connection closed: {}", addr);
|
|
72
|
+
}
|
|
73
|
+
Err(e) => {
|
|
74
|
+
eprintln!("Error during WebSocket handshake for {}: {}", addr, e);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/// Processes messages received from a single client
|
|
80
|
+
async fn process_messages<R: Runtime>(
|
|
81
|
+
mut read: SplitStream<WebSocketStream<TcpStream>>,
|
|
82
|
+
writer: Arc<Mutex<SplitSink<WebSocketStream<TcpStream>, Message>>>,
|
|
83
|
+
app_handle: AppHandle<R>,
|
|
84
|
+
addr: SocketAddr,
|
|
85
|
+
) {
|
|
86
|
+
while let Some(message_result) = read.next().await {
|
|
87
|
+
match message_result {
|
|
88
|
+
Ok(msg) => {
|
|
89
|
+
match msg {
|
|
90
|
+
Message::Text(text) => {
|
|
91
|
+
println!("Received text from {}: {}", addr, text);
|
|
92
|
+
match serde_json::from_str::<IncomingMessage>(&text) {
|
|
93
|
+
Ok(parsed_message) => {
|
|
94
|
+
let writer_clone = writer.clone();
|
|
95
|
+
let app_handle_clone = app_handle.clone();
|
|
96
|
+
let url = parsed_message.url.clone();
|
|
97
|
+
let correlation_id = parsed_message.correlation_id.clone();
|
|
98
|
+
|
|
99
|
+
tokio::spawn(async move {
|
|
100
|
+
let writer_for_route = writer_clone.clone();
|
|
101
|
+
// Using anyhow::Result allows easy error propagation with `?`
|
|
102
|
+
if let Err(e) = route_message(
|
|
103
|
+
parsed_message,
|
|
104
|
+
writer_for_route,
|
|
105
|
+
app_handle_clone,
|
|
106
|
+
)
|
|
107
|
+
.await
|
|
108
|
+
{
|
|
109
|
+
eprintln!(
|
|
110
|
+
"Error handling message for url '{}': {}",
|
|
111
|
+
url, e
|
|
112
|
+
);
|
|
113
|
+
let error_response = ResponseMessage {
|
|
114
|
+
url,
|
|
115
|
+
correlation_id,
|
|
116
|
+
body: ErrorBody {
|
|
117
|
+
success: false,
|
|
118
|
+
error: e.to_string(),
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
if let Ok(json_response) =
|
|
122
|
+
serde_json::to_string(&error_response)
|
|
123
|
+
{
|
|
124
|
+
let mut w = writer_clone.lock().await;
|
|
125
|
+
if let Err(send_err) =
|
|
126
|
+
w.send(Message::Text(json_response)).await
|
|
127
|
+
{
|
|
128
|
+
eprintln!(
|
|
129
|
+
"Failed to send error response: {}",
|
|
130
|
+
send_err
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
Err(e) => {
|
|
138
|
+
eprintln!(
|
|
139
|
+
"Failed to parse JSON from {}: {}. Message: {}",
|
|
140
|
+
addr, e, text
|
|
141
|
+
);
|
|
142
|
+
let response = ResponseMessage {
|
|
143
|
+
url: "unknown".to_string(),
|
|
144
|
+
correlation_id: None,
|
|
145
|
+
body: ErrorBody {
|
|
146
|
+
success: false,
|
|
147
|
+
error: format!("Invalid JSON format: {}", e),
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
if let Ok(json_response) = serde_json::to_string(&response) {
|
|
151
|
+
let mut w = writer.lock().await;
|
|
152
|
+
if let Err(send_err) =
|
|
153
|
+
w.send(Message::Text(json_response)).await
|
|
154
|
+
{
|
|
155
|
+
eprintln!(
|
|
156
|
+
"Failed to send parse error response: {}",
|
|
157
|
+
send_err
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
Message::Binary(_) => println!("Received binary data from {} (ignored)", addr),
|
|
165
|
+
Message::Ping(ping_data) => {
|
|
166
|
+
println!("Received Ping from {}", addr);
|
|
167
|
+
let mut w = writer.lock().await;
|
|
168
|
+
if let Err(e) = w.send(Message::Pong(ping_data)).await {
|
|
169
|
+
eprintln!("Failed to send Pong: {}", e);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
Message::Pong(_) => println!("Received Pong from {}", addr),
|
|
173
|
+
Message::Close(_) => {
|
|
174
|
+
println!("Received Close frame from {}", addr);
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
Message::Frame(_) => println!("Received raw Frame from {} (ignored)", addr),
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
Err(e) => {
|
|
181
|
+
eprintln!("WebSocket error reading message from {}: {}", addr, e);
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/// Routes the parsed message to the appropriate handler
|
|
189
|
+
// --- Fix: Changed return type to anyhow::Result<()> ---
|
|
190
|
+
async fn route_message<R: Runtime>(
|
|
191
|
+
message: IncomingMessage,
|
|
192
|
+
writer: Arc<Mutex<SplitSink<WebSocketStream<TcpStream>, Message>>>,
|
|
193
|
+
app_handle: AppHandle<R>,
|
|
194
|
+
) -> anyhow::Result<()> {
|
|
195
|
+
// Use anyhow::Result for easier error handling
|
|
196
|
+
println!("Routing message for URL: {}", message.url);
|
|
197
|
+
|
|
198
|
+
match message.url.as_str() {
|
|
199
|
+
"/paths" => handle_paths(message, writer, app_handle).await?,
|
|
200
|
+
"/fs/file/write" => handle_fs_write(message, writer, app_handle).await?,
|
|
201
|
+
"/window/maximize" => handle_window_maximize(message, writer, app_handle).await?,
|
|
202
|
+
"/fs/file/read" => handle_not_implemented(message, writer).await?,
|
|
203
|
+
"/fs/file/read/binary" => handle_not_implemented(message, writer).await?,
|
|
204
|
+
"/fs/folder/create" => handle_not_implemented(message, writer).await?,
|
|
205
|
+
"/window/minimize" => handle_window_minimize(message, writer, app_handle).await?,
|
|
206
|
+
"/window/request-attention" => handle_not_implemented(message, writer).await?,
|
|
207
|
+
"/window/restore" => handle_window_restore(message, writer, app_handle).await?,
|
|
208
|
+
"/dialog/folder" => handle_not_implemented(message, writer).await?,
|
|
209
|
+
"/dialog/open" => handle_not_implemented(message, writer).await?,
|
|
210
|
+
"/dialog/save" => handle_not_implemented(message, writer).await?,
|
|
211
|
+
"/window/set-always-on-top" => handle_not_implemented(message, writer).await?,
|
|
212
|
+
"/window/set-height" => handle_not_implemented(message, writer).await?,
|
|
213
|
+
"/window/set-maximum-size" => handle_not_implemented(message, writer).await?,
|
|
214
|
+
"/window/set-minimum-size" => handle_not_implemented(message, writer).await?,
|
|
215
|
+
"/window/set-resizable" => handle_not_implemented(message, writer).await?,
|
|
216
|
+
"/window/set-title" => handle_not_implemented(message, writer).await?,
|
|
217
|
+
"/window/set-width" => handle_not_implemented(message, writer).await?,
|
|
218
|
+
"/window/set-x" => handle_not_implemented(message, writer).await?,
|
|
219
|
+
"/window/set-y" => handle_not_implemented(message, writer).await?,
|
|
220
|
+
"/window/show-dev-tools" => handle_not_implemented(message, writer).await?,
|
|
221
|
+
"/window/unmaximize" => handle_window_unmaximize(message, writer, app_handle).await?,
|
|
222
|
+
"/window/set-fullscreen" => handle_not_implemented(message, writer).await?,
|
|
223
|
+
"/engine" => handle_engine(message, writer).await?,
|
|
224
|
+
"/open" => handle_not_implemented(message, writer).await?,
|
|
225
|
+
"/show-in-explorer" => handle_not_implemented(message, writer).await?,
|
|
226
|
+
"/run" => handle_not_implemented(message, writer).await?,
|
|
227
|
+
"/fs/copy" => handle_not_implemented(message, writer).await?,
|
|
228
|
+
"/fs/delete" => handle_not_implemented(message, writer).await?,
|
|
229
|
+
"/fs/exist" => handle_not_implemented(message, writer).await?,
|
|
230
|
+
"/fs/list" => handle_not_implemented(message, writer).await?,
|
|
231
|
+
"/fs/file/size" => handle_not_implemented(message, writer).await?,
|
|
232
|
+
"/fs/move" => handle_not_implemented(message, writer).await?,
|
|
233
|
+
"/steam/raw" => handle_not_implemented(message, writer).await?,
|
|
234
|
+
"/discord/set-activity" => handle_not_implemented(message, writer).await?,
|
|
235
|
+
"/infos" => handle_not_implemented(message, writer).await?,
|
|
236
|
+
"/exit" => handle_exit(message, writer, app_handle).await?,
|
|
237
|
+
|
|
238
|
+
_ => {
|
|
239
|
+
println!("Received unhandled URL: {}", message.url);
|
|
240
|
+
let response = ResponseMessage {
|
|
241
|
+
url: message.url.clone(),
|
|
242
|
+
correlation_id: message.correlation_id.clone(),
|
|
243
|
+
body: ErrorBody {
|
|
244
|
+
success: false,
|
|
245
|
+
error: format!("Unhandled URL: {}", message.url),
|
|
246
|
+
},
|
|
247
|
+
};
|
|
248
|
+
let json_response = serde_json::to_string(&response)?;
|
|
249
|
+
let mut w = writer.lock().await;
|
|
250
|
+
w.send(Message::Text(json_response)).await?;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
Ok(())
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// --- Example Handler Implementations (Stubs) ---
|
|
257
|
+
|
|
258
|
+
// --- Fix: Changed return type for all handlers to anyhow::Result<()> ---
|
|
259
|
+
|
|
260
|
+
async fn handle_not_implemented(
|
|
261
|
+
message: IncomingMessage,
|
|
262
|
+
writer: Arc<Mutex<SplitSink<WebSocketStream<TcpStream>, Message>>>,
|
|
263
|
+
) -> anyhow::Result<()> {
|
|
264
|
+
// Use anyhow::Result
|
|
265
|
+
println!("Handler not implemented for URL: {}", message.url);
|
|
266
|
+
let response = ResponseMessage {
|
|
267
|
+
url: message.url.clone(),
|
|
268
|
+
correlation_id: message.correlation_id.clone(),
|
|
269
|
+
body: ErrorBody {
|
|
270
|
+
success: false,
|
|
271
|
+
error: format!("Feature not implemented: {}", message.url),
|
|
272
|
+
},
|
|
273
|
+
};
|
|
274
|
+
let json_response = serde_json::to_string(&response)?;
|
|
275
|
+
let mut w = writer.lock().await;
|
|
276
|
+
w.send(Message::Text(json_response)).await?;
|
|
277
|
+
Ok(())
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
async fn handle_engine(
|
|
281
|
+
message: IncomingMessage,
|
|
282
|
+
writer: Arc<Mutex<SplitSink<WebSocketStream<TcpStream>, Message>>>,
|
|
283
|
+
) -> anyhow::Result<()> {
|
|
284
|
+
// Use anyhow::Result
|
|
285
|
+
println!(
|
|
286
|
+
"Handling /engine request. Body (if any): {:?}",
|
|
287
|
+
message.body
|
|
288
|
+
);
|
|
289
|
+
let response = ResponseMessage {
|
|
290
|
+
url: message.url.clone(),
|
|
291
|
+
correlation_id: message.correlation_id.clone(),
|
|
292
|
+
body: SuccessBody {
|
|
293
|
+
success: true,
|
|
294
|
+
data: Option::<()>::None,
|
|
295
|
+
},
|
|
296
|
+
};
|
|
297
|
+
let json_response = serde_json::to_string(&response)?;
|
|
298
|
+
let mut w = writer.lock().await;
|
|
299
|
+
w.send(Message::Text(json_response)).await?;
|
|
300
|
+
Ok(())
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
async fn handle_paths<R: Runtime>(
|
|
304
|
+
message: IncomingMessage,
|
|
305
|
+
writer: Arc<Mutex<SplitSink<WebSocketStream<TcpStream>, Message>>>,
|
|
306
|
+
app_handle: AppHandle<R>,
|
|
307
|
+
) -> anyhow::Result<()> {
|
|
308
|
+
// Use anyhow::Result
|
|
309
|
+
println!("Handling /paths request. Body (if any): {:?}", message.body);
|
|
310
|
+
|
|
311
|
+
// --- Fix: Use ok_or_else correctly on Option ---
|
|
312
|
+
// let user_data_path = app_handle.path().app_data_dir()
|
|
313
|
+
// .ok_or_else(|| anyhow::anyhow!("Could not get app data dir"))?; // ok_or_else is called on Option<PathBuf>
|
|
314
|
+
// let documents_path = dirs::document_dir()
|
|
315
|
+
// .ok_or_else(|| anyhow::anyhow!("Could not get documents dir"))?; // ok_or_else is called on Option<PathBuf>
|
|
316
|
+
|
|
317
|
+
let data = serde_json::json!({
|
|
318
|
+
"appData": "data", // user_data_path,
|
|
319
|
+
"documents": "data", // documents_path,
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
let response = ResponseMessage {
|
|
323
|
+
url: message.url.clone(),
|
|
324
|
+
correlation_id: message.correlation_id.clone(),
|
|
325
|
+
body: SuccessBody {
|
|
326
|
+
success: true,
|
|
327
|
+
data: Some(data),
|
|
328
|
+
},
|
|
329
|
+
};
|
|
330
|
+
let json_response = serde_json::to_string(&response)?;
|
|
331
|
+
let mut w = writer.lock().await;
|
|
332
|
+
w.send(Message::Text(json_response)).await?;
|
|
333
|
+
Ok(())
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
async fn handle_fs_write<R: Runtime>(
|
|
337
|
+
message: IncomingMessage,
|
|
338
|
+
writer: Arc<Mutex<SplitSink<WebSocketStream<TcpStream>, Message>>>,
|
|
339
|
+
_app_handle: AppHandle<R>,
|
|
340
|
+
) -> anyhow::Result<()> {
|
|
341
|
+
// Use anyhow::Result
|
|
342
|
+
println!("Handling /fs/file/write request.");
|
|
343
|
+
|
|
344
|
+
let body_value = message
|
|
345
|
+
.body
|
|
346
|
+
.ok_or_else(|| anyhow::anyhow!("Missing request body for /fs/file/write"))?;
|
|
347
|
+
let path = body_value
|
|
348
|
+
.get("path")
|
|
349
|
+
.and_then(Value::as_str)
|
|
350
|
+
.ok_or_else(|| anyhow::anyhow!("Missing 'path' field in body"))?;
|
|
351
|
+
let content = body_value
|
|
352
|
+
.get("content")
|
|
353
|
+
.and_then(Value::as_str)
|
|
354
|
+
.ok_or_else(|| anyhow::anyhow!("Missing 'content' field in body"))?;
|
|
355
|
+
|
|
356
|
+
println!(
|
|
357
|
+
"Attempting to write to path: '{}', Content length: {}",
|
|
358
|
+
path,
|
|
359
|
+
content.len()
|
|
360
|
+
);
|
|
361
|
+
// tokio::fs::write(path, content).await?; // Add actual file writing logic here
|
|
362
|
+
|
|
363
|
+
let response = ResponseMessage {
|
|
364
|
+
url: message.url.clone(),
|
|
365
|
+
correlation_id: message.correlation_id.clone(),
|
|
366
|
+
body: SuccessBody {
|
|
367
|
+
success: true,
|
|
368
|
+
data: Option::<()>::None,
|
|
369
|
+
},
|
|
370
|
+
};
|
|
371
|
+
let json_response = serde_json::to_string(&response)?;
|
|
372
|
+
let mut w = writer.lock().await;
|
|
373
|
+
w.send(Message::Text(json_response)).await?;
|
|
374
|
+
Ok(())
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
async fn handle_window_maximize<R: Runtime>(
|
|
378
|
+
message: IncomingMessage,
|
|
379
|
+
writer: Arc<Mutex<SplitSink<WebSocketStream<TcpStream>, Message>>>,
|
|
380
|
+
app_handle: AppHandle<R>,
|
|
381
|
+
) -> anyhow::Result<()> {
|
|
382
|
+
// Use anyhow::Result
|
|
383
|
+
println!("Handling /window/maximize request");
|
|
384
|
+
match app_handle.get_webview_window("main") {
|
|
385
|
+
Some(window) => {
|
|
386
|
+
#[cfg(desktop)] // This block only compiles on desktop targets
|
|
387
|
+
{
|
|
388
|
+
window.maximize()?; // This call is safe here
|
|
389
|
+
println!("Window 'main' maximized.");
|
|
390
|
+
}
|
|
391
|
+
#[cfg(mobile)] // This block only compiles on mobile targets
|
|
392
|
+
{
|
|
393
|
+
// On mobile, maximize doesn't exist/make sense in the same way.
|
|
394
|
+
println!("Window maximize is not supported on mobile. Returning error.");
|
|
395
|
+
// Return an error indicating the operation is not supported on this platform.
|
|
396
|
+
return Err(anyhow::anyhow!(
|
|
397
|
+
"Window maximize is not supported on this platform"
|
|
398
|
+
));
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
let response = ResponseMessage {
|
|
402
|
+
url: message.url.clone(),
|
|
403
|
+
correlation_id: message.correlation_id.clone(),
|
|
404
|
+
body: SuccessBody {
|
|
405
|
+
success: true,
|
|
406
|
+
data: Option::<()>::None,
|
|
407
|
+
},
|
|
408
|
+
};
|
|
409
|
+
let json_response = serde_json::to_string(&response)?;
|
|
410
|
+
let mut w = writer.lock().await;
|
|
411
|
+
w.send(Message::Text(json_response)).await?;
|
|
412
|
+
}
|
|
413
|
+
None => {
|
|
414
|
+
return Err(anyhow::anyhow!("Main window not found")); // Return anyhow error
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
Ok(())
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
async fn handle_window_minimize<R: Runtime>(
|
|
421
|
+
message: IncomingMessage,
|
|
422
|
+
writer: Arc<Mutex<SplitSink<WebSocketStream<TcpStream>, Message>>>,
|
|
423
|
+
app_handle: AppHandle<R>,
|
|
424
|
+
) -> anyhow::Result<()> {
|
|
425
|
+
// Use anyhow::Result
|
|
426
|
+
println!("Handling /window/minimize request");
|
|
427
|
+
match app_handle.get_webview_window("main") {
|
|
428
|
+
Some(window) => {
|
|
429
|
+
#[cfg(desktop)]
|
|
430
|
+
{
|
|
431
|
+
window.minimize()?;
|
|
432
|
+
println!("Window 'main' minimized.");
|
|
433
|
+
}
|
|
434
|
+
#[cfg(mobile)]
|
|
435
|
+
{
|
|
436
|
+
println!("Window minimize is not supported on mobile. Returning error.");
|
|
437
|
+
return Err(anyhow::anyhow!(
|
|
438
|
+
"Window minimize is not supported on this platform"
|
|
439
|
+
));
|
|
440
|
+
}
|
|
441
|
+
let response = ResponseMessage {
|
|
442
|
+
url: message.url.clone(),
|
|
443
|
+
correlation_id: message.correlation_id.clone(),
|
|
444
|
+
body: SuccessBody {
|
|
445
|
+
success: true,
|
|
446
|
+
data: Option::<()>::None,
|
|
447
|
+
},
|
|
448
|
+
};
|
|
449
|
+
let json_response = serde_json::to_string(&response)?;
|
|
450
|
+
let mut w = writer.lock().await;
|
|
451
|
+
w.send(Message::Text(json_response)).await?;
|
|
452
|
+
}
|
|
453
|
+
None => {
|
|
454
|
+
return Err(anyhow::anyhow!("Main window not found"));
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
Ok(())
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
async fn handle_window_restore<R: Runtime>(
|
|
461
|
+
message: IncomingMessage,
|
|
462
|
+
writer: Arc<Mutex<SplitSink<WebSocketStream<TcpStream>, Message>>>,
|
|
463
|
+
app_handle: AppHandle<R>,
|
|
464
|
+
) -> anyhow::Result<()> {
|
|
465
|
+
// Use anyhow::Result
|
|
466
|
+
println!("Handling /window/restore request");
|
|
467
|
+
match app_handle.get_webview_window("main") {
|
|
468
|
+
Some(window) => {
|
|
469
|
+
#[cfg(desktop)]
|
|
470
|
+
{
|
|
471
|
+
if window.is_maximized()? {
|
|
472
|
+
window.unmaximize()?;
|
|
473
|
+
}
|
|
474
|
+
window.set_focus()?;
|
|
475
|
+
if !window.is_visible()? {
|
|
476
|
+
window.show()?;
|
|
477
|
+
}
|
|
478
|
+
println!("Window 'main' restored (attempted).");
|
|
479
|
+
}
|
|
480
|
+
#[cfg(mobile)]
|
|
481
|
+
{
|
|
482
|
+
println!("Window restore is not supported on mobile. Returning error.");
|
|
483
|
+
return Err(anyhow::anyhow!(
|
|
484
|
+
"Window restore is not supported on this platform"
|
|
485
|
+
));
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
let response = ResponseMessage {
|
|
489
|
+
url: message.url.clone(),
|
|
490
|
+
correlation_id: message.correlation_id.clone(),
|
|
491
|
+
body: SuccessBody {
|
|
492
|
+
success: true,
|
|
493
|
+
data: Option::<()>::None,
|
|
494
|
+
},
|
|
495
|
+
};
|
|
496
|
+
let json_response = serde_json::to_string(&response)?;
|
|
497
|
+
let mut w = writer.lock().await;
|
|
498
|
+
w.send(Message::Text(json_response)).await?;
|
|
499
|
+
}
|
|
500
|
+
None => {
|
|
501
|
+
return Err(anyhow::anyhow!("Main window not found"));
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
Ok(())
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
async fn handle_window_unmaximize<R: Runtime>(
|
|
508
|
+
message: IncomingMessage,
|
|
509
|
+
writer: Arc<Mutex<SplitSink<WebSocketStream<TcpStream>, Message>>>,
|
|
510
|
+
app_handle: AppHandle<R>,
|
|
511
|
+
) -> anyhow::Result<()> {
|
|
512
|
+
// Use anyhow::Result
|
|
513
|
+
println!("Handling /window/unmaximize request");
|
|
514
|
+
match app_handle.get_webview_window("main") {
|
|
515
|
+
Some(window) => {
|
|
516
|
+
#[cfg(desktop)]
|
|
517
|
+
{
|
|
518
|
+
window.unmaximize()?; // <-- Your original line, now conditional
|
|
519
|
+
println!("Window 'main' unmaximized.");
|
|
520
|
+
}
|
|
521
|
+
#[cfg(mobile)]
|
|
522
|
+
{
|
|
523
|
+
println!("Window unmaximize is not supported on mobile. Returning error.");
|
|
524
|
+
return Err(anyhow::anyhow!(
|
|
525
|
+
"Window unmaximize is not supported on this platform"
|
|
526
|
+
));
|
|
527
|
+
}
|
|
528
|
+
let response = ResponseMessage {
|
|
529
|
+
url: message.url.clone(),
|
|
530
|
+
correlation_id: message.correlation_id.clone(),
|
|
531
|
+
body: SuccessBody {
|
|
532
|
+
success: true,
|
|
533
|
+
data: Option::<()>::None,
|
|
534
|
+
},
|
|
535
|
+
};
|
|
536
|
+
let json_response = serde_json::to_string(&response)?;
|
|
537
|
+
let mut w = writer.lock().await;
|
|
538
|
+
w.send(Message::Text(json_response)).await?;
|
|
539
|
+
}
|
|
540
|
+
None => {
|
|
541
|
+
return Err(anyhow::anyhow!("Main window not found"));
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
Ok(())
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
async fn handle_exit<R: Runtime>(
|
|
548
|
+
message: IncomingMessage,
|
|
549
|
+
writer: Arc<Mutex<SplitSink<WebSocketStream<TcpStream>, Message>>>,
|
|
550
|
+
app_handle: AppHandle<R>,
|
|
551
|
+
) -> anyhow::Result<()> {
|
|
552
|
+
// Use anyhow::Result
|
|
553
|
+
println!("Handling /exit request");
|
|
554
|
+
let response = ResponseMessage {
|
|
555
|
+
url: message.url.clone(),
|
|
556
|
+
correlation_id: message.correlation_id.clone(),
|
|
557
|
+
body: SuccessBody {
|
|
558
|
+
success: true,
|
|
559
|
+
data: Option::<()>::None,
|
|
560
|
+
},
|
|
561
|
+
};
|
|
562
|
+
let json_response = serde_json::to_string(&response)?;
|
|
563
|
+
let mut w = writer.lock().await;
|
|
564
|
+
w.send(Message::Text(json_response)).await?;
|
|
565
|
+
|
|
566
|
+
app_handle.exit(0);
|
|
567
|
+
// TODO: support exit code
|
|
568
|
+
// app_handle.exit(message.body.code);
|
|
569
|
+
Ok(())
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
// --- WebSocket Server ---
|
|
573
|
+
|
|
574
|
+
async fn start_websocket_server<R: Runtime>(app_handle: AppHandle<R>) {
|
|
575
|
+
let addr = SocketAddr::from(([127, 0, 0, 1], 31753));
|
|
576
|
+
let listener = match TcpListener::bind(&addr).await {
|
|
577
|
+
Ok(l) => l,
|
|
578
|
+
Err(e) => {
|
|
579
|
+
eprintln!("Failed to bind WebSocket server to {}: {}", addr, e);
|
|
580
|
+
return;
|
|
581
|
+
}
|
|
582
|
+
};
|
|
583
|
+
println!("WebSocket server running on ws://{}", addr);
|
|
584
|
+
|
|
585
|
+
loop {
|
|
586
|
+
match listener.accept().await {
|
|
587
|
+
Ok((stream, _)) => {
|
|
588
|
+
let app_handle_clone = app_handle.clone();
|
|
589
|
+
tokio::spawn(async move {
|
|
590
|
+
handle_websocket(stream, app_handle_clone).await;
|
|
591
|
+
});
|
|
592
|
+
}
|
|
593
|
+
Err(e) => {
|
|
594
|
+
eprintln!("Failed to accept incoming connection: {}", e);
|
|
595
|
+
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
// --- Tauri Setup ---
|
|
602
|
+
|
|
603
|
+
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
|
604
|
+
pub fn run() {
|
|
605
|
+
tauri::Builder::default()
|
|
606
|
+
.setup(move |app| {
|
|
607
|
+
let app_handle = app.handle().clone();
|
|
608
|
+
async_runtime::spawn(async move {
|
|
609
|
+
start_websocket_server(app_handle).await;
|
|
610
|
+
});
|
|
611
|
+
|
|
612
|
+
Ok(())
|
|
613
|
+
})
|
|
614
|
+
.run(tauri::generate_context!())
|
|
615
|
+
.expect("error while running tauri application");
|
|
616
|
+
}
|