astrodon / astrodon Goto Github PK
View Code? Open in Web Editor NEWMake Desktop apps with Deno 🦕
Home Page: https://astrodon.land
License: MIT License
Make Desktop apps with Deno 🦕
Home Page: https://astrodon.land
License: MIT License
I have been looking into some way to compile GUI deno app to run on Windows with Tauri before, with great success but without making much FFI effort.
Here is what I have (found & made)
First simple method is to change a byte into the deno compile
binary from Typescript after it's done.
the other method is directly a suggestion i have made in the denoland/deno repo that adds the -subsystem option in deno compile
in order to select if you wish to bundle a GUI app or a console app (windows only)
From this issue denoland/deno#11638
// --allow-read --allow-write change_exe_subsystem.ts
async function readN(r: Deno.Reader, n: number): Promise<Uint8Array | null> {
const buf = new Uint8Array(n);
let nRead = 0;
// a null value of r.read() will nullish coalesce into NaN and
// polute nRead, causing (nRead < n) to be false and the loop to exit
while (nRead < n) {
nRead += await r.read(buf.subarray(nRead)) ?? NaN;
}
return isNaN(nRead) ? null : buf;
}
async function writeAll(w: Deno.Writer, buf: ArrayBufferView): Promise<void> {
const bytes = new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
let nWritten = 0;
while (nWritten < bytes.byteLength) {
nWritten += await w.write(bytes.subarray(nWritten));
}
}
function view(buf: ArrayBufferView) {
return new DataView(buf.buffer, buf.byteOffset, buf.byteLength);
}
// replace 'my_oak_server.exe' with your server binary
const PATH_TO_BINARY = "./my_oak_server.exe";
const bin = await Deno.open(PATH_TO_BINARY, { read: true, write: true });
// we're going to validate (loosely) that its a valid EXE/MZ
// reset to index 0, just to be sure
await bin.seek(0, Deno.SeekMode.Start);
const magicBytes = await readN(bin, 2);
if (magicBytes == null) {
console.log("Encountered EOF at magicBytes");
Deno.exit(1);
}
const binMagic = new TextDecoder().decode(magicBytes);
if ("MZ" != binMagic) {
console.log(`☹ we didnt find a valid exe at ${PATH_TO_BINARY}`);
Deno.exit(1);
}
// we now know we have a valid exe file
// next we need to get the offset of the 'PE Header'
const PE_ADDRESS_OFFSET = 0x3C;
await bin.seek(PE_ADDRESS_OFFSET, Deno.SeekMode.Start);
// read the peHeaderPointer as 32bit little-endian int/dword/u32
const peHeaderPointerBytes = await readN(bin, 4);
if (peHeaderPointerBytes == null) {
console.log("Encountered EOF at peHeaderPointerBytes");
Deno.exit(1);
}
const peHeaderPointer = view(peHeaderPointerBytes).getUint32(0, true);
// we've got the offset of the PE header now
// we'll go to that offset, then a further 92 bytes
// this is where the subsytem field is
await bin.seek(peHeaderPointer + 92, Deno.SeekMode.Start);
// WINDOWS subsystem (don't show a terminal)
// CONSOLE subsystem (show a terminal when running)
const SUBSYSTEM_WINDOWS = 2;
const SUBSYSTEM_CONSOLE = 3;
// before we modify the value we'll do a very rough check
// to make sure that we are modifying the right field
// we'll need to get it as a little-endian u16
const subsystemBytes = await readN(bin, 2);
if (subsystemBytes == null) {
console.log("Encountered EOF at subsystemBytes");
Deno.exit(1);
}
const subsystem = view(subsystemBytes).getUint16(0, true);
if (!(SUBSYSTEM_WINDOWS == subsystem || SUBSYSTEM_CONSOLE == subsystem)) {
console.log("Oops! The subsystem is not WINDOWS=2 or CONSOLE=3.");
console.log("We might be editing the wrong field,");
console.log(" _or_ the EXE uses a different subsystem.");
Deno.exit(1);
}
// okay, now we are pretty sure about the file
// let's update its subsystem
const newSubsystemData = new Uint16Array(1);
view(newSubsystemData).setUint16(0, SUBSYSTEM_WINDOWS, true);
// go back to the subsytem field
await bin.seek(peHeaderPointer + 92, Deno.SeekMode.Start);
// write out our data.
await writeAll(bin, newSubsystemData);
// finish up with a helpful message
const newSubsystemValue = view(newSubsystemData).getUint16(0, true);
if (SUBSYSTEM_WINDOWS == newSubsystemValue) {
console.log(`Done! Changed ${PATH_TO_BINARY} subsystem=2, WINDOWS.`);
}
else if (SUBSYSTEM_CONSOLE == newSubsystemValue) {
console.log(`Done! Changed ${PATH_TO_BINARY} subsystem=3, CONSOLE.`);
}
And my suggestion to add directly to Deno :
Both are great, but I believe for Astrodon it can be easier to just not "close" the console and instead just make it a real GUI app (Windows will know the difference)
this is a suggestion, let me know what you think.
Is possible to build todo desktop app based on vue or react and sqlite using astrodon ? If yes I hope see demo
This would be nice to make all icons at once. Pass in one high-res PNG, and it will make all the icons on their respective formats and sizes.
deno run -A --unstable --reload https://deno.land/x/[email protected]/examples/hello_world/demo.ts
error: Uncaught SyntaxError: The requested module 'https://deno.land/x/plug/mod.ts' does not provide an export named 'Plug'
export { Plug } from "https://deno.land/x/plug/mod.ts";
^
at <anonymous> (https://deno.land/x/[email protected]/modules/astrodon/deps.ts:1:10)
There should be a site showing documentation on how to use this tool
I create gui.ts file and copy past
import { AppWindow } from "https://deno.land/x/astrodon/mod.ts";
const win = new AppWindow("Window A");
win.setHtml("<h1>Hello World :)</h1>");
await win.run();
I get the following error :
ely@ely-ThinkPad-T450s:~/Documents/fullstack/astrodon-explore$ deno run -A --unstable gui.ts
Check file:///home/ely/Documents/fullstack/astrodon-explore/gui.ts
error: TS2305 [ERROR]: Module '"https://deno.land/x/[email protected]/mod.ts"' has no exported member 'AppWindow'.
import { AppWindow } from "https://deno.land/x/astrodon/mod.ts";
~~~~~~~~~
at file:///home/ely/Documents/fullstack/astrodon-explore/gui.ts:1:10
I've been talking a lot in the discord server about deno desktop apps, tauri and deno would be a killer feature for both. I know is hard since both deno and tauri are in their early stages, but would be trully awesome.
Any side-projects related to this?
Download https://deno.land/x/[email protected]/examples/hello_world/astrodon.config.ts
error: Uncaught (in promise) Error: Could not open library: Could not open library: libssl.so.1.1: cannot open shared object file: No such file or directory
return Deno.dlopen(file, symbols);
^
at Object.opSync (deno:core/01_core.js:170:12)
Custom URL scehems provide a way to reference endpoints inside an app. Users accessing a custom url e.g. appname://endpoint
in the browser for example would launch the app to the specified endpoint. Other apps can also trigger the endpoint.
A Rust package https://github.com/maidsafe/system_uri exists that does system app URI registration for MacOS, Linux and Windows. Although it does not support passing the url to the application, it would only trigger the launch of the app.
Module loader used for development, should be able to load from redirected URLs.
This can be done by looking at the original_hased_url.metadata.json
, and looking under the "location" header, which tells us the redirected file URL, then it can be hashed to get the file location.
Astrodon desktop store
I know astrodon still in dev and is not yet stable
But I suggess to plan to build ecosystem around astrodon
In some way I aime to get have ecosystem like playStore+androidStudio+... but for desktop app multiplateform base on deno and web tech.
This is just a suggestion.
While tauri on Linux and Windows (with a workaround) can run on a different thread rather than the main, MacOS can't. This is essential, because we don't want to block the main thread (Deno's). We hope there is a better way to do it (maybe without running tauri on ffi side?).
We are open to suggestions and contributions :)
Related: tauri-apps/tauri#3172
deno run -A --unstable --reload https://raw.githubusercontent.com/astrodon/astrodon/main/demo/demo.ts
Check https://raw.githubusercontent.com/astrodon/astrodon/main/demo/demo.ts
error: TS2694 [ERROR]: Namespace 'Deno' has no exported member 'UnsafePointer'.
private app_ptr: Deno.UnsafePointer | undefined;
~~~~~~~~~~~~~
at https://raw.githubusercontent.com/astrodon/astrodon/main/mod.ts:26:25
TS2322 [ERROR]: Type '"pointer"' is not assignable to type 'NativeType | "buffer"'.
create_app: { parameters: ["pointer", "usize"], result: "pointer" },
~~~~~~~~~
at https://raw.githubusercontent.com/astrodon/astrodon/main/mod.ts:43:34
TS2322 [ERROR]: Type '"pointer"' is not assignable to type 'NativeType'.
create_app: { parameters: ["pointer", "usize"], result: "pointer" },
~~~~~~
at https://raw.githubusercontent.com/astrodon/astrodon/main/mod.ts:43:55
TS2322 [ERROR]: Type '"pointer"' is not assignable to type 'NativeType | "buffer"'.
run_app: { parameters: ["pointer"], result: "pointer" },
~~~~~~~~~
at https://raw.githubusercontent.com/astrodon/astrodon/main/mod.ts:44:31
TS2322 [ERROR]: Type '"pointer"' is not assignable to type 'NativeType'.
run_app: { parameters: ["pointer"], result: "pointer" },
~~~~~~
at https://raw.githubusercontent.com/astrodon/astrodon/main/mod.ts:44:43
TS2322 [ERROR]: Type '"pointer"' is not assignable to type 'NativeType | "buffer"'.
parameters: ["pointer", "usize", "pointer"],
~~~~~~~~~
at https://raw.githubusercontent.com/astrodon/astrodon/main/mod.ts:46:22
TS2322 [ERROR]: Type '"pointer"' is not assignable to type 'NativeType | "buffer"'.
parameters: ["pointer", "usize", "pointer"],
~~~~~~~~~
at https://raw.githubusercontent.com/astrodon/astrodon/main/mod.ts:46:42
TS2322 [ERROR]: Type '"pointer"' is not assignable to type 'NativeType'.
result: "pointer",
~~~~~~
at https://raw.githubusercontent.com/astrodon/astrodon/main/mod.ts:47:9
TS2694 [ERROR]: Namespace 'Deno' has no exported member 'UnsafePointer'.
) as Deno.UnsafePointer;
~~~~~~~~~~~~~
at https://raw.githubusercontent.com/astrodon/astrodon/main/mod.ts:61:15
TS2694 [ERROR]: Namespace 'Deno' has no exported member 'UnsafePointer'.
this.app_ptr = this.lib.symbols.run_app(this.app_ptr) as Deno.UnsafePointer;
~~~~~~~~~~~~~
at https://raw.githubusercontent.com/astrodon/astrodon/main/mod.ts:62:67
TS2694 [ERROR]: Namespace 'Deno' has no exported member 'UnsafePointer'.
) as Deno.UnsafePointer;
~~~~~~~~~~~~~
at https://raw.githubusercontent.com/astrodon/astrodon/main/mod.ts:69:15
Found 11 errors.
I hope to see more explanation for relation between ( deno, ts ) and ( Astrodon/tauri , rust )
for Astrodon consumer and Astrodon developer.
for example is rust for Astrodon consumer in certain situation ? what skils needed for me or any dev to be able to contribue to Astrodon ?
deno run -A --unstable --reload https://deno.land/x/[email protected]/examples/hello_world/demo.ts
...
Download https://deno.land/x/[email protected]/examples/hello_world/astrodon.config.ts
Segmentation fault (core dumped)
deno run examples/hello_world/demo.ts
⚠️ ️Deno requests net access to "dog.ceo". Run again with --allow-net to bypass this prompt.
Allow? [y/n (y = yes allow, n = no deny)] y
error: Uncaught (in promise) TypeError: ops[opName] is not a function
return (Deno as any).core.opAsync("run_window", {
^
at Object.opAsync (deno:core/01_core.js:150:35)
when run
deno run -A --unstable https://deno.land/x/astrodon/examples/hello_world/demo.ts
I get
ely@ely-ThinkPad-T450s:~$ deno run -A --unstable https://deno.land/x/astrodon/examples/hello_world/demo.ts
Download https://deno.land/x/astrodon/examples/hello_world/demo.ts
Warning Implicitly using latest version (0.1.0-alpha.2) for https://deno.land/x/astrodon/examples/hello_world/demo.ts
Download https://deno.land/x/astrodon/examples/hello_world/astrodon.config.ts
Warning Implicitly using latest version (0.1.0-alpha.2) for https://deno.land/x/astrodon/examples/hello_world/astrodon.config.ts
error: Uncaught (in promise) CacheError: /home/ely/superapp/astrodon/0.1.0-alpha.2/lib/libastrodon.so is not valid.
throw new CacheError(`${path} is not valid.`);
^
at protocolFile (https://deno.land/x/[email protected]/file_fetcher.ts:12:11)
at async fetchFile (https://deno.land/x/[email protected]/file_fetcher.ts:41:14)
at async FileWrapper.fetch (https://deno.land/x/[email protected]/file.ts:95:18)
at async FileWrapper.get (https://deno.land/x/[email protected]/file.ts:113:12)
at async cache (https://deno.land/x/[email protected]/cache.ts:67:10)
at async Wrapper.cache (https://deno.land/x/[email protected]/cache.ts:21:12)
at async download (https://deno.land/x/[email protected]/plug.ts:96:16)
at async Module.prepare (https://deno.land/x/[email protected]/plug.ts:105:16)
at async Function.new (https://deno.land/x/[email protected]/modules/astrodon/mod.ts:115:21)
at async https://deno.land/x/[email protected]/examples/hello_world/demo.ts:18:13
ely@ely-ThinkPad-T450s:~$
I have ubuntu 20.04 as sytem
it seems there a pb for caching binary, I suggess to have to two command one for dowload and cach binary and the second for create ui
Would be awesome if we had support for common used dialogs, such as a file picker, Yes/No chooser... And also notifications
Research needs to be done
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.