@bobfrankston/msger 0.1.78 → 0.1.79

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.
@@ -9,3 +9,6 @@ tao = "0.30"
9
9
  serde = { version = "1.0", features = ["derive"] }
10
10
  serde_json = "1.0"
11
11
  image = "0.24"
12
+
13
+ [target.'cfg(windows)'.dependencies]
14
+ windows = { version = "0.58", features = ["Win32_UI_WindowsAndMessaging", "Win32_Foundation", "Win32_UI_Shell"] }
Binary file
@@ -5,96 +5,56 @@ use tao::{
5
5
  event_loop::{ControlFlow, EventLoop},
6
6
  keyboard::KeyCode,
7
7
  window::WindowBuilder,
8
+ platform::windows::WindowExtWindows,
8
9
  };
9
10
  use wry::{WebViewBuilder, WebContext};
10
11
 
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;
12
+ #[cfg(windows)]
13
+ use windows::Win32::UI::WindowsAndMessaging::{
14
+ GetSystemMenu, AppendMenuW, MF_STRING, MF_SEPARATOR, WM_SYSCOMMAND,
15
+ };
16
+ #[cfg(windows)]
17
+ use windows::Win32::UI::Shell::{
18
+ SetWindowSubclass, DefSubclassProc,
19
+ };
20
+ #[cfg(windows)]
21
+ use windows::Win32::Foundation::{HWND, LPARAM, WPARAM, LRESULT};
22
+ #[cfg(windows)]
23
+ use windows::core::PCWSTR;
24
+ #[cfg(windows)]
25
+ use std::sync::atomic::{AtomicBool, Ordering};
26
+
27
+ // Custom menu ID for About item (must be < 0xF000 to avoid conflicts with system commands)
28
+ #[cfg(windows)]
29
+ const IDM_ABOUT: u32 = 0x1000;
30
+
31
+ // Global flag to signal About menu was clicked
32
+ #[cfg(windows)]
33
+ static ABOUT_CLICKED: AtomicBool = AtomicBool::new(false);
34
+
35
+ // Window subclass procedure to intercept WM_SYSCOMMAND
36
+ #[cfg(windows)]
37
+ unsafe extern "system" fn subclass_proc(
38
+ hwnd: HWND,
39
+ msg: u32,
40
+ wparam: WPARAM,
41
+ lparam: LPARAM,
42
+ _uidsubclass: usize,
43
+ _dwrefdata: usize,
44
+ ) -> LRESULT {
45
+ if msg == WM_SYSCOMMAND {
46
+ let cmd = wparam.0 & 0xFFF0;
47
+ if cmd == IDM_ABOUT as usize {
48
+ // Set flag that About was clicked
49
+ ABOUT_CLICKED.store(true, Ordering::SeqCst);
50
+ return LRESULT(0);
32
51
  }
33
52
  }
53
+ DefSubclassProc(hwnd, msg, wparam, lparam)
54
+ }
34
55
 
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
- "#;
56
+ // JavaScript API to inject into webviews
57
+ const MSGER_JS_API: &str = include_str!("msger-api.js");
98
58
 
99
59
  #[derive(Deserialize, Debug)]
100
60
  #[serde(rename_all = "camelCase")]
@@ -359,6 +319,22 @@ fn main() {
359
319
  .build(&event_loop)
360
320
  .expect("Failed to create window"));
361
321
 
322
+ // Add About item to Windows system menu and install subclass
323
+ #[cfg(windows)]
324
+ unsafe {
325
+ let hwnd = HWND(window.hwnd() as *mut _);
326
+ let hmenu = GetSystemMenu(hwnd, false);
327
+ if !hmenu.is_invalid() {
328
+ // Add separator
329
+ AppendMenuW(hmenu, MF_SEPARATOR, 0, PCWSTR::null()).ok();
330
+ // Add About item
331
+ let about_text: Vec<u16> = "About (Window Info)\0".encode_utf16().collect();
332
+ AppendMenuW(hmenu, MF_STRING, IDM_ABOUT as usize, PCWSTR(about_text.as_ptr())).ok();
333
+ }
334
+ // Install window subclass to intercept WM_SYSCOMMAND
335
+ SetWindowSubclass(hwnd, Some(subclass_proc), 1, 0).ok();
336
+ }
337
+
362
338
  // Generate HTML
363
339
  let html = generate_html(&options);
364
340
 
@@ -488,6 +464,55 @@ fn main() {
488
464
 
489
465
  // Run event loop
490
466
  event_loop.run(move |event, _,control_flow| {
467
+ // Check for About menu click
468
+ #[cfg(windows)]
469
+ if ABOUT_CLICKED.swap(false, Ordering::SeqCst) {
470
+ // Get window information
471
+ let position = window_clone.outer_position().unwrap_or_default();
472
+ let inner_size = window_clone.inner_size();
473
+ let outer_size = window_clone.outer_size();
474
+ let scale_factor = window_clone.scale_factor();
475
+ let is_maximized = window_clone.is_maximized();
476
+ let is_fullscreen = window_clone.fullscreen().is_some();
477
+ let is_always_on_top = window_clone.is_always_on_top();
478
+
479
+ // Calculate logical sizes (what you specify in the API)
480
+ let logical_inner_width = (inner_size.width as f64 / scale_factor) as i32;
481
+ let logical_inner_height = (inner_size.height as f64 / scale_factor) as i32;
482
+ let logical_outer_width = (outer_size.width as f64 / scale_factor) as i32;
483
+ let logical_outer_height = (outer_size.height as f64 / scale_factor) as i32;
484
+
485
+ let info = format!(
486
+ "Window Information\\n\\n\
487
+ Position: x={}, y={}\\n\\n\
488
+ Logical Size (API): {}x{} inner\\n\
489
+ Physical Size (Screen): {}x{} inner\\n\
490
+ Outer Size (Physical): {}x{}\\n\
491
+ Outer Size (Logical): {}x{}\\n\\n\
492
+ Scale Factor: {:.2} ({}% DPI)\\n\
493
+ Maximized: {}\\n\
494
+ Fullscreen: {}\\n\
495
+ Always On Top: {}\\n\\n\
496
+ Version: {}",
497
+ position.x, position.y,
498
+ logical_inner_width, logical_inner_height,
499
+ inner_size.width, inner_size.height,
500
+ outer_size.width, outer_size.height,
501
+ logical_outer_width, logical_outer_height,
502
+ scale_factor, (scale_factor * 100.0) as i32,
503
+ is_maximized,
504
+ is_fullscreen,
505
+ is_always_on_top,
506
+ env!("NPM_VERSION")
507
+ );
508
+
509
+ // Show the info in an alert
510
+ let script = format!("alert('{}')", info);
511
+ if let Err(e) = webview.evaluate_script(&script) {
512
+ eprintln!("Failed to show about dialog: {}", e);
513
+ }
514
+ }
515
+
491
516
  // Check timeout
492
517
  if let Some(deadline) = timeout_instant {
493
518
  if std::time::Instant::now() >= deadline {
@@ -0,0 +1,84 @@
1
+ (function() {
2
+ console.log('[MSGER] Initializing msger API...');
3
+
4
+ // Check if IPC is available
5
+ const hasIPC = typeof window.ipc !== 'undefined' && window.ipc.postMessage;
6
+ console.log('[MSGER] IPC available:', hasIPC);
7
+
8
+ // Helper to safely send IPC messages
9
+ function safePostMessage(data) {
10
+ try {
11
+ if (!hasIPC) {
12
+ console.error('msger API: window.ipc not available');
13
+ return false;
14
+ }
15
+ window.ipc.postMessage(JSON.stringify(data));
16
+ return true;
17
+ } catch(e) {
18
+ console.error('msger API error:', e);
19
+ return false;
20
+ }
21
+ }
22
+
23
+ window.msger = {
24
+ // Window control - only close() with window.close() workaround (IPC broken)
25
+ close: function(result) {
26
+ // IPC is broken in wry 0.47.2, so we use window.close() instead
27
+ // Result cannot be returned to parent process
28
+ try {
29
+ window.close();
30
+ return true;
31
+ } catch(e) {
32
+ console.error('window.close() failed:', e);
33
+ return false;
34
+ }
35
+ },
36
+ saveData: function(key, value) {
37
+ try {
38
+ localStorage.setItem('msger_' + key, JSON.stringify(value));
39
+ return true;
40
+ } catch(e) {
41
+ console.error('Failed to save data:', e);
42
+ return false;
43
+ }
44
+ },
45
+ loadData: function(key, defaultValue) {
46
+ try {
47
+ const item = localStorage.getItem('msger_' + key);
48
+ return item ? JSON.parse(item) : defaultValue;
49
+ } catch(e) {
50
+ console.error('Failed to load data:', e);
51
+ return defaultValue;
52
+ }
53
+ },
54
+ removeData: function(key) {
55
+ try {
56
+ localStorage.removeItem('msger_' + key);
57
+ return true;
58
+ } catch(e) {
59
+ console.error('Failed to remove data:', e);
60
+ return false;
61
+ }
62
+ },
63
+ clearData: function() {
64
+ try {
65
+ const keys = Object.keys(localStorage);
66
+ for (const key of keys) {
67
+ if (key.startsWith('msger_')) {
68
+ localStorage.removeItem(key);
69
+ }
70
+ }
71
+ return true;
72
+ } catch(e) {
73
+ console.error('Failed to clear data:', e);
74
+ return false;
75
+ }
76
+ },
77
+ // Check if running in msger environment
78
+ isAvailable: function() {
79
+ return true;
80
+ }
81
+ };
82
+
83
+ console.log('[MSGER] API initialized successfully! window.msger =', window.msger);
84
+ })();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobfrankston/msger",
3
- "version": "0.1.78",
3
+ "version": "0.1.79",
4
4
  "description": "Fast, lightweight, cross-platform message box - Rust-powered alternative to msgview",
5
5
  "type": "module",
6
6
  "main": "./index.js",