From 4f92942ccba1bbe6c6b1afb49f54c8fa6a2eea0b Mon Sep 17 00:00:00 2001 From: mysticmomba Date: Thu, 11 Jun 2026 10:43:30 -0400 Subject: [PATCH] Release! --- README.md | 124 +++++++++++++++++++++- logo.png | Bin 2468 -> 0 bytes src-tauri/Cargo.lock | 2 +- src-tauri/Cargo.toml | 6 +- src-tauri/capabilities/default.json | 16 ++- src-tauri/permissions/alert-detected.toml | 4 + src-tauri/src/lib.rs | 17 +++ src-tauri/tauri.conf.json | 15 ++- src/inject.js | 15 ++- 9 files changed, 175 insertions(+), 24 deletions(-) delete mode 100644 logo.png create mode 100644 src-tauri/permissions/alert-detected.toml diff --git a/README.md b/README.md index 441faf7..d95bb2b 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,123 @@ -# Tauri + Vanilla +# WVU Medicine Help Alert Web Client -This template should help get you started developing with Tauri in vanilla HTML, CSS and Javascript. +A lightweight Tauri-based desktop wrapper for the WVU Medicine PinPoint HelpAlert duress alarm system. Built to replace the vendor's crashing desktop client with a stable, tray-resident application that brings alerts to the foreground automatically. -## Recommended IDE Setup +## Why This Exists -- [VS Code](https://code.visualstudio.com/) + [Tauri](https://marketplace.visualstudio.com/items?itemName=tauri-apps.tauri-vscode) + [rust-analyzer](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer) +The vendor's native desktop client crashes hospital workstations. The web version works but has two problems: users get logged out due to inactivity, and browser notifications don't reliably surface when the tab is backgrounded. This app solves both by wrapping the web client in a persistent Tauri window with native alert detection. + +## How It Works + +1. The app loads PinPoint's web login page in a native webview +2. After login, the user clicks "Alerts" to navigate to the HelpAlert GWT page +3. Injected JavaScript monitors the DOM every 2 seconds for new alerts (identified by "Claim" buttons and "Elapsed Time" markers) +4. When a new alert is detected, the app calls into the Rust backend which unminimizes, focuses, and temporarily pins the window on top +5. Closing the window minimizes to the system tray instead of exiting — the app stays running and monitoring + +## Architecture + +- **Frontend:** Vendor's HelpAlert web app loaded directly in the Tauri webview, no custom UI +- **Injection:** `src/inject.js` handles `window.open()` interception (alerts page opens in same window instead of a new tab) and DOM-based alert detection +- **Backend:** Rust/Tauri handles system tray, window management, and the `alert_detected` command that brings the window to the foreground +- **IPC:** JavaScript detects alerts → invokes Tauri command → Rust brings window to front + +## Prerequisites + +- Rust toolchain (`rustup`) +- Tauri CLI: `cargo install tauri-cli --version "^2"` +- For Windows cross-compilation from Linux: `x86_64-pc-windows-gnu` target +- WiX Toolset (for MSI installer generation) + +## WRY Fork Requirement + +This project requires a forked version of [wry](https://github.com/tauri-apps/wry) (Tauri's WebView library). The vendor's HelpAlert page uses GWT (Google Web Toolkit), which declares a global `ipc` variable. WRY also declares `window.ipc` for its IPC bridge, causing a collision that crashes the GWT page. + +The fork renames WRY's `ipc` to `__wry_ipc` in `src/webview2/mod.rs` (line 897), resolving the collision. + +The fork is referenced in `src-tauri/Cargo.toml`: + +```toml +[patch.crates-io] +wry = { path = "../../wry" } +``` + +Adjust the path to match your local wry fork location. + +## Building + +### Development + +```bash +cargo tauri dev +``` + +### Release (Windows cross-compile from Linux) + +```bash +cargo tauri build -- --target x86_64-pc-windows-gnu +``` + +The MSI installer will be in `src-tauri/target/x86_64-pc-windows-gnu/release/bundle/msi/`. + +The installer bundles the WebView2 runtime offline, so target machines don't need internet access. + +### Standalone EXE + +The raw `.exe` is also available at `src-tauri/target/x86_64-pc-windows-gnu/release/` but requires WebView2 to already be installed on the target machine. + +## Project Structure + +``` +duress/ +├── src/ +│ ├── index.html # Fallback loading page (shown briefly during startup) +│ └── inject.js # Injected JS: window.open intercept + alert detection +├── src-tauri/ +│ ├── Cargo.toml # Rust dependencies (includes wry fork patch) +│ ├── tauri.conf.json # App config: name, version, bundling, security +│ ├── capabilities/ +│ │ └── default.json # Tauri v2 permissions and remote domain IPC access +│ ├── permissions/ +│ │ └── alert-detected.toml # Custom command permission +│ ├── src/ +│ │ ├── main.rs # Windows entrypoint +│ │ └── lib.rs # App logic: tray, window management, alert_detected command +│ └── icons/ # App icons (generated via cargo tauri icon) +└── README.md +``` + +## Configuration + +### Adding the vendor URL + +The vendor URL is set in `lib.rs`: + +```rust +let url = WebviewUrl::External("https://helpalert.wvumedicine.org".parse().unwrap()); +``` + +### Alert detection tuning + +Alert detection in `inject.js` looks for lines preceding "Geo:" in the page text. If the vendor changes their alert format, update the detection logic in the `setInterval` callback. + +### Polling interval + +The DOM is checked every 2000ms (2 seconds). Adjust the `setInterval` delay in `inject.js` if needed. + +## Deployment Notes + +- Target machines need Windows 10 or 11 +- The MSI installer includes WebView2 runtime (offline) — no internet required +- The app installs per-machine by default +- Users should be instructed that closing the window minimizes to tray, and "Quit" in the tray menu is the actual exit + +## Known Limitations + +- Alert detection relies on DOM text parsing ("Geo:" pattern preceding alert names). If the vendor updates their GWT UI significantly, detection may need adjustment. +- GWT class names are obfuscated and change between builds, so selectors are text-based rather than class-based. +- Session keepalive has not been implemented yet — if the session times out, the user must re-authenticate manually. Testing suggests the session may persist indefinitely while the app is running. +- The wry fork must be kept in sync with upstream when updating Tauri. + +## License + +Internal use — WVU Medicine / Racoon City Technologies. \ No newline at end of file diff --git a/logo.png b/logo.png deleted file mode 100644 index 78829fc042ec909bbbf045722fe43bee32dcef6d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2468 zcmV;V30wAwP)CD_u8fV?9A}FJ@C6p{`>RotpNZ1`2P9t_}{Pm^WW~U z9Q*Fn`Q*Iw$#(ts>G8i<^225L;IcQNW+i zlK=n+_DMuRRCwC#nhS2?KoCT`tCLa(qK=CRB zD_2!(grGxO0qT3=^afNFl?>l-4!508C^cW7jLp5Ohc{Ky4($ zSVBL*i5P(N0@NB}^~?~ordbF;ML)l6LkvK20c!8;>Tx0mAguuPR-#;tJxE&DWCHj= zxUqj7&LxN<9|4Lti7|m*A%E0 z$bM-K)%*o08nVU4ni_!g0xGt}_OT3DAuj-*@sPKoi;*Sk1{x#yi_E^7d_pyI16z}C zoXyme$Qodd<(2L))d#0POsZCbOjlK8{aDI>R6&K*0Km@eR36L1pf|$D*usOEn%P0N ztL5=DK%^!o6P%rg?;u;tQ)nVv05$mz@9p3tw38>Xx1n!E2PfN=jS-*gDT4R0zvQl$ zAigu&p(7=qS_R`&5!2p;4oFdv&@6NH^GJpS)w|J~l1?5K_eQ8e;K zlT5}VHj{Z6{{JIey+W!sLlRHiblGQ7i;`+V2v?qr$#Y{8#}`2l01^q{3*CuujT&zBj%%xe4CRx0){jg$%x}u0Q<}yA72TB4lWL~6AGz=ivhS) zI2_f*fNq7w9*z%YLLed|MkYLRb!>DYOxAE*uo5bbIxfBtE?0F-^df9pP!&LI@QoPG zf5)lnPc>qlD^K*%bt6HwH$p+S~CBrjf%}wL5NW}ubP=cH3 zB=rZOV&CheKO}AgqXO_LVUf7X6Dnc6)*p4im6reer&a)tH6~&1A?*O}={9;HY?C?hwf%qk%P||+_bo%o* z4k4AWW2y$`@rokQ~@fB}`T+QYfn0+0glo&c%C|F5K z`yP0`zIiLcxyKxN0SGr@?D|VeTJ#*HuUBr?>I?xOWrhAgx_0CpqR z{Mmm>t$S}pk3+NqApjO5#_jo99lv}o6Mi0a@8$=f@=xyonZ6i$I{Ie{kg1D308-Kd4=p@m`(3r;yK!yjO?5gjjyH@C}gjMQ>5dc~E-tTsyZ`M2uMPC}M#ZUqu7Y{&D zz5W3Gx1ys0&`o1#M&SlPmb0?Fqz@(WmG4SZZ#I{-r+Q)z0FT1k+W*%WcmPTSEaNon z7LPRmotlOqQ()KusJyNJuZA0T07{tVK|0tIsN7(DN}7GyD~VDAP|WVY)6P z3hc)?>JO&!{6%8HE^_`DJXh$i5)F2h$0iAMXw;GS5eI^O*vc-H1Lz|T9}lcS+o-`` zxT1;7{h+4_ja5CYr37)9iob9LE0|WQ@Ud3*K^P}x7_l96!i2wq5Qz+dL38oq`f3YUti1|x}_(sf^;P{Og7a_toVjz{l zk%GQkdiX}nPXzLfm@mP6BgRTt@Qs+iSdqV!8#@^+mFG$@pNn$eF~`Sl#G1&EIcl_T z+2r?*685SQNC2t?^EoIl5jcD!_FEY~nK!(hLRj#Pm|qR&=ZLYF-$9}5_bc#?m@C12 zN>Hoee3ojihL6sO0inWgrqAV9u~n`F^Vxy=k~Kb=a;xEd7Rr6oG@lmKYB-<58>-pUH8