├── .github └── workflows │ ├── deno.yml │ └── publish-jsr.yml ├── .gitignore ├── LICENSE ├── README.md ├── bootstrap.bat ├── bootstrap.sh ├── deno.json ├── deps.ts ├── examples ├── custom_file_handler │ ├── assets │ │ ├── test_app.js │ │ └── webui.jpeg │ ├── custom_file_handler.ts │ └── index.html ├── custom_web_server │ ├── custom_web_server.ts │ ├── index.html │ ├── second.html │ └── simple_web_server.py ├── hello_world │ └── hello_world.ts └── send_raw_binary │ ├── send_raw_binary.ts │ └── webui.jpeg ├── img ├── cppcon_2019.png ├── screenshot.png ├── webui.png ├── webui_deno_example.png └── webui_diagram.png ├── mod.ts └── src ├── bootstrap.bat ├── bootstrap.sh ├── lib.ts ├── types.ts ├── utils.ts └── webui.ts /.github/workflows/deno.yml: -------------------------------------------------------------------------------- 1 | name: Deno 2 | 3 | on: 4 | push: 5 | branches: ["main"] 6 | pull_request: 7 | branches: ["main"] 8 | 9 | permissions: 10 | contents: read 11 | 12 | jobs: 13 | test: 14 | runs-on: ${{ matrix.os }} 15 | strategy: 16 | matrix: 17 | os: [ubuntu-latest, macOS-latest, windows-latest] 18 | 19 | steps: 20 | - name: Setup repo 21 | uses: actions/checkout@v4 22 | 23 | - name: Setup Rust 24 | uses: dtolnay/rust-toolchain@master 25 | with: 26 | toolchain: 1.85.0 # Specify minimum Rust version that supports 2024 edition 27 | 28 | - name: Setup Deno 29 | uses: denoland/setup-deno@v2 30 | 31 | - name: Verify formatting 32 | if: runner.os == 'Linux' 33 | run: deno fmt --check 34 | 35 | - name: Run linter 36 | if: runner.os == 'Linux' 37 | run: deno lint 38 | 39 | - name: Run doc linter 40 | if: runner.os == 'Linux' 41 | run: deno doc --lint mod.ts 42 | 43 | - name: Run type checker 44 | if: runner.os == 'Linux' 45 | run: deno check . 46 | -------------------------------------------------------------------------------- /.github/workflows/publish-jsr.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | publish: 9 | runs-on: ubuntu-latest 10 | 11 | permissions: 12 | contents: read 13 | id-token: write 14 | 15 | steps: 16 | - uses: actions/checkout@v4 17 | 18 | - name: Publish package 19 | run: npx jsr publish 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Deno binaries 2 | src/webui-windows-msvc-x64/ 3 | src/webui-windows-msvc-arm/ 4 | src/webui-windows-msvc-arm64/ 5 | src/webui-macos-clang-x64/ 6 | src/webui-macos-clang-arm/ 7 | src/webui-macos-clang-arm64/ 8 | src/webui-linux-gcc-x64/ 9 | src/webui-linux-gcc-arm/ 10 | src/webui-linux-gcc-arm64/ 11 | *.dll 12 | *.so 13 | *.dylib 14 | 15 | # Archives 16 | *.zip 17 | *.tar 18 | *.gz 19 | 20 | # Build 21 | *.exe 22 | *.ilk 23 | *.pdb 24 | *.exp 25 | *.res 26 | *.out 27 | *.def 28 | *.obj 29 | *.iobj 30 | *.o 31 | 32 | # Logs 33 | *.log 34 | *.logs 35 | *.tlog 36 | 37 | # IDE 38 | .vscode/ 39 | .vs/ 40 | .zed/ 41 | 42 | # Visual Studio 43 | .idea/ 44 | *.recipe 45 | *.idb 46 | 47 | # Visual Studio for Mac 48 | .idea/ 49 | 50 | # Visual Studio cache files 51 | ipch/ 52 | *.dbmdl 53 | *.dbproj.schemaview 54 | 55 | # Others 56 | .builds 57 | *~* 58 | *.cache 59 | *.swp 60 | *.bak 61 | *.tmp 62 | *.swp 63 | *.userosscache 64 | *.err 65 | *.vspscc 66 | *.vssscc 67 | *.pidb 68 | *.svclog 69 | *.scc 70 | 71 | # NuGet 72 | packages/ 73 | !packages/repositories.config 74 | *.nupkg 75 | 76 | # Microsoft Azure Build Output 77 | csx/ 78 | *.build.csdef 79 | 80 | # User-specific files 81 | *.suo 82 | *.user 83 | *.userprefs 84 | *.sln.docstates 85 | 86 | # Python 87 | __pycache__/ 88 | dist/ 89 | webui2.egg-info/ 90 | 91 | # Rust 92 | target/ 93 | *.lock 94 | 95 | # Broken NTFS 96 | nul 97 | 98 | # Zig 99 | zig-cache/ 100 | zig-out/ 101 | 102 | # macOS 103 | .DS_Store 104 | .DS_Store? 105 | ._* 106 | .Spotlight-V100 107 | .Trashes 108 | ehthumbs.db 109 | Thumbs.db 110 | 111 | # User-specific private settings 112 | *.DotSettings.user 113 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Hassan Draga 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | ![Logo](https://raw.githubusercontent.com/webui-dev/webui-logo/main/webui_deno.png) 4 | 5 | # Deno-WebUI v2.5.8 (Beta) 6 | 7 | [last-commit]: https://img.shields.io/github/last-commit/webui-dev/webui?style=for-the-badge&logo=github&logoColor=C0CAF5&labelColor=414868 8 | [release-version]: https://img.shields.io/github/v/tag/webui-dev/webui?style=for-the-badge&logo=webtrees&logoColor=C0CAF5&labelColor=414868&color=7664C6 9 | [license]: https://img.shields.io/github/license/webui-dev/webui?style=for-the-badge&logo=opensourcehardware&label=License&logoColor=C0CAF5&labelColor=414868&color=8c73cc 10 | 11 | [![][last-commit]](https://github.com/webui-dev/deno-webui/pulse) 12 | [![][release-version]](https://github.com/webui-dev/deno-webui/releases/latest) 13 | [![][license]](https://github.com/webui-dev/deno-webui/blob/main/LICENSE) 14 | 15 | > Use any web browser or WebView as GUI, with your preferred language in the 16 | > backend and modern web technologies in the frontend, all in a lightweight 17 | > portable library. 18 | 19 | ![Screenshot](https://raw.githubusercontent.com/webui-dev/webui-logo/main/screenshot.png) 20 | 21 |
22 | 23 | ## Download 24 | 25 | - [Latest Stable Release](https://github.com/webui-dev/deno-webui/releases) 26 | 27 | ## Features 28 | 29 | - Portable (_Needs only a web browser or a WebView at runtime_) 30 | - Lightweight (_Few Kb library_) & Small memory footprint 31 | - Fast binary communication protocol 32 | - Multi-platform & Multi-Browser 33 | - Using private profile for safety 34 | - Cross-platform WebView 35 | 36 | ## Screenshot 37 | 38 | This 39 | [hello world example](https://github.com/webui-dev/deno-webui/tree/main/examples/hello_world) 40 | is written in Deno using WebUI as the GUI library. 41 | 42 | ![ScreenShot](img/webui_deno_example.png) 43 | 44 | ## Installation 45 | 46 | Specific version: 47 | 48 | ```js 49 | import { WebUI } from "jsr:@webui/deno-webui@2.5.8"; 50 | // Or 51 | import { WebUI } from "https://deno.land/x/webui@2.5.8/mod.ts"; 52 | ``` 53 | 54 | Latest version: 55 | 56 | ```js 57 | import { WebUI } from "jsr:@webui/deno-webui"; 58 | // Or 59 | import { WebUI } from "https://deno.land/x/webui/mod.ts"; 60 | ``` 61 | 62 | ## Minimal Example 63 | 64 | ```js 65 | import { WebUI } from "jsr:@webui/deno-webui"; 66 | 67 | const myWindow = new WebUI(); 68 | await myWindow.show( 69 | ' Hello World! ', 70 | ); 71 | await WebUI.wait(); 72 | ``` 73 | 74 | ```sh 75 | deno run --allow-read --allow-write --allow-net --allow-env --allow-ffi minimal.ts 76 | ``` 77 | 78 | [More examples](https://github.com/webui-dev/deno-webui/tree/main/examples) 79 | 80 | ## Documentation 81 | 82 | - [Online Documentation](https://webui.me/docs/2.5/#/) 83 | 84 | ## CppCon 2019 Presentation 85 | 86 | [Borislav Stanimirov](https://ibob.bg/) explained at 87 | [C++ Conference 2019 (_YouTube_)](https://www.youtube.com/watch?v=bbbcZd4cuxg) 88 | how beneficial it is to use the web browser as GUI. 89 | 90 | 93 | 94 | ![ScreenShot](img/cppcon_2019.png) 95 | 96 | ## UI & The Web Technologies 97 | 98 | Web application UI design is not just about how a product looks but how it 99 | works. Using web technologies in your UI makes your product modern and 100 | professional, And a well-designed web application will help you make a solid 101 | first impression on potential customers. Great web application design also 102 | assists you in nurturing leads and increasing conversions. In addition, it makes 103 | navigating and using your web app easier for your users. 104 | 105 | ## Why Use Web Browser? 106 | 107 | Today's web browsers have everything a modern UI needs. Web browsers are very 108 | sophisticated and optimized. Therefore, using it as a GUI will be an excellent 109 | choice. While old legacy GUI lib is complex and outdated, a WebView-based app is 110 | still an option. However, a WebView needs a huge SDK to build and many 111 | dependencies to run, and it can only provide some features like a real web 112 | browser. That is why WebUI uses real web browsers to give you full features of 113 | comprehensive web technologies while keeping your software lightweight and 114 | portable. 115 | 116 | ## How does it work? 117 | 118 | ![ScreenShot](img/webui_diagram.png) 119 | 120 | Think of WebUI like a WebView controller, but instead of embedding the WebView 121 | controller in your program, which makes the final program big in size, and 122 | non-portable as it needs the WebView runtimes. Instead, by using WebUI, you use 123 | a tiny static/dynamic library to run any installed web browser and use it as 124 | GUI, which makes your program small, fast, and portable. **All it needs is a web 125 | browser**. 126 | 127 | ## Runtime Dependencies Comparison 128 | 129 | | | Tauri / WebView | Qt | WebUI | 130 | | ------------------------------- | ----------------- | -------------------------- | ------------------- | 131 | | Runtime Dependencies on Windows | _WebView2_ | _QtCore, QtGui, QtWidgets_ | **_A Web Browser_** | 132 | | Runtime Dependencies on Linux | _GTK3, WebKitGTK_ | _QtCore, QtGui, QtWidgets_ | **_A Web Browser_** | 133 | | Runtime Dependencies on macOS | _Cocoa, WebKit_ | _QtCore, QtGui, QtWidgets_ | **_A Web Browser_** | 134 | 135 | ## Supported Web Browsers 136 | 137 | | Browser | Windows | macOS | Linux | 138 | | --------------- | --------------- | ------------- | --------------- | 139 | | Mozilla Firefox | ✔️ | ✔️ | ✔️ | 140 | | Google Chrome | ✔️ | ✔️ | ✔️ | 141 | | Microsoft Edge | ✔️ | ✔️ | ✔️ | 142 | | Chromium | ✔️ | ✔️ | ✔️ | 143 | | Yandex | ✔️ | ✔️ | ✔️ | 144 | | Brave | ✔️ | ✔️ | ✔️ | 145 | | Vivaldi | ✔️ | ✔️ | ✔️ | 146 | | Epic | ✔️ | ✔️ | _not available_ | 147 | | Apple Safari | _not available_ | _coming soon_ | _not available_ | 148 | | Opera | _coming soon_ | _coming soon_ | _coming soon_ | 149 | 150 | ## Supported Languages 151 | 152 | | Language | v2.4.0 API | v2.5.0 API | Link | 153 | | -------------- | -------------- | -------------- | ----------------------------------------------------------------- | 154 | | Python | ✔️ | _not complete_ | [Python-WebUI](https://github.com/webui-dev/python-webui) | 155 | | Go | ✔️ | _not complete_ | [Go-WebUI](https://github.com/webui-dev/go-webui) | 156 | | Zig | ✔️ | _not complete_ | [Zig-WebUI](https://github.com/webui-dev/zig-webui) | 157 | | Nim | ✔️ | _not complete_ | [Nim-WebUI](https://github.com/webui-dev/nim-webui) | 158 | | V | ✔️ | _not complete_ | [V-WebUI](https://github.com/webui-dev/v-webui) | 159 | | Rust | _not complete_ | _not complete_ | [Rust-WebUI](https://github.com/webui-dev/rust-webui) | 160 | | TS / JS (Deno) | ✔️ | _not complete_ | [Deno-WebUI](https://github.com/webui-dev/deno-webui) | 161 | | TS / JS (Bun) | _not complete_ | _not complete_ | [Bun-WebUI](https://github.com/webui-dev/bun-webui) | 162 | | Swift | _not complete_ | _not complete_ | [Swift-WebUI](https://github.com/webui-dev/swift-webui) | 163 | | Odin | _not complete_ | _not complete_ | [Odin-WebUI](https://github.com/webui-dev/odin-webui) | 164 | | Pascal | _not complete_ | _not complete_ | [Pascal-WebUI](https://github.com/webui-dev/pascal-webui) | 165 | | Purebasic | _not complete_ | _not complete_ | [Purebasic-WebUI](https://github.com/webui-dev/purebasic-webui) | 166 | | - | | | | 167 | | Common Lisp | _not complete_ | _not complete_ | [cl-webui](https://github.com/garlic0x1/cl-webui) | 168 | | Delphi | _not complete_ | _not complete_ | [WebUI4Delphi](https://github.com/salvadordf/WebUI4Delphi) | 169 | | C# | _not complete_ | _not complete_ | [WebUI4CSharp](https://github.com/salvadordf/WebUI4CSharp) | 170 | | WebUI.NET | _not complete_ | _not complete_ | [WebUI.NET](https://github.com/Juff-Ma/WebUI.NET) | 171 | | QuickJS | _not complete_ | _not complete_ | [QuickUI](https://github.com/xland/QuickUI) | 172 | | PHP | _not complete_ | _not complete_ | [PHPWebUiComposer](https://github.com/KingBes/php-webui-composer) | 173 | 174 | ## Supported WebView 175 | 176 | | WebView | Status | 177 | | ----------------- | ------ | 178 | | Windows WebView2 | ✔️ | 179 | | Linux GTK WebView | ✔️ | 180 | | macOS WKWebView | ✔️ | 181 | 182 | ### License 183 | 184 | > Licensed under MIT License. 185 | 186 | ### Stargazers 187 | 188 | [![Stargazers repo roster for @webui-dev/deno-webui](https://reporoster.com/stars/webui-dev/deno-webui)](https://github.com/webui-dev/deno-webui/stargazers) 189 | -------------------------------------------------------------------------------- /bootstrap.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | cd src 3 | call bootstrap.bat %* 4 | cd .. 5 | -------------------------------------------------------------------------------- /bootstrap.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd src 3 | sh bootstrap.sh "$@" 4 | cd .. 5 | -------------------------------------------------------------------------------- /deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@webui/deno-webui", 3 | "version": "2.5.8", 4 | "exports": "./mod.ts" 5 | } 6 | -------------------------------------------------------------------------------- /deps.ts: -------------------------------------------------------------------------------- 1 | // Deno WebUI 2 | // Resolves the path to the required native WebUI library, 3 | // ensuring it is downloaded to a central cache if needed. 4 | 5 | import { ensureWebUiLib } from "./src/utils.ts"; 6 | 7 | // Determine the base library filename based 8 | // on the current operating system and architecture. 9 | function getBaseLibName(): string { 10 | let baseName: string; 11 | switch (Deno.build.os) { 12 | case "windows": 13 | baseName = "webui-2.dll"; 14 | // Validate architecture for Windows 15 | if (Deno.build.arch !== "x86_64" && Deno.build.arch !== "aarch64") { 16 | throw new Error( 17 | `Unsupported architecture ${Deno.build.arch} for Windows`, 18 | ); 19 | } 20 | break; 21 | case "darwin": // macOS 22 | baseName = "libwebui-2.dylib"; 23 | // Validate architecture for macOS 24 | if (Deno.build.arch !== "x86_64" && Deno.build.arch !== "aarch64") { 25 | throw new Error( 26 | `Unsupported architecture ${Deno.build.arch} for macOS`, 27 | ); 28 | } 29 | break; 30 | default: // Linux and other Unix-like OSes 31 | baseName = "libwebui-2.so"; 32 | // Validate architecture for Linux/others 33 | if (Deno.build.arch !== "x86_64" && Deno.build.arch !== "aarch64") { 34 | throw new Error( 35 | `Unsupported architecture ${Deno.build.arch} for ${Deno.build.os}`, 36 | ); 37 | } 38 | break; 39 | } 40 | return baseName; 41 | } 42 | 43 | // Determine the required base filename 44 | const baseLibName = getBaseLibName(); 45 | 46 | // Ensure the library exists in the cache (downloads if needed) 47 | // and export the resolved path. 48 | // This promise resolves to the final path of the library file. 49 | export const libPath = await ensureWebUiLib(baseLibName); 50 | 51 | // Optional: Export the base name too if needed elsewhere 52 | export { baseLibName }; 53 | -------------------------------------------------------------------------------- /examples/custom_file_handler/assets/test_app.js: -------------------------------------------------------------------------------- 1 | // deno-lint-ignore no-unused-vars 2 | function test_app() { 3 | alert("Hello from test_app.js"); 4 | } 5 | -------------------------------------------------------------------------------- /examples/custom_file_handler/assets/webui.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webui-dev/deno-webui/684554f83d6d791cedd72c50fb10e667b389d3ac/examples/custom_file_handler/assets/webui.jpeg -------------------------------------------------------------------------------- /examples/custom_file_handler/custom_file_handler.ts: -------------------------------------------------------------------------------- 1 | // To run this script: 2 | // deno run --allow-read --allow-write --allow-net --allow-env --allow-ffi custom_file_handler.ts 3 | 4 | // To import from local (Debugging and Development) 5 | // import { WebUI } from "../../mod.ts"; 6 | 7 | // To import from online package registry (Production) 8 | import { WebUI } from "jsr:@webui/deno-webui@2.5.8"; // import {WebUI} from "https://deno.land/x/webui@2.5.8/mod.ts"; 9 | 10 | // Return HTTP header + file raw binary content 11 | const getFile = async ( 12 | contentType: string, 13 | filename: string, 14 | ): Promise => { 15 | const content = await Deno.readFile(filename); 16 | const header = `HTTP/1.1 200 OK\r\nContent-Type: ${contentType}\r\n\r\n`; 17 | const headerBytes = new TextEncoder().encode(header); 18 | const response = new Uint8Array(headerBytes.length + content.length); 19 | response.set(headerBytes); 20 | response.set(content, headerBytes.length); 21 | return response; 22 | }; 23 | 24 | // Set a custom files handler 25 | async function myFileHandler(myUrl: URL) { 26 | console.log(`File: ${myUrl.pathname}`); 27 | // Index example 28 | if (myUrl.pathname === "/index.html" || myUrl.pathname === "/") { 29 | return await getFile("text/html", "index.html"); 30 | } 31 | // Custom text string example 32 | if (myUrl.pathname === "/test") { 33 | return "HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\nHello"; 34 | } 35 | // File examples 36 | if (myUrl.pathname === "/assets/test_app.js") { 37 | return await getFile("application/javascript", "assets/test_app.js"); 38 | } 39 | if (myUrl.pathname === "/assets/webui.jpeg") { 40 | return await getFile("image/jpeg", "assets/webui.jpeg"); 41 | } 42 | // Error 404 example 43 | return "HTTP/1.1 404 Not Found"; 44 | } 45 | 46 | // Create new window 47 | const myWindow = new WebUI(); 48 | 49 | // Bind Exit 50 | myWindow.bind("exit", () => { 51 | // Close all windows and exit 52 | WebUI.exit(); 53 | }); 54 | 55 | // Set files handler 56 | // Note: Should be called before `.show()` 57 | myWindow.setFileHandler(myFileHandler); 58 | 59 | // Show the window 60 | await myWindow.showBrowser("index.html", WebUI.Browser.AnyBrowser); 61 | 62 | // Wait until all windows get closed 63 | await WebUI.wait(); 64 | 65 | console.log("Thank you."); 66 | -------------------------------------------------------------------------------- /examples/custom_file_handler/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | WebUI 2 - Deno File Handler Example 7 | 8 | 9 |

WebUI 2 - Deno File Handler Example

10 |
11 | 12 |
13 | - 16 | 17 | 18 | -------------------------------------------------------------------------------- /examples/custom_web_server/custom_web_server.ts: -------------------------------------------------------------------------------- 1 | // To run this script: 2 | // deno run --allow-read --allow-write --allow-net --allow-env --allow-ffi --allow-run=deno custom_web_server.ts 3 | 4 | // To import from local (Debugging and Development) 5 | // import { WebUI } from "../../mod.ts"; 6 | 7 | // To import from online package registry (Production) 8 | import { WebUI } from "jsr:@webui/deno-webui@2.5.8"; // import {WebUI} from "https://deno.land/x/webui@2.5.8/mod.ts"; 9 | 10 | function allEvents(e: WebUI.Event) { 11 | /* 12 | e.window: WebUI; 13 | e.eventType: WebUI.EventType; 14 | e.element: string; 15 | */ 16 | console.log(`\nallEvents: window = '${e.window}'`); 17 | console.log(`allEvents: eventType = '${e.eventType}'`); 18 | console.log(`allEvents: element = '${e.element}'`); 19 | switch (e.eventType) { 20 | case WebUI.EventType.Disconnected: 21 | // Window disconnection event 22 | console.log(`Window closed.`); 23 | break; 24 | case WebUI.EventType.Connected: 25 | // Window connection event 26 | console.log(`Window connected.`); 27 | break; 28 | case WebUI.EventType.MouseClick: 29 | // Mouse click event 30 | console.log(`Mouse click.`); 31 | break; 32 | case WebUI.EventType.Navigation: { 33 | // Window navigation event 34 | const url = e.arg.string(0); 35 | console.log(`Navigation to '${url}'`); 36 | // Because we used `webui_bind(MyWindow, "", events);` 37 | // WebUI will block all `href` link clicks and sent here instead. 38 | // We can then control the behaviour of links as needed. 39 | e.window.navigate(url); 40 | break; 41 | } 42 | case WebUI.EventType.Callback: 43 | // Function call event 44 | console.log(`Function call.`); 45 | break; 46 | } 47 | } 48 | 49 | function myBackendFunc(e: WebUI.Event) { 50 | const a = e.arg.number(0); // First argument 51 | const b = e.arg.number(1); // Second argument 52 | const c = e.arg.number(2); // Third argument 53 | console.log(`\nFirst argument: ${a}`); 54 | console.log(`Second argument: ${b}`); 55 | console.log(`Third argument: ${c}`); 56 | } 57 | 58 | // Create new window 59 | const myWindow = new WebUI(); 60 | 61 | // Bind All Events 62 | myWindow.bind("", allEvents); 63 | 64 | // Bind Backend Function 65 | myWindow.bind("myBackendFunc", myBackendFunc); 66 | 67 | // Bind Exit Function 68 | myWindow.bind("exit", () => { 69 | // Close all windows and exit 70 | WebUI.exit(); 71 | }); 72 | 73 | // Set the web-server/WebSocket port that WebUI should 74 | // use. This means `webui.js` will be available at: 75 | // http://localhost:MY_PORT_NUMBER/webui.js 76 | myWindow.setPort(8081); 77 | 78 | // Start our custom web server using Python script `python simple_web_server.py`. 79 | // Or using `file-server` module. 80 | const webServer = new Deno.Command("deno", { 81 | args: ["-RNS", "jsr:@std/http/file-server", "-p", "8080"], 82 | }).spawn(); 83 | await new Promise((r) => setTimeout(r, 500)); 84 | 85 | // Show a new window and point to our custom web server 86 | // Assuming the custom web server is running on port 87 | // 8080... 88 | await myWindow.showBrowser("http://localhost:8080/", WebUI.Browser.AnyBrowser); 89 | 90 | // Wait until all windows get closed 91 | await WebUI.wait(); 92 | 93 | // Stop the web server 94 | webServer.kill("SIGTERM"); 95 | await webServer.status; 96 | 97 | console.log("Thank you."); 98 | -------------------------------------------------------------------------------- /examples/custom_web_server/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | WebUI - Custom Web-Server Example (C) 6 | 7 | 8 | 9 | 10 |

Custom Web-Server Example (C)

11 |

12 | This HTML page is handled by a custom Web-Server other than WebUI.
13 | This window is connected to the back-end because we used:

http://localhost:8081/webui.js
15 |

16 |

Simple link example (Local file)

17 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /examples/custom_web_server/second.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | WebUI - Custom Web-Server second page (C) 6 | 7 | 8 | 9 | 10 |

This is the second page !

11 |

Back

12 | 13 | 14 | -------------------------------------------------------------------------------- /examples/custom_web_server/simple_web_server.py: -------------------------------------------------------------------------------- 1 | import http.server 2 | import socketserver 3 | 4 | PORT = 8080 5 | 6 | Handler = http.server.SimpleHTTPRequestHandler 7 | 8 | with socketserver.TCPServer(("", PORT), Handler) as httpd: 9 | print(f"Server started at http://localhost:{PORT}") 10 | httpd.serve_forever() 11 | -------------------------------------------------------------------------------- /examples/hello_world/hello_world.ts: -------------------------------------------------------------------------------- 1 | // To run this script: 2 | // deno run --allow-read --allow-write --allow-net --allow-env --allow-ffi hello_world.ts 3 | 4 | // To import from local (Debugging and Development) 5 | // import { WebUI } from "../../mod.ts"; 6 | 7 | // To import from online package registry (Production) 8 | import { WebUI } from "jsr:@webui/deno-webui@2.5.8"; // import {WebUI} from "https://deno.land/x/webui@2.5.8/mod.ts"; 9 | 10 | const myHtml = ` 11 | 12 | 13 | 14 | WebUI 2 - Deno Hello World Example 15 | 41 | 42 | 43 |

WebUI 2 - Deno Hello World


44 | A:

45 | B:

46 |
Result: ?


47 | - - 48 | 71 | 72 | `; 73 | 74 | function checkResult(e: WebUI.Event) { 75 | const a = e.arg.number(0); // First argument 76 | const b = e.arg.number(1); // Second argument 77 | const res = e.arg.number(2); // Third argument 78 | if ((a + b) == res) { 79 | return `Correct: ${a} + ${b} = ${res}`; 80 | } else { 81 | return `Incorrect: ${a} + ${b} != ${res}`; 82 | } 83 | } 84 | 85 | async function calculate(e: WebUI.Event) { 86 | // Run JavaScript and wait for response 87 | const getA = await e.window.script("return get_A()").catch((error) => { 88 | console.error(`Error in the JavaScript: ${error}`); 89 | return ""; 90 | }); 91 | const getB = await e.window.script("return get_B()").catch((error) => { 92 | console.error(`Error in the JavaScript: ${error}`); 93 | return ""; 94 | }); 95 | 96 | // Calculate 97 | const result = parseInt(getA) + parseInt(getB); 98 | 99 | // Run JavaScript without waiting for response (Quick) 100 | e.window.run(`set_result(${result});`); 101 | } 102 | 103 | // Create new window 104 | const myWindow = new WebUI(); 105 | 106 | // Bind 107 | myWindow.bind("calculate", calculate); 108 | myWindow.bind("checkResult", checkResult); 109 | myWindow.bind("exit", () => { 110 | // Close all windows and exit 111 | WebUI.exit(); 112 | }); 113 | 114 | // Show the window 115 | await myWindow.showBrowser(myHtml, WebUI.Browser.AnyBrowser); // Or await myWindow.show('./myFile.html'); 116 | 117 | // Wait until all windows get closed 118 | await WebUI.wait(); 119 | 120 | console.log("Thank you."); 121 | -------------------------------------------------------------------------------- /examples/send_raw_binary/send_raw_binary.ts: -------------------------------------------------------------------------------- 1 | // To run this script: 2 | // deno run --allow-read --allow-write --allow-net --allow-env --allow-ffi send_raw_binary.ts 3 | 4 | // To import from local (Debugging and Development) 5 | // import { WebUI } from "../../mod.ts"; 6 | 7 | // To import from online package registry (Production) 8 | import { WebUI } from "jsr:@webui/deno-webui@2.5.8"; // import {WebUI} from "https://deno.land/x/webui@2.5.8/mod.ts"; 9 | 10 | const myHtml = ` 11 | 12 | 13 | 14 | WebUI 2 - Deno Send Raw Binary Example 15 | 41 | 42 | 43 |

WebUI 2 - Deno Send Raw Binary Example

44 |
45 |
Received 0 bytes from backend
46 |
47 | Image 48 |
49 | - - 50 | 67 | 68 | `; 69 | 70 | function get_raw_data(e: WebUI.Event) { 71 | const rawData = new Uint8Array([0x01, 0x02, 0x03]); 72 | console.log(`Sending ${rawData.byteLength} bytes to UI...`); 73 | e.window.sendRaw("processRawData", rawData); 74 | } 75 | 76 | async function get_raw_picture(e: WebUI.Event) { 77 | const pictureRaw = await Deno.readFile("./webui.jpeg"); 78 | console.log(`Sending picture file (${pictureRaw.byteLength} bytes) to UI...`); 79 | e.window.sendRaw("setRawImage", pictureRaw); 80 | } 81 | 82 | // Create new window 83 | const myWindow = new WebUI(); 84 | 85 | // Bind 86 | myWindow.bind("get_raw_data", get_raw_data); 87 | myWindow.bind("get_raw_picture", get_raw_picture); 88 | myWindow.bind("exit", () => { 89 | // Close all windows and exit 90 | WebUI.exit(); 91 | }); 92 | 93 | // Show the window 94 | await myWindow.showBrowser(myHtml, WebUI.Browser.AnyBrowser); // Or await myWindow.show('./myFile.html'); 95 | 96 | // Wait until all windows get closed 97 | await WebUI.wait(); 98 | 99 | console.log("Thank you."); 100 | -------------------------------------------------------------------------------- /examples/send_raw_binary/webui.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webui-dev/deno-webui/684554f83d6d791cedd72c50fb10e667b389d3ac/examples/send_raw_binary/webui.jpeg -------------------------------------------------------------------------------- /img/cppcon_2019.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webui-dev/deno-webui/684554f83d6d791cedd72c50fb10e667b389d3ac/img/cppcon_2019.png -------------------------------------------------------------------------------- /img/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webui-dev/deno-webui/684554f83d6d791cedd72c50fb10e667b389d3ac/img/screenshot.png -------------------------------------------------------------------------------- /img/webui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webui-dev/deno-webui/684554f83d6d791cedd72c50fb10e667b389d3ac/img/webui.png -------------------------------------------------------------------------------- /img/webui_deno_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webui-dev/deno-webui/684554f83d6d791cedd72c50fb10e667b389d3ac/img/webui_deno_example.png -------------------------------------------------------------------------------- /img/webui_diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webui-dev/deno-webui/684554f83d6d791cedd72c50fb10e667b389d3ac/img/webui_diagram.png -------------------------------------------------------------------------------- /mod.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * # Deno WebUI 3 | * 4 | * > Use any web browser as GUI, with Deno in the backend and HTML5 in the 5 | * > frontend, all in a lightweight Deno module. 6 | * 7 | * ## Features 8 | * 9 | * - Fully Independent (_No need for any third-party runtimes_) 10 | * - Lightweight _~900 Kb_ for the whole package & Small memory footprint 11 | * - Fast binary communication protocol between WebUI and the browser (_Instead of JSON_) 12 | * - Multi-platform & Multi-Browser 13 | * - Using private profile for safety 14 | * - Original library written in Pure C 15 | * 16 | * ## Minimal Example 17 | * 18 | * ```ts 19 | * import { WebUI } from "jsr:@webui/deno-webui@2.5.8"; 20 | * 21 | * const myWindow = new WebUI(); 22 | * await myWindow.show(" Hello World! "); 23 | * await WebUI.wait(); 24 | * ``` 25 | * 26 | * @module 27 | * @license MIT 28 | */ 29 | export { WebUI } from "./src/webui.ts"; 30 | export type { BindCallback, Datatypes, WebUIEvent } from "./src/types.ts"; 31 | -------------------------------------------------------------------------------- /src/bootstrap.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | SETLOCAL 3 | 4 | :: This script downloads the trusted WebUI compiled library by GitHub CI for Windows. 5 | 6 | IF "%1"=="minimal" ( 7 | goto MINIMAL 8 | ) 9 | 10 | :: --- Full ------------------------------------- 11 | :: Download WebUI library for all supported OS. 12 | echo WebUI Deno Bootstrap 13 | echo. 14 | 15 | :: Creating the temporary cache folder 16 | mkdir "cache" 2>nul 1>nul 17 | 18 | :: Nightly Build 19 | :: SET "LINUX_ARM=https://github.com/webui-dev/webui/releases/download/nightly/webui-linux-gcc-arm.zip" 20 | :: SET "LINUX_ARM64=https://github.com/webui-dev/webui/releases/download/nightly/webui-linux-gcc-arm64.zip" 21 | :: SET "LINUX_X64=https://github.com/webui-dev/webui/releases/download/nightly/webui-linux-gcc-x64.zip" 22 | :: SET "MACOS_ARM64=https://github.com/webui-dev/webui/releases/download/nightly/webui-macos-clang-arm64.zip" 23 | :: SET "MACOS_X64=https://github.com/webui-dev/webui/releases/download/nightly/webui-macos-clang-x64.zip" 24 | :: SET "WINDOWS_MSVC_X64=https://github.com/webui-dev/webui/releases/download/nightly/webui-windows-msvc-x64.zip" 25 | 26 | :: Release 27 | SET "LINUX_ARM=https://github.com/webui-dev/webui/releases/download/2.5.0-beta.3/webui-linux-gcc-arm.zip" 28 | SET "LINUX_ARM64=https://github.com/webui-dev/webui/releases/download/2.5.0-beta.3/webui-linux-gcc-arm64.zip" 29 | SET "LINUX_X64=https://github.com/webui-dev/webui/releases/download/2.5.0-beta.3/webui-linux-gcc-x64.zip" 30 | SET "MACOS_ARM64=https://github.com/webui-dev/webui/releases/download/2.5.0-beta.3/webui-macos-clang-arm64.zip" 31 | SET "MACOS_X64=https://github.com/webui-dev/webui/releases/download/2.5.0-beta.3/webui-macos-clang-x64.zip" 32 | SET "WINDOWS_MSVC_X64=https://github.com/webui-dev/webui/releases/download/2.5.0-beta.3/webui-windows-msvc-x64.zip" 33 | 34 | :: Download and extract archives 35 | CALL :DOWNLOAD_AND_EXTRACT %LINUX_ARM% webui-linux-gcc-arm libwebui-2.so 36 | CALL :DOWNLOAD_AND_EXTRACT %LINUX_ARM64% webui-linux-gcc-arm64 libwebui-2.so 37 | CALL :DOWNLOAD_AND_EXTRACT %LINUX_X64% webui-linux-gcc-x64 libwebui-2.so 38 | CALL :DOWNLOAD_AND_EXTRACT %MACOS_ARM64% webui-macos-clang-arm64 libwebui-2.dylib 39 | CALL :DOWNLOAD_AND_EXTRACT %MACOS_X64% webui-macos-clang-x64 libwebui-2.dylib 40 | CALL :DOWNLOAD_AND_EXTRACT %WINDOWS_MSVC_X64% webui-windows-msvc-x64 webui-2.dll 41 | 42 | :: Remove cache folder 43 | echo * Cleaning... 44 | rmdir /S /Q "cache" 2>nul 1>nul 45 | exit /b 46 | 47 | :: Download and Extract Function 48 | :DOWNLOAD_AND_EXTRACT 49 | echo * Downloading [%1]... 50 | SET FULL_URL=%1 51 | SET FILE_NAME=%2 52 | SET LIB_DYN=%3 53 | SET LIB_STATIC=%4 54 | powershell -Command "Invoke-WebRequest '%FULL_URL%' -OutFile 'cache\%FILE_NAME%.zip'" 55 | echo * Extracting [%FILE_NAME%.zip]... 56 | mkdir "cache\%FILE_NAME%" 2>nul 1>nul 57 | tar -xf "cache\%FILE_NAME%.zip" -C "cache" 58 | IF NOT "%LIB_DYN%"=="" ( 59 | :: Copy dynamic library 60 | echo * Copying [%LIB_DYN%]... 61 | mkdir "%FILE_NAME%" 2>nul 1>nul 62 | copy /Y "cache\%FILE_NAME%\%LIB_DYN%" "%FILE_NAME%\%LIB_DYN%" 2>nul 1>nul 63 | ) 64 | IF NOT "%LIB_STATIC%"=="" ( 65 | :: Copy dynamic library 66 | echo * Copying [%LIB_STATIC%]... 67 | mkdir "%FILE_NAME%" 2>nul 1>nul 68 | copy /Y "cache\%FILE_NAME%\%LIB_STATIC%" "%FILE_NAME%\%LIB_STATIC%" 2>nul 1>nul 69 | ) 70 | GOTO :EOF 71 | 72 | :: --- Minimal ---------------------------------- 73 | :: Download WebUI library for only the current OS. 74 | :MINIMAL 75 | 76 | SET "BASE_URL=https://github.com/webui-dev/webui/releases/download/2.5.0-beta.3/" 77 | 78 | :: Check the CPU architecture 79 | IF "%PROCESSOR_ARCHITECTURE%"=="x86" ( 80 | :: x86 32Bit 81 | :: SET "FILENAME=webui-windows-msvc-x86" 82 | ECHO Error: Windows x86 32Bit architecture is not supported yet 83 | exit /b 84 | ) ELSE IF "%PROCESSOR_ARCHITECTURE%"=="AMD64" ( 85 | :: x86 64Bit 86 | SET "FILENAME=webui-windows-msvc-x64" 87 | ) ELSE IF "%PROCESSOR_ARCHITECTURE%"=="ARM" ( 88 | :: ARM 32Bit 89 | :: SET "FILENAME=webui-windows-msvc-arm" 90 | ECHO Error: Windows ARM architecture is unsupported yet 91 | exit /b 92 | ) ELSE IF "%PROCESSOR_ARCHITECTURE%"=="ARM64" ( 93 | :: ARM 64Bit 94 | :: SET "FILENAME=webui-windows-msvc-arm64" 95 | ECHO Error: Windows ARM64 architecture is unsupported yet 96 | exit /b 97 | ) ELSE ( 98 | ECHO Error: Unknown architecture '%PROCESSOR_ARCHITECTURE%' 99 | exit /b 100 | ) 101 | 102 | :: Creating the temporary cache folder 103 | mkdir "cache" 2>nul 1>nul 104 | mkdir "cache\%FILENAME%" 2>nul 1>nul 105 | 106 | :: Download the archive using PowerShell 107 | powershell -Command "Invoke-WebRequest '%BASE_URL%%FILENAME%.zip' -OutFile 'cache\%FILENAME%.zip'" 108 | 109 | :: Extract archive (Windows 10 and later) 110 | tar -xf "cache\%FILENAME%.zip" -C "cache" 111 | 112 | :: Copy library 113 | mkdir "%FILENAME%" 2>nul 1>nul 114 | copy /Y "cache\%FILENAME%\webui-2.dll" "%FILENAME%\webui-2.dll" 2>nul 1>nul 115 | 116 | :: Remove cache folder 117 | rmdir /S /Q "cache" 2>nul 1>nul 118 | 119 | ENDLOCAL 120 | -------------------------------------------------------------------------------- /src/bootstrap.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This script downloads the trusted WebUI compiled library by GitHub CI for Linux. 4 | 5 | if [[ "$1" == "" ]]; then 6 | 7 | # --- Full ------------------------------------- 8 | # Download WebUI library for all supported OS. 9 | echo "WebUI Deno Bootstrap" 10 | echo 11 | 12 | # Creating the temporary cache folder 13 | mkdir -p "cache" 2>/dev/null 14 | 15 | # Nightly Build 16 | # LINUX_ARM="https://github.com/webui-dev/webui/releases/download/nightly/webui-linux-gcc-arm.zip" 17 | # LINUX_ARM64="https://github.com/webui-dev/webui/releases/download/nightly/webui-linux-gcc-arm64.zip" 18 | # LINUX_X64="https://github.com/webui-dev/webui/releases/download/nightly/webui-linux-gcc-x64.zip" 19 | # MACOS_ARM64="https://github.com/webui-dev/webui/releases/download/nightly/webui-macos-clang-arm64.zip" 20 | # MACOS_X64="https://github.com/webui-dev/webui/releases/download/nightly/webui-macos-clang-x64.zip" 21 | # WINDOWS_MSVC_X64="https://github.com/webui-dev/webui/releases/download/nightly/webui-windows-msvc-x64.zip" 22 | 23 | # Release 24 | LINUX_ARM="https://github.com/webui-dev/webui/releases/download/2.5.0-beta.3/webui-linux-gcc-arm.zip" 25 | LINUX_ARM64="https://github.com/webui-dev/webui/releases/download/2.5.0-beta.3/webui-linux-gcc-arm64.zip" 26 | LINUX_X64="https://github.com/webui-dev/webui/releases/download/2.5.0-beta.3/webui-linux-gcc-x64.zip" 27 | MACOS_ARM64="https://github.com/webui-dev/webui/releases/download/2.5.0-beta.3/webui-macos-clang-arm64.zip" 28 | MACOS_X64="https://github.com/webui-dev/webui/releases/download/2.5.0-beta.3/webui-macos-clang-x64.zip" 29 | WINDOWS_MSVC_X64="https://github.com/webui-dev/webui/releases/download/2.5.0-beta.3/webui-windows-msvc-x64.zip" 30 | 31 | # Download and extract archives 32 | download_and_extract() { 33 | echo "* Downloading [$1]..." 34 | wget -q "$1" -O "cache/$2.zip" 35 | echo "* Extracting [$2.zip]..." 36 | mkdir -p "cache/$2" 2>/dev/null 37 | unzip -q "cache/$2.zip" -d "cache" 38 | if [ -n "$3" ]; then 39 | echo "* Copying [$3]..." 40 | mkdir -p "$2" 2>/dev/null 41 | cp -f "cache/$2/$3" "$2/$3" 42 | fi 43 | if [ -n "$4" ]; then 44 | echo "* Copying [$4]..." 45 | mkdir -p "$2" 2>/dev/null 46 | cp -f "cache/$2/$4" "$2/$4" 47 | fi 48 | } 49 | 50 | download_and_extract $LINUX_ARM "webui-linux-gcc-arm" "libwebui-2.so" 51 | download_and_extract $LINUX_ARM64 "webui-linux-gcc-arm64" "libwebui-2.so" 52 | download_and_extract $LINUX_X64 "webui-linux-gcc-x64" "libwebui-2.so" 53 | download_and_extract $MACOS_ARM64 "webui-macos-clang-arm64" "libwebui-2.dylib" 54 | download_and_extract $MACOS_X64 "webui-macos-clang-x64" "libwebui-2.dylib" 55 | download_and_extract $WINDOWS_MSVC_X64 "webui-windows-msvc-x64" "webui-2.dll" 56 | 57 | # Remove cache folder 58 | echo "* Cleaning..." 59 | rm -rf "cache" 60 | exit 0 61 | fi 62 | 63 | if [[ "$1" == "minimal" ]]; then 64 | 65 | # --- Minimal ---------------------------------- 66 | # Download WebUI library for only the current OS. 67 | 68 | # Nightly Build 69 | # BASE_URL="https://github.com/webui-dev/webui/releases/download/nightly/" 70 | 71 | # Release 72 | BASE_URL="https://github.com/webui-dev/webui/releases/download/2.5.0-beta.3/" 73 | 74 | # Detect OS (macOS / Linux) 75 | OS="linux" 76 | CC="gcc" 77 | EXT="so" 78 | if [[ "$OSTYPE" == "darwin"* ]]; then 79 | OS="macos" 80 | CC="clang" 81 | EXT="dylib" 82 | fi 83 | 84 | # Check the CPU architecture 85 | ARCH=$(uname -m) 86 | if [ "$ARCH" = "x86" ]; then 87 | # x86 32Bit 88 | # FILENAME="webui-${OS}-${CC}-x86" 89 | echo "Error: Linux/macOS x86 32Bit architecture is not supported yet" 90 | exit 1 91 | elif [ "$ARCH" = "x86_64" ]; then 92 | # x86 64Bit 93 | FILENAME="webui-${OS}-${CC}-x64" 94 | elif [ "$ARCH" = "arm" ]; then 95 | # ARM 32Bit 96 | FILENAME="webui-${OS}-${CC}-arm" 97 | elif [ "$ARCH" = "aarch64" ]; then 98 | # ARM 64Bit 99 | FILENAME="webui-${OS}-${CC}-arm64" 100 | else 101 | echo "Error: Unknown architecture '$ARCH'" 102 | exit 1 103 | fi 104 | 105 | # Creating the temporary cache folder 106 | mkdir -p "cache/$FILENAME" 2>/dev/null 107 | 108 | # Download the archive using wget 109 | wget -q "$BASE_URL$FILENAME.zip" -O "cache/$FILENAME.zip" 110 | 111 | # Extract archive 112 | unzip -q "cache/$FILENAME.zip" -d "cache" 113 | 114 | # Copy library 115 | mkdir -p "$FILENAME" 2>/dev/null 116 | cp -f "cache/$FILENAME/webui-2.${EXT}" "$FILENAME/webui-2.${EXT}" 117 | 118 | # Remove cache folder 119 | rm -rf "cache" 120 | 121 | exit 0 122 | fi 123 | -------------------------------------------------------------------------------- /src/lib.ts: -------------------------------------------------------------------------------- 1 | // Deno WebUI 2 | // FFI (Foreign Function Interface) for webui.ts 3 | 4 | import { libPath } from "../deps.ts"; 5 | 6 | const symbols = { 7 | webui_wait: { 8 | // void webui_wait(void) 9 | parameters: [], 10 | result: "void", 11 | nonblocking: true, 12 | }, 13 | webui_new_window: { 14 | // size_t webui_new_window(void) 15 | parameters: [], 16 | result: "usize", 17 | }, 18 | webui_show: { 19 | // bool webui_show(size_t window, const char* content) 20 | parameters: ["usize", "buffer"], 21 | result: "bool", 22 | }, 23 | webui_show_browser: { 24 | // bool webui_show_browser(size_t window, const char* content, size_t browser) 25 | parameters: ["usize", "buffer", "usize"], 26 | result: "bool", 27 | }, 28 | webui_interface_bind: { 29 | // size_t webui_interface_bind(size_t window, const char* element, void (*func)(size_t, size_t, char*, size_t, size_t)); 30 | parameters: ["usize", "buffer", "function"], 31 | result: "usize", 32 | }, 33 | webui_script: { 34 | // bool webui_script(size_t window, const char* script, size_t timeout, char* buffer, size_t buffer_length) 35 | parameters: ["usize", "buffer", "usize", "buffer", "usize"], 36 | result: "bool", 37 | }, 38 | webui_run: { 39 | // void webui_run(size_t window, const char* script) 40 | parameters: ["usize", "buffer"], 41 | result: "void", 42 | }, 43 | webui_interface_set_response: { 44 | // void webui_interface_set_response(size_t window, size_t event_number, const char* response) 45 | parameters: ["usize", "usize", "buffer"], 46 | result: "void", 47 | }, 48 | webui_exit: { 49 | // void webui_exit(void) 50 | parameters: [], 51 | result: "void", 52 | }, 53 | webui_is_shown: { 54 | // bool webui_is_shown(size_t window) 55 | parameters: ["usize"], 56 | result: "bool", 57 | }, 58 | webui_close: { 59 | // void webui_close(size_t window) 60 | parameters: ["usize"], 61 | result: "void", 62 | }, 63 | webui_set_file_handler: { 64 | // void webui_set_file_handler(size_t window, const void* (*handler)(const char* filename, int* length)) 65 | parameters: ["usize", "function"], 66 | result: "void", 67 | }, 68 | webui_interface_is_app_running: { 69 | // bool webui_interface_is_app_running(void) 70 | parameters: [], 71 | result: "bool", 72 | }, 73 | webui_set_profile: { 74 | // void webui_set_profile(size_t window, const char* name, const char* path) 75 | parameters: ["usize", "buffer", "buffer"], 76 | result: "void", 77 | }, 78 | webui_interface_get_int_at: { 79 | // long long int webui_interface_get_int_at(size_t window, size_t event_number, size_t index) 80 | parameters: ["usize", "usize", "usize"], 81 | result: "i64", 82 | }, 83 | webui_interface_get_string_at: { 84 | // const char* webui_interface_get_string_at(size_t window, size_t event_number, size_t index) 85 | parameters: ["usize", "usize", "usize"], 86 | result: "buffer", 87 | }, 88 | webui_interface_get_bool_at: { 89 | // bool webui_interface_get_bool_at(size_t window, size_t event_number, size_t index) 90 | parameters: ["usize", "usize", "usize"], 91 | result: "bool", 92 | }, 93 | // webui_interface_get_size_at: { 94 | // // size_t webui_interface_get_size_at(size_t window, size_t event_number, size_t index) 95 | // parameters: ["usize", "usize", "usize"], 96 | // result: "usize", 97 | // }, 98 | webui_clean: { 99 | // void webui_clean() 100 | parameters: [], 101 | result: "void", 102 | }, 103 | webui_set_root_folder: { 104 | // bool webui_set_root_folder(size_t window, const char* path) 105 | parameters: ["usize", "buffer"], 106 | result: "bool", 107 | }, 108 | webui_set_tls_certificate: { 109 | // bool webui_set_tls_certificate(const char* certificate_pem, const char* private_key_pem) 110 | parameters: ["buffer", "buffer"], 111 | result: "bool", 112 | }, 113 | webui_set_kiosk: { 114 | // void webui_set_kiosk(size_t window, bool status) 115 | parameters: ["usize", "bool"], 116 | result: "void", 117 | }, 118 | webui_destroy: { 119 | // void webui_destroy(size_t window) 120 | parameters: ["usize"], 121 | result: "void", 122 | }, 123 | webui_set_timeout: { 124 | // void webui_set_timeout(size_t second) 125 | parameters: ["usize"], 126 | result: "void", 127 | }, 128 | webui_set_icon: { 129 | // void webui_set_icon(size_t window, const char* icon, const char* icon_type) 130 | parameters: ["usize", "buffer", "buffer"], 131 | result: "void", 132 | }, 133 | webui_encode: { 134 | // char* webui_encode(const char* str) 135 | parameters: ["buffer"], 136 | result: "buffer", 137 | }, 138 | webui_decode: { 139 | // char* webui_decode(const char* str) 140 | parameters: ["buffer"], 141 | result: "buffer", 142 | }, 143 | webui_free: { 144 | // void webui_free(void* ptr) 145 | parameters: ["pointer"], 146 | result: "void", 147 | }, 148 | webui_malloc: { 149 | // void* webui_malloc(size_t size) 150 | parameters: ["usize"], 151 | result: "pointer", 152 | }, 153 | webui_send_raw: { 154 | // void webui_send_raw(size_t window, const char* function, const void* raw, size_t size) 155 | parameters: ["usize", "buffer", "buffer", "usize"], 156 | result: "void", 157 | }, 158 | webui_set_hide: { 159 | // void webui_set_hide(size_t window, bool status) 160 | parameters: ["usize", "bool"], 161 | result: "void", 162 | }, 163 | webui_set_size: { 164 | // void webui_set_size(size_t window, unsigned int width, unsigned int height) 165 | parameters: ["usize", "u32", "u32"], 166 | result: "void", 167 | }, 168 | webui_set_position: { 169 | // void webui_set_position(size_t window, unsigned int x, unsigned int y) 170 | parameters: ["usize", "u32", "u32"], 171 | result: "void", 172 | }, 173 | webui_get_url: { 174 | // const char* webui_get_url(size_t window) 175 | parameters: ["usize"], 176 | result: "buffer", 177 | }, 178 | webui_set_public: { 179 | // void webui_set_public(size_t window, bool status) 180 | parameters: ["usize", "bool"], 181 | result: "void", 182 | }, 183 | webui_navigate: { 184 | // void webui_navigate(size_t window, const char* url) 185 | parameters: ["usize", "buffer"], 186 | result: "void", 187 | }, 188 | webui_delete_all_profiles: { 189 | // void webui_delete_all_profiles(void) 190 | parameters: [], 191 | result: "void", 192 | }, 193 | webui_delete_profile: { 194 | // void webui_delete_profile(size_t window) 195 | parameters: ["usize"], 196 | result: "void", 197 | }, 198 | webui_get_parent_process_id: { 199 | // size_t webui_get_parent_process_id(size_t window) 200 | parameters: ["usize"], 201 | result: "usize", 202 | }, 203 | webui_get_child_process_id: { 204 | // size_t webui_get_child_process_id(size_t window) 205 | parameters: ["usize"], 206 | result: "usize", 207 | }, 208 | webui_set_port: { 209 | // bool webui_set_port(size_t window, size_t port) 210 | parameters: ["usize", "usize"], 211 | result: "bool", 212 | }, 213 | webui_set_runtime: { 214 | // void webui_set_runtime(size_t window, size_t runtime) 215 | parameters: ["usize", "usize"], 216 | result: "void", 217 | }, 218 | webui_set_config: { 219 | // void webui_set_config(webui_config option, bool status) 220 | // show_wait_connection: 0 221 | // ui_event_blocking: 1 222 | // folder_monitor: 2 223 | // multi_client: 3 224 | // use_cookies: 4 225 | // asynchronous_response: 5 226 | parameters: ["usize", "bool"], 227 | result: "void", 228 | }, 229 | webui_interface_show_client: { 230 | // bool webui_interface_show_client(size_t window, size_t event_number, const char* content) 231 | parameters: ["usize", "usize", "buffer"], 232 | result: "bool", 233 | }, 234 | webui_interface_close_client: { 235 | // void webui_interface_close_client(size_t window, size_t event_number) 236 | parameters: ["usize", "usize"], 237 | result: "void", 238 | }, 239 | webui_interface_send_raw_client: { 240 | // void webui_interface_send_raw_client( 241 | // size_t window, size_t event_number, const char* function, const void* raw, size_t size) 242 | parameters: ["usize", "usize", "buffer", "buffer", "usize"], 243 | result: "void", 244 | }, 245 | webui_interface_navigate_client: { 246 | // void webui_interface_navigate_client(size_t window, size_t event_number, const char* url) 247 | parameters: ["usize", "usize", "buffer"], 248 | result: "void", 249 | }, 250 | webui_interface_run_client: { 251 | // void webui_interface_run_client(size_t window, size_t event_number, const char* script) 252 | parameters: ["usize", "usize", "buffer"], 253 | result: "void", 254 | }, 255 | webui_interface_script_client: { 256 | // bool webui_interface_script_client( 257 | // size_t window, size_t event_number, const char* script, size_t timeout, char* buffer, size_t buffer_length) 258 | parameters: ["usize", "usize", "buffer", "usize", "buffer", "usize"], 259 | result: "bool", 260 | }, 261 | webui_send_raw_client: { 262 | // void webui_send_raw_client(webui_event_t* e, const char* function, const void* raw, size_t size) 263 | parameters: ["pointer", "buffer", "buffer", "usize"], 264 | result: "void", 265 | }, 266 | webui_interface_set_response_file_handler: { 267 | // void webui_interface_set_response_file_handler(size_t window, const void* response, int length) 268 | parameters: ["usize", "pointer", "usize"], 269 | result: "void", 270 | }, 271 | webui_get_best_browser: { 272 | // size_t webui_get_best_browser(size_t window) 273 | parameters: ["usize"], 274 | result: "usize", 275 | }, 276 | webui_start_server: { 277 | // const char* webui_start_server(size_t window, const char* content) 278 | parameters: ["usize", "buffer"], 279 | result: "buffer", 280 | }, 281 | webui_show_wv: { 282 | // bool webui_show_wv(size_t window, const char* content) 283 | parameters: ["usize", "buffer"], 284 | result: "bool", 285 | }, 286 | webui_set_custom_parameters: { 287 | // void webui_set_custom_parameters(size_t window, char *params) 288 | parameters: ["usize", "buffer"], 289 | result: "void", 290 | }, 291 | webui_set_high_contrast: { 292 | // void webui_set_high_contrast(size_t window, bool status) 293 | parameters: ["usize", "bool"], 294 | result: "void", 295 | }, 296 | webui_is_high_contrast: { 297 | // bool webui_is_high_contrast(void) 298 | parameters: [], 299 | result: "bool", 300 | }, 301 | webui_browser_exist: { 302 | // bool webui_browser_exist(size_t browser) 303 | parameters: ["usize"], 304 | result: "bool", 305 | }, 306 | webui_set_default_root_folder: { 307 | // bool webui_set_default_root_folder(const char* path) 308 | parameters: ["buffer"], 309 | result: "bool", 310 | }, 311 | webui_set_minimum_size: { 312 | // void webui_set_minimum_size(size_t window, unsigned int width, unsigned int height) 313 | parameters: ["usize", "u32", "u32"], 314 | result: "void", 315 | }, 316 | webui_set_proxy: { 317 | // void webui_set_proxy(size_t window, const char* proxy_server) 318 | parameters: ["usize", "buffer"], 319 | result: "void", 320 | }, 321 | webui_open_url: { 322 | // void webui_open_url(const char* url) 323 | parameters: ["buffer"], 324 | result: "void", 325 | }, 326 | webui_get_free_port: { 327 | // size_t webui_get_free_port(void) 328 | parameters: [], 329 | result: "usize", 330 | }, 331 | } as const; 332 | 333 | export function loadLib(): Deno.DynamicLibrary { 334 | return Deno.dlopen( 335 | libPath, 336 | symbols, 337 | ); 338 | } 339 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | import type { WebUI } from "../mod.ts"; 2 | import type { loadLib } from "./lib.ts"; 3 | 4 | export type Usize = number | bigint; 5 | 6 | /** 7 | * Defines the signature for callback functions used with `WebUI.prototype.bind`. 8 | * These functions are executed when a corresponding event occurs in the UI. 9 | * 10 | * @template T The expected return type of the callback function. Can be a basic data type (`string`, `number`, `boolean`), `undefined`, or `void`. Can also be a Promise resolving to one of these types for asynchronous operations. 11 | * @param event A `WebUIEvent` object containing details about the event. 12 | * @returns The result to be sent back to the UI (if the call originated from `webui.call`), or `undefined`/`void` if no response is needed. Can be a direct value or a Promise. 13 | */ 14 | export type BindCallback< 15 | T extends Datatypes | undefined | void, 16 | > = (event: WebUIEvent) => T | Promise; 17 | 18 | export type WebUILib = Awaited>; 19 | 20 | /** 21 | * Represents an event object passed to bound callback functions. 22 | */ 23 | export interface WebUIEvent { 24 | /** The WebUI window instance associated with this event. */ 25 | window: WebUI; 26 | /** The type of the event (e.g., Connected, Disconnected, MouseClick, Callback). See `WebUI.EventType`. */ 27 | eventType: number; 28 | /** A unique identifier for this specific event instance. */ 29 | eventNumber: number; 30 | /** The ID ('#' attribute) of the HTML element that triggered the event, if applicable. */ 31 | element: string; 32 | /** 33 | * An object containing methods to retrieve arguments passed from the UI JavaScript function call. 34 | */ 35 | arg: { 36 | /** 37 | * Retrieves a numeric argument passed from the UI at the specified index. 38 | * @param index The zero-based index of the argument. 39 | * @returns The numeric value of the argument. 40 | */ 41 | number: (index: number) => number; 42 | /** 43 | * Retrieves a string argument passed from the UI at the specified index. 44 | * @param index The zero-based index of the argument. 45 | * @returns The string value of the argument. 46 | */ 47 | string: (index: number) => string; 48 | /** 49 | * Retrieves a boolean argument passed from the UI at the specified index. 50 | * @param index The zero-based index of the argument. 51 | * @returns The boolean value of the argument. 52 | */ 53 | boolean: (index: number) => boolean; 54 | }; 55 | } 56 | 57 | /** 58 | * Represents the basic data types that can be returned from a `BindCallback` 59 | * function and serialized back to the UI. 60 | */ 61 | export type Datatypes = 62 | | string 63 | | number 64 | | boolean; 65 | -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | // Deno WebUI 2 | // Utilities 3 | import { dirname, join } from "jsr:@std/path@1.0.8"; 4 | import { exists } from "jsr:@std/fs@0.229.3/exists"; 5 | import { BlobReader, BlobWriter, ZipReader } from "jsr:@zip-js/zip-js@2.7.60"; 6 | 7 | // The WebUI core version to download (Consider using this if not using nightly) 8 | export const WebUICoreVersion = "2.5.0-beta.3"; 9 | export const useNightly = true; // Set to false to use WebUICoreVersion 10 | 11 | // --- Cache Directory Logic --- 12 | 13 | /** 14 | * Gets the base WebUI cache directory for the current platform. 15 | * Creates the base directory if it doesn't exist. 16 | * This directory will contain version-specific subdirectories. 17 | * Example: ~/.cache/deno_webui (Linux) or ~/Library/Caches/deno_webui (macOS) 18 | * @returns {Promise} The path to the base WebUI cache directory. 19 | */ 20 | async function getBaseWebUICacheDir(): Promise { 21 | let baseCacheDir: string | undefined; 22 | 23 | switch (Deno.build.os) { 24 | case "windows": { 25 | baseCacheDir = Deno.env.get("LOCALAPPDATA"); 26 | break; 27 | } 28 | case "darwin": { 29 | const home = Deno.env.get("HOME"); 30 | if (home) { 31 | baseCacheDir = join(home, "Library", "Caches"); 32 | } 33 | break; 34 | } 35 | default: // Linux, FreeBSD, etc. 36 | baseCacheDir = Deno.env.get("XDG_CACHE_HOME"); 37 | if (!baseCacheDir) { 38 | const home = Deno.env.get("HOME"); 39 | if (home) { 40 | baseCacheDir = join(home, ".cache"); 41 | } 42 | } 43 | break; 44 | } 45 | 46 | if (!baseCacheDir) { 47 | // Fallback to a temporary directory if no standard cache is found 48 | console.warn( 49 | "Could not determine standard cache directory. Using Deno temporary directory.", 50 | ); 51 | // Note: Deno's temp dir might be cleaned up unexpectedly. 52 | // A more persistent fallback might be needed in production scenarios. 53 | baseCacheDir = await Deno.makeTempDir({ prefix: "deno_webui_cache_base_" }); 54 | } 55 | 56 | // The main directory for all deno_webui cached libs 57 | const webuiBaseCacheDir = join(baseCacheDir, "deno_webui"); 58 | 59 | // Ensure the base directory exists 60 | await Deno.mkdir(webuiBaseCacheDir, { recursive: true }); 61 | 62 | return webuiBaseCacheDir; 63 | } 64 | 65 | /** 66 | * Determines the specific version directory name based on configuration. 67 | * @returns {string} "nightly" or the specific WebUICoreVersion. 68 | */ 69 | function getVersionDirName(): string { 70 | return useNightly ? "nightly" : WebUICoreVersion; 71 | } 72 | 73 | // --- Download and Extraction Logic --- 74 | 75 | /** 76 | * Downloads and extracts the required WebUI library to the specific version cache directory. 77 | * @param {string} targetLibPath - The final path where the library should exist in the cache (e.g., ~/.cache/deno_webui/2.5.0-beta.3/webui-2.dll). 78 | * @param {string} osName - OS identifier (e.g., "windows", "macos", "linux"). 79 | * @param {string} compilerName - Compiler identifier (e.g., "msvc", "clang", "gcc"). 80 | * @param {string} archName - Architecture identifier (e.g., "x64", "arm64"). 81 | * @param {string} libFileNameInZip - The full path of the library *inside* the zip archive. 82 | * @returns {Promise} 83 | * @throws {Error} If download or extraction fails. 84 | */ 85 | async function downloadAndExtractLibrary( 86 | targetLibPath: string, 87 | osName: string, 88 | compilerName: string, 89 | archName: string, 90 | libFileNameInZip: string, 91 | ): Promise { 92 | // The cache directory for this *specific version* 93 | const versionCacheDir = dirname(targetLibPath); 94 | 95 | // Determine download URL 96 | const version = getVersionDirName(); // Get "nightly" or the specific version string 97 | const baseUrl = version === "nightly" 98 | ? `https://github.com/webui-dev/webui/releases/download/nightly/` 99 | : `https://github.com/webui-dev/webui/releases/download/${version}/`; 100 | 101 | const zipFileName = `webui-${osName}-${compilerName}-${archName}.zip`; 102 | const zipUrl = `${baseUrl}${zipFileName}`; 103 | // Temporary download path inside the version-specific cache dir 104 | const tempZipPath = join(versionCacheDir, `${zipFileName}.download`); 105 | 106 | // console.log(`Downloading WebUI library (${version}) from ${zipUrl}...`); 107 | 108 | try { 109 | // Ensure the target version directory exists before downloading 110 | await Deno.mkdir(versionCacheDir, { recursive: true }); 111 | 112 | // Download the archive 113 | const res = await fetch(zipUrl); 114 | if (!res.ok) { 115 | throw new Error( 116 | `Failed to download ${zipUrl}: ${res.status} ${res.statusText}`, 117 | ); 118 | } 119 | const zipData = await res.arrayBuffer(); 120 | await Deno.writeFile(tempZipPath, new Uint8Array(zipData)); 121 | // console.log(`Downloaded to ${tempZipPath}`); 122 | 123 | // Extract the specific library file 124 | // console.log(`Extracting ${libFileNameInZip} from ${tempZipPath}...`); 125 | const zipBlob = new Blob([zipData]); 126 | const zipReader = new ZipReader(new BlobReader(zipBlob)); 127 | const entries = await zipReader.getEntries(); 128 | 129 | let foundEntry = false; 130 | for (const entry of entries) { 131 | // Normalize zip entry filename (might contain different slashes) 132 | const entryPath = entry.filename.replace(/\\/g, "/"); 133 | const targetEntryPath = libFileNameInZip.replace(/\\/g, "/"); 134 | 135 | if (!entry.directory && entryPath === targetEntryPath) { 136 | // console.log(`Found entry: ${entry.filename}`); 137 | const writer = new BlobWriter(); 138 | const data = await entry.getData!(writer); 139 | await Deno.writeFile( 140 | targetLibPath, 141 | new Uint8Array(await data.arrayBuffer()), 142 | ); 143 | foundEntry = true; 144 | // console.log(`Extracted library to ${targetLibPath}`); 145 | break; // Found the file, no need to check others 146 | } 147 | } 148 | await zipReader.close(); 149 | 150 | if (!foundEntry) { 151 | throw new Error( 152 | `Library file "${libFileNameInZip}" not found inside downloaded archive ${zipFileName}`, 153 | ); 154 | } 155 | } catch (error) { 156 | console.error("WebUI library download/extraction failed:", error); 157 | // Clean up partial download if it exists 158 | try { 159 | await Deno.remove(targetLibPath).catch(() => {}); // Remove potentially incomplete extraction 160 | } catch (e) { 161 | if (!(e instanceof Deno.errors.NotFound)) { 162 | console.error("Cleanup error:", e); 163 | } 164 | } 165 | throw error; // Re-throw the error 166 | } finally { 167 | // Clean up the downloaded zip file regardless of success/failure 168 | try { 169 | await Deno.remove(tempZipPath); 170 | // console.log(`Removed temporary file ${tempZipPath}`); 171 | } catch (e) { 172 | if (!(e instanceof Deno.errors.NotFound)) { 173 | console.error(`Failed to remove temporary zip file ${tempZipPath}:`, e); 174 | } 175 | } 176 | } 177 | } 178 | 179 | /** 180 | * Ensures the correct WebUI native library exists in the versioned cache, downloading it if necessary. 181 | * @param {string} baseLibName - The OS-specific library filename (e.g., "webui-2.dll"). 182 | * @returns {Promise} The full path to the cached library file (e.g., ~/.cache/deno_webui/2.5.0-beta.3/webui-2.dll). 183 | */ 184 | export async function ensureWebUiLib(baseLibName: string): Promise { 185 | // 1. Get the base cache directory (e.g., ~/.cache/deno_webui) 186 | const baseWebUICacheDir = await getBaseWebUICacheDir(); 187 | 188 | // 2. Determine the version-specific subdirectory name ("nightly" or "2.5.0-beta.3") 189 | const versionDirName = getVersionDirName(); 190 | 191 | // 3. Construct the path to the version-specific cache directory 192 | const versionCacheDir = join(baseWebUICacheDir, versionDirName); 193 | 194 | // 4. Construct the final target path for the library file 195 | const targetLibPath = join(versionCacheDir, baseLibName); 196 | 197 | // 5. Ensure the version-specific cache directory exists *before* checking for the file 198 | // (downloadAndExtractLibrary also does this, but doing it here prevents 199 | // an unnecessary download attempt if only the directory is missing) 200 | await Deno.mkdir(versionCacheDir, { recursive: true }); 201 | 202 | // 6. Check if the library already exists in the cache 203 | if (await exists(targetLibPath)) { 204 | // console.log( 205 | // `Using cached WebUI library (${versionDirName}): ${targetLibPath}`, 206 | // ); 207 | return targetLibPath; 208 | } 209 | 210 | // 7. Determine download parameters if not cached 211 | // console.log( 212 | // `WebUI library (${versionDirName}) not found in cache. Attempting download...`, 213 | // ); 214 | let osName: string; 215 | let compilerName: string; 216 | 217 | const archMap: { [key: string]: string } = { 218 | "x86_64": "x64", 219 | "aarch64": "arm64", 220 | }; 221 | const archName = archMap[Deno.build.arch]; 222 | if (!archName) { 223 | throw new Error( 224 | `Unsupported architecture: ${Deno.build.arch} for ${Deno.build.os}`, 225 | ); 226 | } 227 | 228 | switch (Deno.build.os) { 229 | case "windows": 230 | osName = "windows"; 231 | compilerName = "msvc"; 232 | break; 233 | case "darwin": 234 | osName = "macos"; 235 | compilerName = "clang"; 236 | break; 237 | default: // Linux and others 238 | osName = "linux"; 239 | compilerName = "gcc"; 240 | break; 241 | } 242 | 243 | const zipDirName = `webui-${osName}-${compilerName}-${archName}`; 244 | const libFileNameInZip = `${zipDirName}/${baseLibName}`; // Path inside the zip 245 | 246 | // 8. Download and extract 247 | await downloadAndExtractLibrary( 248 | targetLibPath, // Pass the full final path 249 | osName, 250 | compilerName, 251 | archName, 252 | libFileNameInZip, 253 | ); 254 | 255 | // 9. Return the path 256 | return targetLibPath; 257 | } 258 | 259 | // --- String Conversions (Keep as they are useful) --- 260 | 261 | /** 262 | * Convert a String to C-String. 263 | * @param {string} value 264 | * @returns a char[]. 265 | */ 266 | export function toCString(value: string): Uint8Array { 267 | return new TextEncoder().encode(value + "\0"); 268 | } 269 | 270 | /** 271 | * Convert a C-String to String. 272 | * @param {Uint8Array} value - an `char* / Uint8Array` that contains a C-String. 273 | * @returns a string. 274 | */ 275 | export function fromCString(value: Uint8Array): string { 276 | const end = value.findIndex((byte) => byte === 0x00); //find C-string end 277 | return new TextDecoder().decode(value.slice(0, end)); 278 | } 279 | 280 | export class WebUIError extends Error {} 281 | -------------------------------------------------------------------------------- /src/webui.ts: -------------------------------------------------------------------------------- 1 | /* 2 | WebUI Deno 2.5.8 3 | http://webui.me 4 | https://github.com/webui-dev/deno-webui 5 | Copyright (c) 2020-2025 Hassan Draga. 6 | Licensed under MIT License. 7 | All rights reserved. 8 | Canada. 9 | */ 10 | 11 | import { loadLib } from "./lib.ts"; 12 | import type { 13 | BindCallback, 14 | Datatypes, 15 | Usize, 16 | WebUIEvent, 17 | WebUILib, 18 | } from "./types.ts"; 19 | import { fromCString, toCString, WebUIError } from "./utils.ts"; 20 | 21 | // Register windows to bind instance to WebUI.Event 22 | const windows: Map = new Map(); 23 | 24 | // Global lib entry 25 | let _lib: WebUILib; 26 | 27 | /** 28 | * Represents a WebUI window instance. Allows interaction with a web browser 29 | * window, including displaying HTML content, executing JavaScript, and binding 30 | * backend functions to UI elements. 31 | */ 32 | export class WebUI { 33 | #window: Usize = 0; 34 | #lib: WebUILib; 35 | #isFileHandler: boolean = false; 36 | 37 | /** 38 | * Instanciate a new WebUI window. 39 | * @returns Window id. 40 | * @throws {WebUIError} - If optional local lib not found. 41 | * @example 42 | * ```ts 43 | * const myWindow1 = new WebUI() 44 | * ``` 45 | */ 46 | constructor() { 47 | WebUI.init(); // Init lib if not already initialized 48 | this.#lib = _lib; 49 | this.#window = _lib.symbols.webui_new_window(); 50 | windows.set(BigInt(this.#window), this); 51 | } 52 | 53 | /** 54 | * Set root folder for proper loading resources 55 | * @param rootFolder Root folder to set 56 | * @throws {WebUIError} - If lib return false status. 57 | * @example 58 | * ```ts 59 | * const myWindow = new WebUI() 60 | * 61 | * // Show the current time 62 | * myWindow.setRootFolder('some/root/folder') 63 | * 64 | * // Show a local file 65 | * await myWindow.show('some/root/folder/index.html') 66 | * 67 | * // Await to ensure WebUI.script and WebUI.run can send datas to the client 68 | * console.assert(myWindow.isShown, true) 69 | * ``` 70 | */ 71 | setRootFolder(rootFolder: string) { 72 | const status = this.#lib.symbols.webui_set_root_folder( 73 | BigInt(this.#window), 74 | toCString(rootFolder), 75 | ); 76 | if (!status) { 77 | throw new WebUIError(`unable to set root folder`); 78 | } 79 | } 80 | 81 | /** 82 | * Show the window or update the UI with the new content. 83 | * @returns Promise that resolves when the client bridge is linked. 84 | * @param {string} content - Valid html content or same root file path. 85 | * @throws {WebUIError} - If lib return false status. 86 | * @example 87 | * ```ts 88 | * const myWindow = new WebUI() 89 | * 90 | * // Show the current time 91 | * await myWindow.show(`

View 2

`) 199 | * 200 | * myWindow2.close() 201 | * 202 | * myWindow1.isShown // true 203 | * myWindow2.isShown // false 204 | * ``` 205 | */ 206 | close(): void { 207 | return this.#lib.symbols.webui_close(BigInt(this.#window)); 208 | } 209 | 210 | /** 211 | * Execute a JavaScript string in the UI and returns a boolean indicating whether the 212 | * script execution was successful. 213 | * @param {string} script - js code to execute. 214 | * @param options - response timeout (0 means no timeout), bufferSize, 215 | * default is `{ timeout: 0, bufferSize: 1024 * 1000 }`. 216 | * @returns Promise that resolve or reject the client response. 217 | * @example 218 | * ```ts 219 | * const response = await myWindow.script('return 6 + 4;').catch(console.error) 220 | * // response == "10" 221 | * ``` 222 | */ 223 | script( 224 | script: string, 225 | options?: { 226 | timeout?: number; 227 | bufferSize?: number; 228 | }, 229 | ): Promise { 230 | // Response Buffer 231 | const bufferSize = 232 | (options?.bufferSize !== undefined && options.bufferSize > 0) 233 | ? options.bufferSize 234 | : 1024 * 1000; 235 | const buffer = new Uint8Array(bufferSize); 236 | const timeout = options?.timeout ?? 0; 237 | 238 | // Execute the script 239 | const status = this.#lib.symbols.webui_script( 240 | BigInt(this.#window), 241 | toCString(script), 242 | BigInt(timeout), 243 | buffer, 244 | BigInt(bufferSize), 245 | ); 246 | 247 | const response = fromCString(buffer); 248 | 249 | // TODO: 250 | // Call symbol asynchronously 251 | if (status) { 252 | return Promise.resolve(response); 253 | } 254 | return Promise.reject(response); 255 | } 256 | 257 | /** 258 | * Same as `.script()`, but for one specific client. Use this API when using `setMultiClient(true)`. 259 | * @param {WebUIEvent} e - event. 260 | * @param {string} script - js code to execute. 261 | * @param options - response timeout (0 means no timeout), bufferSize, 262 | * default is `{ timeout: 0, bufferSize: 1024 * 1000 }`. 263 | * @returns Promise that resolve or reject the client response. 264 | * @example 265 | * ```ts 266 | * setMultiClient(true); 267 | * myWindow.bind('myBackend', (e: WebUI.Event) => { 268 | * const response = await myWindow.scriptClient(e, 'return 6 + 4;').catch(console.error) 269 | * // response == "10" 270 | * }); 271 | * ``` 272 | */ 273 | scriptClient( 274 | e: WebUIEvent, 275 | script: string, 276 | options?: { 277 | timeout?: number; 278 | bufferSize?: number; 279 | }, 280 | ): Promise { 281 | // Response Buffer 282 | const bufferSize = 283 | (options?.bufferSize !== undefined && options.bufferSize > 0) 284 | ? options.bufferSize 285 | : 1024 * 1000; 286 | const buffer = new Uint8Array(bufferSize); 287 | const timeout = options?.timeout ?? 0; 288 | 289 | // Execute the script 290 | const status = this.#lib.symbols.webui_interface_script_client( 291 | BigInt(this.#window), 292 | BigInt(e.eventNumber), 293 | toCString(script), 294 | BigInt(timeout), 295 | buffer, 296 | BigInt(bufferSize), 297 | ); 298 | 299 | const response = fromCString(buffer); 300 | 301 | // TODO: 302 | // Call symbol asynchronously 303 | if (status) { 304 | return Promise.resolve(response); 305 | } 306 | return Promise.reject(response); 307 | } 308 | 309 | /** 310 | * Execute a JavaScript string in the UI without waiting for the result. 311 | * @param {string} script - js code to execute. 312 | * @example 313 | * ```ts 314 | * myWindow.run('alert("Hello!")') 315 | * ``` 316 | */ 317 | run(script: string) { 318 | // Execute the script 319 | this.#lib.symbols.webui_run( 320 | BigInt(this.#window), 321 | toCString(script), 322 | ); 323 | } 324 | 325 | /** 326 | * Bind a callback function to a an HTML element 327 | * 328 | * @param {string} id - DOM element id. Blank string bind to all DOM elements. 329 | * @param callback - The callback function. 330 | * 331 | * @example 332 | * ```ts 333 | * const myWindow = new WebUI(); 334 | * await myWindow.show( 335 | * ` 336 | * 337 | * 338 | * 339 | * ` 340 | * ) 341 | * 342 | * async function myBtn(e: WebUI.Event) { 343 | * console.log(`${e.element} was clicked`); 344 | * } 345 | * myWindow.bind('myBtn', myBtn); 346 | * 347 | * myWindow.bind('myBackend', (e: WebUI.Event) => { 348 | * const myArg1 = e.arg.string(0); // Test 349 | * const myArg2 = e.arg.number(1); // 123456 350 | * return "backend response"; 351 | * }); 352 | * ``` 353 | */ 354 | bind( 355 | id: string, 356 | callback: BindCallback, 357 | ) { 358 | // Create the callback 359 | const callbackResource = new Deno.UnsafeCallback( 360 | { 361 | // size_t webui_interface_bind(..., void (*func)(size_t, size_t, char*, size_t, size_t)) 362 | // func = (Window, EventType, Element, EventNumber, BindID) 363 | parameters: ["usize", "usize", "pointer", "usize", "usize"], 364 | result: "void", 365 | } as const, 366 | async ( 367 | param_window: number | bigint, 368 | param_event_type: number | bigint, 369 | param_element: Deno.PointerValue, 370 | param_event_number: number | bigint, 371 | param_bind_id: number | bigint, 372 | ) => { 373 | // Create elements 374 | const win = param_window; 375 | const event_type = typeof param_event_type === "bigint" 376 | ? Number(param_event_type) 377 | : Math.trunc(param_event_type); 378 | const element = param_element !== null 379 | ? new Deno.UnsafePointerView(param_element).getCString() 380 | : ""; 381 | const event_number = typeof param_event_number === "bigint" 382 | ? Number(param_event_number) 383 | : Math.trunc(param_event_number); 384 | const _bind_id = typeof param_bind_id === "bigint" 385 | ? Number(param_bind_id) 386 | : Math.trunc(param_bind_id); 387 | 388 | // Set get argument methods 389 | const args = { 390 | number: (index: number): number => { 391 | return Number( 392 | this.#lib.symbols.webui_interface_get_int_at( 393 | BigInt(win), 394 | BigInt(event_number), 395 | BigInt(index), 396 | ), 397 | ); 398 | }, 399 | string: (index: number): string => { 400 | return ( 401 | new Deno.UnsafePointerView( 402 | this.#lib.symbols.webui_interface_get_string_at( 403 | BigInt(win), 404 | BigInt(event_number), 405 | BigInt(index), 406 | ) as Deno.PointerObject, 407 | ).getCString() 408 | ) as string; 409 | }, 410 | boolean: (index: number): boolean => { 411 | return this.#lib.symbols.webui_interface_get_bool_at( 412 | BigInt(win), 413 | BigInt(event_number), 414 | BigInt(index), 415 | ) as boolean; 416 | }, 417 | }; 418 | 419 | // Create struct 420 | const e: WebUIEvent = { 421 | window: windows.get(win)!, 422 | eventType: event_type, 423 | eventNumber: event_number, 424 | element: element, 425 | arg: args, 426 | }; 427 | 428 | // Call the user callback 429 | const result: string = (await callback(e) as string) ?? ""; 430 | 431 | // Send back the response 432 | this.#lib.symbols.webui_interface_set_response( 433 | BigInt(this.#window), 434 | BigInt(event_number), 435 | toCString(result), 436 | ); 437 | }, 438 | ); 439 | // Pass the callback pointer to WebUI 440 | this.#lib.symbols.webui_interface_bind( 441 | BigInt(this.#window), 442 | toCString(id), 443 | callbackResource.pointer, 444 | ); 445 | } 446 | 447 | /** 448 | * Sets a custom files handler to respond to HTTP requests. 449 | * 450 | * @param handler - Callback that takes an URL, and return a full HTTP header 451 | * + body. (`string` or `Uint8Array`). 452 | * 453 | * @example 454 | * 455 | * async function myFileHandler(myUrl: URL) { 456 | * if (myUrl.pathname === '/test') { 457 | * return "HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\nHello"; 458 | * } 459 | * }; 460 | * 461 | * myWindow.setFileHandler(myFileHandler); 462 | */ 463 | setFileHandler(callback: (url: URL) => Promise) { 464 | // C: .show_wait_connection = false; // 0 465 | // Disable `.show()` auto waiting for window connection, 466 | // otherwise `.setFileHandler()` will be blocked. 467 | _lib.symbols.webui_set_config(BigInt(0), false); 468 | 469 | // C: .use_cookies = false; // 4 470 | // We need to disable WebUI Cookies because 471 | // user will use his own custom HTTP header 472 | // in `.setFileHandler()`. 473 | _lib.symbols.webui_set_config(BigInt(4), false); 474 | 475 | // Let `.show()` knows that the user is using `.setFileHandler()` 476 | // so no need to wait for window connection in `.show()`. 477 | this.#isFileHandler = true; 478 | 479 | // Create the callback 480 | const callbackResource = new Deno.UnsafeCallback( 481 | { 482 | // const void* (*handler)(const char *filename, int *length) 483 | parameters: ["buffer", "pointer"], 484 | result: "void", 485 | } as const, 486 | async ( 487 | param_url: Deno.PointerValue, 488 | _param_length: Deno.PointerValue, 489 | ) => { 490 | // Get URL as string 491 | const url_str: string = param_url !== null 492 | ? new Deno.UnsafePointerView(param_url).getCString() 493 | : ""; 494 | 495 | // Create URL Obj 496 | const url_obj: URL = new URL(url_str, "http://localhost"); 497 | 498 | // Call the user callback 499 | const user_response: string | Uint8Array = await callback(url_obj); 500 | 501 | // We can pass a local buffer to WebUI like this: 502 | // `return Deno.UnsafePointer.of(user_response);` However, 503 | // this may create a memory leak because WebUI cannot free 504 | // it, or cause memory corruption as Deno may free the buffer 505 | // before WebUI uses it. Therefore, the solution is to create 506 | // a safe WebUI buffer through WebUI API. This WebUI buffer will 507 | // be automatically freed by WebUI later. 508 | const webui_buffer: Deno.PointerValue = _lib.symbols.webui_malloc( 509 | BigInt(user_response.length), 510 | ); 511 | if (!webui_buffer) { 512 | throw new Error("Failed to allocate memory for WebUI buffer"); 513 | } 514 | 515 | // Copy data to C safe buffer 516 | if (typeof user_response === "string") { 517 | // copy `user_response` to `webui_buffer` as String data 518 | const cString = toCString(user_response); 519 | const webui_buffer_ref = new Uint8Array( 520 | Deno.UnsafePointerView.getArrayBuffer( 521 | webui_buffer, 522 | cString.byteLength, 523 | ), 524 | ); 525 | webui_buffer_ref.set(new Uint8Array(cString)); 526 | } else { 527 | // copy `user_response` to `webui_buffer` as Uint8Array data 528 | const webui_buffer_ref = new Uint8Array( 529 | Deno.UnsafePointerView.getArrayBuffer( 530 | webui_buffer, 531 | user_response.byteLength, 532 | ), 533 | ); 534 | webui_buffer_ref.set(user_response); 535 | } 536 | 537 | // Send back the response 538 | this.#lib.symbols.webui_interface_set_response_file_handler( 539 | BigInt(this.#window), 540 | webui_buffer, 541 | BigInt(user_response.length), 542 | ); 543 | }, 544 | ); 545 | // Pass the callback pointer to WebUI 546 | this.#lib.symbols.webui_set_file_handler( 547 | BigInt(this.#window), 548 | callbackResource.pointer, 549 | ); 550 | } 551 | 552 | /** 553 | * Sets the profile name and path for the current window. 554 | * @param name - Profile name. 555 | * @param path - Profile path. 556 | * @example 557 | * ```ts 558 | * const myWindow = new WebUI(); 559 | * myWindow.setProfile("myProfile", "/path/to/profile"); 560 | * ``` 561 | */ 562 | setProfile(name: string, path: string): void { 563 | return this.#lib.symbols.webui_set_profile( 564 | BigInt(this.#window), 565 | toCString(name), 566 | toCString(path), 567 | ); 568 | } 569 | 570 | /** 571 | * Set the kiosk mode of a WebUI window. 572 | * 573 | * @param status - True to enable kiosk mode, false to disable. 574 | * @example 575 | * ```ts 576 | * const myWindow = new WebUI(); 577 | * myWindow.setKiosk(true); 578 | * ``` 579 | */ 580 | setKiosk(status: boolean): void { 581 | this.#lib.symbols.webui_set_kiosk(BigInt(this.#window), status); 582 | } 583 | 584 | /** 585 | * Close a specific window and free all memory resources. 586 | */ 587 | destroy(): void { 588 | this.#lib.symbols.webui_destroy(BigInt(this.#window)); 589 | } 590 | 591 | /** 592 | * Set the default embedded HTML favicon. 593 | * 594 | * @param icon - The icon as string: `...` 595 | * @param iconType - The icon type: `image/svg+xml` 596 | */ 597 | setIcon(icon: string, iconType: string): void { 598 | this.#lib.symbols.webui_set_icon( 599 | BigInt(this.#window), 600 | toCString(icon), 601 | toCString(iconType), 602 | ); 603 | } 604 | 605 | /** 606 | * Safely send raw data to the UI. 607 | * 608 | * @param functionName - The name of the function to send data to. 609 | * @param raw - The raw data to send. 610 | */ 611 | sendRaw(functionName: string, raw: Uint8Array): void { 612 | this.#lib.symbols.webui_send_raw( 613 | BigInt(this.#window), 614 | toCString(functionName), 615 | raw, 616 | BigInt(raw.length), 617 | ); 618 | } 619 | 620 | /** 621 | * Set a window in hidden mode. Should be called before `.show()`. 622 | * 623 | * @param status - True to hide, false to show. 624 | */ 625 | setHide(status: boolean): void { 626 | this.#lib.symbols.webui_set_hide(BigInt(this.#window), status); 627 | } 628 | 629 | /** 630 | * Set the window size. 631 | * 632 | * @param width - The width of the window. 633 | * @param height - The height of the window. 634 | */ 635 | setSize(width: number, height: number): void { 636 | this.#lib.symbols.webui_set_size(BigInt(this.#window), width, height); 637 | } 638 | 639 | /** 640 | * Set the window position. 641 | * 642 | * @param x - The x-coordinate of the window. 643 | * @param y - The y-coordinate of the window. 644 | */ 645 | setPosition(x: number, y: number): void { 646 | this.#lib.symbols.webui_set_position(BigInt(this.#window), x, y); 647 | } 648 | 649 | /** 650 | * Get the full current URL. 651 | * 652 | * @return - The current URL. 653 | */ 654 | getUrl(): string { 655 | return ( 656 | new Deno.UnsafePointerView( 657 | this.#lib.symbols.webui_get_url( 658 | BigInt(this.#window), 659 | ) as Deno.PointerObject, 660 | ).getCString() 661 | ) as string; 662 | } 663 | 664 | /** 665 | * Allow the window address to be accessible from a public network. 666 | * 667 | * @param status - True to allow public access, false to restrict. 668 | */ 669 | setPublic(status: boolean): void { 670 | this.#lib.symbols.webui_set_public(BigInt(this.#window), status); 671 | } 672 | 673 | /** 674 | * Navigate to a specific URL. 675 | * 676 | * @param {string} url - The URL to navigate to. 677 | * @example 678 | * ```ts 679 | * myWindow.navigate("https://webui.me"); 680 | * ``` 681 | */ 682 | navigate(url: string): void { 683 | this.#lib.symbols.webui_navigate(BigInt(this.#window), toCString(url)); 684 | } 685 | 686 | /** 687 | * Delete the web-browser local profile folder. 688 | */ 689 | deleteProfile(): void { 690 | this.#lib.symbols.webui_delete_profile(BigInt(this.#window)); 691 | } 692 | 693 | /** 694 | * Get the ID of the parent process (The web browser may re-create 695 | * another new process). 696 | * 697 | * @return - The parent process ID. 698 | */ 699 | getParentProcessId(): bigint { 700 | return this.#lib.symbols.webui_get_parent_process_id(BigInt(this.#window)); 701 | } 702 | 703 | /** 704 | * Get the ID of the last child process. 705 | * 706 | * @return - The last child process ID. 707 | */ 708 | getChildProcessId(): number { 709 | return Number( 710 | this.#lib.symbols.webui_get_child_process_id(BigInt(this.#window)), 711 | ); 712 | } 713 | 714 | /** 715 | * Set a custom web-server network port to be used by WebUI. 716 | * This can be useful to determine the HTTP link of `webui.js` in case 717 | * you are trying to use WebUI with an external web-server like NGNIX 718 | * 719 | * @param port - The port number. 720 | * @return - True if the port is set successfully. 721 | */ 722 | setPort(port: number): boolean { 723 | return this.#lib.symbols.webui_set_port(BigInt(this.#window), BigInt(port)); 724 | } 725 | 726 | /** 727 | * Chose between Deno and Nodejs as runtime for .js and .ts files. 728 | * 729 | * @param runtime - The runtime value. 730 | */ 731 | setRuntime(runtime: number): void { 732 | this.#lib.symbols.webui_set_runtime(BigInt(this.#window), BigInt(runtime)); 733 | } 734 | 735 | /** 736 | * Get the recommended web browser ID to use. If you are already using one, 737 | * this function will return the same ID. 738 | * 739 | * @return Returns a web browser ID. 740 | * @example 741 | * ```ts 742 | * const browserID = myWindow.getBestBrowser(); 743 | * ``` 744 | */ 745 | getBestBrowser(): number { 746 | return Number( 747 | this.#lib.symbols.webui_get_best_browser(BigInt(this.#window)), 748 | ); 749 | } 750 | 751 | /** 752 | * Start only the web server and return the URL. No window will be shown. 753 | * 754 | * @param {string} content - The HTML, Or a local file 755 | * @return Returns the url of this window server. 756 | * @example 757 | * ```ts 758 | * const url = myWindow.startServer("/full/root/path"); 759 | * ``` 760 | */ 761 | startServer(content: string): string { 762 | const url = this.#lib.symbols.webui_start_server( 763 | BigInt(this.#window), 764 | toCString(content), 765 | ); 766 | return Deno.UnsafePointerView.getCString(url!); 767 | } 768 | 769 | /** 770 | * Show a WebView window using embedded HTML, or a file. If the window is already 771 | * open, it will be refreshed. Note: Win32 need `WebView2Loader.dll`. 772 | * 773 | * @param {string} content - The HTML, URL, Or a local file 774 | * @return Returns True if showing the WebView window is successful. 775 | * @example 776 | * ```ts 777 | * await myWindow.showWebView("..."); 778 | * // or 779 | * await myWindow.showWebView("index.html"); 780 | * ``` 781 | */ 782 | showWebView(content: string): boolean { 783 | return this.#lib.symbols.webui_show_wv( 784 | BigInt(this.#window), 785 | toCString(content), 786 | ); 787 | } 788 | 789 | /** 790 | * Add a user-defined web browser's CLI parameters. 791 | * 792 | * @param {string} params - Command line parameters 793 | * @example 794 | * ```ts 795 | * myWindow.setCustomParameters("--remote-debugging-port=9222"); 796 | * ``` 797 | */ 798 | setCustomParameters(params: string): void { 799 | this.#lib.symbols.webui_set_custom_parameters( 800 | BigInt(this.#window), 801 | toCString(params), 802 | ); 803 | } 804 | 805 | /** 806 | * Set the window with high-contrast support. Useful when you want to 807 | * build a better high-contrast theme with CSS. 808 | * 809 | * @param {boolean} status - True or False 810 | * @example 811 | * ```ts 812 | * myWindow.setHighContrast(true); 813 | * ``` 814 | */ 815 | setHighContrast(status: boolean): void { 816 | this.#lib.symbols.webui_set_high_contrast(BigInt(this.#window), status); 817 | } 818 | 819 | /** 820 | * Set the window minimum size. 821 | * 822 | * @param {number} width - The window width 823 | * @param {number} height - The window height 824 | * @example 825 | * ```ts 826 | * myWindow.setMinimumSize(800, 600); 827 | * ``` 828 | */ 829 | setMinimumSize(width: number, height: number): void { 830 | this.#lib.symbols.webui_set_minimum_size( 831 | BigInt(this.#window), 832 | width, 833 | height, 834 | ); 835 | } 836 | 837 | /** 838 | * Set the web browser proxy server to use. Need to be called before `show()`. 839 | * 840 | * @param {string} proxyServer - The web browser proxy server 841 | * @example 842 | * ```ts 843 | * myWindow.setProxy("http://127.0.0.1:8888"); 844 | * ``` 845 | */ 846 | setProxy(proxyServer: string): void { 847 | this.#lib.symbols.webui_set_proxy( 848 | BigInt(this.#window), 849 | toCString(proxyServer), 850 | ); 851 | } 852 | 853 | // Static methods 854 | 855 | /** 856 | * Get OS high contrast preference. 857 | * 858 | * @return Returns True if OS is using high contrast theme 859 | * @example 860 | * ```ts 861 | * const hc = WebUI.isHighContrast(); 862 | * ``` 863 | */ 864 | static isHighContrast(): boolean { 865 | WebUI.init(); 866 | return _lib.symbols.webui_is_high_contrast(); 867 | } 868 | 869 | /** 870 | * Check if a web browser is installed. 871 | * 872 | * @param {WebUI.Browser} browser - The browser to check 873 | * @return Returns True if the specified browser is available 874 | * @example 875 | * ```ts 876 | * const status = WebUI.browserExist(WebUI.Browser.Chrome); 877 | * ``` 878 | */ 879 | static browserExist(browser: WebUI.Browser): boolean { 880 | WebUI.init(); 881 | return _lib.symbols.webui_browser_exist(BigInt(browser)); 882 | } 883 | 884 | /** 885 | * Set the web-server root folder path for all windows. Should be used 886 | * before `show()`. 887 | * 888 | * @param {string} path - The local folder full path 889 | * @return Returns True if the path is valid 890 | * @example 891 | * ```ts 892 | * WebUI.setDefaultRootFolder("/home/Foo/Bar/"); 893 | * ``` 894 | */ 895 | static setDefaultRootFolder(path: string): boolean { 896 | WebUI.init(); 897 | return _lib.symbols.webui_set_default_root_folder(toCString(path)); 898 | } 899 | 900 | /** 901 | * Open an URL in the native default web browser. 902 | * 903 | * @param {string} url - The URL to open 904 | * @example 905 | * ```ts 906 | * WebUI.openUrl("https://webui.me"); 907 | * ``` 908 | */ 909 | static openUrl(url: string): void { 910 | WebUI.init(); 911 | _lib.symbols.webui_open_url(toCString(url)); 912 | } 913 | 914 | /** 915 | * Get an available usable free network port. 916 | * 917 | * @return Returns a free port 918 | * @example 919 | * ```ts 920 | * const port = WebUI.getFreePort(); 921 | * ``` 922 | */ 923 | static getFreePort(): number { 924 | WebUI.init(); 925 | return Number(_lib.symbols.webui_get_free_port()); 926 | } 927 | 928 | /** 929 | * Automatically refresh the window UI when any file in the root folder gets changed. 930 | * 931 | * @param {boolean} status - True to enable monitoring, false to disable 932 | * @example 933 | * ```ts 934 | * WebUI.setFolderMonitor(true); 935 | * ``` 936 | */ 937 | static setFolderMonitor(status: boolean): void { 938 | WebUI.init(); 939 | _lib.symbols.webui_set_config(BigInt(2), status); 940 | } 941 | 942 | // --[ Static Methods ]------------------------ 943 | 944 | /** 945 | * Initialize WebUI library if it's not already initialized. 946 | */ 947 | private static init() { 948 | if (typeof _lib === "undefined") { 949 | _lib = loadLib(); 950 | // C: .asynchronous_response = true; // 5 951 | // Enable async calls, this is needed for `.bind()` 952 | _lib.symbols.webui_set_config(BigInt(5), true); 953 | } 954 | } 955 | 956 | /** 957 | * Tries to close all opened windows and make WebUI.wait() break. 958 | * @example 959 | * ```ts 960 | * const myWindow1 = new WebUI() 961 | * const myWindow2 = new WebUI() 962 | * 963 | * myWindow1.show(`

View 2

`) 965 | * 966 | * WebUI.exit() 967 | * 968 | * myWindow1.isShown // false 969 | * myWindow2.isShown // false 970 | * ``` 971 | */ 972 | static exit() { 973 | WebUI.init(); 974 | _lib.symbols.webui_exit(); 975 | } 976 | 977 | /** 978 | * Set certificate 979 | * @param certificatePem Set certificate 980 | * @param privateKeyPem Set private key 981 | * @throws {WebUIError} - If lib return false status. 982 | * @example 983 | * ```ts 984 | * const myWindow = new WebUI() 985 | * 986 | * // Show the current time 987 | * myWindow.setRootFolder('some/root/folder') 988 | * 989 | * const certificatePem = await Deno.readTextFile("some/root/certificate.pem"); 990 | * const privateKeyPem = await Deno.readTextFile("some/root/private_key.pem"); 991 | * WebUI.setTLSCertificate(certificatePem, privateKeyPem); 992 | * 993 | * // Show a local file 994 | * await myWindow.show('some/root/folder/index.html') 995 | * 996 | * // Await to ensure WebUI.script and WebUI.run can send datas to the client 997 | * console.assert(myWindow.isShown, true) 998 | * ``` 999 | */ 1000 | static setTLSCertificate(certificatePem: string, privateKeyPem: string) { 1001 | WebUI.init(); 1002 | const status = _lib.symbols.webui_set_tls_certificate( 1003 | toCString(certificatePem), 1004 | toCString(privateKeyPem), 1005 | ); 1006 | if (!status) { 1007 | throw new WebUIError(`unable to set certificate`); 1008 | } 1009 | } 1010 | 1011 | /** 1012 | * Waits until all opened windows are closed for preventing exiting the main thread. 1013 | * 1014 | * @example 1015 | * ```ts 1016 | * const myWindow = new WebUI() 1017 | * await myWindow.show(`