@corelauncher/rod 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +2 -0
- package/package.json +43 -0
- package/src-ts/classes/eventloop.ts +55 -0
- package/src-ts/classes/rod.ts +57 -0
- package/src-ts/classes/webview.ts +83 -0
- package/src-ts/classes/window.ts +239 -0
- package/src-ts/ffi.ts +365 -0
- package/src-ts/index.ts +2 -0
- package/src-ts/types.ts +51 -0
- package/src-ts/utilities/options.ts +39 -0
- package/src-ts/utilities/strings.ts +11 -0
- package/target/release/rod.dll +0 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 CoreLauncher
|
|
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.
|
package/README.md
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@corelauncher/rod",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Webview for bun based on tauri's tao and wry.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"corelauncher",
|
|
7
|
+
"webview",
|
|
8
|
+
"bun",
|
|
9
|
+
"tauri",
|
|
10
|
+
"tao",
|
|
11
|
+
"wry"
|
|
12
|
+
],
|
|
13
|
+
"homepage": "https://github.com/CoreLauncher/rod#readme",
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/CoreLauncher/rod/issues"
|
|
16
|
+
},
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"author": "CoreByte",
|
|
19
|
+
"files": [
|
|
20
|
+
"/src-ts/*",
|
|
21
|
+
"/target/release/rod.dll"
|
|
22
|
+
],
|
|
23
|
+
"type": "module",
|
|
24
|
+
"main": "/src-ts/index.ts",
|
|
25
|
+
"module": "/src-ts/index.ts",
|
|
26
|
+
"scripts": {
|
|
27
|
+
"postinstall": "husky",
|
|
28
|
+
"prepack": "cargo build --release",
|
|
29
|
+
"dev": "bun run --watch ./src-playground/index.ts",
|
|
30
|
+
"lint": "bun run biome check --fix",
|
|
31
|
+
"knip": "bun run knip-bun"
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"tiny-typed-emitter": "^2.1.0"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@biomejs/biome": "2.3.10",
|
|
38
|
+
"@types/bun": "latest",
|
|
39
|
+
"husky": "^9.1.7",
|
|
40
|
+
"typescript": "^5",
|
|
41
|
+
"knip": "^5.76.3"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { CString, FFIType, JSCallback, type Pointer } from "bun:ffi";
|
|
2
|
+
import { TypedEmitter } from "tiny-typed-emitter";
|
|
3
|
+
import {
|
|
4
|
+
rod_event_loop_create,
|
|
5
|
+
rod_event_loop_destroy,
|
|
6
|
+
rod_event_loop_poll,
|
|
7
|
+
} from "../ffi";
|
|
8
|
+
import type { Position, Size } from "../types";
|
|
9
|
+
|
|
10
|
+
interface EventLoopEvents {
|
|
11
|
+
window_close_requested: (id: number) => void;
|
|
12
|
+
window_focused: (id: number, focused: boolean) => void;
|
|
13
|
+
window_moved: (id: number, position: Position) => void;
|
|
14
|
+
window_resized: (id: number, size: Size) => void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export default class EventLoop extends TypedEmitter<EventLoopEvents> {
|
|
18
|
+
eventloopPtr: Pointer;
|
|
19
|
+
|
|
20
|
+
private callback: JSCallback;
|
|
21
|
+
private interval: NodeJS.Timeout;
|
|
22
|
+
constructor() {
|
|
23
|
+
super();
|
|
24
|
+
|
|
25
|
+
const eventloopPtr = rod_event_loop_create();
|
|
26
|
+
if (!eventloopPtr) throw new Error("Failed to create EventLoop");
|
|
27
|
+
this.eventloopPtr = eventloopPtr;
|
|
28
|
+
|
|
29
|
+
this.callback = new JSCallback(
|
|
30
|
+
(eventPtr: Pointer, rawDataPtr: Pointer) => {
|
|
31
|
+
const event = new CString(eventPtr).toString();
|
|
32
|
+
const rawData = new CString(rawDataPtr).toString();
|
|
33
|
+
console.log(event, rawData);
|
|
34
|
+
const data = JSON.parse(rawData);
|
|
35
|
+
console.log("EventLoop Event:", event, data);
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
args: [FFIType.cstring, FFIType.cstring],
|
|
39
|
+
returns: FFIType.void,
|
|
40
|
+
},
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
this.interval = setInterval(() => {
|
|
44
|
+
rod_event_loop_poll(this.eventloopPtr, this.callback);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
destroy() {
|
|
49
|
+
clearInterval(this.interval);
|
|
50
|
+
if (this.eventloopPtr) {
|
|
51
|
+
rod_event_loop_destroy(this.eventloopPtr);
|
|
52
|
+
this.eventloopPtr = null as unknown as Pointer;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { WebViewOptions, WindowOptions } from "../types";
|
|
2
|
+
import EventLoop from "./eventloop";
|
|
3
|
+
import WebView from "./webview";
|
|
4
|
+
|
|
5
|
+
let idIndex = 1;
|
|
6
|
+
|
|
7
|
+
export default class Rod {
|
|
8
|
+
eventLoop: EventLoop;
|
|
9
|
+
private webviews: WebView[];
|
|
10
|
+
constructor() {
|
|
11
|
+
this.eventLoop = new EventLoop();
|
|
12
|
+
this.webviews = [];
|
|
13
|
+
|
|
14
|
+
this.eventLoop.on("window_close_requested", (id) => {
|
|
15
|
+
const webview = this.retrieveWebViewById(id);
|
|
16
|
+
if (!webview) return;
|
|
17
|
+
webview.emit("close_requested");
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
this.eventLoop.on("window_focused", (id, focused) => {
|
|
21
|
+
const webview = this.retrieveWebViewById(id);
|
|
22
|
+
if (!webview) return;
|
|
23
|
+
webview.emit("focused", focused);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
this.eventLoop.on("window_moved", (id, position) => {
|
|
27
|
+
const webview = this.retrieveWebViewById(id);
|
|
28
|
+
if (!webview) return;
|
|
29
|
+
webview.emit("moved", position);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
this.eventLoop.on("window_resized", (id, size) => {
|
|
33
|
+
const webview = this.retrieveWebViewById(id);
|
|
34
|
+
if (!webview) return;
|
|
35
|
+
webview.emit("resized", size);
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
private generateId() {
|
|
40
|
+
return idIndex++;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
private retrieveWebViewById(id: number) {
|
|
44
|
+
return this.webviews.find((webview) => webview.id === id);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
createWebView(options: WebViewOptions & WindowOptions = {}) {
|
|
48
|
+
const id = this.generateId();
|
|
49
|
+
const webview = new WebView(this.eventLoop, id, options);
|
|
50
|
+
|
|
51
|
+
webview.on("destroyed", () => {
|
|
52
|
+
this.webviews = this.webviews.filter((wv) => wv.id !== webview.id);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
return webview;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { Pointer } from "bun:ffi";
|
|
2
|
+
import {
|
|
3
|
+
rod_webview_clear_all_browsing_data,
|
|
4
|
+
rod_webview_close_devtools,
|
|
5
|
+
rod_webview_create,
|
|
6
|
+
rod_webview_destroy,
|
|
7
|
+
rod_webview_get_url,
|
|
8
|
+
rod_webview_is_devtools_open,
|
|
9
|
+
rod_webview_open_devtools,
|
|
10
|
+
rod_webview_reload,
|
|
11
|
+
rod_webview_set_html,
|
|
12
|
+
rod_webview_set_url,
|
|
13
|
+
rod_webview_zoom,
|
|
14
|
+
} from "../ffi";
|
|
15
|
+
import type { WebViewOptions, WindowOptions } from "../types";
|
|
16
|
+
import { transformWebViewOptions } from "../utilities/options";
|
|
17
|
+
import { encodeString } from "../utilities/strings";
|
|
18
|
+
import type EventLoop from "./eventloop";
|
|
19
|
+
import Window from "./window";
|
|
20
|
+
|
|
21
|
+
export default class WebView extends Window {
|
|
22
|
+
protected webviewPtr: Pointer;
|
|
23
|
+
constructor(
|
|
24
|
+
eventLoop: EventLoop,
|
|
25
|
+
id: number,
|
|
26
|
+
options: WebViewOptions & WindowOptions,
|
|
27
|
+
) {
|
|
28
|
+
super(eventLoop.eventloopPtr, id, options);
|
|
29
|
+
|
|
30
|
+
const webviewPtr = rod_webview_create(
|
|
31
|
+
this.windowPtr,
|
|
32
|
+
encodeString(JSON.stringify(transformWebViewOptions(options))),
|
|
33
|
+
);
|
|
34
|
+
if (!webviewPtr) throw new Error("Failed to create WebView");
|
|
35
|
+
this.webviewPtr = webviewPtr;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
get url() {
|
|
39
|
+
const raw = rod_webview_get_url(this.webviewPtr);
|
|
40
|
+
return raw.toString();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
get isDevtoolsOpen() {
|
|
44
|
+
return rod_webview_is_devtools_open(this.webviewPtr);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
setUrl(url: string) {
|
|
48
|
+
rod_webview_set_url(this.webviewPtr, encodeString(url));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
setHtml(html: string) {
|
|
52
|
+
rod_webview_set_html(this.webviewPtr, encodeString(html));
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
reload() {
|
|
56
|
+
rod_webview_reload(this.webviewPtr);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
clearAllBrowsingData() {
|
|
60
|
+
rod_webview_clear_all_browsing_data(this.webviewPtr);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
openDevtools() {
|
|
64
|
+
rod_webview_open_devtools(this.webviewPtr);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
closeDevtools() {
|
|
68
|
+
rod_webview_close_devtools(this.webviewPtr);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
zoom(scaleFactor: number) {
|
|
72
|
+
rod_webview_zoom(this.webviewPtr, scaleFactor);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
override destroy() {
|
|
76
|
+
if (!this.webviewPtr) return;
|
|
77
|
+
|
|
78
|
+
rod_webview_destroy(this.webviewPtr);
|
|
79
|
+
this.webviewPtr = null as unknown as Pointer;
|
|
80
|
+
|
|
81
|
+
super.destroy();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import type { Pointer } from "bun:ffi";
|
|
2
|
+
import { TypedEmitter } from "tiny-typed-emitter";
|
|
3
|
+
import {
|
|
4
|
+
rod_window_create,
|
|
5
|
+
rod_window_destroy,
|
|
6
|
+
rod_window_get_always_on_top,
|
|
7
|
+
rod_window_get_closable,
|
|
8
|
+
rod_window_get_decorated,
|
|
9
|
+
rod_window_get_focused,
|
|
10
|
+
rod_window_get_maximizable,
|
|
11
|
+
rod_window_get_maximized,
|
|
12
|
+
rod_window_get_minimizable,
|
|
13
|
+
rod_window_get_minimized,
|
|
14
|
+
rod_window_get_position,
|
|
15
|
+
rod_window_get_resizable,
|
|
16
|
+
rod_window_get_size,
|
|
17
|
+
rod_window_get_title,
|
|
18
|
+
rod_window_get_visible,
|
|
19
|
+
rod_window_set_always_on_bottom,
|
|
20
|
+
rod_window_set_always_on_top,
|
|
21
|
+
rod_window_set_closable,
|
|
22
|
+
rod_window_set_content_protection,
|
|
23
|
+
rod_window_set_decorations,
|
|
24
|
+
rod_window_set_focus,
|
|
25
|
+
rod_window_set_focusable,
|
|
26
|
+
rod_window_set_fullscreen,
|
|
27
|
+
rod_window_set_ignore_cursor_events,
|
|
28
|
+
rod_window_set_maximizable,
|
|
29
|
+
rod_window_set_maximized,
|
|
30
|
+
rod_window_set_maximum_size,
|
|
31
|
+
rod_window_set_minimizable,
|
|
32
|
+
rod_window_set_minimized,
|
|
33
|
+
rod_window_set_minimum_size,
|
|
34
|
+
rod_window_set_position,
|
|
35
|
+
rod_window_set_progress_bar,
|
|
36
|
+
rod_window_set_resizable,
|
|
37
|
+
rod_window_set_size,
|
|
38
|
+
rod_window_set_title,
|
|
39
|
+
rod_window_set_visible,
|
|
40
|
+
rod_window_set_visible_on_all_workspaces,
|
|
41
|
+
rod_window_start_drag,
|
|
42
|
+
} from "../ffi";
|
|
43
|
+
import type { Position, ProgressState, Size, WindowOptions } from "../types";
|
|
44
|
+
import { transformWindowOptions } from "../utilities/options";
|
|
45
|
+
import { encodeString } from "../utilities/strings";
|
|
46
|
+
|
|
47
|
+
interface WindowEvents {
|
|
48
|
+
close_requested: () => void;
|
|
49
|
+
focused: (focused: boolean) => void;
|
|
50
|
+
moved: (position: Position) => void;
|
|
51
|
+
resized: (size: Size) => void;
|
|
52
|
+
destroyed: () => void;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export default class Window extends TypedEmitter<WindowEvents> {
|
|
56
|
+
id: number;
|
|
57
|
+
protected windowPtr: Pointer;
|
|
58
|
+
constructor(eventLoop: Pointer, id: number, options: WindowOptions) {
|
|
59
|
+
super();
|
|
60
|
+
|
|
61
|
+
this.id = id;
|
|
62
|
+
|
|
63
|
+
const windowPtr = rod_window_create(
|
|
64
|
+
eventLoop,
|
|
65
|
+
id,
|
|
66
|
+
encodeString(JSON.stringify(transformWindowOptions(options))),
|
|
67
|
+
);
|
|
68
|
+
if (!windowPtr) throw new Error("Failed to create Window");
|
|
69
|
+
this.windowPtr = windowPtr;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
get title() {
|
|
73
|
+
const title = rod_window_get_title(this.windowPtr);
|
|
74
|
+
return title.toString();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
get size() {
|
|
78
|
+
const rawSize = rod_window_get_size(this.windowPtr);
|
|
79
|
+
return JSON.parse(rawSize.toString()) as Size;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
get position() {
|
|
83
|
+
const rawPosition = rod_window_get_position(this.windowPtr);
|
|
84
|
+
return JSON.parse(rawPosition.toString()) as Position;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
get isAlwaysOnTop() {
|
|
88
|
+
return rod_window_get_always_on_top(this.windowPtr);
|
|
89
|
+
}
|
|
90
|
+
get isClosable() {
|
|
91
|
+
return rod_window_get_closable(this.windowPtr);
|
|
92
|
+
}
|
|
93
|
+
get isDecorated() {
|
|
94
|
+
return rod_window_get_decorated(this.windowPtr);
|
|
95
|
+
}
|
|
96
|
+
get isFocused() {
|
|
97
|
+
return rod_window_get_focused(this.windowPtr);
|
|
98
|
+
}
|
|
99
|
+
get isMaximizable() {
|
|
100
|
+
return rod_window_get_maximizable(this.windowPtr);
|
|
101
|
+
}
|
|
102
|
+
get isMaximized() {
|
|
103
|
+
return rod_window_get_maximized(this.windowPtr);
|
|
104
|
+
}
|
|
105
|
+
get isMinimizable() {
|
|
106
|
+
return rod_window_get_minimizable(this.windowPtr);
|
|
107
|
+
}
|
|
108
|
+
get isMinimized() {
|
|
109
|
+
return rod_window_get_minimized(this.windowPtr);
|
|
110
|
+
}
|
|
111
|
+
get isResizable() {
|
|
112
|
+
return rod_window_get_resizable(this.windowPtr);
|
|
113
|
+
}
|
|
114
|
+
get isVisible() {
|
|
115
|
+
return rod_window_get_visible(this.windowPtr);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
setAlwaysOnBottom(alwaysOnBottom: boolean) {
|
|
119
|
+
rod_window_set_always_on_bottom(this.windowPtr, alwaysOnBottom);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
setAlwaysOnTop(alwaysOnTop: boolean) {
|
|
123
|
+
rod_window_set_always_on_top(this.windowPtr, alwaysOnTop);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
setClosable(closable: boolean) {
|
|
127
|
+
rod_window_set_closable(this.windowPtr, closable);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
setContentProtection(contentProtection: boolean) {
|
|
131
|
+
rod_window_set_content_protection(this.windowPtr, contentProtection);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
setDecorations(decorations: boolean) {
|
|
135
|
+
rod_window_set_decorations(this.windowPtr, decorations);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
setFocus() {
|
|
139
|
+
rod_window_set_focus(this.windowPtr);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
setFocusable(focusable: boolean) {
|
|
143
|
+
rod_window_set_focusable(this.windowPtr, focusable);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
setFullscreen(fullscreen: boolean) {
|
|
147
|
+
rod_window_set_fullscreen(this.windowPtr, fullscreen);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
setIgnoreCursorEvents(ignore: boolean) {
|
|
151
|
+
rod_window_set_ignore_cursor_events(this.windowPtr, ignore);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
setSize(size: Size) {
|
|
155
|
+
rod_window_set_size(this.windowPtr, encodeString(JSON.stringify(size)));
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
setMaximumSize(size: Size | null) {
|
|
159
|
+
if (size === null) {
|
|
160
|
+
rod_window_set_maximum_size(this.windowPtr, encodeString(""));
|
|
161
|
+
} else {
|
|
162
|
+
rod_window_set_maximum_size(
|
|
163
|
+
this.windowPtr,
|
|
164
|
+
encodeString(JSON.stringify(size)),
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
setMaximizable(maximizable: boolean) {
|
|
170
|
+
rod_window_set_maximizable(this.windowPtr, maximizable);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
setMaximized(maximized: boolean) {
|
|
174
|
+
rod_window_set_maximized(this.windowPtr, maximized);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
setMinimumSize(size: Size | null) {
|
|
178
|
+
if (size === null) {
|
|
179
|
+
rod_window_set_minimum_size(this.windowPtr, encodeString(""));
|
|
180
|
+
} else {
|
|
181
|
+
rod_window_set_minimum_size(
|
|
182
|
+
this.windowPtr,
|
|
183
|
+
encodeString(JSON.stringify(size)),
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
setMinimizable(minimizable: boolean) {
|
|
189
|
+
rod_window_set_minimizable(this.windowPtr, minimizable);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
setMinimized(minimized: boolean) {
|
|
193
|
+
rod_window_set_minimized(this.windowPtr, minimized);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
setPosition(position: Position) {
|
|
197
|
+
rod_window_set_position(
|
|
198
|
+
this.windowPtr,
|
|
199
|
+
encodeString(JSON.stringify(position)),
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
setProgressBar(state: ProgressState, progress: number) {
|
|
204
|
+
const payload = { state: state as number, progress };
|
|
205
|
+
rod_window_set_progress_bar(
|
|
206
|
+
this.windowPtr,
|
|
207
|
+
encodeString(JSON.stringify(payload)),
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
setResizable(resizable: boolean) {
|
|
212
|
+
rod_window_set_resizable(this.windowPtr, resizable);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
setTitle(title: string) {
|
|
216
|
+
rod_window_set_title(this.windowPtr, encodeString(title));
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
setVisible(visible: boolean) {
|
|
220
|
+
rod_window_set_visible(this.windowPtr, visible);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
setVisibleOnAllWorkspaces(visible: boolean) {
|
|
224
|
+
rod_window_set_visible_on_all_workspaces(this.windowPtr, visible);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
startDrag() {
|
|
228
|
+
rod_window_start_drag(this.windowPtr);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
destroy() {
|
|
232
|
+
if (!this.windowPtr) return;
|
|
233
|
+
|
|
234
|
+
this.emit("destroyed");
|
|
235
|
+
|
|
236
|
+
rod_window_destroy(this.windowPtr);
|
|
237
|
+
this.windowPtr = null as unknown as Pointer;
|
|
238
|
+
}
|
|
239
|
+
}
|
package/src-ts/ffi.ts
ADDED
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
import { dlopen, FFIType, suffix } from "bun:ffi";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
|
|
4
|
+
const path = join(import.meta.dir, "..", "target", "debug", `rod.${suffix}`);
|
|
5
|
+
|
|
6
|
+
const {
|
|
7
|
+
symbols: {
|
|
8
|
+
// event loop
|
|
9
|
+
rod_event_loop_create,
|
|
10
|
+
rod_event_loop_destroy,
|
|
11
|
+
|
|
12
|
+
// event loop actions
|
|
13
|
+
rod_event_loop_poll,
|
|
14
|
+
|
|
15
|
+
// window
|
|
16
|
+
rod_window_create,
|
|
17
|
+
rod_window_destroy,
|
|
18
|
+
|
|
19
|
+
// window getters
|
|
20
|
+
rod_window_get_title,
|
|
21
|
+
rod_window_get_size,
|
|
22
|
+
rod_window_get_position,
|
|
23
|
+
rod_window_get_always_on_top,
|
|
24
|
+
rod_window_get_closable,
|
|
25
|
+
rod_window_get_decorated,
|
|
26
|
+
rod_window_get_focused,
|
|
27
|
+
rod_window_get_maximizable,
|
|
28
|
+
rod_window_get_maximized,
|
|
29
|
+
rod_window_get_minimizable,
|
|
30
|
+
rod_window_get_minimized,
|
|
31
|
+
rod_window_get_resizable,
|
|
32
|
+
rod_window_get_visible,
|
|
33
|
+
|
|
34
|
+
// window setters
|
|
35
|
+
rod_window_set_always_on_bottom,
|
|
36
|
+
rod_window_set_always_on_top,
|
|
37
|
+
rod_window_set_closable,
|
|
38
|
+
rod_window_set_content_protection,
|
|
39
|
+
rod_window_set_decorations,
|
|
40
|
+
rod_window_set_focus,
|
|
41
|
+
rod_window_set_focusable,
|
|
42
|
+
rod_window_set_fullscreen,
|
|
43
|
+
rod_window_set_ignore_cursor_events,
|
|
44
|
+
rod_window_set_size,
|
|
45
|
+
rod_window_set_maximum_size,
|
|
46
|
+
rod_window_set_maximizable,
|
|
47
|
+
rod_window_set_maximized,
|
|
48
|
+
rod_window_set_minimum_size,
|
|
49
|
+
rod_window_set_minimizable,
|
|
50
|
+
rod_window_set_minimized,
|
|
51
|
+
rod_window_set_position,
|
|
52
|
+
rod_window_set_progress_bar,
|
|
53
|
+
rod_window_set_resizable,
|
|
54
|
+
rod_window_set_title,
|
|
55
|
+
rod_window_set_visible,
|
|
56
|
+
rod_window_set_visible_on_all_workspaces,
|
|
57
|
+
|
|
58
|
+
// window actions
|
|
59
|
+
rod_window_start_drag,
|
|
60
|
+
|
|
61
|
+
// webview
|
|
62
|
+
rod_webview_create,
|
|
63
|
+
rod_webview_destroy,
|
|
64
|
+
|
|
65
|
+
// webview getters
|
|
66
|
+
rod_webview_get_url,
|
|
67
|
+
rod_webview_is_devtools_open,
|
|
68
|
+
|
|
69
|
+
// webview setters
|
|
70
|
+
rod_webview_set_url,
|
|
71
|
+
rod_webview_set_html,
|
|
72
|
+
rod_webview_zoom,
|
|
73
|
+
|
|
74
|
+
// webview actions
|
|
75
|
+
rod_webview_open_devtools,
|
|
76
|
+
rod_webview_close_devtools,
|
|
77
|
+
rod_webview_reload,
|
|
78
|
+
rod_webview_clear_all_browsing_data,
|
|
79
|
+
},
|
|
80
|
+
} = dlopen(path, {
|
|
81
|
+
// event loop
|
|
82
|
+
rod_event_loop_create: {
|
|
83
|
+
args: [],
|
|
84
|
+
returns: FFIType.ptr,
|
|
85
|
+
},
|
|
86
|
+
rod_event_loop_destroy: {
|
|
87
|
+
args: [FFIType.ptr],
|
|
88
|
+
returns: FFIType.void,
|
|
89
|
+
},
|
|
90
|
+
rod_event_loop_poll: {
|
|
91
|
+
args: [FFIType.ptr, FFIType.function],
|
|
92
|
+
returns: FFIType.void,
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
// window
|
|
96
|
+
rod_window_create: {
|
|
97
|
+
args: [FFIType.ptr, FFIType.u16, FFIType.cstring],
|
|
98
|
+
returns: FFIType.ptr,
|
|
99
|
+
},
|
|
100
|
+
rod_window_destroy: {
|
|
101
|
+
args: [FFIType.ptr],
|
|
102
|
+
returns: FFIType.void,
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
// window getters
|
|
106
|
+
rod_window_get_title: {
|
|
107
|
+
args: [FFIType.ptr],
|
|
108
|
+
returns: FFIType.cstring,
|
|
109
|
+
},
|
|
110
|
+
rod_window_get_size: {
|
|
111
|
+
args: [FFIType.ptr],
|
|
112
|
+
returns: FFIType.cstring,
|
|
113
|
+
},
|
|
114
|
+
rod_window_get_position: {
|
|
115
|
+
args: [FFIType.ptr],
|
|
116
|
+
returns: FFIType.cstring,
|
|
117
|
+
},
|
|
118
|
+
rod_window_get_always_on_top: {
|
|
119
|
+
args: [FFIType.ptr],
|
|
120
|
+
returns: FFIType.bool,
|
|
121
|
+
},
|
|
122
|
+
rod_window_get_closable: {
|
|
123
|
+
args: [FFIType.ptr],
|
|
124
|
+
returns: FFIType.bool,
|
|
125
|
+
},
|
|
126
|
+
rod_window_get_decorated: {
|
|
127
|
+
args: [FFIType.ptr],
|
|
128
|
+
returns: FFIType.bool,
|
|
129
|
+
},
|
|
130
|
+
rod_window_get_focused: {
|
|
131
|
+
args: [FFIType.ptr],
|
|
132
|
+
returns: FFIType.bool,
|
|
133
|
+
},
|
|
134
|
+
rod_window_get_maximizable: {
|
|
135
|
+
args: [FFIType.ptr],
|
|
136
|
+
returns: FFIType.bool,
|
|
137
|
+
},
|
|
138
|
+
rod_window_get_maximized: {
|
|
139
|
+
args: [FFIType.ptr],
|
|
140
|
+
returns: FFIType.bool,
|
|
141
|
+
},
|
|
142
|
+
rod_window_get_minimizable: {
|
|
143
|
+
args: [FFIType.ptr],
|
|
144
|
+
returns: FFIType.bool,
|
|
145
|
+
},
|
|
146
|
+
rod_window_get_minimized: {
|
|
147
|
+
args: [FFIType.ptr],
|
|
148
|
+
returns: FFIType.bool,
|
|
149
|
+
},
|
|
150
|
+
rod_window_get_resizable: {
|
|
151
|
+
args: [FFIType.ptr],
|
|
152
|
+
returns: FFIType.bool,
|
|
153
|
+
},
|
|
154
|
+
rod_window_get_visible: {
|
|
155
|
+
args: [FFIType.ptr],
|
|
156
|
+
returns: FFIType.bool,
|
|
157
|
+
},
|
|
158
|
+
|
|
159
|
+
// window setters
|
|
160
|
+
rod_window_set_always_on_bottom: {
|
|
161
|
+
args: [FFIType.ptr, FFIType.bool],
|
|
162
|
+
returns: FFIType.void,
|
|
163
|
+
},
|
|
164
|
+
rod_window_set_always_on_top: {
|
|
165
|
+
args: [FFIType.ptr, FFIType.bool],
|
|
166
|
+
returns: FFIType.void,
|
|
167
|
+
},
|
|
168
|
+
rod_window_set_closable: {
|
|
169
|
+
args: [FFIType.ptr, FFIType.bool],
|
|
170
|
+
returns: FFIType.void,
|
|
171
|
+
},
|
|
172
|
+
rod_window_set_content_protection: {
|
|
173
|
+
args: [FFIType.ptr, FFIType.bool],
|
|
174
|
+
returns: FFIType.void,
|
|
175
|
+
},
|
|
176
|
+
rod_window_set_decorations: {
|
|
177
|
+
args: [FFIType.ptr, FFIType.bool],
|
|
178
|
+
returns: FFIType.void,
|
|
179
|
+
},
|
|
180
|
+
rod_window_set_focus: {
|
|
181
|
+
args: [FFIType.ptr],
|
|
182
|
+
returns: FFIType.void,
|
|
183
|
+
},
|
|
184
|
+
rod_window_set_focusable: {
|
|
185
|
+
args: [FFIType.ptr, FFIType.bool],
|
|
186
|
+
returns: FFIType.void,
|
|
187
|
+
},
|
|
188
|
+
rod_window_set_fullscreen: {
|
|
189
|
+
args: [FFIType.ptr, FFIType.bool],
|
|
190
|
+
returns: FFIType.void,
|
|
191
|
+
},
|
|
192
|
+
rod_window_set_ignore_cursor_events: {
|
|
193
|
+
args: [FFIType.ptr, FFIType.bool],
|
|
194
|
+
returns: FFIType.void,
|
|
195
|
+
},
|
|
196
|
+
rod_window_set_size: {
|
|
197
|
+
args: [FFIType.ptr, FFIType.cstring],
|
|
198
|
+
returns: FFIType.void,
|
|
199
|
+
},
|
|
200
|
+
rod_window_set_maximum_size: {
|
|
201
|
+
args: [FFIType.ptr, FFIType.cstring],
|
|
202
|
+
returns: FFIType.void,
|
|
203
|
+
},
|
|
204
|
+
rod_window_set_maximizable: {
|
|
205
|
+
args: [FFIType.ptr, FFIType.bool],
|
|
206
|
+
returns: FFIType.void,
|
|
207
|
+
},
|
|
208
|
+
rod_window_set_maximized: {
|
|
209
|
+
args: [FFIType.ptr, FFIType.bool],
|
|
210
|
+
returns: FFIType.void,
|
|
211
|
+
},
|
|
212
|
+
rod_window_set_minimum_size: {
|
|
213
|
+
args: [FFIType.ptr, FFIType.cstring],
|
|
214
|
+
returns: FFIType.void,
|
|
215
|
+
},
|
|
216
|
+
rod_window_set_minimizable: {
|
|
217
|
+
args: [FFIType.ptr, FFIType.bool],
|
|
218
|
+
returns: FFIType.void,
|
|
219
|
+
},
|
|
220
|
+
rod_window_set_minimized: {
|
|
221
|
+
args: [FFIType.ptr, FFIType.bool],
|
|
222
|
+
returns: FFIType.void,
|
|
223
|
+
},
|
|
224
|
+
rod_window_set_position: {
|
|
225
|
+
args: [FFIType.ptr, FFIType.cstring],
|
|
226
|
+
returns: FFIType.void,
|
|
227
|
+
},
|
|
228
|
+
rod_window_set_progress_bar: {
|
|
229
|
+
args: [FFIType.ptr, FFIType.cstring],
|
|
230
|
+
returns: FFIType.void,
|
|
231
|
+
},
|
|
232
|
+
rod_window_set_resizable: {
|
|
233
|
+
args: [FFIType.ptr, FFIType.bool],
|
|
234
|
+
returns: FFIType.void,
|
|
235
|
+
},
|
|
236
|
+
rod_window_set_title: {
|
|
237
|
+
args: [FFIType.ptr, FFIType.cstring],
|
|
238
|
+
returns: FFIType.void,
|
|
239
|
+
},
|
|
240
|
+
rod_window_set_visible: {
|
|
241
|
+
args: [FFIType.ptr, FFIType.bool],
|
|
242
|
+
returns: FFIType.void,
|
|
243
|
+
},
|
|
244
|
+
rod_window_set_visible_on_all_workspaces: {
|
|
245
|
+
args: [FFIType.ptr, FFIType.bool],
|
|
246
|
+
returns: FFIType.void,
|
|
247
|
+
},
|
|
248
|
+
|
|
249
|
+
// window actions
|
|
250
|
+
rod_window_start_drag: {
|
|
251
|
+
args: [FFIType.ptr],
|
|
252
|
+
returns: FFIType.void,
|
|
253
|
+
},
|
|
254
|
+
|
|
255
|
+
// webview
|
|
256
|
+
rod_webview_create: {
|
|
257
|
+
args: [FFIType.ptr, FFIType.cstring],
|
|
258
|
+
returns: FFIType.ptr,
|
|
259
|
+
},
|
|
260
|
+
rod_webview_destroy: {
|
|
261
|
+
args: [FFIType.ptr],
|
|
262
|
+
returns: FFIType.void,
|
|
263
|
+
},
|
|
264
|
+
rod_webview_set_url: {
|
|
265
|
+
args: [FFIType.ptr, FFIType.cstring],
|
|
266
|
+
returns: FFIType.void,
|
|
267
|
+
},
|
|
268
|
+
rod_webview_get_url: {
|
|
269
|
+
args: [FFIType.ptr],
|
|
270
|
+
returns: FFIType.cstring,
|
|
271
|
+
},
|
|
272
|
+
rod_webview_open_devtools: {
|
|
273
|
+
args: [FFIType.ptr],
|
|
274
|
+
returns: FFIType.void,
|
|
275
|
+
},
|
|
276
|
+
rod_webview_close_devtools: {
|
|
277
|
+
args: [FFIType.ptr],
|
|
278
|
+
returns: FFIType.void,
|
|
279
|
+
},
|
|
280
|
+
rod_webview_is_devtools_open: {
|
|
281
|
+
args: [FFIType.ptr],
|
|
282
|
+
returns: FFIType.bool,
|
|
283
|
+
},
|
|
284
|
+
rod_webview_zoom: {
|
|
285
|
+
args: [FFIType.ptr, FFIType.f64],
|
|
286
|
+
returns: FFIType.void,
|
|
287
|
+
},
|
|
288
|
+
rod_webview_reload: {
|
|
289
|
+
args: [FFIType.ptr],
|
|
290
|
+
returns: FFIType.void,
|
|
291
|
+
},
|
|
292
|
+
rod_webview_set_html: {
|
|
293
|
+
args: [FFIType.ptr, FFIType.cstring],
|
|
294
|
+
returns: FFIType.void,
|
|
295
|
+
},
|
|
296
|
+
rod_webview_clear_all_browsing_data: {
|
|
297
|
+
args: [FFIType.ptr],
|
|
298
|
+
returns: FFIType.void,
|
|
299
|
+
},
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
export {
|
|
303
|
+
// event loop
|
|
304
|
+
rod_event_loop_create,
|
|
305
|
+
rod_event_loop_destroy,
|
|
306
|
+
// event loop actions
|
|
307
|
+
rod_event_loop_poll,
|
|
308
|
+
// window
|
|
309
|
+
rod_window_create,
|
|
310
|
+
rod_window_destroy,
|
|
311
|
+
// window getters
|
|
312
|
+
rod_window_get_title,
|
|
313
|
+
rod_window_get_size,
|
|
314
|
+
rod_window_get_position,
|
|
315
|
+
rod_window_get_always_on_top,
|
|
316
|
+
rod_window_get_closable,
|
|
317
|
+
rod_window_get_decorated,
|
|
318
|
+
rod_window_get_focused,
|
|
319
|
+
rod_window_get_maximizable,
|
|
320
|
+
rod_window_get_maximized,
|
|
321
|
+
rod_window_get_minimizable,
|
|
322
|
+
rod_window_get_minimized,
|
|
323
|
+
rod_window_get_resizable,
|
|
324
|
+
rod_window_get_visible,
|
|
325
|
+
// window setters
|
|
326
|
+
rod_window_set_always_on_bottom,
|
|
327
|
+
rod_window_set_always_on_top,
|
|
328
|
+
rod_window_set_closable,
|
|
329
|
+
rod_window_set_content_protection,
|
|
330
|
+
rod_window_set_decorations,
|
|
331
|
+
rod_window_set_focus,
|
|
332
|
+
rod_window_set_focusable,
|
|
333
|
+
rod_window_set_fullscreen,
|
|
334
|
+
rod_window_set_ignore_cursor_events,
|
|
335
|
+
rod_window_set_size,
|
|
336
|
+
rod_window_set_maximum_size,
|
|
337
|
+
rod_window_set_maximizable,
|
|
338
|
+
rod_window_set_maximized,
|
|
339
|
+
rod_window_set_minimum_size,
|
|
340
|
+
rod_window_set_minimizable,
|
|
341
|
+
rod_window_set_minimized,
|
|
342
|
+
rod_window_set_position,
|
|
343
|
+
rod_window_set_progress_bar,
|
|
344
|
+
rod_window_set_resizable,
|
|
345
|
+
rod_window_set_title,
|
|
346
|
+
rod_window_set_visible,
|
|
347
|
+
rod_window_set_visible_on_all_workspaces,
|
|
348
|
+
// window actions
|
|
349
|
+
rod_window_start_drag,
|
|
350
|
+
// webview
|
|
351
|
+
rod_webview_create,
|
|
352
|
+
rod_webview_destroy,
|
|
353
|
+
// webview getters
|
|
354
|
+
rod_webview_get_url,
|
|
355
|
+
rod_webview_is_devtools_open,
|
|
356
|
+
// webview setters
|
|
357
|
+
rod_webview_set_url,
|
|
358
|
+
rod_webview_set_html,
|
|
359
|
+
rod_webview_zoom,
|
|
360
|
+
// webview actions
|
|
361
|
+
rod_webview_open_devtools,
|
|
362
|
+
rod_webview_close_devtools,
|
|
363
|
+
rod_webview_reload,
|
|
364
|
+
rod_webview_clear_all_browsing_data,
|
|
365
|
+
};
|
package/src-ts/index.ts
ADDED
package/src-ts/types.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export type WebViewOptions = {
|
|
2
|
+
autoplay?: boolean;
|
|
3
|
+
backForwardNavigationGestures?: boolean;
|
|
4
|
+
devTools?: boolean;
|
|
5
|
+
hotkeysZoom?: boolean;
|
|
6
|
+
transparent?: boolean;
|
|
7
|
+
html?: string;
|
|
8
|
+
url?: string;
|
|
9
|
+
incognito?: boolean;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type WindowOptions = {
|
|
13
|
+
alwaysOnBottom?: boolean;
|
|
14
|
+
alwaysOnTop?: boolean;
|
|
15
|
+
closable?: boolean;
|
|
16
|
+
contentProtection?: boolean;
|
|
17
|
+
decorations?: boolean;
|
|
18
|
+
focusable?: boolean;
|
|
19
|
+
focused?: boolean;
|
|
20
|
+
// fullscreen?: boolean;
|
|
21
|
+
size?: Size;
|
|
22
|
+
minimumSize?: Size;
|
|
23
|
+
maximumSize?: Size;
|
|
24
|
+
maximizable?: boolean;
|
|
25
|
+
maximized?: boolean;
|
|
26
|
+
minimizable?: boolean;
|
|
27
|
+
position?: Position;
|
|
28
|
+
resizable?: boolean;
|
|
29
|
+
title?: string;
|
|
30
|
+
transparent?: boolean;
|
|
31
|
+
visible?: boolean;
|
|
32
|
+
visibleOnAllWorkspaces?: boolean;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export type Size = {
|
|
36
|
+
width: number;
|
|
37
|
+
height: number;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export type Position = {
|
|
41
|
+
x: number;
|
|
42
|
+
y: number;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export enum ProgressState {
|
|
46
|
+
Normal = 0,
|
|
47
|
+
Intermediate = 1,
|
|
48
|
+
Paused = 2,
|
|
49
|
+
Error = 3,
|
|
50
|
+
None = 4,
|
|
51
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { WebViewOptions, WindowOptions } from "../types";
|
|
2
|
+
|
|
3
|
+
export function transformWebViewOptions(options: WebViewOptions) {
|
|
4
|
+
return {
|
|
5
|
+
autoplay: options.autoplay,
|
|
6
|
+
back_forward_navigation_gestures: options.backForwardNavigationGestures,
|
|
7
|
+
dev_tools: options.devTools,
|
|
8
|
+
hotkeys_zoom: options.hotkeysZoom,
|
|
9
|
+
transparent: options.transparent,
|
|
10
|
+
html: options.html,
|
|
11
|
+
url: options.url,
|
|
12
|
+
incognito: options.incognito,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function transformWindowOptions(options: WindowOptions) {
|
|
17
|
+
return {
|
|
18
|
+
always_on_bottom: options.alwaysOnBottom,
|
|
19
|
+
always_on_top: options.alwaysOnTop,
|
|
20
|
+
closable: options.closable,
|
|
21
|
+
content_protection: options.contentProtection,
|
|
22
|
+
decorations: options.decorations,
|
|
23
|
+
focusable: options.focusable,
|
|
24
|
+
focused: options.focused,
|
|
25
|
+
// fullscreen: options.fullscreen,
|
|
26
|
+
size: options.size,
|
|
27
|
+
minimum_size: options.minimumSize,
|
|
28
|
+
maximum_size: options.maximumSize,
|
|
29
|
+
maximizable: options.maximizable,
|
|
30
|
+
maximized: options.maximized,
|
|
31
|
+
minimizable: options.minimizable,
|
|
32
|
+
position: options.position,
|
|
33
|
+
resizable: options.resizable,
|
|
34
|
+
title: options.title,
|
|
35
|
+
transparent: options.transparent,
|
|
36
|
+
visible: options.visible,
|
|
37
|
+
visible_on_all_workspaces: options.visibleOnAllWorkspaces,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type Pointer, ptr, toArrayBuffer } from "bun:ffi";
|
|
2
|
+
|
|
3
|
+
export function encodeString(str: string) {
|
|
4
|
+
return ptr(new TextEncoder().encode(`${str}\0`));
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function decodeString(ptr: Pointer): string {
|
|
8
|
+
const arr = toArrayBuffer(ptr);
|
|
9
|
+
const enc = new TextDecoder("utf-8");
|
|
10
|
+
return enc.decode(arr);
|
|
11
|
+
}
|
|
Binary file
|