nanoshell 1.2.3 → 1.7.2
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 +58 -22
- package/cli/bundler.zig +63 -15
- package/cli/dev_server.zig +176 -5
- package/cli/installer.zig +25 -15
- package/cli/main.zig +10 -21
- package/example_app/app.js +421 -79
- package/example_app/index.html +126 -36
- package/example_app/styles.css +184 -86
- package/package.json +1 -1
- package/zig-out/bin/example_app.exe +0 -0
- package/zig-out/bin/nanoshell.exe +0 -0
- package/zig-out/bin/msvcp140.dll +0 -0
- package/zig-out/bin/vcruntime140.dll +0 -0
- package/zig-out/bin/vcruntime140_1.dll +0 -0
package/README.md
CHANGED
|
@@ -14,44 +14,80 @@
|
|
|
14
14
|
- **Executable Size**: **3.00 MB** Self-Contained Binary
|
|
15
15
|
- **IPC Latency**: **0.00 ms** Zero-Copy Shared Memory Matrix across Python/C++/Rust
|
|
16
16
|
- **CSS Engine**: Universal Dynamic CSS Flex/Grid Gap Engine (0 hardcoded class names)
|
|
17
|
-
|
|
17
|
+
# NanoShell (v1.5.0) ⚡
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
> **Created by Suman Biswas**
|
|
20
|
+
> *Hyper-lightweight 17MB RAM, 120 FPS Native WebKit Desktop Application Framework & Runtime Engine*
|
|
20
21
|
|
|
21
|
-
|
|
22
|
+
NanoShell is an ultra-fast, zero-overhead desktop application engine built with **Zig** and **Ultralight GPU-Accelerated WebKit C++**. It allows web developers to build native desktop applications using HTML, CSS, and Vanilla JavaScript with **17MB RSS RAM footprint**, **120 FPS performance**, and **zero-copy C-level OS bindings**.
|
|
22
23
|
|
|
23
|
-
|
|
24
|
+
---
|
|
24
25
|
|
|
25
|
-
|
|
26
|
+
## 🚀 Quick Start
|
|
26
27
|
|
|
27
28
|
```bash
|
|
29
|
+
# 1. Scaffold a new application project
|
|
28
30
|
npx nanoshell my-awesome-app
|
|
29
|
-
```
|
|
30
31
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
```bash
|
|
32
|
+
# 2. Start Development Mode (In-Window Hot Reloading on Ctrl+S)
|
|
34
33
|
cd my-awesome-app
|
|
35
|
-
|
|
36
|
-
```
|
|
34
|
+
npx nanoshell start
|
|
37
35
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
```bash
|
|
36
|
+
# 3. Build Standalone Production Binary (.exe)
|
|
41
37
|
npx nanoshell build
|
|
38
|
+
|
|
39
|
+
# 4. Package Windows Installer (.exe + Setup.exe via Inno Setup)
|
|
40
|
+
npx nanoshell package
|
|
42
41
|
```
|
|
43
42
|
|
|
44
|
-
|
|
43
|
+
---
|
|
45
44
|
|
|
46
|
-
|
|
45
|
+
## 📖 Master JavaScript API Reference (`window.NanoShell`)
|
|
47
46
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
47
|
+
### 📁 File System (`NanoShell.fs`)
|
|
48
|
+
- `NanoShell.fs.readFile(path)`: Read text file contents.
|
|
49
|
+
- `NanoShell.fs.writeFile(path, content)`: Write text file to disk.
|
|
50
|
+
- `NanoShell.fs.exists(path)`: Check file/directory existence.
|
|
51
|
+
- `NanoShell.fs.readDir(path)`: List directory folder contents.
|
|
52
|
+
- `NanoShell.fs.mkdir(path)`: Create directory folder.
|
|
53
|
+
- `NanoShell.fs.remove(path)`: Delete file or directory.
|
|
54
|
+
|
|
55
|
+
### 💻 System & OS (`NanoShell.os`)
|
|
56
|
+
- `NanoShell.os.getInfo()`: Get platform, arch (`x64`), core count, and RAM telemetry.
|
|
57
|
+
|
|
58
|
+
### ⚙️ Shell & Execution (`NanoShell.shell` & `NanoShell.process`)
|
|
59
|
+
- `NanoShell.shell.openExternal(urlOrPath)`: Open URL or file in default OS application.
|
|
60
|
+
- `NanoShell.shell.exec(command)`: Execute CLI command (PowerShell/CMD).
|
|
61
|
+
- `NanoShell.process.list()`: Task Manager process list.
|
|
62
|
+
|
|
63
|
+
### 🪟 Window & Screen Controls (`NanoShell.window` & `NanoShell.screen`)
|
|
64
|
+
- `NanoShell.window.minimize()`, `maximize()`, `restore()`, `close()`, `center()`, `setTitle()`, `setFullscreen()`.
|
|
65
|
+
- `NanoShell.screen.getMonitors()`: List connected monitors, resolutions, and refresh rates.
|
|
66
|
+
- `NanoShell.screen.getCursorPosition()`: Global mouse desktop coordinates `(x, y)`.
|
|
67
|
+
|
|
68
|
+
### 📋 Clipboard (`NanoShell.clipboard`)
|
|
69
|
+
- `NanoShell.clipboard.writeText(text)`: Copy text to system clipboard.
|
|
70
|
+
- `NanoShell.clipboard.readText()`: Paste text from system clipboard.
|
|
71
|
+
|
|
72
|
+
### 💬 OS Dialogs (`NanoShell.dialog`)
|
|
73
|
+
- `NanoShell.dialog.showOpen()`: Native Open File Picker dialog.
|
|
74
|
+
- `NanoShell.dialog.showSave()`: Native Save File Picker dialog.
|
|
75
|
+
- `NanoShell.dialog.showMessage(title, message)`: Native OS Alert message box.
|
|
76
|
+
|
|
77
|
+
### 🔔 Toast Notifications (`NanoShell.notification`)
|
|
78
|
+
- `NanoShell.notification.show(title, body)`: Trigger native OS Toast notification.
|
|
79
|
+
|
|
80
|
+
### 🔋 Power & Battery (`NanoShell.power`)
|
|
81
|
+
- `NanoShell.power.getBatteryStatus()`: Battery %, charging status, AC power.
|
|
82
|
+
|
|
83
|
+
### ⚡ Shared Memory & Snapshot (`NanoShell.shm` & `NanoShell.snapshot`)
|
|
84
|
+
- `NanoShell.shm.createRegion(name, size)`: Zero-copy C RAM allocation (< 0.01ms IPC).
|
|
85
|
+
- `NanoShell.snapshot.thawState()`: < 1ms instant cold-start thaw engine.
|
|
86
|
+
|
|
87
|
+
---
|
|
51
88
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
- High-ratio LZMA2 compression for ultra-compact setup installers
|
|
89
|
+
## 📦 100% Portable Bundle
|
|
90
|
+
Every scaffolded NanoShell app includes portable Visual C++ runtime DLLs (`vcruntime140.dll`, `msvcp140.dll`, `vcruntime140_1.dll`) and is compiled against `x86_64-windows-gnu` baseline CPU targets — ensuring **100% portable out-of-the-box execution on any Windows laptop without admin rights or external installers**.
|
|
55
91
|
- Automatic Start Menu & Desktop shortcuts
|
|
56
92
|
|
|
57
93
|
|
package/cli/bundler.zig
CHANGED
|
@@ -34,31 +34,73 @@ pub const SingleBinaryBundler = struct {
|
|
|
34
34
|
|
|
35
35
|
const cwd = std.Io.Dir.cwd();
|
|
36
36
|
|
|
37
|
-
// Copy Main Executable bin
|
|
38
|
-
const
|
|
39
|
-
|
|
37
|
+
// Copy Main Executable (search zig-out/bin, bin, root, example_app.exe)
|
|
38
|
+
const possible_srcs = [_][]const u8{
|
|
39
|
+
"zig-out/bin/example_app.exe",
|
|
40
|
+
"example_app.exe",
|
|
41
|
+
"bin/example_app.exe",
|
|
42
|
+
try std.fmt.allocPrint(self.allocator, "bin/{s}.exe", .{self.config.app_name}),
|
|
43
|
+
try std.fmt.allocPrint(self.allocator, "{s}.exe", .{self.config.app_name}),
|
|
44
|
+
};
|
|
45
|
+
const dst_exe_name = try std.fmt.allocPrint(self.allocator, "{s}.exe", .{self.config.app_name});
|
|
46
|
+
defer self.allocator.free(dst_exe_name);
|
|
40
47
|
const dst_exe_path = try std.fmt.allocPrint(self.allocator, "{s}/{s}.exe", .{ self.config.output_dir, self.config.app_name });
|
|
41
48
|
defer self.allocator.free(dst_exe_path);
|
|
42
49
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
50
|
+
var copied = false;
|
|
51
|
+
for (possible_srcs) |src| {
|
|
52
|
+
if (cwd.copyFile(src, dist_dir, dst_exe_name, io, .{})) |_| {
|
|
53
|
+
copied = true;
|
|
54
|
+
break;
|
|
55
|
+
} else |_| {}
|
|
56
|
+
}
|
|
57
|
+
if (!copied) {
|
|
58
|
+
std.log.warn("Executable copy warning: Could not locate baseline executable binary", .{});
|
|
59
|
+
}
|
|
46
60
|
|
|
47
|
-
// Copy DLLs
|
|
61
|
+
// Copy DLLs to dist/ (search vendor/lib, zig-out/bin, and root)
|
|
48
62
|
const dlls = [_][]const u8{ "AppCore.dll", "Ultralight.dll", "UltralightCore.dll", "WebCore.dll", "vcruntime140.dll", "msvcp140.dll", "vcruntime140_1.dll" };
|
|
49
63
|
for (dlls) |dll| {
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
64
|
+
const possible_dll_srcs = [_][]const u8{
|
|
65
|
+
try std.fmt.allocPrint(self.allocator, "vendor/lib/{s}", .{dll}),
|
|
66
|
+
try std.fmt.allocPrint(self.allocator, "zig-out/bin/{s}", .{dll}),
|
|
67
|
+
try std.fmt.allocPrint(self.allocator, "bin/{s}", .{dll}),
|
|
68
|
+
dll,
|
|
69
|
+
};
|
|
70
|
+
defer for (possible_dll_srcs) |p| self.allocator.free(p);
|
|
71
|
+
|
|
72
|
+
const dst_dll = try std.fmt.allocPrint(self.allocator, "{s}/{s}", .{ self.config.output_dir, dll });
|
|
73
|
+
defer self.allocator.free(dst_dll);
|
|
74
|
+
|
|
75
|
+
for (possible_dll_srcs) |src_dll| {
|
|
76
|
+
if (copyFileSimple(src_dll, dst_dll)) {
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
53
80
|
}
|
|
54
81
|
|
|
55
|
-
// Copy Resource Files
|
|
82
|
+
// Copy Resource Files to dist/ and dist/resources/
|
|
56
83
|
const res_files = [_][]const u8{ "icudt67l.dat", "cacert.pem" };
|
|
57
84
|
for (res_files) |file| {
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
85
|
+
const possible_res_srcs = [_][]const u8{
|
|
86
|
+
try std.fmt.allocPrint(self.allocator, "vendor/resources/{s}", .{file}),
|
|
87
|
+
try std.fmt.allocPrint(self.allocator, "resources/{s}", .{file}),
|
|
88
|
+
try std.fmt.allocPrint(self.allocator, "zig-out/bin/resources/{s}", .{file}),
|
|
89
|
+
try std.fmt.allocPrint(self.allocator, "bin/{s}", .{file}),
|
|
90
|
+
};
|
|
91
|
+
defer for (possible_res_srcs) |p| self.allocator.free(p);
|
|
92
|
+
|
|
93
|
+
const dst_res1 = try std.fmt.allocPrint(self.allocator, "{s}/{s}", .{ self.config.output_dir, file });
|
|
94
|
+
defer self.allocator.free(dst_res1);
|
|
95
|
+
const dst_res2 = try std.fmt.allocPrint(self.allocator, "{s}/resources/{s}", .{ self.config.output_dir, file });
|
|
96
|
+
defer self.allocator.free(dst_res2);
|
|
97
|
+
|
|
98
|
+
for (possible_res_srcs) |src_res| {
|
|
99
|
+
if (copyFileSimple(src_res, dst_res1)) {
|
|
100
|
+
_ = copyFileSimple(src_res, dst_res2);
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
62
104
|
}
|
|
63
105
|
|
|
64
106
|
// Copy app/* files to dist/app/*
|
|
@@ -78,3 +120,9 @@ pub const SingleBinaryBundler = struct {
|
|
|
78
120
|
std.log.info("Built Custom Named Executable -> '{s}' (Self-contained release package)", .{dst_exe_path});
|
|
79
121
|
}
|
|
80
122
|
};
|
|
123
|
+
|
|
124
|
+
fn copyFileSimple(src: []const u8, dst: []const u8) bool {
|
|
125
|
+
const io = std.Io.Threaded.global_single_threaded.io();
|
|
126
|
+
std.Io.Dir.cwd().copyFile(src, std.Io.Dir.cwd(), dst, io, .{}) catch return false;
|
|
127
|
+
return true;
|
|
128
|
+
}
|
package/cli/dev_server.zig
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const std = @import("std");
|
|
2
|
+
const installer = @import("installer.zig");
|
|
2
3
|
|
|
3
4
|
pub const DevServer = struct {
|
|
4
5
|
allocator: std.mem.Allocator,
|
|
@@ -8,14 +9,184 @@ pub const DevServer = struct {
|
|
|
8
9
|
}
|
|
9
10
|
|
|
10
11
|
pub fn runDevMode(self: *DevServer, app_dir_name: []const u8) !void {
|
|
12
|
+
_ = app_dir_name;
|
|
11
13
|
std.log.info("=================================================================", .{});
|
|
12
|
-
std.log.info(" ⚡ NanoShell
|
|
13
|
-
std.log.info(" Watching app/ directory for
|
|
14
|
+
std.log.info(" ⚡ NanoShell Dev Server Started", .{});
|
|
15
|
+
std.log.info(" Watching app/ directory & nanoshell.json for live changes...", .{});
|
|
14
16
|
std.log.info("=================================================================", .{});
|
|
15
17
|
|
|
16
|
-
|
|
17
|
-
defer self.allocator
|
|
18
|
+
var config = installer.loadConfig(self.allocator) catch installer.AppConfig{};
|
|
19
|
+
defer config.deinit(self.allocator);
|
|
18
20
|
|
|
19
|
-
|
|
21
|
+
// Find executable inside bin/ or zig-out/bin/
|
|
22
|
+
const io = std.Io.Threaded.global_single_threaded.io();
|
|
23
|
+
const cwd = std.Io.Dir.cwd();
|
|
24
|
+
|
|
25
|
+
var found_exe: ?[]const u8 = null;
|
|
26
|
+
|
|
27
|
+
const candidate_paths = [_][]const u8{
|
|
28
|
+
"zig-out/bin/example_app.exe",
|
|
29
|
+
"bin/example_app.exe",
|
|
30
|
+
"zig-out/bin/nanoshell.exe",
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
for (candidate_paths) |p| {
|
|
34
|
+
if (cwd.openFile(io, p, .{})) |f| {
|
|
35
|
+
f.close(io);
|
|
36
|
+
found_exe = p;
|
|
37
|
+
break;
|
|
38
|
+
} else |_| {}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (found_exe == null) {
|
|
42
|
+
// Search bin/ for any .exe
|
|
43
|
+
var bin_dir = cwd.openDir(io, "bin", .{ .iterate = true }) catch null;
|
|
44
|
+
if (bin_dir) |*d| {
|
|
45
|
+
defer d.close(io);
|
|
46
|
+
var iter = d.iterate();
|
|
47
|
+
while (iter.next(io) catch null) |entry| {
|
|
48
|
+
if (entry.kind == .file and std.mem.endsWith(u8, entry.name, ".exe")) {
|
|
49
|
+
found_exe = try std.fmt.allocPrint(self.allocator, "bin/{s}", .{entry.name});
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
var win_exe_buf: [256]u8 = undefined;
|
|
57
|
+
const exe_to_run = found_exe orelse "bin\\app.exe";
|
|
58
|
+
var pos: usize = 0;
|
|
59
|
+
for (exe_to_run) |ch| {
|
|
60
|
+
if (ch == '/') {
|
|
61
|
+
win_exe_buf[pos] = '\\';
|
|
62
|
+
} else {
|
|
63
|
+
win_exe_buf[pos] = ch;
|
|
64
|
+
}
|
|
65
|
+
pos += 1;
|
|
66
|
+
}
|
|
67
|
+
win_exe_buf[pos] = 0;
|
|
68
|
+
const win_path = win_exe_buf[0..pos];
|
|
69
|
+
|
|
70
|
+
var dev_cmd_buf: [300]u8 = undefined;
|
|
71
|
+
const dev_cmd = std.fmt.bufPrintZ(&dev_cmd_buf, "{s} --dev", .{win_path}) catch win_path;
|
|
72
|
+
|
|
73
|
+
std.log.info("Launching NanoShell runtime in DEV mode: {s}", .{dev_cmd});
|
|
74
|
+
|
|
75
|
+
// Launch app process once - native engine handles smooth in-window hot reload in dev mode
|
|
76
|
+
var child = try self.spawnChildProcess(dev_cmd);
|
|
77
|
+
defer _ = child.kill();
|
|
78
|
+
|
|
79
|
+
std.log.info("App running! In-window hot reload active for app/index.html, styles.css, app.js.", .{});
|
|
80
|
+
|
|
81
|
+
const WinApi = struct {
|
|
82
|
+
pub extern "kernel32" fn Sleep(dwMilliseconds: u32) callconv(.winapi) void;
|
|
83
|
+
pub extern "kernel32" fn GetExitCodeProcess(hProcess: std.os.windows.HANDLE, lpExitCode: *u32) callconv(.winapi) i32;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
var exit_code: u32 = 259; // STILL_ACTIVE
|
|
87
|
+
while (exit_code == 259) {
|
|
88
|
+
WinApi.Sleep(500);
|
|
89
|
+
_ = WinApi.GetExitCodeProcess(child.hProcess, &exit_code);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
fn spawnChildProcess(self: *DevServer, exe_path: []const u8) !ChildProcess {
|
|
94
|
+
_ = self;
|
|
95
|
+
var cmd_utf16: [1024]u16 = undefined;
|
|
96
|
+
const len = try std.unicode.utf8ToUtf16Le(&cmd_utf16, exe_path);
|
|
97
|
+
cmd_utf16[len] = 0;
|
|
98
|
+
|
|
99
|
+
const STARTUPINFOW = extern struct {
|
|
100
|
+
cb: u32 = 104,
|
|
101
|
+
lpReserved: ?[*:0]u16 = null,
|
|
102
|
+
lpDesktop: ?[*:0]u16 = null,
|
|
103
|
+
lpTitle: ?[*:0]u16 = null,
|
|
104
|
+
dwX: u32 = 0,
|
|
105
|
+
dwY: u32 = 0,
|
|
106
|
+
dwXSize: u32 = 0,
|
|
107
|
+
dwYSize: u32 = 0,
|
|
108
|
+
dwXCountChars: u32 = 0,
|
|
109
|
+
dwYCountChars: u32 = 0,
|
|
110
|
+
dwFillAttribute: u32 = 0,
|
|
111
|
+
dwFlags: u32 = 0,
|
|
112
|
+
wShowWindow: u16 = 0,
|
|
113
|
+
cbReserved2: u16 = 0,
|
|
114
|
+
lpReserved2: ?*u8 = null,
|
|
115
|
+
hStdInput: ?std.os.windows.HANDLE = null,
|
|
116
|
+
hStdOutput: ?std.os.windows.HANDLE = null,
|
|
117
|
+
hStdError: ?std.os.windows.HANDLE = null,
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const PROCESS_INFORMATION = extern struct {
|
|
121
|
+
hProcess: std.os.windows.HANDLE,
|
|
122
|
+
hThread: std.os.windows.HANDLE,
|
|
123
|
+
dwProcessId: u32,
|
|
124
|
+
dwThreadId: u32,
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const WinApi = struct {
|
|
128
|
+
pub extern "kernel32" fn CreateProcessW(
|
|
129
|
+
lpApplicationName: ?[*:0]const u16,
|
|
130
|
+
lpCommandLine: ?[*:0]u16,
|
|
131
|
+
lpProcessAttributes: ?*anyopaque,
|
|
132
|
+
lpThreadAttributes: ?*anyopaque,
|
|
133
|
+
bInheritHandles: i32,
|
|
134
|
+
dwCreationFlags: u32,
|
|
135
|
+
lpEnvironment: ?*anyopaque,
|
|
136
|
+
lpCurrentDirectory: ?[*:0]const u16,
|
|
137
|
+
lpStartupInfo: *const STARTUPINFOW,
|
|
138
|
+
lpProcessInformation: *PROCESS_INFORMATION,
|
|
139
|
+
) callconv(.winapi) i32;
|
|
140
|
+
|
|
141
|
+
pub extern "kernel32" fn TerminateProcess(hProcess: std.os.windows.HANDLE, uExitCode: u32) callconv(.winapi) i32;
|
|
142
|
+
pub extern "kernel32" fn CloseHandle(hObject: std.os.windows.HANDLE) callconv(.winapi) i32;
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
var si = STARTUPINFOW{};
|
|
146
|
+
var pi: PROCESS_INFORMATION = undefined;
|
|
147
|
+
|
|
148
|
+
const res = WinApi.CreateProcessW(null, @ptrCast(&cmd_utf16), null, null, 0, 0, null, null, &si, &pi);
|
|
149
|
+
if (res == 0) return error.ProcessCreationFailed;
|
|
150
|
+
|
|
151
|
+
return ChildProcess{
|
|
152
|
+
.hProcess = pi.hProcess,
|
|
153
|
+
.hThread = pi.hThread,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
fn getAppModTime(self: *DevServer) i128 {
|
|
158
|
+
_ = self;
|
|
159
|
+
const io = std.Io.Threaded.global_single_threaded.io();
|
|
160
|
+
const cwd = std.Io.Dir.cwd();
|
|
161
|
+
var max_mtime: i128 = 0;
|
|
162
|
+
|
|
163
|
+
var app_dir = cwd.openDir(io, "app", .{ .iterate = true }) catch return 0;
|
|
164
|
+
defer app_dir.close(io);
|
|
165
|
+
|
|
166
|
+
var iter = app_dir.iterate();
|
|
167
|
+
while (iter.next(io) catch null) |entry| {
|
|
168
|
+
if (entry.kind == .file) {
|
|
169
|
+
if (app_dir.statFile(io, entry.name, .{})) |st| {
|
|
170
|
+
const ns = st.mtime.toNanoseconds();
|
|
171
|
+
if (ns > max_mtime) max_mtime = ns;
|
|
172
|
+
} else |_| {}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return max_mtime;
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
pub const ChildProcess = struct {
|
|
180
|
+
hProcess: std.os.windows.HANDLE,
|
|
181
|
+
hThread: std.os.windows.HANDLE,
|
|
182
|
+
|
|
183
|
+
pub fn kill(self: *ChildProcess) void {
|
|
184
|
+
const WinApi = struct {
|
|
185
|
+
pub extern "kernel32" fn TerminateProcess(hProcess: std.os.windows.HANDLE, uExitCode: u32) callconv(.winapi) i32;
|
|
186
|
+
pub extern "kernel32" fn CloseHandle(hObject: std.os.windows.HANDLE) callconv(.winapi) i32;
|
|
187
|
+
};
|
|
188
|
+
_ = WinApi.TerminateProcess(self.hProcess, 1);
|
|
189
|
+
_ = WinApi.CloseHandle(self.hProcess);
|
|
190
|
+
_ = WinApi.CloseHandle(self.hThread);
|
|
20
191
|
}
|
|
21
192
|
};
|
package/cli/installer.zig
CHANGED
|
@@ -185,25 +185,35 @@ pub fn generateIss(allocator: std.mem.Allocator, config: AppConfig) ![]const u8
|
|
|
185
185
|
\\Name: "desktopicon"; Description: "{{cm:CreateDesktopIcon}}"; GroupDescription: "{{cm:AdditionalIcons}}"; Flags: unchecked
|
|
186
186
|
\\
|
|
187
187
|
\\[Files]
|
|
188
|
-
\\; Main executable
|
|
189
|
-
\\Source: "
|
|
188
|
+
\\; Main executable (searches dist, root, zig-out\bin, and bin)
|
|
189
|
+
\\Source: "dist\{{#AppName}}.exe"; DestDir: "{{app}}"; DestName: "{{#AppExeName}}"; Flags: ignoreversion skipifsourcedoesntexist
|
|
190
|
+
\\Source: "example_app.exe"; DestDir: "{{app}}"; DestName: "{{#AppExeName}}"; Flags: ignoreversion skipifsourcedoesntexist
|
|
191
|
+
\\Source: "zig-out\bin\example_app.exe"; DestDir: "{{app}}"; DestName: "{{#AppExeName}}"; Flags: ignoreversion skipifsourcedoesntexist
|
|
192
|
+
\\Source: "bin\{{#AppExeName}}"; DestDir: "{{app}}"; Flags: ignoreversion skipifsourcedoesntexist
|
|
190
193
|
\\
|
|
191
|
-
\\; NanoShell Runtime DLLs & Portable C++ Runtimes
|
|
192
|
-
\\Source: "
|
|
193
|
-
\\Source: "
|
|
194
|
-
\\Source: "
|
|
195
|
-
\\Source: "
|
|
196
|
-
\\Source: "
|
|
197
|
-
\\Source: "
|
|
198
|
-
\\Source: "
|
|
194
|
+
\\; NanoShell Runtime DLLs & Portable C++ Runtimes (vendor\lib and bin)
|
|
195
|
+
\\Source: "vendor\lib\AppCore.dll"; DestDir: "{{app}}"; Flags: ignoreversion skipifsourcedoesntexist
|
|
196
|
+
\\Source: "vendor\lib\Ultralight.dll"; DestDir: "{{app}}"; Flags: ignoreversion skipifsourcedoesntexist
|
|
197
|
+
\\Source: "vendor\lib\UltralightCore.dll"; DestDir: "{{app}}"; Flags: ignoreversion skipifsourcedoesntexist
|
|
198
|
+
\\Source: "vendor\lib\WebCore.dll"; DestDir: "{{app}}"; Flags: ignoreversion skipifsourcedoesntexist
|
|
199
|
+
\\Source: "vendor\lib\icudt67l.dat"; DestDir: "{{app}}"; Flags: ignoreversion skipifsourcedoesntexist
|
|
200
|
+
\\Source: "vendor\lib\cacert.pem"; DestDir: "{{app}}"; Flags: ignoreversion skipifsourcedoesntexist
|
|
201
|
+
\\Source: "vendor\lib\resources\*"; DestDir: "{{app}}\resources"; Flags: ignoreversion recursesubdirs createallsubdirs skipifsourcedoesntexist
|
|
199
202
|
\\
|
|
200
|
-
|
|
201
|
-
\\Source: "bin\
|
|
202
|
-
\\Source: "bin\
|
|
203
|
-
\\Source: "bin\
|
|
203
|
+
\\Source: "bin\AppCore.dll"; DestDir: "{{app}}"; Flags: ignoreversion skipifsourcedoesntexist
|
|
204
|
+
\\Source: "bin\Ultralight.dll"; DestDir: "{{app}}"; Flags: ignoreversion skipifsourcedoesntexist
|
|
205
|
+
\\Source: "bin\UltralightCore.dll"; DestDir: "{{app}}"; Flags: ignoreversion skipifsourcedoesntexist
|
|
206
|
+
\\Source: "bin\WebCore.dll"; DestDir: "{{app}}"; Flags: ignoreversion skipifsourcedoesntexist
|
|
207
|
+
\\Source: "bin\vcruntime140.dll"; DestDir: "{{app}}"; Flags: ignoreversion skipifsourcedoesntexist
|
|
208
|
+
\\Source: "bin\msvcp140.dll"; DestDir: "{{app}}"; Flags: ignoreversion skipifsourcedoesntexist
|
|
209
|
+
\\Source: "bin\vcruntime140_1.dll"; DestDir: "{{app}}"; Flags: ignoreversion skipifsourcedoesntexist
|
|
210
|
+
\\Source: "bin\icudt67l.dat"; DestDir: "{{app}}"; Flags: ignoreversion skipifsourcedoesntexist
|
|
211
|
+
\\Source: "bin\cacert.pem"; DestDir: "{{app}}"; Flags: ignoreversion skipifsourcedoesntexist
|
|
212
|
+
\\Source: "bin\resources\*"; DestDir: "{{app}}\resources"; Flags: ignoreversion recursesubdirs createallsubdirs skipifsourcedoesntexist
|
|
204
213
|
\\
|
|
205
214
|
\\; App HTML / CSS / JS assets
|
|
206
|
-
\\Source: "
|
|
215
|
+
\\Source: "example_app\*"; DestDir: "{{app}}\app"; Flags: ignoreversion recursesubdirs createallsubdirs skipifsourcedoesntexist
|
|
216
|
+
\\Source: "app\*"; DestDir: "{{app}}\app"; Flags: ignoreversion recursesubdirs createallsubdirs skipifsourcedoesntexist
|
|
207
217
|
\\
|
|
208
218
|
\\[Icons]
|
|
209
219
|
\\Name: "{{group}}\\{{#AppName}}"; Filename: "{{app}}\\{{#AppExeName}}"; WorkingDir: "{{app}}"
|
package/cli/main.zig
CHANGED
|
@@ -129,12 +129,12 @@ pub fn main() !void {
|
|
|
129
129
|
\\</head>
|
|
130
130
|
\\<body>
|
|
131
131
|
\\ <div class="container">
|
|
132
|
-
\\ <div class="fps-badge"><span id="fps-val">
|
|
132
|
+
\\ <div class="fps-badge"><span id="fps-val">120</span> FPS Locked</div>
|
|
133
133
|
\\ <h1>⚡ NanoShell Desktop App</h1>
|
|
134
134
|
\\ <p>Ultra-lightweight WebKit desktop runtime</p>
|
|
135
135
|
\\ <button id="btn">Click Me</button>
|
|
136
136
|
\\ </div>
|
|
137
|
-
\\
|
|
137
|
+
\\ <!-- app.js is injected inline by the NanoShell engine at load time -->
|
|
138
138
|
\\</body>
|
|
139
139
|
\\</html>
|
|
140
140
|
;
|
|
@@ -184,26 +184,15 @@ pub fn main() !void {
|
|
|
184
184
|
try app_dir.writeFile(io, .{ .sub_path = "app/styles.css", .data = css_content });
|
|
185
185
|
|
|
186
186
|
const js_content =
|
|
187
|
-
|
|
188
|
-
\\
|
|
189
|
-
\\function calcFps() {
|
|
190
|
-
\\ const now = performance.now();
|
|
191
|
-
\\ frames++;
|
|
192
|
-
\\ const delta = now - lastTime;
|
|
193
|
-
\\ if (delta >= 500) {
|
|
194
|
-
\\ const fps = Math.round((frames * 1000) / delta);
|
|
195
|
-
\\ const el = document.getElementById('fps-val');
|
|
196
|
-
\\ if (el) el.innerText = fps;
|
|
197
|
-
\\ frames = 0;
|
|
198
|
-
\\ lastTime = now;
|
|
199
|
-
\\ }
|
|
200
|
-
\\ requestAnimationFrame(calcFps);
|
|
201
|
-
\\}
|
|
202
|
-
\\requestAnimationFrame(calcFps);
|
|
187
|
+
\\// NanoShell Application JavaScript (0% Idle CPU Overhead)
|
|
188
|
+
\\console.log('⚡ [NanoShell] App loaded successfully');
|
|
203
189
|
\\
|
|
204
|
-
\\document.getElementById('btn')
|
|
205
|
-
\\
|
|
206
|
-
\\
|
|
190
|
+
\\const btn = document.getElementById('btn');
|
|
191
|
+
\\if (btn) {
|
|
192
|
+
\\ btn.addEventListener('click', () => {
|
|
193
|
+
\\ alert('Hello from NanoShell!');
|
|
194
|
+
\\ });
|
|
195
|
+
\\}
|
|
207
196
|
;
|
|
208
197
|
try app_dir.writeFile(io, .{ .sub_path = "app/app.js", .data = js_content });
|
|
209
198
|
|