├── bootstrap.bat ├── bootstrap.sh ├── img ├── webui.png ├── screenshot.png ├── cppcon_2019.png ├── webui_diagram.png └── webui_deno_example.png ├── examples ├── send_raw_binary │ ├── webui.jpeg │ └── send_raw_binary.ts ├── custom_file_handler │ ├── assets │ │ ├── webui.jpeg │ │ └── test_app.js │ ├── index.html │ └── custom_file_handler.ts ├── custom_web_server │ ├── simple_web_server.py │ ├── second.html │ ├── index.html │ └── custom_web_server.ts ├── frameless │ └── frameless.ts └── hello_world │ └── hello_world.ts ├── deno.json ├── .github └── workflows │ ├── publish-jsr.yml │ └── deno.yml ├── mod.ts ├── LICENSE ├── .gitignore ├── deps.ts ├── src ├── types.ts ├── bootstrap.sh ├── bootstrap.bat ├── utils.ts ├── lib.ts └── webui.ts └── README.md /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 | -------------------------------------------------------------------------------- /img/webui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webui-dev/deno-webui/HEAD/img/webui.png -------------------------------------------------------------------------------- /img/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webui-dev/deno-webui/HEAD/img/screenshot.png -------------------------------------------------------------------------------- /img/cppcon_2019.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webui-dev/deno-webui/HEAD/img/cppcon_2019.png -------------------------------------------------------------------------------- /img/webui_diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webui-dev/deno-webui/HEAD/img/webui_diagram.png -------------------------------------------------------------------------------- /img/webui_deno_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webui-dev/deno-webui/HEAD/img/webui_deno_example.png -------------------------------------------------------------------------------- /examples/send_raw_binary/webui.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webui-dev/deno-webui/HEAD/examples/send_raw_binary/webui.jpeg -------------------------------------------------------------------------------- /examples/custom_file_handler/assets/webui.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webui-dev/deno-webui/HEAD/examples/custom_file_handler/assets/webui.jpeg -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@webui/deno-webui", 3 | "version": "2.5.13", 4 | "exports": "./mod.ts", 5 | "imports": { 6 | "@std/fs": "jsr:@std/fs@^1.0.19", 7 | "@std/path": "jsr:@std/path@^1.1.2", 8 | "@zip-js/zip-js": "jsr:@zip-js/zip-js@^2.8.7" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /.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: Setup Deno 19 | uses: denoland/setup-deno@v2 20 | 21 | - name: Publish package 22 | run: deno publish 23 | -------------------------------------------------------------------------------- /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/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: 14 |

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

16 |

Simple link example (Local file)

17 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /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.13"; 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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.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 | /.claude 114 | CLAUDE.md 115 | -------------------------------------------------------------------------------- /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/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 compile this script 5 | // deno compile --allow-all --no-terminal custom_file_handler.ts 6 | 7 | // To import from local (Debugging and Development) 8 | // import { WebUI } from "../../mod.ts"; 9 | 10 | // To import from online package registry (Production) 11 | import { WebUI } from "@webui/deno-webui"; 12 | 13 | // Return HTTP header + file raw binary content 14 | const getFile = async ( 15 | contentType: string, 16 | filename: string, 17 | ): Promise => { 18 | const content = await Deno.readFile(filename); 19 | const header = `HTTP/1.1 200 OK\r\nContent-Type: ${contentType}\r\n\r\n`; 20 | const headerBytes = new TextEncoder().encode(header); 21 | const response = new Uint8Array(headerBytes.length + content.length); 22 | response.set(headerBytes); 23 | response.set(content, headerBytes.length); 24 | return response; 25 | }; 26 | 27 | // Set a custom files handler 28 | async function myFileHandler(myUrl: URL) { 29 | console.log(`File: ${myUrl.pathname}`); 30 | // Index example 31 | if (myUrl.pathname === "/index.html" || myUrl.pathname === "/") { 32 | return await getFile("text/html", "index.html"); 33 | } 34 | // Custom text string example 35 | if (myUrl.pathname === "/test") { 36 | return "HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\nHello"; 37 | } 38 | // File examples 39 | if (myUrl.pathname === "/assets/test_app.js") { 40 | return await getFile("application/javascript", "assets/test_app.js"); 41 | } 42 | if (myUrl.pathname === "/assets/webui.jpeg") { 43 | return await getFile("image/jpeg", "assets/webui.jpeg"); 44 | } 45 | // Error 404 example 46 | return "HTTP/1.1 404 Not Found"; 47 | } 48 | 49 | // Create new window 50 | const myWindow = new WebUI(); 51 | 52 | // Bind Exit 53 | myWindow.bind("exit", () => { 54 | // Close all windows and exit 55 | WebUI.exit(); 56 | }); 57 | 58 | // Set files handler 59 | // Note: Should be called before `.show()` 60 | myWindow.setFileHandler(myFileHandler); 61 | 62 | // Show the window 63 | await myWindow.showBrowser("index.html", WebUI.Browser.AnyBrowser); 64 | 65 | // Wait until all windows get closed 66 | await WebUI.wait(); 67 | 68 | console.log("Thank you."); 69 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /examples/frameless/frameless.ts: -------------------------------------------------------------------------------- 1 | // To run this script: 2 | // deno run --allow-read --allow-write --allow-net --allow-env --allow-ffi frameless.ts 3 | 4 | // To compile this script 5 | // deno compile --allow-all --no-terminal frameless.ts 6 | 7 | // To import from local (Debugging and Development) 8 | // import { WebUI } from "../../mod.ts"; 9 | 10 | // To import from online package registry (Production) 11 | import { WebUI } from "@webui/deno-webui"; 12 | 13 | const myHtml = ` 14 | 15 | 16 | 17 | 18 | 65 | 66 | 67 |
68 | WebUI Deno Frameless Window 69 |
70 | 71 | 72 |
73 |
74 |
75 | This is a WebUI Deno frameless example 76 |
77 | 78 | `; 79 | 80 | // Create new window 81 | const myWindow = new WebUI(); 82 | 83 | // Bind 84 | myWindow.bind("minimize", () => { 85 | myWindow.minimize(); 86 | }); 87 | myWindow.bind("close_win", () => { 88 | WebUI.exit(); 89 | }); 90 | 91 | myWindow.setSize(800, 600); 92 | myWindow.setFrameless(true); 93 | myWindow.setTransparent(true); 94 | myWindow.setResizable(false); 95 | myWindow.setCenter(); 96 | 97 | // Show the window 98 | await myWindow.showWebView(myHtml); // in Microsoft Windows you may need `WebView2Loader.dll` 99 | 100 | // Wait until all windows get closed 101 | await WebUI.wait(); 102 | 103 | console.log("Thank you."); 104 | -------------------------------------------------------------------------------- /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 compile this script 5 | // deno compile --allow-all --no-terminal custom_web_server.ts 6 | 7 | // To import from local (Debugging and Development) 8 | // import { WebUI } from "../../mod.ts"; 9 | 10 | // To import from online package registry (Production) 11 | import { WebUI } from "@webui/deno-webui"; 12 | 13 | function allEvents(e: WebUI.Event) { 14 | /* 15 | e.window: WebUI; 16 | e.eventType: WebUI.EventType; 17 | e.element: string; 18 | */ 19 | console.log(`\nallEvents: window = '${e.window}'`); 20 | console.log(`allEvents: eventType = '${e.eventType}'`); 21 | console.log(`allEvents: element = '${e.element}'`); 22 | switch (e.eventType) { 23 | case WebUI.EventType.Disconnected: 24 | // Window disconnection event 25 | console.log(`Window closed.`); 26 | break; 27 | case WebUI.EventType.Connected: 28 | // Window connection event 29 | console.log(`Window connected.`); 30 | break; 31 | case WebUI.EventType.MouseClick: 32 | // Mouse click event 33 | console.log(`Mouse click.`); 34 | break; 35 | case WebUI.EventType.Navigation: { 36 | // Window navigation event 37 | const url = e.arg.string(0); 38 | console.log(`Navigation to '${url}'`); 39 | // Because we used `webui_bind(MyWindow, "", events);` 40 | // WebUI will block all `href` link clicks and sent here instead. 41 | // We can then control the behaviour of links as needed. 42 | e.window.navigate(url); 43 | break; 44 | } 45 | case WebUI.EventType.Callback: 46 | // Function call event 47 | console.log(`Function call.`); 48 | break; 49 | } 50 | } 51 | 52 | function myBackendFunc(e: WebUI.Event) { 53 | const a = e.arg.number(0); // First argument 54 | const b = e.arg.number(1); // Second argument 55 | const c = e.arg.number(2); // Third argument 56 | console.log(`\nFirst argument: ${a}`); 57 | console.log(`Second argument: ${b}`); 58 | console.log(`Third argument: ${c}`); 59 | } 60 | 61 | // Create new window 62 | const myWindow = new WebUI(); 63 | 64 | // Bind All Events 65 | myWindow.bind("", allEvents); 66 | 67 | // Bind Backend Function 68 | myWindow.bind("myBackendFunc", myBackendFunc); 69 | 70 | // Bind Exit Function 71 | myWindow.bind("exit", () => { 72 | // Close all windows and exit 73 | WebUI.exit(); 74 | }); 75 | 76 | // Set the web-server/WebSocket port that WebUI should 77 | // use. This means `webui.js` will be available at: 78 | // http://localhost:MY_PORT_NUMBER/webui.js 79 | myWindow.setPort(8081); 80 | 81 | // Start our custom web server using Python script `python simple_web_server.py`. 82 | // Or using `file-server` module. 83 | const webServer = new Deno.Command("deno", { 84 | args: ["-RNS", "jsr:@std/http/file-server", "-p", "8080"], 85 | }).spawn(); 86 | await new Promise((r) => setTimeout(r, 500)); 87 | 88 | // Show a new window and point to our custom web server 89 | // Assuming the custom web server is running on port 90 | // 8080... 91 | await myWindow.showBrowser("http://localhost:8080/", WebUI.Browser.AnyBrowser); 92 | 93 | // Wait until all windows get closed 94 | await WebUI.wait(); 95 | 96 | // Stop the web server 97 | webServer.kill("SIGTERM"); 98 | await webServer.status; 99 | 100 | console.log("Thank you."); 101 | -------------------------------------------------------------------------------- /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 "@webui/deno-webui"; 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/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 compile this script 5 | // deno compile --allow-all --no-terminal hello_world.ts 6 | 7 | // To import from local (Debugging and Development) 8 | // import { WebUI } from "../../mod.ts"; 9 | 10 | // To import from online package registry (Production) 11 | import { WebUI } from "@webui/deno-webui"; 12 | 13 | const myHtml = ` 14 | 15 | 16 | 17 | WebUI 2 - Deno Hello World Example 18 | 44 | 45 | 46 |

WebUI 2 - Deno Hello World


47 | A:

48 | B:

49 |
Result: ?


50 | - - 51 | 74 | 75 | `; 76 | 77 | function checkResult(e: WebUI.Event) { 78 | const a = e.arg.number(0); // First argument 79 | const b = e.arg.number(1); // Second argument 80 | const res = e.arg.number(2); // Third argument 81 | if ((a + b) == res) { 82 | return `Correct: ${a} + ${b} = ${res}`; 83 | } else { 84 | return `Incorrect: ${a} + ${b} != ${res}`; 85 | } 86 | } 87 | 88 | async function calculate(e: WebUI.Event) { 89 | // Run JavaScript and wait for response 90 | const getA = await e.window.script("return get_A()").catch((error) => { 91 | console.error(`Error in the JavaScript: ${error}`); 92 | return ""; 93 | }); 94 | const getB = await e.window.script("return get_B()").catch((error) => { 95 | console.error(`Error in the JavaScript: ${error}`); 96 | return ""; 97 | }); 98 | 99 | // Calculate 100 | const result = parseInt(getA) + parseInt(getB); 101 | 102 | // Run JavaScript without waiting for response (Quick) 103 | e.window.run(`set_result(${result});`); 104 | } 105 | 106 | // Create new window 107 | const myWindow = new WebUI(); 108 | 109 | // Bind 110 | myWindow.bind("calculate", calculate); 111 | myWindow.bind("checkResult", checkResult); 112 | myWindow.bind("exit", () => { 113 | // Close all windows and exit 114 | WebUI.exit(); 115 | }); 116 | 117 | // Show the window 118 | await myWindow.showBrowser(myHtml, WebUI.Browser.AnyBrowser); // Or await myWindow.show('./myFile.html'); 119 | 120 | // Wait until all windows get closed 121 | await WebUI.wait(); 122 | 123 | console.log("Thank you."); 124 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | ![Logo](https://raw.githubusercontent.com/webui-dev/webui-logo/main/webui_deno.png) 4 | 5 | # Deno-WebUI v2.5.13 (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.13"; 50 | ``` 51 | 52 | Latest version: 53 | 54 | ```js 55 | import { WebUI } from "jsr:@webui/deno-webui"; 56 | ``` 57 | 58 | ## Minimal Example 59 | 60 | ```js 61 | import { WebUI } from "jsr:@webui/deno-webui"; 62 | 63 | const myWindow = new WebUI(); 64 | await myWindow.show( 65 | ' Hello World! ', 66 | ); 67 | await WebUI.wait(); 68 | ``` 69 | 70 | ```sh 71 | deno run --allow-read --allow-write --allow-net --allow-env --allow-ffi minimal.ts 72 | ``` 73 | 74 | [More examples](https://github.com/webui-dev/deno-webui/tree/main/examples) 75 | 76 | ## Documentation 77 | 78 | - [Online Documentation](https://webui.me/docs/2.5/#/) 79 | 80 | ## CppCon 2019 Presentation 81 | 82 | [Borislav Stanimirov](https://ibob.bg/) explained at 83 | [C++ Conference 2019 (_YouTube_)](https://www.youtube.com/watch?v=bbbcZd4cuxg) 84 | how beneficial it is to use the web browser as GUI. 85 | 86 | 89 | 90 | ![ScreenShot](img/cppcon_2019.png) 91 | 92 | ## UI & The Web Technologies 93 | 94 | Web application UI design is not just about how a product looks but how it 95 | works. Using web technologies in your UI makes your product modern and 96 | professional, And a well-designed web application will help you make a solid 97 | first impression on potential customers. Great web application design also 98 | assists you in nurturing leads and increasing conversions. In addition, it makes 99 | navigating and using your web app easier for your users. 100 | 101 | ## Why Use Web Browser? 102 | 103 | Today's web browsers have everything a modern UI needs. Web browsers are very 104 | sophisticated and optimized. Therefore, using it as a GUI will be an excellent 105 | choice. While old legacy GUI lib is complex and outdated, a WebView-based app is 106 | still an option. However, a WebView needs a huge SDK to build and many 107 | dependencies to run, and it can only provide some features like a real web 108 | browser. That is why WebUI uses real web browsers to give you full features of 109 | comprehensive web technologies while keeping your software lightweight and 110 | portable. 111 | 112 | ## How does it work? 113 | 114 | ![ScreenShot](img/webui_diagram.png) 115 | 116 | Think of WebUI like a WebView controller, but instead of embedding the WebView 117 | controller in your program, which makes the final program big in size, and 118 | non-portable as it needs the WebView runtimes. Instead, by using WebUI, you use 119 | a tiny static/dynamic library to run any installed web browser and use it as 120 | GUI, which makes your program small, fast, and portable. **All it needs is a web 121 | browser**. 122 | 123 | ## Runtime Dependencies Comparison 124 | 125 | | | Tauri / WebView | Qt | WebUI | 126 | | ------------------------------- | ----------------- | -------------------------- | ------------------- | 127 | | Runtime Dependencies on Windows | _WebView2_ | _QtCore, QtGui, QtWidgets_ | **_A Web Browser_** | 128 | | Runtime Dependencies on Linux | _GTK3, WebKitGTK_ | _QtCore, QtGui, QtWidgets_ | **_A Web Browser_** | 129 | | Runtime Dependencies on macOS | _Cocoa, WebKit_ | _QtCore, QtGui, QtWidgets_ | **_A Web Browser_** | 130 | 131 | ## Supported Web Browsers 132 | 133 | | Browser | Windows | macOS | Linux | 134 | | --------------- | --------------- | ------------- | --------------- | 135 | | Mozilla Firefox | ✔️ | ✔️ | ✔️ | 136 | | Google Chrome | ✔️ | ✔️ | ✔️ | 137 | | Microsoft Edge | ✔️ | ✔️ | ✔️ | 138 | | Chromium | ✔️ | ✔️ | ✔️ | 139 | | Yandex | ✔️ | ✔️ | ✔️ | 140 | | Brave | ✔️ | ✔️ | ✔️ | 141 | | Vivaldi | ✔️ | ✔️ | ✔️ | 142 | | Epic | ✔️ | ✔️ | _not available_ | 143 | | Apple Safari | _not available_ | _coming soon_ | _not available_ | 144 | | Opera | _coming soon_ | _coming soon_ | _coming soon_ | 145 | 146 | ## Supported Languages 147 | 148 | | Language | v2.4.0 API | v2.5.0 API | Link | 149 | | -------------- | -------------- | -------------- | ----------------------------------------------------------------- | 150 | | Python | ✔️ | _not complete_ | [Python-WebUI](https://github.com/webui-dev/python-webui) | 151 | | Go | ✔️ | _not complete_ | [Go-WebUI](https://github.com/webui-dev/go-webui) | 152 | | Zig | ✔️ | _not complete_ | [Zig-WebUI](https://github.com/webui-dev/zig-webui) | 153 | | Nim | ✔️ | _not complete_ | [Nim-WebUI](https://github.com/webui-dev/nim-webui) | 154 | | V | ✔️ | _not complete_ | [V-WebUI](https://github.com/webui-dev/v-webui) | 155 | | Rust | _not complete_ | _not complete_ | [Rust-WebUI](https://github.com/webui-dev/rust-webui) | 156 | | TS / JS (Deno) | ✔️ | _not complete_ | [Deno-WebUI](https://github.com/webui-dev/deno-webui) | 157 | | TS / JS (Bun) | _not complete_ | _not complete_ | [Bun-WebUI](https://github.com/webui-dev/bun-webui) | 158 | | Swift | _not complete_ | _not complete_ | [Swift-WebUI](https://github.com/webui-dev/swift-webui) | 159 | | Odin | _not complete_ | _not complete_ | [Odin-WebUI](https://github.com/webui-dev/odin-webui) | 160 | | Pascal | _not complete_ | _not complete_ | [Pascal-WebUI](https://github.com/webui-dev/pascal-webui) | 161 | | Purebasic | _not complete_ | _not complete_ | [Purebasic-WebUI](https://github.com/webui-dev/purebasic-webui) | 162 | | - | | | | 163 | | Common Lisp | _not complete_ | _not complete_ | [cl-webui](https://github.com/garlic0x1/cl-webui) | 164 | | Delphi | _not complete_ | _not complete_ | [WebUI4Delphi](https://github.com/salvadordf/WebUI4Delphi) | 165 | | C# | _not complete_ | _not complete_ | [WebUI4CSharp](https://github.com/salvadordf/WebUI4CSharp) | 166 | | WebUI.NET | _not complete_ | _not complete_ | [WebUI.NET](https://github.com/Juff-Ma/WebUI.NET) | 167 | | QuickJS | _not complete_ | _not complete_ | [QuickUI](https://github.com/xland/QuickUI) | 168 | | PHP | _not complete_ | _not complete_ | [PHPWebUiComposer](https://github.com/KingBes/php-webui-composer) | 169 | 170 | ## Supported WebView 171 | 172 | | WebView | Status | 173 | | ----------------- | ------ | 174 | | Windows WebView2 | ✔️ | 175 | | Linux GTK WebView | ✔️ | 176 | | macOS WKWebView | ✔️ | 177 | 178 | ### License 179 | 180 | > Licensed under MIT License. 181 | 182 | ### Stargazers 183 | 184 | [![Stargazers repo roster for @webui-dev/deno-webui](https://reporoster.com/stars/webui-dev/deno-webui)](https://github.com/webui-dev/deno-webui/stargazers) 185 | -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | // Deno WebUI 2 | // Utilities 3 | import { dirname, join } from "@std/path"; 4 | import { exists } from "@std/fs"; 5 | import { BlobReader, BlobWriter, ZipReader } from "@zip-js/zip-js"; 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 {Promise} "nightly-$hashOfLastModifiedNightly" or the specific WebUICoreVersion. 68 | */ 69 | async function getVersionDirName(): Promise { 70 | if (useNightly) { 71 | return `nightly-${await getlLastModifedNightlyDateAsHash()}`; 72 | } 73 | return WebUICoreVersion; 74 | } 75 | 76 | // --- Download and Extraction Logic --- 77 | 78 | /** 79 | * Downloads and extracts the required WebUI library to the specific version cache directory. 80 | * @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). 81 | * @param {string} osName - OS identifier (e.g., "windows", "macos", "linux"). 82 | * @param {string} compilerName - Compiler identifier (e.g., "msvc", "clang", "gcc"). 83 | * @param {string} archName - Architecture identifier (e.g., "x64", "arm64"). 84 | * @param {string} libFileNameInZip - The full path of the library *inside* the zip archive. 85 | * @returns {Promise} 86 | * @throws {Error} If download or extraction fails. 87 | */ 88 | async function downloadAndExtractLibrary( 89 | targetLibPath: string, 90 | osName: string, 91 | compilerName: string, 92 | archName: string, 93 | libFileNameInZip: string, 94 | ): Promise { 95 | // The cache directory for this *specific version* 96 | const versionCacheDir = dirname(targetLibPath); 97 | 98 | // Determine download URL 99 | const version = await getVersionDirName(); // Get "nightly-$hashOfLastModifiedNightlyRelease" or the specific version string 100 | const baseUrl = version.startsWith("nightly") 101 | ? `https://github.com/webui-dev/webui/releases/download/nightly/` 102 | : `https://github.com/webui-dev/webui/releases/download/${version}/`; 103 | 104 | const zipFileName = `webui-${osName}-${compilerName}-${archName}.zip`; 105 | const zipUrl = `${baseUrl}${zipFileName}`; 106 | // Temporary download path inside the version-specific cache dir 107 | const tempZipPath = join(versionCacheDir, `${zipFileName}.download`); 108 | 109 | // console.log(`Downloading WebUI library (${version}) from ${zipUrl}...`); 110 | 111 | try { 112 | // Ensure the target version directory exists before downloading 113 | await Deno.mkdir(versionCacheDir, { recursive: true }); 114 | 115 | // Download the archive 116 | const res = await fetch(zipUrl); 117 | if (!res.ok) { 118 | throw new Error( 119 | `Failed to download ${zipUrl}: ${res.status} ${res.statusText}`, 120 | ); 121 | } 122 | const zipData = await res.arrayBuffer(); 123 | await Deno.writeFile(tempZipPath, new Uint8Array(zipData)); 124 | // console.log(`Downloaded to ${tempZipPath}`); 125 | 126 | // Extract the specific library file 127 | // console.log(`Extracting ${libFileNameInZip} from ${tempZipPath}...`); 128 | const zipBlob = new Blob([zipData]); 129 | const zipReader = new ZipReader(new BlobReader(zipBlob)); 130 | const entries = await zipReader.getEntries(); 131 | 132 | let foundEntry = false; 133 | for (const entry of entries) { 134 | // Normalize zip entry filename (might contain different slashes) 135 | const entryPath = entry.filename.replace(/\\/g, "/"); 136 | const targetEntryPath = libFileNameInZip.replace(/\\/g, "/"); 137 | 138 | if (!entry.directory && entryPath === targetEntryPath) { 139 | // console.log(`Found entry: ${entry.filename}`); 140 | const writer = new BlobWriter(); 141 | const data = await entry.getData!(writer); 142 | await Deno.writeFile( 143 | targetLibPath, 144 | new Uint8Array(await data.arrayBuffer()), 145 | ); 146 | foundEntry = true; 147 | // console.log(`Extracted library to ${targetLibPath}`); 148 | break; // Found the file, no need to check others 149 | } 150 | } 151 | await zipReader.close(); 152 | 153 | if (!foundEntry) { 154 | throw new Error( 155 | `Library file "${libFileNameInZip}" not found inside downloaded archive ${zipFileName}`, 156 | ); 157 | } 158 | } catch (error) { 159 | console.error("WebUI library download/extraction failed:", error); 160 | // Clean up partial download if it exists 161 | try { 162 | await Deno.remove(targetLibPath).catch(() => {}); // Remove potentially incomplete extraction 163 | } catch (e) { 164 | if (!(e instanceof Deno.errors.NotFound)) { 165 | console.error("Cleanup error:", e); 166 | } 167 | } 168 | throw error; // Re-throw the error 169 | } finally { 170 | // Clean up the downloaded zip file regardless of success/failure 171 | try { 172 | await Deno.remove(tempZipPath); 173 | // console.log(`Removed temporary file ${tempZipPath}`); 174 | } catch (e) { 175 | if (!(e instanceof Deno.errors.NotFound)) { 176 | console.error(`Failed to remove temporary zip file ${tempZipPath}:`, e); 177 | } 178 | } 179 | } 180 | } 181 | 182 | /** 183 | * Ensures the correct WebUI native library exists in the versioned cache, downloading it if necessary. 184 | * @param {string} baseLibName - The OS-specific library filename (e.g., "webui-2.dll"). 185 | * @returns {Promise} The full path to the cached library file (e.g., ~/.cache/deno_webui/2.5.0-beta.3/webui-2.dll). 186 | */ 187 | export async function ensureWebUiLib(baseLibName: string): Promise { 188 | // 1. Get the base cache directory (e.g., ~/.cache/deno_webui) 189 | const baseWebUICacheDir = await getBaseWebUICacheDir(); 190 | 191 | // 2. Determine the version-specific subdirectory name ("nightly-$hashOfTheLastModifiedNighylyDate" or "2.5.0-beta.3") 192 | const versionDirName = await getVersionDirName(); 193 | 194 | // 3. Construct the path to the version-specific cache directory 195 | const versionCacheDir = join(baseWebUICacheDir, versionDirName); 196 | 197 | // 4. Construct the final target path for the library file 198 | const targetLibPath = join(versionCacheDir, baseLibName); 199 | 200 | // 5. Ensure the version-specific cache directory exists *before* checking for the file 201 | // (downloadAndExtractLibrary also does this, but doing it here prevents 202 | // an unnecessary download attempt if only the directory is missing) 203 | await Deno.mkdir(versionCacheDir, { recursive: true }); 204 | 205 | // 6. Check if the library already exists in the cache 206 | if (await exists(targetLibPath)) { 207 | // console.log( 208 | // `Using cached WebUI library (${versionDirName}): ${targetLibPath}`, 209 | // ); 210 | return targetLibPath; 211 | } 212 | 213 | // 7. Determine download parameters if not cached 214 | // console.log( 215 | // `WebUI library (${versionDirName}) not found in cache. Attempting download...`, 216 | // ); 217 | let osName: string; 218 | let compilerName: string; 219 | 220 | const archMap: { [key: string]: string } = { 221 | "x86_64": "x64", 222 | "aarch64": "arm64", 223 | }; 224 | const archName = archMap[Deno.build.arch]; 225 | if (!archName) { 226 | throw new Error( 227 | `Unsupported architecture: ${Deno.build.arch} for ${Deno.build.os}`, 228 | ); 229 | } 230 | 231 | switch (Deno.build.os) { 232 | case "windows": 233 | osName = "windows"; 234 | compilerName = "msvc"; 235 | break; 236 | case "darwin": 237 | osName = "macos"; 238 | compilerName = "clang"; 239 | break; 240 | default: // Linux and others 241 | osName = "linux"; 242 | compilerName = "gcc"; 243 | break; 244 | } 245 | 246 | const zipDirName = `webui-${osName}-${compilerName}-${archName}`; 247 | const libFileNameInZip = `${zipDirName}/${baseLibName}`; // Path inside the zip 248 | 249 | // 8. Download and extract 250 | await downloadAndExtractLibrary( 251 | targetLibPath, // Pass the full final path 252 | osName, 253 | compilerName, 254 | archName, 255 | libFileNameInZip, 256 | ); 257 | 258 | // 9. Return the path 259 | return targetLibPath; 260 | } 261 | 262 | // --- String Conversions (Keep as they are useful) --- 263 | 264 | /** 265 | * Convert a String to C-String. 266 | * @param {string} value 267 | * @returns a char[]. 268 | */ 269 | export function toCString(value: string): Uint8Array { 270 | return new TextEncoder().encode(value + "\0"); 271 | } 272 | 273 | /** 274 | * Convert a C-String to String. 275 | * @param {Uint8Array} value - an `char* / Uint8Array` that contains a C-String. 276 | * @returns a string. 277 | */ 278 | export function fromCString(value: Uint8Array): string { 279 | const end = value.findIndex((byte) => byte === 0x00); //find C-string end 280 | return new TextDecoder().decode(value.slice(0, end)); 281 | } 282 | 283 | export class WebUIError extends Error {} 284 | 285 | async function getlLastModifedNightlyDateAsHash() { 286 | // it doesn't matter that we're using a specific build, its just to determine last modificaiton date 287 | const url = 288 | "https://github.com/webui-dev/webui/releases/download/nightly/webui-linux-gcc-x64.zip"; 289 | try { 290 | // Perform a HEAD request to get only the headers 291 | const response = await fetch(url, { 292 | method: "HEAD", 293 | }); 294 | 295 | if (response.ok) { 296 | const headers = response.headers; 297 | 298 | const lastModified = headers.get("last-modified"); 299 | 300 | if (lastModified) { 301 | const encoder = new TextEncoder(); 302 | const data = encoder.encode(lastModified); 303 | 304 | // Calculate the SHA-256 hash 305 | const hashBuffer = await crypto.subtle.digest("SHA-256", data); 306 | 307 | // Hash it 308 | return Array.from( 309 | new Uint8Array(hashBuffer), 310 | (byte) => byte.toString(16).padStart(2, "0"), 311 | ) 312 | .join("") 313 | .slice(0, 8); 314 | } else { 315 | throw new Error( 316 | "could not find last-modified header in nightly release", 317 | ); 318 | } 319 | } else { 320 | throw new Error( 321 | `Error fetching headers: ${response.status} ${response.statusText}`, 322 | ); 323 | } 324 | } catch (error) { 325 | throw new Error("Network error: " + error); 326 | } 327 | } 328 | -------------------------------------------------------------------------------- /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_new_window_id: { 19 | // size_t webui_new_window_id(size_t window_number) 20 | parameters: ["usize"], 21 | result: "usize", 22 | }, 23 | webui_get_new_window_id: { 24 | // size_t webui_get_new_window_id(void) 25 | parameters: [], 26 | result: "usize", 27 | }, 28 | webui_show: { 29 | // bool webui_show(size_t window, const char* content) 30 | parameters: ["usize", "buffer"], 31 | result: "bool", 32 | // https://github.com/webui-dev/deno-webui/issues/91 33 | nonblocking: Deno.build.os !== "darwin", 34 | }, 35 | webui_show_browser: { 36 | // bool webui_show_browser(size_t window, const char* content, size_t browser) 37 | parameters: ["usize", "buffer", "usize"], 38 | result: "bool", 39 | // https://github.com/webui-dev/deno-webui/issues/91 40 | nonblocking: Deno.build.os !== "darwin", 41 | }, 42 | webui_interface_bind: { 43 | // size_t webui_interface_bind(size_t window, const char* element, void (*func)(size_t, size_t, char*, size_t, size_t)); 44 | parameters: ["usize", "buffer", "function"], 45 | result: "usize", 46 | }, 47 | webui_script: { 48 | // bool webui_script(size_t window, const char* script, size_t timeout, char* buffer, size_t buffer_length) 49 | parameters: ["usize", "buffer", "usize", "buffer", "usize"], 50 | result: "bool", 51 | }, 52 | webui_run: { 53 | // void webui_run(size_t window, const char* script) 54 | parameters: ["usize", "buffer"], 55 | result: "void", 56 | }, 57 | webui_interface_set_response: { 58 | // void webui_interface_set_response(size_t window, size_t event_number, const char* response) 59 | parameters: ["usize", "usize", "buffer"], 60 | result: "void", 61 | }, 62 | webui_exit: { 63 | // void webui_exit(void) 64 | parameters: [], 65 | result: "void", 66 | }, 67 | webui_is_shown: { 68 | // bool webui_is_shown(size_t window) 69 | parameters: ["usize"], 70 | result: "bool", 71 | }, 72 | webui_close: { 73 | // void webui_close(size_t window) 74 | parameters: ["usize"], 75 | result: "void", 76 | }, 77 | webui_minimize: { 78 | // void webui_minimize(size_t window) 79 | parameters: ["usize"], 80 | result: "void", 81 | }, 82 | webui_maximize: { 83 | // void webui_maximize(size_t window) 84 | parameters: ["usize"], 85 | result: "void", 86 | }, 87 | webui_set_file_handler: { 88 | // void webui_set_file_handler(size_t window, const void* (*handler)(const char* filename, int* length)) 89 | parameters: ["usize", "function"], 90 | result: "void", 91 | }, 92 | webui_set_file_handler_window: { 93 | // void webui_set_file_handler_window(size_t window, const void* (*handler)(size_t window, const char* filename, int* length)) 94 | parameters: ["usize", "function"], 95 | result: "void", 96 | }, 97 | webui_interface_is_app_running: { 98 | // bool webui_interface_is_app_running(void) 99 | parameters: [], 100 | result: "bool", 101 | }, 102 | webui_set_profile: { 103 | // void webui_set_profile(size_t window, const char* name, const char* path) 104 | parameters: ["usize", "buffer", "buffer"], 105 | result: "void", 106 | }, 107 | webui_interface_get_int_at: { 108 | // long long int webui_interface_get_int_at(size_t window, size_t event_number, size_t index) 109 | parameters: ["usize", "usize", "usize"], 110 | result: "i64", 111 | }, 112 | webui_interface_get_string_at: { 113 | // const char* webui_interface_get_string_at(size_t window, size_t event_number, size_t index) 114 | parameters: ["usize", "usize", "usize"], 115 | result: "buffer", 116 | }, 117 | webui_interface_get_bool_at: { 118 | // bool webui_interface_get_bool_at(size_t window, size_t event_number, size_t index) 119 | parameters: ["usize", "usize", "usize"], 120 | result: "bool", 121 | }, 122 | webui_interface_get_size_at: { 123 | // size_t webui_interface_get_size_at(size_t window, size_t event_number, size_t index) 124 | parameters: ["usize", "usize", "usize"], 125 | result: "usize", 126 | }, 127 | webui_interface_get_float_at: { 128 | // double webui_interface_get_float_at(size_t window, size_t event_number, size_t index) 129 | parameters: ["usize", "usize", "usize"], 130 | result: "f64", 131 | }, 132 | webui_clean: { 133 | // void webui_clean() 134 | parameters: [], 135 | result: "void", 136 | }, 137 | webui_set_root_folder: { 138 | // bool webui_set_root_folder(size_t window, const char* path) 139 | parameters: ["usize", "buffer"], 140 | result: "bool", 141 | }, 142 | webui_set_browser_folder: { 143 | // void webui_set_browser_folder(const char* path) 144 | parameters: ["buffer"], 145 | result: "void", 146 | }, 147 | webui_set_tls_certificate: { 148 | // bool webui_set_tls_certificate(const char* certificate_pem, const char* private_key_pem) 149 | parameters: ["buffer", "buffer"], 150 | result: "bool", 151 | }, 152 | webui_set_kiosk: { 153 | // void webui_set_kiosk(size_t window, bool status) 154 | parameters: ["usize", "bool"], 155 | result: "void", 156 | }, 157 | webui_destroy: { 158 | // void webui_destroy(size_t window) 159 | parameters: ["usize"], 160 | result: "void", 161 | }, 162 | webui_set_timeout: { 163 | // void webui_set_timeout(size_t second) 164 | parameters: ["usize"], 165 | result: "void", 166 | }, 167 | webui_set_icon: { 168 | // void webui_set_icon(size_t window, const char* icon, const char* icon_type) 169 | parameters: ["usize", "buffer", "buffer"], 170 | result: "void", 171 | }, 172 | webui_encode: { 173 | // char* webui_encode(const char* str) 174 | parameters: ["buffer"], 175 | result: "buffer", 176 | }, 177 | webui_decode: { 178 | // char* webui_decode(const char* str) 179 | parameters: ["buffer"], 180 | result: "buffer", 181 | }, 182 | webui_free: { 183 | // void webui_free(void* ptr) 184 | parameters: ["pointer"], 185 | result: "void", 186 | }, 187 | webui_malloc: { 188 | // void* webui_malloc(size_t size) 189 | parameters: ["usize"], 190 | result: "pointer", 191 | }, 192 | webui_send_raw: { 193 | // void webui_send_raw(size_t window, const char* function, const void* raw, size_t size) 194 | parameters: ["usize", "buffer", "buffer", "usize"], 195 | result: "void", 196 | }, 197 | webui_set_hide: { 198 | // void webui_set_hide(size_t window, bool status) 199 | parameters: ["usize", "bool"], 200 | result: "void", 201 | }, 202 | webui_set_size: { 203 | // void webui_set_size(size_t window, unsigned int width, unsigned int height) 204 | parameters: ["usize", "u32", "u32"], 205 | result: "void", 206 | }, 207 | webui_set_position: { 208 | // void webui_set_position(size_t window, unsigned int x, unsigned int y) 209 | parameters: ["usize", "u32", "u32"], 210 | result: "void", 211 | }, 212 | webui_set_center: { 213 | // void webui_set_center(size_t window) 214 | parameters: ["usize"], 215 | result: "void", 216 | }, 217 | webui_get_url: { 218 | // const char* webui_get_url(size_t window) 219 | parameters: ["usize"], 220 | result: "buffer", 221 | }, 222 | webui_set_public: { 223 | // void webui_set_public(size_t window, bool status) 224 | parameters: ["usize", "bool"], 225 | result: "void", 226 | }, 227 | webui_navigate: { 228 | // void webui_navigate(size_t window, const char* url) 229 | parameters: ["usize", "buffer"], 230 | result: "void", 231 | }, 232 | webui_delete_all_profiles: { 233 | // void webui_delete_all_profiles(void) 234 | parameters: [], 235 | result: "void", 236 | }, 237 | webui_delete_profile: { 238 | // void webui_delete_profile(size_t window) 239 | parameters: ["usize"], 240 | result: "void", 241 | }, 242 | webui_get_parent_process_id: { 243 | // size_t webui_get_parent_process_id(size_t window) 244 | parameters: ["usize"], 245 | result: "usize", 246 | }, 247 | webui_get_child_process_id: { 248 | // size_t webui_get_child_process_id(size_t window) 249 | parameters: ["usize"], 250 | result: "usize", 251 | }, 252 | webui_win32_get_hwnd: { 253 | // void* webui_win32_get_hwnd(size_t window) 254 | parameters: ["usize"], 255 | result: "pointer", 256 | }, 257 | webui_get_hwnd: { 258 | // void* webui_get_hwnd(size_t window) 259 | parameters: ["usize"], 260 | result: "pointer", 261 | }, 262 | webui_get_port: { 263 | // size_t webui_get_port(size_t window) 264 | parameters: ["usize"], 265 | result: "usize", 266 | }, 267 | webui_set_port: { 268 | // bool webui_set_port(size_t window, size_t port) 269 | parameters: ["usize", "usize"], 270 | result: "bool", 271 | }, 272 | webui_set_runtime: { 273 | // void webui_set_runtime(size_t window, size_t runtime) 274 | parameters: ["usize", "usize"], 275 | result: "void", 276 | }, 277 | webui_set_config: { 278 | // void webui_set_config(webui_config option, bool status) 279 | // show_wait_connection: 0 280 | // ui_event_blocking: 1 281 | // folder_monitor: 2 282 | // multi_client: 3 283 | // use_cookies: 4 284 | // asynchronous_response: 5 285 | parameters: ["usize", "bool"], 286 | result: "void", 287 | }, 288 | webui_set_event_blocking: { 289 | // void webui_set_event_blocking(size_t window, bool status) 290 | parameters: ["usize", "bool"], 291 | result: "void", 292 | }, 293 | webui_set_frameless: { 294 | // void webui_set_frameless(size_t window, bool status) 295 | parameters: ["usize", "bool"], 296 | result: "void", 297 | }, 298 | webui_set_transparent: { 299 | // void webui_set_transparent(size_t window, bool status) 300 | parameters: ["usize", "bool"], 301 | result: "void", 302 | }, 303 | webui_interface_show_client: { 304 | // bool webui_interface_show_client(size_t window, size_t event_number, const char* content) 305 | parameters: ["usize", "usize", "buffer"], 306 | result: "bool", 307 | }, 308 | webui_interface_close_client: { 309 | // void webui_interface_close_client(size_t window, size_t event_number) 310 | parameters: ["usize", "usize"], 311 | result: "void", 312 | }, 313 | webui_interface_send_raw_client: { 314 | // void webui_interface_send_raw_client( 315 | // size_t window, size_t event_number, const char* function, const void* raw, size_t size) 316 | parameters: ["usize", "usize", "buffer", "buffer", "usize"], 317 | result: "void", 318 | }, 319 | webui_interface_navigate_client: { 320 | // void webui_interface_navigate_client(size_t window, size_t event_number, const char* url) 321 | parameters: ["usize", "usize", "buffer"], 322 | result: "void", 323 | }, 324 | webui_interface_run_client: { 325 | // void webui_interface_run_client(size_t window, size_t event_number, const char* script) 326 | parameters: ["usize", "usize", "buffer"], 327 | result: "void", 328 | }, 329 | webui_interface_script_client: { 330 | // bool webui_interface_script_client( 331 | // size_t window, size_t event_number, const char* script, size_t timeout, char* buffer, size_t buffer_length) 332 | parameters: ["usize", "usize", "buffer", "usize", "buffer", "usize"], 333 | result: "bool", 334 | }, 335 | webui_send_raw_client: { 336 | // void webui_send_raw_client(webui_event_t* e, const char* function, const void* raw, size_t size) 337 | parameters: ["pointer", "buffer", "buffer", "usize"], 338 | result: "void", 339 | }, 340 | webui_interface_set_response_file_handler: { 341 | // void webui_interface_set_response_file_handler(size_t window, const void* response, int length) 342 | parameters: ["usize", "pointer", "usize"], 343 | result: "void", 344 | }, 345 | webui_get_best_browser: { 346 | // size_t webui_get_best_browser(size_t window) 347 | parameters: ["usize"], 348 | result: "usize", 349 | }, 350 | webui_start_server: { 351 | // const char* webui_start_server(size_t window, const char* content) 352 | parameters: ["usize", "buffer"], 353 | result: "buffer", 354 | }, 355 | webui_show_wv: { 356 | // bool webui_show_wv(size_t window, const char* content) 357 | parameters: ["usize", "buffer"], 358 | result: "bool", 359 | }, 360 | webui_set_custom_parameters: { 361 | // void webui_set_custom_parameters(size_t window, char *params) 362 | parameters: ["usize", "buffer"], 363 | result: "void", 364 | }, 365 | webui_set_high_contrast: { 366 | // void webui_set_high_contrast(size_t window, bool status) 367 | parameters: ["usize", "bool"], 368 | result: "void", 369 | }, 370 | webui_set_resizable: { 371 | // void webui_set_resizable(size_t window, bool status) 372 | parameters: ["usize", "bool"], 373 | result: "void", 374 | }, 375 | webui_is_high_contrast: { 376 | // bool webui_is_high_contrast(void) 377 | parameters: [], 378 | result: "bool", 379 | }, 380 | webui_browser_exist: { 381 | // bool webui_browser_exist(size_t browser) 382 | parameters: ["usize"], 383 | result: "bool", 384 | }, 385 | webui_set_default_root_folder: { 386 | // bool webui_set_default_root_folder(const char* path) 387 | parameters: ["buffer"], 388 | result: "bool", 389 | }, 390 | webui_set_minimum_size: { 391 | // void webui_set_minimum_size(size_t window, unsigned int width, unsigned int height) 392 | parameters: ["usize", "u32", "u32"], 393 | result: "void", 394 | }, 395 | webui_set_proxy: { 396 | // void webui_set_proxy(size_t window, const char* proxy_server) 397 | parameters: ["usize", "buffer"], 398 | result: "void", 399 | }, 400 | webui_open_url: { 401 | // void webui_open_url(const char* url) 402 | parameters: ["buffer"], 403 | result: "void", 404 | }, 405 | webui_get_free_port: { 406 | // size_t webui_get_free_port(void) 407 | parameters: [], 408 | result: "usize", 409 | }, 410 | webui_get_mime_type: { 411 | // const char* webui_get_mime_type(const char* file) 412 | parameters: ["buffer"], 413 | result: "buffer", 414 | }, 415 | webui_memcpy: { 416 | // void webui_memcpy(void* dest, void* src, size_t count) 417 | parameters: ["pointer", "pointer", "usize"], 418 | result: "void", 419 | }, 420 | webui_set_logger: { 421 | // void webui_set_logger(void (*func)(size_t level, const char* log, void* user_data), void *user_data) 422 | parameters: ["function", "pointer"], 423 | result: "void", 424 | }, 425 | webui_set_close_handler_wv: { 426 | // void webui_set_close_handler_wv(size_t window, bool (*close_handler)(size_t window)) 427 | parameters: ["usize", "function"], 428 | result: "void", 429 | }, 430 | webui_get_last_error_number: { 431 | // size_t webui_get_last_error_number() 432 | parameters: [], 433 | result: "usize", 434 | }, 435 | webui_get_last_error_message: { 436 | // const char* webui_get_last_error_message() 437 | parameters: [], 438 | result: "buffer", 439 | }, 440 | webui_interface_get_window_id: { 441 | // size_t webui_interface_get_window_id(size_t window) 442 | parameters: ["usize"], 443 | result: "usize", 444 | }, 445 | } as const; 446 | 447 | export function loadLib(): Deno.DynamicLibrary { 448 | return Deno.dlopen( 449 | libPath, 450 | symbols, 451 | ); 452 | } 453 | -------------------------------------------------------------------------------- /src/webui.ts: -------------------------------------------------------------------------------- 1 | /* 2 | WebUI Deno 2.5.13 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 | import metadata from "../deno.json" with { type: "json" }; 21 | 22 | // Register windows to bind instance to WebUI.Event 23 | const windows: Map = new Map(); 24 | 25 | // Global lib entry 26 | let _lib: WebUILib; 27 | 28 | /** 29 | * Represents a WebUI window instance. Allows interaction with a web browser 30 | * window, including displaying HTML content, executing JavaScript, and binding 31 | * backend functions to UI elements. 32 | */ 33 | export class WebUI { 34 | #window: Usize = 0; 35 | #lib: WebUILib; 36 | #isFileHandler: boolean = false; 37 | 38 | /** 39 | * Instanciate a new WebUI window. 40 | * @returns Window id. 41 | * @throws {WebUIError} - If optional local lib not found. 42 | * @example 43 | * ```ts 44 | * const myWindow1 = new WebUI() 45 | * ``` 46 | */ 47 | constructor() { 48 | WebUI.init(); // Init lib if not already initialized 49 | this.#lib = _lib; 50 | this.#window = _lib.symbols.webui_new_window(); 51 | windows.set(BigInt(this.#window), this); 52 | } 53 | 54 | /** 55 | * Set root folder for proper loading resources 56 | * @param rootFolder Root folder to set 57 | * @throws {WebUIError} - If lib return false status. 58 | * @example 59 | * ```ts 60 | * const myWindow = new WebUI() 61 | * 62 | * // Show the current time 63 | * myWindow.setRootFolder('some/root/folder') 64 | * 65 | * // Show a local file 66 | * await myWindow.show('some/root/folder/index.html') 67 | * 68 | * // Await to ensure WebUI.script and WebUI.run can send datas to the client 69 | * console.assert(myWindow.isShown, true) 70 | * ``` 71 | */ 72 | setRootFolder(rootFolder: string) { 73 | const status = this.#lib.symbols.webui_set_root_folder( 74 | BigInt(this.#window), 75 | toCString(rootFolder), 76 | ); 77 | if (!status) { 78 | throw new WebUIError(`unable to set root folder`); 79 | } 80 | } 81 | 82 | /** 83 | * Show the window or update the UI with the new content. 84 | * @returns Promise that resolves when the client bridge is linked. 85 | * @param {string} content - Valid html content or same root file path. 86 | * @throws {WebUIError} - If lib return false status. 87 | * @example 88 | * ```ts 89 | * const myWindow = new WebUI() 90 | * 91 | * // Show the current time 92 | * await myWindow.show(`

View 2

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

View 2

`) 1077 | * 1078 | * WebUI.exit() 1079 | * 1080 | * myWindow1.isShown // false 1081 | * myWindow2.isShown // false 1082 | * ``` 1083 | */ 1084 | static exit() { 1085 | WebUI.init(); 1086 | _lib.symbols.webui_exit(); 1087 | } 1088 | 1089 | /** 1090 | * Set certificate 1091 | * @param certificatePem Set certificate 1092 | * @param privateKeyPem Set private key 1093 | * @throws {WebUIError} - If lib return false status. 1094 | * @example 1095 | * ```ts 1096 | * const myWindow = new WebUI() 1097 | * 1098 | * // Show the current time 1099 | * myWindow.setRootFolder('some/root/folder') 1100 | * 1101 | * const certificatePem = await Deno.readTextFile("some/root/certificate.pem"); 1102 | * const privateKeyPem = await Deno.readTextFile("some/root/private_key.pem"); 1103 | * WebUI.setTLSCertificate(certificatePem, privateKeyPem); 1104 | * 1105 | * // Show a local file 1106 | * await myWindow.show('some/root/folder/index.html') 1107 | * 1108 | * // Await to ensure WebUI.script and WebUI.run can send datas to the client 1109 | * console.assert(myWindow.isShown, true) 1110 | * ``` 1111 | */ 1112 | static setTLSCertificate(certificatePem: string, privateKeyPem: string) { 1113 | WebUI.init(); 1114 | const status = _lib.symbols.webui_set_tls_certificate( 1115 | toCString(certificatePem), 1116 | toCString(privateKeyPem), 1117 | ); 1118 | if (!status) { 1119 | throw new WebUIError(`unable to set certificate`); 1120 | } 1121 | } 1122 | 1123 | /** 1124 | * Waits until all opened windows are closed for preventing exiting the main thread. 1125 | * 1126 | * @example 1127 | * ```ts 1128 | * const myWindow = new WebUI() 1129 | * await myWindow.show(`