janela 0.9.0 → 0.10.1
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 +19 -1
- package/bin/janela.mjs +262 -12
- package/package.json +4 -3
- package/runtime/ios.ts +483 -0
- package/shim/ios/app.cc +330 -0
- package/templates/janela.conf.json +5 -0
- package/templates/react/files/janela.conf.json +5 -0
- package/templates/solid/files/janela.conf.json +5 -0
- package/templates/svelte/files/janela.conf.json +5 -0
- package/templates/vue/files/janela.conf.json +5 -0
- package/vendor-webview/core/include/webview/backends.hh +1 -0
- package/vendor-webview/core/include/webview/detail/backends/cocoa_webkit.hh +4 -1
- package/vendor-webview/core/include/webview/detail/backends/uikit_webkit.hh +460 -0
- package/vendor-webview/core/include/webview/detail/platform/darwin/cocoa/NSBundle.hh +4 -2
- package/vendor-webview/core/include/webview/detail/platform/darwin/cocoa/NSInvocation.hh +4 -2
- package/vendor-webview/core/include/webview/detail/platform/darwin/cocoa/NSMethodSignature.hh +4 -2
- package/vendor-webview/core/include/webview/detail/platform/darwin/cocoa/NSNotification.hh +4 -2
- package/vendor-webview/core/include/webview/detail/platform/darwin/cocoa/NSNumber.hh +4 -2
- package/vendor-webview/core/include/webview/detail/platform/darwin/cocoa/NSObject.hh +4 -2
- package/vendor-webview/core/include/webview/detail/platform/darwin/cocoa/NSPoint.hh +4 -2
- package/vendor-webview/core/include/webview/detail/platform/darwin/cocoa/NSRect.hh +4 -2
- package/vendor-webview/core/include/webview/detail/platform/darwin/cocoa/NSSize.hh +4 -2
- package/vendor-webview/core/include/webview/detail/platform/darwin/cocoa/NSString.hh +4 -2
- package/vendor-webview/core/include/webview/detail/platform/darwin/cocoa/NSURL.hh +4 -2
- package/vendor-webview/core/include/webview/detail/platform/darwin/cocoa/NSURLRequest.hh +4 -2
- package/vendor-webview/core/include/webview/detail/platform/darwin/cocoa/NSValue.hh +4 -2
- package/vendor-webview/core/include/webview/detail/platform/darwin/cocoa/types.hh +4 -2
- package/vendor-webview/core/include/webview/detail/platform/darwin/objc/Class.hh +4 -1
- package/vendor-webview/core/include/webview/detail/platform/darwin/objc/autoreleasepool.hh +4 -1
- package/vendor-webview/core/include/webview/detail/platform/darwin/objc/invoke.hh +4 -1
- package/vendor-webview/core/include/webview/detail/platform/darwin/objc/memory.hh +4 -1
- package/vendor-webview/core/include/webview/detail/platform/darwin/objc/objc.hh +4 -1
- package/vendor-webview/core/include/webview/detail/platform/darwin/uikit/UIApplication.hh +80 -0
- package/vendor-webview/core/include/webview/detail/platform/darwin/uikit/UIScreen.hh +57 -0
- package/vendor-webview/core/include/webview/detail/platform/darwin/uikit/UIView.hh +79 -0
- package/vendor-webview/core/include/webview/detail/platform/darwin/uikit/UIViewController.hh +70 -0
- package/vendor-webview/core/include/webview/detail/platform/darwin/uikit/UIWindow.hh +67 -0
- package/vendor-webview/core/include/webview/detail/platform/darwin/uikit/uikit.hh +43 -0
- package/vendor-webview/core/include/webview/detail/platform/darwin/webkit/WKOpenPanelParameters.hh +4 -2
- package/vendor-webview/core/include/webview/detail/platform/darwin/webkit/WKScriptMessage.hh +4 -2
- package/vendor-webview/core/include/webview/detail/platform/darwin/webkit/WKUserContentController.hh +4 -2
- package/vendor-webview/core/include/webview/detail/platform/darwin/webkit/WKUserScript.hh +4 -2
- package/vendor-webview/core/include/webview/detail/platform/darwin/webkit/WKWebView.hh +4 -2
- package/vendor-webview/core/include/webview/detail/platform/darwin/webkit/WKWebViewConfiguration.hh +4 -2
- package/vendor-webview/core/include/webview/detail/platform/darwin/webkit/webkit.hh +4 -2
- package/vendor-webview/core/include/webview/macros.h +12 -2
package/README.md
CHANGED
|
@@ -542,10 +542,28 @@ no `-framework` support) and the binary is wrapped into an ad-hoc-signed
|
|
|
542
542
|
- `console.log` from commands goes to stdout — visible under `janela dev`,
|
|
543
543
|
not when launched from Finder.
|
|
544
544
|
|
|
545
|
+
## iOS
|
|
546
|
+
|
|
547
|
+
An iOS build runs the same app from the same source — same `main.ts`, same
|
|
548
|
+
contract, same frontend:
|
|
549
|
+
|
|
550
|
+
```bash
|
|
551
|
+
janela build --target ios # -> .janela/out-ios/<name>.app (simulator)
|
|
552
|
+
janela dev --target ios # build, boot a simulator, install, launch
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
It is **not part of a release yet** and is simulator-only. Commands, the typed
|
|
556
|
+
contract, events, Vite frontends, async commands (`commandAsync`, `defer`,
|
|
557
|
+
`sleep`) and file I/O all work the same as on desktop — the shell owns the
|
|
558
|
+
clock and the file queue on both. File dialogs are not on iOS yet and report
|
|
559
|
+
clearly when called; window control is a no-op there by nature. See
|
|
560
|
+
[docs/ios.md](../../docs/ios.md).
|
|
561
|
+
|
|
545
562
|
## Status
|
|
546
563
|
|
|
547
564
|
Early proof of concept, on macOS (arm64), Linux (WebKitGTK) and Windows
|
|
548
|
-
(WebView2). The design notes and scriptc
|
|
565
|
+
(WebView2), with iOS on a branch (above). The design notes and scriptc
|
|
566
|
+
findings behind it are in
|
|
549
567
|
[docs/findings.md](../../docs/findings.md). Not yet: async commands that run in
|
|
550
568
|
parallel (host code is single-threaded; `commandAsync` interleaves instead),
|
|
551
569
|
tray icons and menus, multi-window, directory picking on Windows,
|
package/bin/janela.mjs
CHANGED
|
@@ -263,8 +263,215 @@ function webview2Include(cacheDir) {
|
|
|
263
263
|
return incDir;
|
|
264
264
|
}
|
|
265
265
|
|
|
266
|
+
// ---- iOS ------------------------------------------------------------------
|
|
267
|
+
//
|
|
268
|
+
// The second build lane. Desktop compiles TypeScript to an EXECUTABLE that
|
|
269
|
+
// drives a C library over FFI; iOS compiles it to a LIBRARY that a UIKit shell
|
|
270
|
+
// drives. scriptc refuses executables for iOS targets, so the inversion is not
|
|
271
|
+
// a preference — and library mode links no event loop (SC4005), which is why
|
|
272
|
+
// the async surface is desktop-only there.
|
|
273
|
+
//
|
|
274
|
+
// Everything below is simulator-only: a device build additionally needs a
|
|
275
|
+
// signing identity and a provisioning profile, which is its own project.
|
|
276
|
+
|
|
277
|
+
const IOS_MIN_VERSION = "15.0";
|
|
278
|
+
const IOS_PREFIX = "jl_";
|
|
279
|
+
|
|
280
|
+
function iosDeviceOrFail(name) {
|
|
281
|
+
const json = capture(["xcrun", "simctl", "list", "devices", "available", "--json"]);
|
|
282
|
+
const devices = JSON.parse(json).devices ?? {};
|
|
283
|
+
const runtimes = Object.keys(devices).filter((k) => /iOS/i.test(k));
|
|
284
|
+
if (runtimes.length === 0) {
|
|
285
|
+
fail(
|
|
286
|
+
"no iOS simulator runtime is installed — open Xcode > Settings > Components " +
|
|
287
|
+
"and get an iOS simulator, or run `xcodebuild -downloadPlatform iOS` in a terminal " +
|
|
288
|
+
"(it needs admin rights, so it cannot run unattended)",
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
const all = runtimes.flatMap((k) => devices[k]);
|
|
292
|
+
const pick = name
|
|
293
|
+
? all.find((d) => d.name === name)
|
|
294
|
+
: all.find((d) => /^iPhone/.test(d.name)) ?? all[0];
|
|
295
|
+
if (!pick) fail(`no simulator named '${name}' — see \`xcrun simctl list devices\``);
|
|
296
|
+
return pick;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function iosConf(conf) {
|
|
300
|
+
const ios = conf.ios ?? {};
|
|
301
|
+
return {
|
|
302
|
+
identifier: ios.identifier ?? conf.identifier,
|
|
303
|
+
displayName: ios.displayName ?? conf.window?.title ?? conf.name,
|
|
304
|
+
minimumVersion: String(ios.minimumVersion ?? IOS_MIN_VERSION),
|
|
305
|
+
device: ios.device ?? null,
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// The library profile: one export carries every command, so a project's own
|
|
310
|
+
// commands need no ABI of their own. `janelaEmit` is the reverse channel the
|
|
311
|
+
// shell registers before init.
|
|
312
|
+
function iosProfile() {
|
|
313
|
+
return {
|
|
314
|
+
profile_format: 1,
|
|
315
|
+
name: "janela",
|
|
316
|
+
entry: "./entry.ts",
|
|
317
|
+
emission: "llvm",
|
|
318
|
+
abi: {
|
|
319
|
+
prefix: IOS_PREFIX,
|
|
320
|
+
init_symbol: `${IOS_PREFIX}init`,
|
|
321
|
+
sink_register_symbol: `${IOS_PREFIX}set_panic_sink`,
|
|
322
|
+
collect_symbol: `${IOS_PREFIX}collect`,
|
|
323
|
+
result_reset_symbol: `${IOS_PREFIX}reset`,
|
|
324
|
+
callback_register_symbol: `${IOS_PREFIX}set_callback`,
|
|
325
|
+
},
|
|
326
|
+
exports: [
|
|
327
|
+
{
|
|
328
|
+
export: "handleInvoke",
|
|
329
|
+
symbol: `${IOS_PREFIX}handle_invoke`,
|
|
330
|
+
params: ["string", "string"],
|
|
331
|
+
returns: "string",
|
|
332
|
+
},
|
|
333
|
+
{
|
|
334
|
+
export: "indexHtml",
|
|
335
|
+
symbol: `${IOS_PREFIX}index_html`,
|
|
336
|
+
params: [],
|
|
337
|
+
returns: "string",
|
|
338
|
+
},
|
|
339
|
+
// The shell calls these back on the main queue when work it owns comes
|
|
340
|
+
// due. They are the mirror of the desktop shim's wv_on_timer: the
|
|
341
|
+
// library parks a continuation under an id and is re-entered with it.
|
|
342
|
+
{
|
|
343
|
+
export: "onTimer",
|
|
344
|
+
symbol: `${IOS_PREFIX}on_timer`,
|
|
345
|
+
params: ["f64"],
|
|
346
|
+
returns: "void",
|
|
347
|
+
},
|
|
348
|
+
{
|
|
349
|
+
export: "onFsDone",
|
|
350
|
+
symbol: `${IOS_PREFIX}on_fs_done`,
|
|
351
|
+
params: ["f64", "bool", "string"],
|
|
352
|
+
returns: "void",
|
|
353
|
+
},
|
|
354
|
+
],
|
|
355
|
+
// TS -> shell. A channel handler must never re-enter the library (see
|
|
356
|
+
// upstream #263: violations silently appear to work), so every one of
|
|
357
|
+
// these only records the request; the shell acts on its own queue and
|
|
358
|
+
// re-enters through an export above on a later turn.
|
|
359
|
+
callbacks: [
|
|
360
|
+
{ name: "janelaEmit", params: ["string", "string"], returns: "void" },
|
|
361
|
+
{ name: "hostSchedule", params: ["f64", "f64"], returns: "void" },
|
|
362
|
+
{ name: "hostSettle", params: ["f64", "string"], returns: "void" },
|
|
363
|
+
{ name: "hostReadFile", params: ["f64", "string"], returns: "void" },
|
|
364
|
+
{ name: "hostWriteFile", params: ["f64", "string", "string"], returns: "void" },
|
|
365
|
+
],
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
function iosPlist(conf) {
|
|
370
|
+
const ios = iosConf(conf);
|
|
371
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
372
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
373
|
+
<plist version="1.0">
|
|
374
|
+
<dict>
|
|
375
|
+
<key>CFBundleName</key><string>${conf.name}</string>
|
|
376
|
+
<key>CFBundleDisplayName</key><string>${ios.displayName}</string>
|
|
377
|
+
<key>CFBundleIdentifier</key><string>${ios.identifier}</string>
|
|
378
|
+
<key>CFBundleExecutable</key><string>${conf.name}</string>
|
|
379
|
+
<key>CFBundlePackageType</key><string>APPL</string>
|
|
380
|
+
<key>CFBundleVersion</key><string>${conf.version ?? "0.1.0"}</string>
|
|
381
|
+
<key>CFBundleShortVersionString</key><string>${conf.version ?? "0.1.0"}</string>
|
|
382
|
+
<key>LSRequiresIPhoneOS</key><true/>
|
|
383
|
+
<key>UILaunchScreen</key><dict/>
|
|
384
|
+
<key>MinimumOSVersion</key><string>${ios.minimumVersion}</string>
|
|
385
|
+
<key>CFBundleSupportedPlatforms</key><array><string>iPhoneSimulator</string></array>
|
|
386
|
+
</dict>
|
|
387
|
+
</plist>
|
|
388
|
+
`;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
function buildIos(root, conf, buildDir, outDir) {
|
|
392
|
+
if (process.platform !== "darwin") {
|
|
393
|
+
fail("iOS builds need macOS with Xcode — `--target ios` is only available there");
|
|
394
|
+
}
|
|
395
|
+
if (!which("zig")) {
|
|
396
|
+
fail(
|
|
397
|
+
"iOS builds need zig on PATH: scriptc routes mobile targets through `zig cc`. " +
|
|
398
|
+
"Install it with `brew install zig`",
|
|
399
|
+
);
|
|
400
|
+
}
|
|
401
|
+
const ios = iosConf(conf);
|
|
402
|
+
|
|
403
|
+
writeFileSync(join(buildDir, "profile.json"), JSON.stringify(iosProfile(), null, 2) + "\n");
|
|
404
|
+
|
|
405
|
+
console.log("janela: compiling TypeScript to an iOS library");
|
|
406
|
+
run(["node", scriptcBin(), "build", "--lib", "--profile", "profile.json"], {
|
|
407
|
+
cwd: buildDir,
|
|
408
|
+
env: {
|
|
409
|
+
...process.env,
|
|
410
|
+
SCRIPTC_CC: "zigcc",
|
|
411
|
+
SCRIPTC_TARGET: "aarch64-apple-ios-simulator",
|
|
412
|
+
},
|
|
413
|
+
});
|
|
414
|
+
const lib = join(buildDir, ".scriptc", "entry.lib.a");
|
|
415
|
+
if (!existsSync(lib)) fail(`scriptc produced no library at ${lib}`);
|
|
416
|
+
|
|
417
|
+
console.log("janela: compiling the UIKit shell");
|
|
418
|
+
const bundle = join(outDir, `${conf.name}.app`);
|
|
419
|
+
mkdirSync(bundle, { recursive: true });
|
|
420
|
+
const sdk = capture(["xcrun", "--sdk", "iphonesimulator", "--show-sdk-path"]);
|
|
421
|
+
// Plain C++: the shell drives the webview through webview.h's UIKit
|
|
422
|
+
// backend, which uses the Objective-C runtime rather than Objective-C, so
|
|
423
|
+
// no .mm and no -fobjc-arc. Blocks still need -fblocks for libdispatch.
|
|
424
|
+
run([
|
|
425
|
+
"xcrun", "clang++",
|
|
426
|
+
join(KIT, "shim", "ios", "app.cc"),
|
|
427
|
+
"-target", `arm64-apple-ios${ios.minimumVersion}-simulator`,
|
|
428
|
+
"-isysroot", sdk,
|
|
429
|
+
"-std=c++17", "-O2", "-fblocks",
|
|
430
|
+
`-I${join(KIT, "vendor-webview", "core", "include")}`,
|
|
431
|
+
"-framework", "UIKit", "-framework", "WebKit", "-framework", "Foundation",
|
|
432
|
+
lib,
|
|
433
|
+
"-o", join(bundle, conf.name),
|
|
434
|
+
]);
|
|
435
|
+
run(["strip", join(bundle, conf.name)]);
|
|
436
|
+
writeFileSync(join(bundle, "Info.plist"), iosPlist(conf));
|
|
437
|
+
|
|
438
|
+
console.log(
|
|
439
|
+
`janela: built ${relative(root, bundle)} ` +
|
|
440
|
+
`(${statSync(join(bundle, conf.name)).size} bytes, iOS Simulator)`,
|
|
441
|
+
);
|
|
442
|
+
return bundle;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
async function devIos(root) {
|
|
446
|
+
const conf = loadConf(root);
|
|
447
|
+
const device = iosDeviceOrFail(iosConf(conf).device);
|
|
448
|
+
const bundle = build(root, { target: "ios" });
|
|
449
|
+
|
|
450
|
+
if (device.state !== "Booted") {
|
|
451
|
+
console.log(`janela: booting ${device.name}`);
|
|
452
|
+
spawnSync("xcrun", ["simctl", "boot", device.udid]);
|
|
453
|
+
spawnSync("xcrun", ["simctl", "bootstatus", device.udid, "-b"]);
|
|
454
|
+
}
|
|
455
|
+
spawnSync("open", ["-a", "Simulator"]);
|
|
456
|
+
|
|
457
|
+
console.log(`janela: installing on ${device.name}`);
|
|
458
|
+
run(["xcrun", "simctl", "install", device.udid, bundle]);
|
|
459
|
+
console.log("janela: launching (Ctrl-C to stop following the log)");
|
|
460
|
+
run([
|
|
461
|
+
"xcrun", "simctl", "launch", "--console-pty",
|
|
462
|
+
device.udid, iosConf(conf).identifier,
|
|
463
|
+
]);
|
|
464
|
+
}
|
|
465
|
+
|
|
266
466
|
// ---- shim ----------------------------------------------------------------
|
|
267
467
|
|
|
468
|
+
function which(bin) {
|
|
469
|
+
const r = spawnSync(process.platform === "win32" ? "where" : "which", [bin], {
|
|
470
|
+
encoding: "utf8",
|
|
471
|
+
});
|
|
472
|
+
return r.status === 0 ? r.stdout.trim().split("\n")[0] : null;
|
|
473
|
+
}
|
|
474
|
+
|
|
268
475
|
function buildShim(cacheDir) {
|
|
269
476
|
const src = join(KIT, "shim", "wvshim.cc");
|
|
270
477
|
const win = process.platform === "win32";
|
|
@@ -470,17 +677,23 @@ function makeGuiSubsystem(exePath) {
|
|
|
470
677
|
|
|
471
678
|
// `devUrl` points the window at a running vite server instead of inlining the
|
|
472
679
|
// frontend; `gui` asks for a GUI-subsystem .exe on Windows (build, not dev).
|
|
473
|
-
function build(root, { devUrl = null, gui = true } = {}) {
|
|
680
|
+
function build(root, { devUrl = null, gui = true, target = "desktop" } = {}) {
|
|
474
681
|
const conf = loadConf(root);
|
|
475
|
-
const
|
|
682
|
+
const ios = target === "ios";
|
|
683
|
+
const buildDir = join(root, ".janela", ios ? "build-ios" : "build");
|
|
476
684
|
const cacheDir = join(root, ".janela", "cache");
|
|
477
|
-
const outDir = join(root, ".janela", "out");
|
|
685
|
+
const outDir = join(root, ".janela", ios ? "out-ios" : "out");
|
|
478
686
|
for (const d of [buildDir, cacheDir, outDir]) mkdirSync(d, { recursive: true });
|
|
479
687
|
|
|
480
|
-
|
|
688
|
+
// Desktop links a C shim over webview.h; iOS links no shim at all — the
|
|
689
|
+
// UIKit shell is the program, and it links this build's output instead.
|
|
690
|
+
const shimLib = ios ? null : buildShim(cacheDir);
|
|
481
691
|
|
|
482
692
|
// Assemble the compile unit: runtime + user's commands + generated modules.
|
|
483
|
-
|
|
693
|
+
// Which runtime lane lands here as "./janela" is the whole difference
|
|
694
|
+
// between the two targets — a project's main.ts is compiled unchanged
|
|
695
|
+
// against either.
|
|
696
|
+
cpSync(join(KIT, "runtime", ios ? "ios.ts" : "janela.ts"), join(buildDir, "janela.ts"));
|
|
484
697
|
cpSync(join(KIT, "runtime", "types.ts"), join(buildDir, "types.ts"));
|
|
485
698
|
const mainSrc = join(root, "src-host", "main.ts");
|
|
486
699
|
if (!existsSync(mainSrc)) fail("missing src-host/main.ts");
|
|
@@ -512,6 +725,34 @@ function build(root, { devUrl = null, gui = true } = {}) {
|
|
|
512
725
|
`width: ${Number(w.width ?? 800)}, height: ${Number(w.height ?? 600)} };\n`,
|
|
513
726
|
);
|
|
514
727
|
|
|
728
|
+
// The iOS entry exports the library's two entry points instead of running a
|
|
729
|
+
// loop: UIKit owns the loop and calls in. Everything above this line — the
|
|
730
|
+
// contract, main.ts, the flattened frontend — is identical to desktop.
|
|
731
|
+
const entryTail = ios
|
|
732
|
+
? `const app = createApp<CmdsOf<typeof setup>, EvtsOf<typeof setup>>(WINDOW);\n` +
|
|
733
|
+
`setup(app);\n` +
|
|
734
|
+
`app.setHtml(INDEX_HTML);\n\n` +
|
|
735
|
+
`/** One page invoke; the UIKit shell calls this through the library ABI. */\n` +
|
|
736
|
+
`export function handleInvoke(cmd: string, argsJson: string): string {\n` +
|
|
737
|
+
` return app.dispatch(cmd, argsJson);\n` +
|
|
738
|
+
`}\n\n` +
|
|
739
|
+
`/** The document the shell loads into its WKWebView. */\n` +
|
|
740
|
+
`export function indexHtml(): string {\n` +
|
|
741
|
+
` return app.indexHtml();\n` +
|
|
742
|
+
`}\n\n` +
|
|
743
|
+
`/** A continuation the shell parked has come due (main queue). */\n` +
|
|
744
|
+
`export function onTimer(id: number): void {\n` +
|
|
745
|
+
` app.onTimer(id);\n` +
|
|
746
|
+
`}\n\n` +
|
|
747
|
+
`/** A file job the shell owns has finished (main queue). */\n` +
|
|
748
|
+
`export function onFsDone(id: number, ok: boolean, payload: string): void {\n` +
|
|
749
|
+
` app.onFsDone(id, ok, payload);\n` +
|
|
750
|
+
`}\n`
|
|
751
|
+
: `const app = createApp<CmdsOf<typeof setup>, EvtsOf<typeof setup>>(WINDOW);\n` +
|
|
752
|
+
`setup(app);\n` +
|
|
753
|
+
`const rc = app.run(INDEX_HTML) + 0;\n` +
|
|
754
|
+
`console.log("[janela] run returned", rc);\n`;
|
|
755
|
+
|
|
515
756
|
writeFileSync(
|
|
516
757
|
join(buildDir, "entry.ts"),
|
|
517
758
|
`// Generated by janela — do not edit.\n` +
|
|
@@ -529,12 +770,11 @@ function build(root, { devUrl = null, gui = true } = {}) {
|
|
|
529
770
|
`// and what is wanted here is the normalised table anyway.\n` +
|
|
530
771
|
`type CmdsOf<F> = F extends (app: JanelaAppImpl<infer C, infer _E>) => void ? C : CommandShapes;\n` +
|
|
531
772
|
`type EvtsOf<F> = F extends (app: JanelaAppImpl<infer _C, infer E>) => void ? E : Record<string, unknown>;\n\n` +
|
|
532
|
-
|
|
533
|
-
`setup(app);\n` +
|
|
534
|
-
`const rc = app.run(INDEX_HTML) + 0;\n` +
|
|
535
|
-
`console.log("[janela] run returned", rc);\n`,
|
|
773
|
+
entryTail,
|
|
536
774
|
);
|
|
537
775
|
|
|
776
|
+
if (ios) return buildIos(root, conf, buildDir, outDir);
|
|
777
|
+
|
|
538
778
|
writeFileSync(join(buildDir, "janela.ffi.json"), JSON.stringify(ffiManifest(shimLib), null, 2) + "\n");
|
|
539
779
|
|
|
540
780
|
console.log("janela: compiling TypeScript to a native binary");
|
|
@@ -730,20 +970,30 @@ function positionals() {
|
|
|
730
970
|
return out;
|
|
731
971
|
}
|
|
732
972
|
|
|
973
|
+
const TARGETS = ["desktop", "ios"];
|
|
974
|
+
|
|
975
|
+
function targetOrFail() {
|
|
976
|
+
const t = flag("target", "desktop");
|
|
977
|
+
if (!TARGETS.includes(t)) fail(`unknown target '${t}' (${TARGETS.join(", ")})`);
|
|
978
|
+
return t;
|
|
979
|
+
}
|
|
980
|
+
|
|
733
981
|
switch (cmd) {
|
|
734
982
|
case "init":
|
|
735
983
|
init(positionals()[0], flag("template", "vanilla"));
|
|
736
984
|
break;
|
|
737
985
|
case "build":
|
|
738
|
-
build(process.cwd());
|
|
986
|
+
build(process.cwd(), { target: targetOrFail() });
|
|
739
987
|
break;
|
|
740
988
|
case "dev":
|
|
741
|
-
await
|
|
989
|
+
if (targetOrFail() === "ios") await devIos(process.cwd());
|
|
990
|
+
else await dev(process.cwd());
|
|
742
991
|
break;
|
|
743
992
|
default:
|
|
744
993
|
console.log(
|
|
745
994
|
"usage: janela init <name> [--template vanilla|vue|react|svelte|solid]\n" +
|
|
746
|
-
" janela build |
|
|
995
|
+
" janela build [--target desktop|ios]\n" +
|
|
996
|
+
" janela dev [--target desktop|ios]",
|
|
747
997
|
);
|
|
748
998
|
process.exit(cmd ? 1 : 0);
|
|
749
999
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "janela",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Desktop apps in pure TypeScript, compiled to native. No Rust, no Node, no Electron.",
|
|
3
|
+
"version": "0.10.1",
|
|
4
|
+
"description": "Desktop and iOS apps in pure TypeScript, compiled to native. No Rust, no Node, no Electron.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"janela": "bin/janela.mjs",
|
|
@@ -40,7 +40,8 @@
|
|
|
40
40
|
"native",
|
|
41
41
|
"tauri",
|
|
42
42
|
"electron-alternative",
|
|
43
|
-
"scriptc"
|
|
43
|
+
"scriptc",
|
|
44
|
+
"ios"
|
|
44
45
|
],
|
|
45
46
|
"engines": {
|
|
46
47
|
"node": ">=24"
|