nanoshell 1.7.3 → 1.7.5

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/SKILL.md DELETED
@@ -1,118 +0,0 @@
1
- ---
2
- name: nanoshell-desktop-framework
3
- description: Build ultra-fast, 120 FPS, 17MB RAM native desktop applications using HTML, CSS, JS, and Zig with the NanoShell runtime engine (nanoshell) created by Suman Biswas.
4
- ---
5
-
6
- # NanoShell Native Desktop Framework Skill Guide (`nanoshell`)
7
-
8
- **Created & Engineered by Suman Biswas**
9
-
10
- NanoShell is a hyper-lightweight, browser-free native desktop application framework created by Suman Biswas that compiles to a single 3MB executable, boots in < 0.8ms, runs at 120 FPS, and consumes only **17.0 MB RAM**.
11
-
12
- ---
13
-
14
- ## Starter Template Scaffolding Command
15
-
16
- AI agents or developers can scaffold a new application using:
17
-
18
- ```bash
19
- npx nanoshell <app-name> # Scaffold new app template
20
- npx nanoshell start # Launch dev engine
21
- npx nanoshell build # Build production release binary only
22
- npx nanoshell package # Build binary and generate Windows Inno Setup installer (.exe)
23
- ```
24
-
25
- `npx nanoshell package` generates a Windows Setup `.exe` with a installer dialog allowing end-users to choose:
26
- - **Per-User Install** (No admin rights required, installs to `%LocalAppData%`)
27
- - **System-Wide Install** (Requires admin rights, installs to `Program Files`)
28
-
29
-
30
- ---
31
-
32
- ## Developer Workflow Guide
33
-
34
- ### 1. Project Directory Structure
35
-
36
- ```text
37
- my_nanoshell_app/
38
- ├── assets/
39
- │ ├── index.html <-- Standard HTML markup
40
- │ ├── styles.css <-- Standard CSS (Flexbox, Grid, Glassmorphism)
41
- │ └── app.js <-- Application JavaScript logic
42
- ├── resources/
43
- │ ├── icudt67l.dat <-- Unicode ICU data
44
- │ └── cacert.pem <-- SSL certificates
45
- ├── build.zig <-- Zig build script
46
- ├── src/
47
- │ └── main.zig <-- NanoShell host runner
48
- └── bin/
49
- └── my_app.exe <-- Compiled self-contained executable
50
- ```
51
-
52
- ---
53
-
54
- ## 2. Developer HTML & CSS Best Practices
55
-
56
- Developers write standard HTML, CSS, and JS. NanoShell automatically polyfills and optimizes layout rendering at runtime.
57
-
58
- ### `assets/index.html`
59
- ```html
60
- <!DOCTYPE html>
61
- <html lang="en">
62
- <head>
63
- <meta charset="UTF-8">
64
- <title>My NanoShell Native App</title>
65
- <link rel="stylesheet" href="styles.css">
66
- </head>
67
- <body>
68
- <main class="content-area">
69
- <header class="glass-card">
70
- <h1>My NanoShell App</h1>
71
- </header>
72
- </main>
73
- <script src="app.js"></script>
74
- </body>
75
- </html>
76
- ```
77
-
78
- ### `assets/styles.css`
79
- ```css
80
- * {
81
- box-sizing: border-box;
82
- margin: 0;
83
- padding: 0;
84
- font-family: 'Segoe UI', sans-serif;
85
- }
86
-
87
- body {
88
- background: #0b0d19;
89
- color: #ffffff;
90
- height: 100vh;
91
- overflow: hidden;
92
- }
93
-
94
- .content-area {
95
- display: flex;
96
- flex-direction: column;
97
- gap: 24px; /* NanoShell engine auto-polyfills flex gap for WebKit */
98
- padding: 32px;
99
- }
100
-
101
- .glass-card {
102
- background: rgba(30, 41, 59, 0.45);
103
- border: 1px solid rgba(255, 255, 255, 0.1);
104
- border-radius: 16px;
105
- padding: 24px;
106
- }
107
- ```
108
-
109
- ---
110
-
111
- ## 3. How AI Agents Use This Framework
112
-
113
- Any AI agent (Antigravity, Claude, Copilot, ChatGPT) can use this framework by following these rules:
114
-
115
- 1. **Scaffold Web Assets in `assets/`**: Put all developer HTML, CSS, and JS in `assets/`. Never hardcode UI elements inside C or Zig source files.
116
- 2. **Standard CSS Rules**: Write standard CSS with `gap`, `display: flex`, and `display: grid`. NanoShell's engine pre-parser handles WebKit layout rules automatically.
117
- 3. **Build via Zig**: Execute `zig build` to bundle DLLs (`Ultralight.dll`, `AppCore.dll`) and deploy assets into `zig-out/bin/`.
118
- 4. **Low Memory Overhead**: NanoShell automatically purges memory caches via `ulPurgeMemory()` and `EmptyWorkingSet()` to maintain 17MB RAM footprint.
package/cli/bundler.zig DELETED
@@ -1,128 +0,0 @@
1
- const std = @import("std");
2
-
3
- pub const BundlerConfig = struct {
4
- app_name: []const u8 = "ZeroApp",
5
- entry_point: []const u8 = "assets/index.html",
6
- output_dir: []const u8 = "dist",
7
- };
8
-
9
- pub const SingleBinaryBundler = struct {
10
- allocator: std.mem.Allocator,
11
- config: BundlerConfig,
12
-
13
- pub fn init(allocator: std.mem.Allocator, config: BundlerConfig) SingleBinaryBundler {
14
- return .{
15
- .allocator = allocator,
16
- .config = config,
17
- };
18
- }
19
-
20
- pub fn bundle(self: *SingleBinaryBundler) !void {
21
- std.log.info("Starting ZeroUI Native App Bundler for '{s}'...", .{self.config.app_name});
22
- std.log.info("Packaging Assets -> {s}", .{self.config.entry_point});
23
-
24
- const io = std.Io.Threaded.global_single_threaded.io();
25
- _ = std.Io.Dir.cwd().createDir(io, self.config.output_dir, .default_dir) catch {};
26
-
27
- var dist_dir = std.Io.Dir.cwd().openDir(io, self.config.output_dir, .{}) catch |err| {
28
- std.log.err("Failed to open output dir: {s}", .{@errorName(err)});
29
- return err;
30
- };
31
-
32
- _ = dist_dir.createDir(io, "app", .default_dir) catch {};
33
- _ = dist_dir.createDir(io, "resources", .default_dir) catch {};
34
-
35
- const cwd = std.Io.Dir.cwd();
36
-
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);
47
- const dst_exe_path = try std.fmt.allocPrint(self.allocator, "{s}/{s}.exe", .{ self.config.output_dir, self.config.app_name });
48
- defer self.allocator.free(dst_exe_path);
49
-
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
- }
60
-
61
- // Copy DLLs to dist/ (search vendor/lib, zig-out/bin, and root)
62
- const dlls = [_][]const u8{ "AppCore.dll", "Ultralight.dll", "UltralightCore.dll", "WebCore.dll", "vcruntime140.dll", "msvcp140.dll", "vcruntime140_1.dll" };
63
- for (dlls) |dll| {
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
- }
80
- }
81
-
82
- // Copy Resource Files to dist/ and dist/resources/
83
- const res_files = [_][]const u8{ "icudt67l.dat", "cacert.pem" };
84
- for (res_files) |file| {
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
- }
104
- }
105
-
106
- // Copy app/* files to dist/app/*
107
- var app_dir_opt = cwd.openDir(io, "app", .{ .iterate = true }) catch null;
108
- if (app_dir_opt) |*app_dir| {
109
- var dist_app_dir = dist_dir.openDir(io, "app", .{}) catch null;
110
- if (dist_app_dir) |*dst_app| {
111
- var iter = app_dir.iterate();
112
- while (iter.next(io) catch null) |entry| {
113
- if (entry.kind == .file) {
114
- _ = app_dir.copyFile(entry.name, dst_app.*, entry.name, io, .{}) catch {};
115
- }
116
- }
117
- }
118
- }
119
-
120
- std.log.info("Built Custom Named Executable -> '{s}' (Self-contained release package)", .{dst_exe_path});
121
- }
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
- }
@@ -1,192 +0,0 @@
1
- const std = @import("std");
2
- const installer = @import("installer.zig");
3
-
4
- pub const DevServer = struct {
5
- allocator: std.mem.Allocator,
6
-
7
- pub fn init(allocator: std.mem.Allocator) DevServer {
8
- return .{ .allocator = allocator };
9
- }
10
-
11
- pub fn runDevMode(self: *DevServer, app_dir_name: []const u8) !void {
12
- _ = app_dir_name;
13
- std.log.info("=================================================================", .{});
14
- std.log.info(" ⚡ NanoShell Dev Server Started", .{});
15
- std.log.info(" Watching app/ directory & nanoshell.json for live changes...", .{});
16
- std.log.info("=================================================================", .{});
17
-
18
- var config = installer.loadConfig(self.allocator) catch installer.AppConfig{};
19
- defer config.deinit(self.allocator);
20
-
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);
191
- }
192
- };