@bobfrankston/msger 0.1.74 → 0.1.75

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.
Files changed (134) hide show
  1. package/KNOWN-BUGS.md +121 -0
  2. package/MSGER-API-SUMMARY.md +162 -0
  3. package/MSGER-API.md +376 -0
  4. package/README.md +93 -0
  5. package/SESSION-2025-11-06.md +191 -0
  6. package/SESSION-NOTES.md +678 -0
  7. package/clihandler.d.ts.map +1 -1
  8. package/clihandler.js +62 -2
  9. package/clihandler.js.map +1 -1
  10. package/clihandler.ts +60 -2
  11. package/icon.png +0 -0
  12. package/icon1.png +0 -0
  13. package/msger-native/Cargo.toml +1 -0
  14. package/msger-native/bin/msgernative.exe +0 -0
  15. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Breadcrumbs +12 -118
  16. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/BrowserMetrics/{BrowserMetrics-690552AF-DCD4.pma → BrowserMetrics-690B9AD3-657C.pma} +0 -0
  17. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/BrowserMetrics/{BrowserMetrics-69055373-F88C.pma → BrowserMetrics-690BA05A-501C.pma} +0 -0
  18. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Crashpad/settings.dat +0 -0
  19. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/BrowsingTopicsState +1 -1
  20. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/Cache/Cache_Data/data_0 +0 -0
  21. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/Cache/Cache_Data/data_1 +0 -0
  22. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/{BrowserMetrics/BrowserMetrics-69055587-A65C.pma → Default/Cache/Cache_Data/data_2} +0 -0
  23. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/Cache/Cache_Data/data_3 +0 -0
  24. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/Cache/Cache_Data/f_000001 +383 -0
  25. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/Cache/Cache_Data/f_000002 +1091 -0
  26. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/Cache/Cache_Data/f_000003 +2153 -0
  27. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/Cache/Cache_Data/f_000004 +0 -0
  28. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/Cache/Cache_Data/f_000005 +626 -0
  29. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/Cache/Cache_Data/f_000006 +393 -0
  30. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/Cache/Cache_Data/index +0 -0
  31. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/Code Cache/js/01241693cfdc32b9_0 +0 -0
  32. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/Code Cache/js/0ba1eea781f3552c_0 +0 -0
  33. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/Code Cache/js/323aa210eebefe2c_0 +0 -0
  34. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/Code Cache/js/4608446ac118e77a_0 +0 -0
  35. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/Code Cache/js/6938205dc2f77841_0 +0 -0
  36. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/Code Cache/js/6de12299dc89e5f3_0 +0 -0
  37. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/Code Cache/js/8f403c112eaa455b_0 +0 -0
  38. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/Code Cache/js/9a3aceb491137f07_0 +0 -0
  39. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/Code Cache/js/aedb266cbaf9c28f_0 +0 -0
  40. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/Code Cache/js/ca526fdda86d0b9d_0 +0 -0
  41. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/Code Cache/js/f5d11d783c9fdf69_0 +0 -0
  42. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/Code Cache/js/index-dir/the-real-index +0 -0
  43. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/Collections/collectionsSQLite +0 -0
  44. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/Collections/collectionsSQLite-journal +0 -0
  45. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/DIPS +0 -0
  46. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/DawnGraphiteCache/data_1 +0 -0
  47. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/DawnGraphiteCache/index +0 -0
  48. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/DawnWebGPUCache/data_1 +0 -0
  49. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/DawnWebGPUCache/index +0 -0
  50. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/Extension State/LOG +3 -3
  51. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/Extension State/LOG.old +3 -3
  52. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/Favicons +0 -0
  53. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/GPUCache/data_0 +0 -0
  54. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/GPUCache/data_1 +0 -0
  55. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/GPUCache/data_2 +0 -0
  56. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/GPUCache/index +0 -0
  57. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/History +0 -0
  58. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/Local Storage/leveldb/000003.log +0 -0
  59. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/Local Storage/leveldb/LOG +3 -3
  60. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/Local Storage/leveldb/LOG.old +3 -3
  61. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/MediaDeviceSalts +0 -0
  62. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/MediaDeviceSalts-journal +0 -0
  63. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/Network/Network Persistent State +1 -1
  64. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/Network/TransportSecurity +1 -0
  65. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/Preferences +1 -1
  66. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/Service Worker/CacheStorage/14e849fa8522d406112ea607cf7fd6342b71b987/249ee9af-c3df-4a86-89a8-2c51f3370ee0/index +0 -0
  67. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/Service Worker/CacheStorage/14e849fa8522d406112ea607cf7fd6342b71b987/249ee9af-c3df-4a86-89a8-2c51f3370ee0/index-dir/the-real-index +0 -0
  68. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/Service Worker/CacheStorage/14e849fa8522d406112ea607cf7fd6342b71b987/index.txt +0 -0
  69. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/Service Worker/Database/000003.log +0 -0
  70. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/Service Worker/Database/CURRENT +1 -0
  71. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/Service Worker/Database/LOCK +0 -0
  72. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/Service Worker/Database/LOG +3 -0
  73. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/Service Worker/Database/LOG.old +3 -0
  74. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/Service Worker/Database/MANIFEST-000001 +0 -0
  75. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/Service Worker/ScriptCache/2cc80dabc69f58b6_0 +0 -0
  76. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/Service Worker/ScriptCache/2cc80dabc69f58b6_1 +0 -0
  77. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/Service Worker/ScriptCache/index +0 -0
  78. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/Service Worker/ScriptCache/index-dir/the-real-index +0 -0
  79. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/Session Storage/000003.log +0 -0
  80. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/Session Storage/LOG +3 -3
  81. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/Session Storage/LOG.old +3 -3
  82. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/Site Characteristics Database/000003.log +0 -0
  83. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/Site Characteristics Database/LOG +3 -3
  84. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/Site Characteristics Database/LOG.old +3 -3
  85. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/Sync Data/LevelDB/LOG +3 -3
  86. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/Sync Data/LevelDB/LOG.old +3 -3
  87. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/WebStorage/QuotaManager +0 -0
  88. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/WebStorage/QuotaManager-journal +0 -0
  89. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/favorites_diagnostic.log +27 -0
  90. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/shared_proto_db/000003.log +0 -0
  91. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/shared_proto_db/LOG +3 -3
  92. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/shared_proto_db/LOG.old +3 -3
  93. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/shared_proto_db/metadata/000003.log +0 -0
  94. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/shared_proto_db/metadata/LOG +3 -3
  95. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Default/shared_proto_db/metadata/LOG.old +3 -3
  96. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/GrShaderCache/data_0 +0 -0
  97. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/GrShaderCache/data_1 +0 -0
  98. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/GrShaderCache/data_3 +0 -0
  99. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/GrShaderCache/f_000003 +0 -0
  100. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/GrShaderCache/f_000004 +0 -0
  101. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/GrShaderCache/index +0 -0
  102. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/GraphiteDawnCache/data_1 +0 -0
  103. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/GraphiteDawnCache/index +0 -0
  104. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/Local State +1 -1
  105. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/RevisitationBloomfilter +0 -0
  106. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/ShaderCache/data_1 +0 -0
  107. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/ShaderCache/index +0 -0
  108. package/msger-native/src/main.rs +343 -37
  109. package/msger-native/src/template.html +103 -28
  110. package/msger-storage-demo.html +290 -0
  111. package/msger.code-workspace +3 -0
  112. package/msgerdefs/README.md +122 -0
  113. package/msgerdefs/msgerdefs.d.ts +322 -0
  114. package/msgerdefs/msgerdefs.d.ts.map +1 -0
  115. package/msgerdefs/msgerdefs.js +110 -0
  116. package/msgerdefs/msgerdefs.js.map +1 -0
  117. package/msgerdefs/msgerdefs.ts +427 -0
  118. package/msgerdefs/package.json +38 -0
  119. package/msgerdefs/samples.html +431 -0
  120. package/msgerdefs/test1.cmd +1 -0
  121. package/msgerdefs/tsconfig.json +17 -0
  122. package/msgernative-linux-x64 +0 -0
  123. package/package.json +5 -1
  124. package/shower.d.ts +2 -0
  125. package/shower.d.ts.map +1 -1
  126. package/shower.js +17 -0
  127. package/shower.js.map +1 -1
  128. package/shower.ts +24 -0
  129. package/test-data-persistence.html +315 -0
  130. package/test-htmlfrom.html +29 -0
  131. package/test-ipc-reach.html +113 -0
  132. package/test-msger-api.html +120 -0
  133. package/test-msger-functions.html +325 -0
  134. package/msger-native/bin/msgernative.exe.WebView2/EBWebView/BrowserMetrics/BrowserMetrics-69055419-C8A0.pma +0 -0
@@ -1,11 +1,100 @@
1
1
  use serde::{Deserialize, Serialize};
2
2
  use std::io::{self, Read};
3
3
  use tao::{
4
- event::{Event, WindowEvent},
4
+ event::{Event, WindowEvent, ElementState, KeyEvent},
5
5
  event_loop::{ControlFlow, EventLoop},
6
+ keyboard::KeyCode,
6
7
  window::WindowBuilder,
7
8
  };
8
- use wry::WebViewBuilder;
9
+ use wry::{WebViewBuilder, WebContext};
10
+
11
+ // JavaScript API to inject into webviews
12
+ const MSGER_JS_API: &str = r#"
13
+ (function() {
14
+ console.log('[MSGER] Initializing msger API...');
15
+
16
+ // Check if IPC is available
17
+ const hasIPC = typeof window.ipc !== 'undefined' && window.ipc.postMessage;
18
+ console.log('[MSGER] IPC available:', hasIPC);
19
+
20
+ // Helper to safely send IPC messages
21
+ function safePostMessage(data) {
22
+ try {
23
+ if (!hasIPC) {
24
+ console.error('msger API: window.ipc not available');
25
+ return false;
26
+ }
27
+ window.ipc.postMessage(JSON.stringify(data));
28
+ return true;
29
+ } catch(e) {
30
+ console.error('msger API error:', e);
31
+ return false;
32
+ }
33
+ }
34
+
35
+ window.msger = {
36
+ // Window control - only close() with window.close() workaround (IPC broken)
37
+ close: function(result) {
38
+ // IPC is broken in wry 0.47.2, so we use window.close() instead
39
+ // Result cannot be returned to parent process
40
+ try {
41
+ window.close();
42
+ return true;
43
+ } catch(e) {
44
+ console.error('window.close() failed:', e);
45
+ return false;
46
+ }
47
+ },
48
+ saveData: function(key, value) {
49
+ try {
50
+ localStorage.setItem('msger_' + key, JSON.stringify(value));
51
+ return true;
52
+ } catch(e) {
53
+ console.error('Failed to save data:', e);
54
+ return false;
55
+ }
56
+ },
57
+ loadData: function(key, defaultValue) {
58
+ try {
59
+ const item = localStorage.getItem('msger_' + key);
60
+ return item ? JSON.parse(item) : defaultValue;
61
+ } catch(e) {
62
+ console.error('Failed to load data:', e);
63
+ return defaultValue;
64
+ }
65
+ },
66
+ removeData: function(key) {
67
+ try {
68
+ localStorage.removeItem('msger_' + key);
69
+ return true;
70
+ } catch(e) {
71
+ console.error('Failed to remove data:', e);
72
+ return false;
73
+ }
74
+ },
75
+ clearData: function() {
76
+ try {
77
+ const keys = Object.keys(localStorage);
78
+ for (const key of keys) {
79
+ if (key.startsWith('msger_')) {
80
+ localStorage.removeItem(key);
81
+ }
82
+ }
83
+ return true;
84
+ } catch(e) {
85
+ console.error('Failed to clear data:', e);
86
+ return false;
87
+ }
88
+ },
89
+ // Check if running in msger environment
90
+ isAvailable: function() {
91
+ return true;
92
+ }
93
+ };
94
+
95
+ console.log('[MSGER] API initialized successfully! window.msger =', window.msger);
96
+ })();
97
+ "#;
9
98
 
10
99
  #[derive(Deserialize, Debug)]
11
100
  #[serde(rename_all = "camelCase")]
@@ -54,6 +143,10 @@ struct MessageBoxOptions {
54
143
  zoom_percent: Option<f64>,
55
144
  #[serde(default)]
56
145
  debug: bool,
146
+ #[serde(default)]
147
+ icon: Option<String>,
148
+ #[serde(default)]
149
+ dev: bool,
57
150
  }
58
151
 
59
152
  #[derive(Serialize, Deserialize, Debug, Clone)]
@@ -98,6 +191,29 @@ fn default_buttons() -> Vec<String> {
98
191
  vec!["OK".to_string()]
99
192
  }
100
193
 
194
+ fn load_icon(icon_path: &str) -> Option<tao::window::Icon> {
195
+ use image::GenericImageView;
196
+
197
+ match image::open(icon_path) {
198
+ Ok(img) => {
199
+ let (width, height) = img.dimensions();
200
+ let rgba = img.to_rgba8().into_raw();
201
+
202
+ match tao::window::Icon::from_rgba(rgba, width, height) {
203
+ Ok(icon) => Some(icon),
204
+ Err(e) => {
205
+ eprintln!("Failed to create icon from {}: {}", icon_path, e);
206
+ None
207
+ }
208
+ }
209
+ }
210
+ Err(e) => {
211
+ eprintln!("Failed to load icon {}: {}", icon_path, e);
212
+ None
213
+ }
214
+ }
215
+ }
216
+
101
217
  fn generate_html(options: &MessageBoxOptions) -> String {
102
218
  let html_content = if let Some(ref html) = options.html {
103
219
  html.clone()
@@ -148,10 +264,11 @@ fn generate_html(options: &MessageBoxOptions) -> String {
148
264
  .replace("{INPUT}", &input_html)
149
265
  .replace("{BUTTONS}", &buttons_html)
150
266
  .replace("{AUTO_SIZE}", if options.auto_size { "true" } else { "false" })
267
+ .replace("{FULLSCREEN}", if options.fullscreen { "true" } else { "false" })
151
268
  .replace("{ZOOM_PERCENT}", &options.zoom_percent.unwrap_or(100.0).to_string())
152
269
  .replace("{DEFAULT_BUTTON}", options.buttons.first().unwrap_or(&"Cancel".to_string()))
153
270
  .replace("{TIMEOUT_SECONDS}", &options.timeout.map(|t| t.to_string()).unwrap_or_else(|| "0".to_string()));
154
-
271
+
155
272
  html
156
273
  }
157
274
 
@@ -178,32 +295,41 @@ fn main() {
178
295
 
179
296
  // Calculate position with optional screen offset (Windows only)
180
297
  let final_position = if let Some(ref pos) = options.pos {
181
- #[cfg(target_os = "windows")]
182
298
  let mut x = pos.x;
183
- #[cfg(not(target_os = "windows"))]
184
- let x = pos.x;
185
- let y = pos.y;
186
-
187
- // On Windows, calculate screen offset if screen number provided
299
+ let mut y = pos.y;
300
+
301
+ // On Windows, get the actual monitor position if screen number provided
188
302
  #[cfg(target_os = "windows")]
189
303
  if let Some(screen_num) = pos.screen {
190
- if screen_num > 0 {
191
- // Get all monitors and calculate X offset
192
- let monitors: Vec<_> = event_loop.available_monitors().collect();
193
- for (idx, monitor) in monitors.iter().enumerate() {
194
- if idx < screen_num as usize {
195
- let size = monitor.size();
196
- x += size.width as i32;
197
- }
198
- }
304
+ // Get all monitors
305
+ let monitors: Vec<_> = event_loop.available_monitors().collect();
306
+
307
+ // Get the specific monitor by index
308
+ if let Some(monitor) = monitors.get(screen_num as usize) {
309
+ // Get the monitor's actual position from the OS
310
+ let monitor_pos = monitor.position();
311
+
312
+ // Add user's offset to monitor's position
313
+ x += monitor_pos.x;
314
+ y += monitor_pos.y;
315
+ } else {
316
+ eprintln!("Warning: Screen {} not found, using primary monitor", screen_num);
199
317
  }
200
318
  }
201
-
319
+
202
320
  Some(tao::dpi::LogicalPosition::new(x as f64, y as f64))
203
321
  } else {
204
322
  None
205
323
  };
206
-
324
+
325
+ // Load window icon (default or custom)
326
+ let icon = if let Some(ref icon_path) = options.icon {
327
+ load_icon(icon_path)
328
+ } else {
329
+ // Try default icon.png in current directory
330
+ load_icon("icon.png")
331
+ };
332
+
207
333
  let mut window_builder = WindowBuilder::new()
208
334
  .with_title(&options.title)
209
335
  .with_inner_size(tao::dpi::LogicalSize::new(
@@ -213,6 +339,11 @@ fn main() {
213
339
  .with_resizable(true)
214
340
  .with_always_on_top(options.always_on_top);
215
341
 
342
+ // Set icon if loaded successfully
343
+ if let Some(icon) = icon {
344
+ window_builder = window_builder.with_window_icon(Some(icon));
345
+ }
346
+
216
347
  // Set fullscreen mode if requested
217
348
  if options.fullscreen {
218
349
  window_builder = window_builder.with_fullscreen(Some(tao::window::Fullscreen::Borderless(None)));
@@ -222,9 +353,9 @@ fn main() {
222
353
  window_builder = window_builder.with_position(position);
223
354
  }
224
355
 
225
- let window = window_builder
356
+ let window = std::sync::Arc::new(window_builder
226
357
  .build(&event_loop)
227
- .expect("Failed to create window");
358
+ .expect("Failed to create window"));
228
359
 
229
360
  // Generate HTML
230
361
  let html = generate_html(&options);
@@ -248,6 +379,14 @@ fn main() {
248
379
  let debug_info_clone = debug_info.clone();
249
380
  let debug_info_clone2 = debug_info.clone();
250
381
 
382
+ // Create action holder for window operations from JavaScript
383
+ let pending_action = std::sync::Arc::new(std::sync::Mutex::new(None::<serde_json::Value>));
384
+ let pending_action_clone = pending_action.clone();
385
+ let pending_action_clone2 = pending_action.clone();
386
+
387
+ // Clone window for event loop
388
+ let window_clone = window.clone();
389
+
251
390
  // Set up timeout if specified
252
391
  let timeout_instant = options.timeout.map(|secs| {
253
392
  std::time::Instant::now() + std::time::Duration::from_secs(secs)
@@ -256,22 +395,42 @@ fn main() {
256
395
  let timeout_debug = debug_info.clone();
257
396
  let timeout_buttons = options.buttons.clone();
258
397
 
398
+ // Create WebContext with custom user data directory for msger (separate from other WebView2 apps)
399
+ let data_directory = if let Some(local_app_data) = std::env::var_os("LOCALAPPDATA") {
400
+ let mut path = std::path::PathBuf::from(local_app_data);
401
+ path.push("msger");
402
+ path.push("webview2");
403
+ Some(path)
404
+ } else {
405
+ None
406
+ };
407
+ let mut web_context = WebContext::new(data_directory);
408
+
259
409
  // Create webview with IPC handler
260
- let _webview = if let Some(ref url) = options.url {
261
- // Load URL
262
- WebViewBuilder::new()
410
+ let mut webview = if let Some(ref url) = options.url {
411
+ // Load URL with msger API injected
412
+ WebViewBuilder::with_web_context(&mut web_context)
263
413
  .with_url(url)
414
+ .with_initialization_script(MSGER_JS_API)
264
415
  .with_devtools(true)
265
416
  .with_ipc_handler(move |msg| {
266
- // Check for resize message first
267
- if let Ok(resize_msg) = serde_json::from_str::<serde_json::Value>(msg.body()) {
268
- if resize_msg.get("_resize").and_then(|v| v.as_bool()).unwrap_or(false) {
417
+ // Parse message as JSON
418
+ if let Ok(json_msg) = serde_json::from_str::<serde_json::Value>(msg.body()) {
419
+ // Check for action messages
420
+ if json_msg.get("_action").is_some() {
421
+ let mut action_lock = pending_action_clone.lock().unwrap();
422
+ *action_lock = Some(json_msg);
423
+ return;
424
+ }
425
+
426
+ // Check for resize message
427
+ if json_msg.get("_resize").and_then(|v| v.as_bool()).unwrap_or(false) {
269
428
  // This is a resize message - we'll handle it in the event loop
270
429
  // For now, just ignore it (window resize from IPC is complex in wry/tao)
271
430
  return;
272
431
  }
273
432
  }
274
-
433
+
275
434
  // Parse the result from JavaScript (msg is now a Request<String>)
276
435
  if let Ok(mut res) = serde_json::from_str::<MessageBoxResult>(msg.body()) {
277
436
  // Add debug info if requested
@@ -280,23 +439,31 @@ fn main() {
280
439
  *result_lock = Some(res);
281
440
  }
282
441
  })
283
- .build(&window)
442
+ .build(window.as_ref())
284
443
  .expect("Failed to create webview")
285
444
  } else {
286
- // Use HTML content
287
- WebViewBuilder::new()
445
+ // Use HTML content (msger API already in template.html)
446
+ WebViewBuilder::with_web_context(&mut web_context)
288
447
  .with_html(&html)
289
448
  .with_devtools(true)
290
449
  .with_ipc_handler(move |msg| {
291
- // Check for resize message first
292
- if let Ok(resize_msg) = serde_json::from_str::<serde_json::Value>(msg.body()) {
293
- if resize_msg.get("_resize").and_then(|v| v.as_bool()).unwrap_or(false) {
450
+ // Parse message as JSON
451
+ if let Ok(json_msg) = serde_json::from_str::<serde_json::Value>(msg.body()) {
452
+ // Check for action messages
453
+ if json_msg.get("_action").is_some() {
454
+ let mut action_lock = pending_action_clone2.lock().unwrap();
455
+ *action_lock = Some(json_msg);
456
+ return;
457
+ }
458
+
459
+ // Check for resize message
460
+ if json_msg.get("_resize").and_then(|v| v.as_bool()).unwrap_or(false) {
294
461
  // This is a resize message - we'll handle it in the event loop
295
462
  // For now, just ignore it (window resize from IPC is complex in wry/tao)
296
463
  return;
297
464
  }
298
465
  }
299
-
466
+
300
467
  // Parse the result from JavaScript (msg is now a Request<String>)
301
468
  if let Ok(mut res) = serde_json::from_str::<MessageBoxResult>(msg.body()) {
302
469
  // Add debug info if requested
@@ -305,10 +472,18 @@ fn main() {
305
472
  *result_lock = Some(res);
306
473
  }
307
474
  })
308
- .build(&window)
475
+ .build(window.as_ref())
309
476
  .expect("Failed to create webview")
310
477
  };
311
478
 
479
+ // Open DevTools if -dev flag was specified
480
+ if options.dev {
481
+ // TODO: open_devtools() method not available in wry 0.47.2
482
+ // webview.open_devtools();
483
+ eprintln!("Warning: -dev flag specified but open_devtools() not available in wry 0.47.2");
484
+ eprintln!("DevTools are enabled - press F12 to open manually");
485
+ }
486
+
312
487
  // Run event loop
313
488
  event_loop.run(move |event, _,control_flow| {
314
489
  // Check timeout
@@ -335,6 +510,56 @@ fn main() {
335
510
  }
336
511
 
337
512
  match event {
513
+ Event::WindowEvent {
514
+ event: WindowEvent::KeyboardInput {
515
+ event: KeyEvent {
516
+ physical_key: key_code,
517
+ state: ElementState::Pressed,
518
+ ..
519
+ },
520
+ ..
521
+ },
522
+ ..
523
+ } => {
524
+ match key_code {
525
+ // F11 toggles fullscreen
526
+ KeyCode::F11 => {
527
+ let is_fullscreen = window_clone.fullscreen().is_some();
528
+ if is_fullscreen {
529
+ window_clone.set_fullscreen(None);
530
+ } else {
531
+ // Get current monitor for fullscreen
532
+ let monitor = window_clone.current_monitor()
533
+ .or_else(|| window_clone.primary_monitor());
534
+ window_clone.set_fullscreen(Some(tao::window::Fullscreen::Borderless(monitor)));
535
+ }
536
+ }
537
+ // Escape exits fullscreen or dismisses window
538
+ KeyCode::Escape => {
539
+ let is_fullscreen = window_clone.fullscreen().is_some();
540
+ if is_fullscreen {
541
+ // Exit fullscreen
542
+ window_clone.set_fullscreen(None);
543
+ } else {
544
+ // Dismiss window
545
+ let mut result_lock = result.lock().unwrap();
546
+ if result_lock.is_none() {
547
+ *result_lock = Some(MessageBoxResult {
548
+ button: "Cancel".to_string(),
549
+ value: None,
550
+ form: None,
551
+ closed: None,
552
+ dismissed: Some(true),
553
+ timeout: None,
554
+ debug: debug_info.clone(),
555
+ });
556
+ }
557
+ *control_flow = ControlFlow::Exit;
558
+ }
559
+ }
560
+ _ => {}
561
+ }
562
+ }
338
563
  Event::WindowEvent {
339
564
  event: WindowEvent::CloseRequested,
340
565
  ..
@@ -355,6 +580,87 @@ fn main() {
355
580
  *control_flow = ControlFlow::Exit;
356
581
  }
357
582
  Event::MainEventsCleared => {
583
+ // Process pending actions from JavaScript
584
+ let mut action_lock = pending_action.lock().unwrap();
585
+ if let Some(action_msg) = action_lock.take() {
586
+ if let Some(action) = action_msg.get("_action").and_then(|v| v.as_str()) {
587
+ eprintln!("DEBUG: Received IPC action: {}", action);
588
+ // Wrap all window operations in catch_unwind to prevent crashes
589
+ let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
590
+ match action {
591
+ "toggleFullscreen" => {
592
+ let is_fullscreen = window_clone.fullscreen().is_some();
593
+ if is_fullscreen {
594
+ window_clone.set_fullscreen(None);
595
+ } else {
596
+ // Get current monitor for fullscreen
597
+ let monitor = window_clone.current_monitor()
598
+ .or_else(|| window_clone.primary_monitor());
599
+ window_clone.set_fullscreen(Some(tao::window::Fullscreen::Borderless(monitor)));
600
+ }
601
+ }
602
+ "setFullscreen" => {
603
+ if let Some(enabled) = action_msg.get("enabled").and_then(|v| v.as_bool()) {
604
+ if enabled {
605
+ // Get current monitor for fullscreen
606
+ let monitor = window_clone.current_monitor()
607
+ .or_else(|| window_clone.primary_monitor());
608
+ window_clone.set_fullscreen(Some(tao::window::Fullscreen::Borderless(monitor)));
609
+ } else {
610
+ window_clone.set_fullscreen(None);
611
+ }
612
+ }
613
+ }
614
+ "minimize" => {
615
+ window_clone.set_minimized(true);
616
+ }
617
+ "maximize" => {
618
+ window_clone.set_maximized(!window_clone.is_maximized());
619
+ }
620
+ "setSize" => {
621
+ if let (Some(width), Some(height)) = (
622
+ action_msg.get("width").and_then(|v| v.as_f64()),
623
+ action_msg.get("height").and_then(|v| v.as_f64())
624
+ ) {
625
+ window_clone.set_inner_size(tao::dpi::LogicalSize::new(width, height));
626
+ }
627
+ }
628
+ "setPosition" => {
629
+ if let (Some(x), Some(y)) = (
630
+ action_msg.get("x").and_then(|v| v.as_f64()),
631
+ action_msg.get("y").and_then(|v| v.as_f64())
632
+ ) {
633
+ window_clone.set_outer_position(tao::dpi::LogicalPosition::new(x, y));
634
+ }
635
+ }
636
+ "setAlwaysOnTop" => {
637
+ if let Some(enabled) = action_msg.get("enabled").and_then(|v| v.as_bool()) {
638
+ window_clone.set_always_on_top(enabled);
639
+ }
640
+ }
641
+ "setTitle" => {
642
+ if let Some(title) = action_msg.get("title").and_then(|v| v.as_str()) {
643
+ window_clone.set_title(title);
644
+ }
645
+ }
646
+ "test" => {
647
+ eprintln!("DEBUG: Test IPC message received successfully!");
648
+ // Don't do anything, just testing if IPC works
649
+ }
650
+ _ => {
651
+ eprintln!("DEBUG: Unknown action: {}", action);
652
+ }
653
+ }
654
+ }));
655
+
656
+ // Log if operation failed
657
+ if let Err(e) = result {
658
+ eprintln!("Window operation '{}' panicked: {:?}", action, e);
659
+ }
660
+ }
661
+ }
662
+ drop(action_lock);
663
+
358
664
  // Check if result is set
359
665
  let result_lock = result.lock().unwrap();
360
666
  if result_lock.is_some() {