lucynui 0.1.4 → 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/README.md CHANGED
@@ -2,9 +2,20 @@
2
2
 
3
3
  Desktop apps from plain HTML, CSS and JS. Write web files, get a native window.
4
4
 
5
- Built on [webview-bun](https://github.com/tr1ckydev/webview-bun) and [Elysia](https://elysiajs.com), running on Bun.
5
+ Built on a [fork of webview-bun](https://github.com/LucyNBrowserEngine/webview-bun) and [Elysia](https://elysiajs.com), running on Bun.
6
6
  No React. No Rust. No Electron.
7
7
 
8
+ Part of the [LucyN engine family](https://github.com/LucyNBrowserEngine/LucyNEngine).
9
+
10
+ ## Why this exists
11
+
12
+ Electron is 200+ MB before you've written a line of code. Qt was next — building a UI in QPainter was miserable.
13
+ So the question became: can a desktop app just be plain HTML, CSS and JS, no framework underneath? Turned out yes.
14
+
15
+ The search for a lightweight window led to webview.h, the famous single-header C library. The plan was to write a
16
+ Bun wrapper for it — then [webview-bun](https://github.com/tr1ckydev/webview-bun) turned up, already doing exactly
17
+ that. So, like any decent open-source citizen: forked it, merged it in, and LucyN UI was born.
18
+
8
19
  ## Quick start
9
20
 
10
21
  Scaffold a new app and start it:
@@ -86,7 +97,8 @@ no `app.ts` in your project root.
86
97
  bin/lucyn.ts the CLI entry point
87
98
  cli/ create-ui, init, dev, build, install hook, terminal styling
88
99
  src/ the framework
89
- lucyn.ts createApp — starts server + window
100
+ lucyn.ts createApp — starts the server (in a Worker) + opens the window
101
+ server-worker.ts runs the Elysia file server, off the main thread
90
102
  config.ts reads lucyn.config.ts
91
103
  server.ts Elysia file server + hot reload
92
104
  build.ts compile step
@@ -106,6 +118,12 @@ webview-bun/ native window bindings + prebuilt libraries for linux, mac a
106
118
 
107
119
  Paths are resolved inside the project root only — anything escaping it gets a 403.
108
120
 
121
+ ## Architecture note
122
+
123
+ `webview.run()` is a blocking native call — once the window opens, it freezes Bun's main thread until the window
124
+ closes. If the file server ran on that same thread it would go dead the instant the window appeared, so `createApp()`
125
+ runs the server in a `Worker` instead, keeping it responsive for the whole life of the window.
126
+
109
127
  ## Notes
110
128
 
111
129
  - On Windows you get a warning on startup: LucyN UI is built and tested on Linux, and Windows builds differ.
@@ -113,3 +131,11 @@ Paths are resolved inside the project root only — anything escaping it gets a
113
131
  - If `index.html` is missing, the window shows a helpful error instead of a blank page.
114
132
  - Ports: if the configured port is taken, LucyN UI keeps incrementing until it finds a free one and logs the port it
115
133
  actually used.
134
+
135
+ ## Note
136
+
137
+ At v1.0.0 this might be done. Elysia isn't updating its API anytime soon, neither is webview, neither is Bun — me
138
+ bolting on more APIs after that is just too much work for too little reason. I made this lib because I wanted an
139
+ easier alternative to Qt's paint-and-draw, and it works for that. So don't come in acting like this is dead — what
140
+ more do you want me to add?
141
+ Lucyn is just a new baby of me being a cpp god but just tired writing ui in cpp forgive me cpp gods i got tired good luck
package/cli/proc.ts CHANGED
@@ -7,9 +7,6 @@ import * as ui from "./ui";
7
7
  /** Marks the root of a LucyN UI project. */
8
8
  export const CONFIG_FILE = "lucyn.config.ts";
9
9
 
10
- /** Where `create-ui` clones the starter app from. */
11
- export const TEMPLATE_REPO = "https://github.com/LucyNBrowserEngine/lucynui-template";
12
-
13
10
  /** Absolute path to the package root, wherever it is installed. */
14
11
  export function packageRoot() {
15
12
  return fileURLToPath(new URL("..", import.meta.url));
@@ -20,6 +17,11 @@ export function frameworkPath(file: string) {
20
17
  return fileURLToPath(new URL(`../src/${file}`, import.meta.url));
21
18
  }
22
19
 
20
+ /** Absolute path to the package's own `template/` folder, wherever it is installed. */
21
+ export function templatePath() {
22
+ return fileURLToPath(new URL("../template", import.meta.url));
23
+ }
24
+
23
25
  export async function packageVersion() {
24
26
  const pkg = await Bun.file(new URL("../package.json", import.meta.url)).json();
25
27
  return String(pkg.version ?? "0.0.0");
@@ -91,8 +93,4 @@ export async function ensureSystemDeps() {
91
93
  ui.info("Installing system dependencies...");
92
94
  await runStep(process.cwd(), ["sudo", "apt", "install", "-y", "libgtk-4-1", "libwebkitgtk-6.0-4"], "inherit");
93
95
  ui.ok("Dependencies installed");
94
- }
95
-
96
- export function requireGit() {
97
- if (!Bun.which("git")) throw new Error("git is required to fetch the template");
98
- }
96
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lucynui",
3
- "version": "0.1.4",
3
+ "version": "1.0.0",
4
4
  "description": "Desktop apps from plain HTML, CSS and JS. No React, no Rust, no Electron.",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/src/lucyn.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { resolve } from "node:path";
2
2
  import { SizeHint, Webview } from "../webview-bun/src/webview";
3
3
  import { DEFAULTS, loadConfig, type LucynConfig } from "./config";
4
- import { errorPage, startServer, watchForChanges } from "./server";
4
+ import { errorPage, watchForChanges } from "./server";
5
5
 
6
6
  const WINDOWS_NOTICE =
7
7
  "You are on Windows. LucyN UI on Windows is not top grade as Windows builds differ from Linux where the framework was made and tested. If you encounter any bugs or build errors please dont hesitate to open an issue at the LucyN org. Thank you.";
@@ -9,6 +9,10 @@ const WINDOWS_NOTICE =
9
9
  /**
10
10
  * Starts the file server, opens the native window and blocks until it closes.
11
11
  *
12
+ * The server runs in a Worker: `webview.run()` is a blocking FFI call that
13
+ * freezes Bun's main-thread event loop for as long as the window is open, so
14
+ * the server would never be able to answer requests if it lived there too.
15
+ *
12
16
  * Reads `lucyn.config.ts` from the project root; anything passed here wins.
13
17
  */
14
18
  export async function createApp(config: LucynConfig = {}) {
@@ -17,15 +21,22 @@ export async function createApp(config: LucynConfig = {}) {
17
21
  const root = resolve(process.cwd());
18
22
  const { title, width, height, port, dev } = { ...DEFAULTS, ...(await loadConfig(root)), ...config };
19
23
 
20
- const server = await startServer(root, port);
24
+ const worker = new Worker(new URL("./server-worker.ts", import.meta.url).href);
25
+ const actualPort = await new Promise<number>((resolvePort, rejectPort) => {
26
+ worker.onmessage = (e: MessageEvent) => {
27
+ if (e.data?.ready) resolvePort(e.data.port);
28
+ };
29
+ worker.onerror = (err) => rejectPort(err);
30
+ worker.postMessage({ root, port });
31
+ });
21
32
 
22
- console.log(dev ? `LucyN UI dev server running on http://localhost:${server.port}` : `LucyN UI running on http://localhost:${server.port}`);
33
+ console.log(dev ? `LucyN UI dev server running on http://localhost:${actualPort}` : `LucyN UI running on http://localhost:${actualPort}`);
23
34
 
24
35
  const webview = new Webview(dev, { width, height, hint: SizeHint.NONE });
25
36
  webview.title = title;
26
37
 
27
38
  if (await Bun.file(resolve(root, "index.html")).exists()) {
28
- webview.navigate(`http://localhost:${server.port}`);
39
+ webview.navigate(`http://localhost:${actualPort}`);
29
40
  } else {
30
41
  webview.setHTML(
31
42
  errorPage(
@@ -42,9 +53,10 @@ export async function createApp(config: LucynConfig = {}) {
42
53
  webview.run();
43
54
 
44
55
  watcher?.close();
45
- await server.stop();
56
+ worker.postMessage({ stop: true });
57
+ worker.terminate();
46
58
  }
47
59
 
48
60
  if (import.meta.main) {
49
61
  await createApp(process.argv.includes("--dev") ? { dev: true } : {});
50
- }
62
+ }
@@ -0,0 +1,15 @@
1
+ import { startServer } from "./server";
2
+
3
+ declare const self: Worker;
4
+
5
+ self.onmessage = async (e: MessageEvent) => {
6
+ const { root, port } = e.data as { root: string; port: number };
7
+ const server = await startServer(root, port);
8
+ self.postMessage({ ready: true, port: server.port });
9
+
10
+ self.onmessage = (stop: MessageEvent) => {
11
+ if (stop.data?.stop) {
12
+ server.stop().then(() => process.exit(0));
13
+ }
14
+ };
15
+ };
package/template/app.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { createApp } from "lucynui";
1
+ import { createApp } from "../src/index.ts";
2
2
 
3
3
  // Entry point for `lucyn dev` and `lucyn build`.
4
4
  createApp();
@@ -14,7 +14,12 @@
14
14
  Edit <code>index.html</code>, <code>style.css</code> or <code>app.js</code> and the window reloads by itself.
15
15
  </p>
16
16
  <button id="ping">Say hello</button>
17
+
18
+ <nav class="links">
19
+ <a href="/pages/about.html">About</a>
20
+ <a href="/pages/docs.html">Docs</a>
21
+ </nav>
17
22
  </main>
18
23
  <script src="/app.js"></script>
19
24
  </body>
20
- </html>
25
+ </html>
@@ -7,7 +7,7 @@
7
7
  "build": "lucyn build"
8
8
  },
9
9
  "dependencies": {
10
- "lucynui": "^0.1.0"
10
+ "lucynui": "link:.."
11
11
  },
12
12
  "trustedDependencies": ["lucynui"],
13
13
  "devDependencies": {
@@ -0,0 +1,39 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>About — LucyN UI</title>
7
+ <link rel="stylesheet" href="/style.css" />
8
+ </head>
9
+ <body>
10
+ <main class="app">
11
+ <p class="brand">LucyN UI</p>
12
+ <h1>Why this exists</h1>
13
+
14
+ <p>
15
+ LucyN UI is part of the LucyN engine family. It was mainly made because I didn't like
16
+ Electron being big, and also hated how hard Qt made UI to build.
17
+ </p>
18
+ <p>
19
+ So I asked myself: is it possible to build a desktop app in plain, simple HTML, CSS and JS?
20
+ Turned out it is. I came across the likes of
21
+ <a href="https://tauri.app">Tauri</a> and
22
+ <a href="https://www.electronjs.org">Electron</a>, but learning they were 200+ MB for a
23
+ start, I stopped there.
24
+ </p>
25
+ <p>
26
+ Then I found a webview library — the infamous single <code>webview.h</code> file. I
27
+ thought about writing a Bun wrapper for it myself, then discovered
28
+ <a href="https://github.com/tr1ckydev/webview-bun">webview-bun</a>, which had already
29
+ written that exact wrapper.
30
+ </p>
31
+ <p>
32
+ So what did I do? Like a noble citizen of open source: forked it, merged it in. And well,
33
+ here we are — <strong>LucyN UI</strong>.
34
+ </p>
35
+
36
+ <button id="back" onclick="window.location.href='/index.html'">Back home</button>
37
+ </main>
38
+ </body>
39
+ </html>
@@ -0,0 +1,57 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Docs — LucyN UI</title>
7
+ <link rel="stylesheet" href="/style.css" />
8
+ </head>
9
+ <body>
10
+ <main class="app">
11
+ <p class="brand">LucyN UI</p>
12
+ <h1>Docs</h1>
13
+
14
+ <p>Your app is just files plus a dependency on <code>lucynui</code>:</p>
15
+ <p>
16
+ <code>app.ts</code> — entry point, calls <code>createApp()</code><br />
17
+ <code>index.html</code> — your UI<br />
18
+ <code>style.css</code><br />
19
+ <code>app.js</code><br />
20
+ <code>lucyn.config.ts</code> — title / size / port
21
+ </p>
22
+
23
+ <h1>Commands</h1>
24
+ <p>
25
+ <code>bun run app.ts</code> — start the app<br />
26
+ <code>lucyn dev</code> — dev server + window, hot reload on html/css/js changes<br />
27
+ <code>lucyn build</code> — single binary in <code>./dist</code>
28
+ </p>
29
+
30
+ <h1>Config</h1>
31
+ <p>
32
+ <code>lucyn.config.ts</code> in your project root, every field optional:
33
+ <code>title</code>, <code>width</code>, <code>height</code>, <code>port</code>,
34
+ <code>dev</code>.
35
+ </p>
36
+
37
+ <h1>API Reference</h1>
38
+ <p>
39
+ I mean — do you know HTML? Yes. Do you know CSS? Yes. Do you know JS? Yes. Then what the
40
+ hell are you doing here, just go edit the template.
41
+ </p>
42
+ <p>
43
+ From <code>&lt;createdappfolder&gt;/app.ts</code>, you've got two ways to run it:
44
+ </p>
45
+ <p>
46
+ <code>bun run app.ts</code> or <code>lucynui dev</code> — start the app<br />
47
+ <code>bun run build</code> or <code>lucynui build</code> — ship a binary
48
+ </p>
49
+ <p>That's the whole API. You already know the rest.</p>
50
+
51
+ <nav class="links">
52
+ <a href="/index.html">Back home</a>
53
+ <a href="/pages/about.html">About</a>
54
+ </nav>
55
+ </main>
56
+ </body>
57
+ </html>