janela 0.14.2 → 0.15.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
@@ -1,3 +1,8 @@
1
+ <picture>
2
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/mmamedel/janela/main/packages/janela/brand/janela-mark-dark.svg">
3
+ <img src="https://raw.githubusercontent.com/mmamedel/janela/main/packages/janela/brand/janela-mark-light.svg" alt="" width="88" align="right">
4
+ </picture>
5
+
1
6
  # janela
2
7
 
3
8
  > *janela* — Portuguese for **window**.
@@ -6,9 +11,9 @@ Desktop and mobile apps in pure TypeScript, compiled to native. No Rust, no
6
11
  Node, no Electron. The backend is TypeScript compiled to a native binary by
7
12
  [scriptc](https://scriptc.dev); the window is the OS webview via
8
13
  [webview/webview](https://github.com/webview/webview). A desktop binary comes
9
- out around 225–420 KB — 225 KB for the smallest template — with no bundled
14
+ out around 240–420 KB — 241 KB for the smallest template — with no bundled
10
15
  browser and no bundled runtime; iOS and Android bundles land around
11
- 228–421 KB. Those are the *starter's* figures, and about 35 KB of each is the
16
+ 244–421 KB. Those are the *starter's* figures, and about 35 KB of each is the
12
17
  native file dialog and worker-thread reader it demonstrates: delete the "Files
13
18
  and the window" card and its two commands and the smallest binary is 212 KB. Per-template figures are in
14
19
  [docs/frontend.md](../../docs/frontend.md).
@@ -31,7 +36,11 @@ desktop-only for now; on mobile they report clearly when called.
31
36
  ## Quick start
32
37
 
33
38
  ```bash
34
- npm install -g janela # or: npx janela init my-app
39
+ pnpm create janela # the front door: prompts, scaffolds, installs
40
+ # (npm create janela@latest / yarn / bun also work)
41
+
42
+ # or drive the CLI yourself:
43
+ npm install -g janela
35
44
  janela --version # which janela you actually have
36
45
  janela init my-app # (or `jn init my-app`)
37
46
  cd my-app
@@ -43,10 +52,15 @@ Or start from a frontend framework:
43
52
 
44
53
  ```bash
45
54
  janela init my-app --template vue # or react | svelte | solid | vanilla
46
- cd my-app && npm install
55
+ cd my-app # deps are already installed
47
56
  janela dev # Vite dev server + HMR, in a native window
48
57
  ```
49
58
 
59
+ `init` installs the template's dependencies with whichever package manager
60
+ ran it — `pnpm janela init` uses pnpm — so `janela dev` works straight
61
+ afterwards. Pass `--no-install` to skip that, and if the install fails the
62
+ project is still written; the message names the one command to retry.
63
+
50
64
  A `--template` a globally installed CLI has never heard of is now an error
51
65
  rather than a silent fallback, and `janela --version` says which one you are
52
66
  running — worth checking first if a scaffolded project comes out looking
package/bin/janela.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  // janela — the CLI (the tauri-cli analogue).
3
3
  //
4
- // janela init <name> scaffold a new project
4
+ // janela init <name> scaffold a new project (and install its dependencies)
5
5
  // janela build compile the project to a native binary (+ .app on macOS)
6
6
  // janela dev build, then run the binary with logs in the terminal
7
7
  // janela --version which janela this is
@@ -21,8 +21,8 @@ import { dirname, join, relative, resolve, sep } from "node:path";
21
21
  import { fileURLToPath } from "node:url";
22
22
  import {
23
23
  ANDROID_ABI, ANDROID_TARGET_SDK, androidConf, ffiManifest, iosConf,
24
- libraryProfile, mimeFor, NAME_RE, patchPeSubsystem, PeError,
25
- rewriteHostSpecifier, suggestName,
24
+ installCommand, libraryProfile, mimeFor, NAME_RE, packageManager as pmFor,
25
+ patchPeSubsystem, PeError, rewriteHostSpecifier, suggestName,
26
26
  } from "./lib.mjs";
27
27
 
28
28
  const KIT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
@@ -107,8 +107,9 @@ function viteCommand(root) {
107
107
  if (existsSync(shim)) return { argv: [shim], shell: process.platform === "win32" };
108
108
 
109
109
  fail(
110
- "this project has a vite config but no local vite — run your package manager's " +
111
- "install first (npm install / pnpm install)",
110
+ "this project has a vite config but no local vite — its dependencies are not " +
111
+ `installed yet:\n ${installCommand(pmFor(process.env)).join(" ")}\n` +
112
+ " (janela init installs them for you unless you pass --no-install)",
112
113
  );
113
114
  }
114
115
 
@@ -1160,7 +1161,29 @@ function copyTemplate(from, to, name) {
1160
1161
 
1161
1162
  // Best-effort repair of a rejected name, so the error can suggest something
1162
1163
  // that would have worked instead of only stating the rule.
1163
- function init(name, template) {
1164
+ /**
1165
+ * Install a scaffolded project's dependencies.
1166
+ *
1167
+ * Returns true on success. A failure is NOT fatal: the project is already
1168
+ * written, and the useful thing to do is name the one command to retry rather
1169
+ * than exit non-zero on a tree that is fine. Offline, an unreachable registry
1170
+ * and missing auth all land here.
1171
+ *
1172
+ * Windows needs a shell: npm/pnpm/yarn are .cmd shims, and Node has refused to
1173
+ * spawn those directly since the CVE-2024-27980 fix.
1174
+ */
1175
+ function installDeps(dir, pm) {
1176
+ const cmd = installCommand(pm);
1177
+ console.log(`janela: installing dependencies with ${pm}…`);
1178
+ const r = spawnSync(cmd[0], cmd.slice(1), {
1179
+ stdio: "inherit",
1180
+ cwd: dir,
1181
+ shell: process.platform === "win32",
1182
+ });
1183
+ return r.status === 0;
1184
+ }
1185
+
1186
+ function init(name, template, { install = true } = {}) {
1164
1187
  if (!name) {
1165
1188
  fail(
1166
1189
  "no project name given.\n" +
@@ -1216,8 +1239,27 @@ function init(name, template) {
1216
1239
  writeFileSync(join(dir, ".gitignore"), ".janela/\nnode_modules/\ndist/\n");
1217
1240
  writeFileSync(join(dir, "package.json"), JSON.stringify(pkg, null, 2) + "\n");
1218
1241
 
1219
- const install = template === "vanilla" ? "" : "npm install && ";
1220
- console.log(`janela: created ${name}/ (${template}) next: cd ${name} && ${install}janela dev`);
1242
+ // The no-framework template has no dependencies to install: it needs no
1243
+ // frontend toolchain, and `janela dev` works on it straight out of init.
1244
+ const hasDeps = template !== "vanilla";
1245
+ if (!hasDeps || !install) {
1246
+ // Only a skipped install on a template that HAS dependencies leaves the
1247
+ // caller something to run.
1248
+ const manual = hasDeps ? `${installCommand(pmFor(process.env)).join(" ")} && ` : "";
1249
+ console.log(`janela: created ${name}/ (${template}) — next: cd ${name} && ${manual}janela dev`);
1250
+ return;
1251
+ }
1252
+
1253
+ const pm = pmFor(process.env);
1254
+ if (installDeps(dir, pm)) {
1255
+ console.log(`janela: created ${name}/ (${template}) — next: cd ${name} && janela dev`);
1256
+ } else {
1257
+ console.log(
1258
+ `janela: created ${name}/ (${template}), but installing its dependencies failed.\n` +
1259
+ ` The project is written and fine — retry the install and you are set:\n` +
1260
+ ` cd ${name} && ${installCommand(pm).join(" ")} && janela dev`,
1261
+ );
1262
+ }
1221
1263
  }
1222
1264
 
1223
1265
  // ---- dev --------------------------------------------------------------------
@@ -1349,9 +1391,11 @@ function assertPositionals(max) {
1349
1391
 
1350
1392
  switch (cmd) {
1351
1393
  case "init":
1352
- assertKnownFlags(["template"]);
1394
+ assertKnownFlags(["template", "no-install"]);
1353
1395
  assertPositionals(1);
1354
- init(positionals()[0], flag("template", "vanilla"));
1396
+ init(positionals()[0], flag("template", "vanilla"), {
1397
+ install: !argv.includes("--no-install"),
1398
+ });
1355
1399
  break;
1356
1400
  case "build":
1357
1401
  assertKnownFlags(["target"]);
@@ -1371,7 +1415,7 @@ switch (cmd) {
1371
1415
  default:
1372
1416
  console.log(
1373
1417
  `janela ${VERSION}\n\n` +
1374
- "usage: janela init <name> [--template vanilla|vue|react|svelte|solid]\n" +
1418
+ "usage: janela init <name> [--template vanilla|vue|react|svelte|solid] [--no-install]\n" +
1375
1419
  " janela build [--target desktop|ios|android]\n" +
1376
1420
  " janela dev [--target desktop|ios|android]\n" +
1377
1421
  " janela --version",
package/bin/lib.mjs CHANGED
@@ -345,3 +345,30 @@ export function patchPeSubsystem(input) {
345
345
  buf.writeUInt16LE(IMAGE_SUBSYSTEM_WINDOWS_GUI, subOff);
346
346
  return { patched: true, buf };
347
347
  }
348
+
349
+ /**
350
+ * Which package manager to install a scaffolded project with.
351
+ *
352
+ * `npm_config_user_agent` is set by whichever manager ran janela, so
353
+ * `pnpm janela init` installs with pnpm. Run as a plain global binary it is
354
+ * unset, and npm is the only manager guaranteed to exist alongside node.
355
+ *
356
+ * Takes the environment rather than reading process.env, so it is testable.
357
+ */
358
+ export function packageManager(env = {}) {
359
+ const ua = env.npm_config_user_agent ?? "";
360
+ for (const pm of ["pnpm", "yarn", "bun"]) if (ua.startsWith(`${pm}/`)) return pm;
361
+ return "npm";
362
+ }
363
+
364
+ /**
365
+ * The install invocation for a manager.
366
+ *
367
+ * yarn's is the bare command. npm gets --no-fund --no-audit: neither is useful
368
+ * on a freshly scaffolded tree, and the audit is the slowest part of it.
369
+ */
370
+ export function installCommand(pm) {
371
+ if (pm === "yarn") return ["yarn"];
372
+ if (pm === "npm") return ["npm", "install", "--no-fund", "--no-audit"];
373
+ return [pm, "install"];
374
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "janela",
3
- "version": "0.14.2",
3
+ "version": "0.15.0",
4
4
  "description": "Desktop, iOS and Android apps in pure TypeScript, compiled to native. No Rust, no Node, no Electron.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -7,9 +7,7 @@
7
7
  <style>
8
8
  /* Everything here is yours to replace. It is deliberately one file with
9
9
  no dependencies: this template has no bundler, so what you see is what
10
- runs. The mark below is drawn in CSS rather than shipped as an image —
11
- a janela frontend is embedded in the binary, so every asset has to be
12
- inlined anyway, and a window needs no artwork. */
10
+ runs. */
13
11
  :root {
14
12
  color-scheme: light dark;
15
13
  --bg: #f6f6f7;
@@ -17,7 +15,7 @@
17
15
  --text: #18181b;
18
16
  --muted: #71717a;
19
17
  --line: #e4e4e7;
20
- --accent: #2563eb;
18
+ --accent: #1c6fd4;
21
19
  --accent-soft: #eff4ff;
22
20
  --shadow: 0 1px 2px rgb(0 0 0 / 0.04), 0 8px 24px -12px rgb(0 0 0 / 0.12);
23
21
  }
@@ -28,7 +26,7 @@
28
26
  --text: #fafafa;
29
27
  --muted: #a1a1aa;
30
28
  --line: #2e2e34;
31
- --accent: #60a5fa;
29
+ --accent: #58a6ff;
32
30
  --accent-soft: #1d2433;
33
31
  --shadow: 0 1px 2px rgb(0 0 0 / 0.3), 0 8px 24px -12px rgb(0 0 0 / 0.5);
34
32
  }
@@ -49,18 +47,14 @@
49
47
 
50
48
  /* --- header ------------------------------------------------------- */
51
49
  header { text-align: center; margin-bottom: 20px; }
52
- .mark {
53
- width: 56px; height: 44px; margin: 0 auto 14px;
54
- border: 2px solid var(--text); border-radius: 8px;
55
- display: flex; flex-direction: column; overflow: hidden;
56
- }
57
- .mark .bar {
58
- height: 13px; flex: none;
59
- border-bottom: 2px solid var(--text);
60
- display: flex; align-items: center; gap: 3px; padding: 0 4px;
61
- }
62
- .mark .bar i { width: 3px; height: 3px; border-radius: 50%; background: var(--text); }
63
- .mark .pane { flex: 1; background: linear-gradient(135deg, var(--accent), transparent 70%); opacity: 0.85; }
50
+ /* The mark: a round window, which is what janela means. Inlined rather
51
+ than linked, because a janela frontend is embedded in the binary and
52
+ every asset has to be inlined anyway — and inline rather than a CSS
53
+ data URI so ONE copy serves both themes: the frame follows the text
54
+ colour, the glass follows the accent. Percent-encoding a data URI
55
+ would also have cost 4x the bytes. Delete it and the header still
56
+ works. */
57
+ .mark { display: block; width: 56px; height: 56px; margin: 0 auto 14px; }
64
58
  h1 { margin: 0; font-size: 26px; letter-spacing: -0.02em; font-weight: 650; }
65
59
  .sub { margin: 5px 0 0; color: var(--muted); font-size: 13.5px; }
66
60
 
@@ -145,10 +139,7 @@
145
139
  <body>
146
140
  <main>
147
141
  <header>
148
- <div class="mark" aria-hidden="true">
149
- <div class="bar"><i></i><i></i><i></i></div>
150
- <div class="pane"></div>
151
- </div>
142
+ <svg class="mark" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 970.64 970.64" role="img" aria-label="janela"><path fill="currentColor" d="M970.64,485.32c0,268.03-217.28,485.32-485.32,485.32S0,753.35,0,485.32,217.28,0,485.32,0s485.32,217.28,485.32,485.32ZM938.11,485.34c0-250.08-202.73-452.8-452.8-452.8S32.51,235.26,32.51,485.34s202.73,452.8,452.8,452.8,452.8-202.73,452.8-452.8Z"/><path fill="currentColor" d="M892.79,485.25c0,225.05-182.44,407.49-407.49,407.49S77.82,710.3,77.82,485.25,260.26,77.77,485.31,77.77s407.49,182.44,407.49,407.49ZM470.27,112.09v335.5c0,1.05,13.11,12.99,15.36,14.43,1.65-2.06,14.64-13.4,14.64-14.43V112.59c0-1.1-2.44-1.43-3.45-1.55-8.42-.99-18.1.83-26.55,1.05ZM858.27,438.09c-20.14-166.94-158.46-303.59-325-324v324h325ZM437.27,115.09c-21.65,1.62-44.22,7.07-65,13.5-137.62,42.61-242.41,165.92-260,309.5h325V115.09ZM109.27,500.09h338.5c1,0,12.36-12.44,13.54-14.5,0-1-12.54-14.5-13.54-14.5H109.27c-1.81,9.94-.24,19.09,0,29ZM861.27,471.09h-338.5c-1,0-13.53,13.5-13.54,14.5.52,2.76,3.61,3.57,5.57,5.47,1.64,1.59,7.37,9.03,7.97,9.03h338.5c.9-9.78.9-19.22,0-29ZM500.27,860.09v-336.5c0-1.03-12.98-12.37-14.64-14.43-2.26,1.44-15.36,13.37-15.36,14.43v336.5h30ZM437.27,533.09H112.27c19.95,166.03,159.46,303.1,325,323v-323ZM858.27,533.09h-325v324c165.97-22.51,304.48-156.63,325-324Z"/><path fill="var(--accent)" d="M858.27,438.09h-325V114.09c166.54,20.41,304.86,157.06,325,324Z"/><path fill="var(--accent)" d="M858.27,533.09c-20.52,167.37-159.03,301.49-325,324v-324h325Z"/><path fill="var(--accent)" d="M437.27,533.09v323c-165.54-19.9-305.05-156.97-325-323h325Z"/><path fill="var(--accent)" d="M437.27,115.09v323H112.27c17.6-143.57,122.38-266.89,260-309.5,20.78-6.43,43.34-11.88,65-13.5ZM324.93,239.42c-5.22-5.22-14.48-5.8-20.62-1.79-23.51,27.2-56.28,50.77-79.07,77.93-3.21,3.83-6.62,7.81-7,13.05-.97,13.35,13.8,21.88,24.98,14.93,28.09-28.84,58.16-56.14,84.78-86.22,2.71-5.58,1.25-13.58-3.07-17.9ZM375.06,280.3c-4.15.41-7.65,2.76-10.8,5.27-2.42,1.93-17.06,17.64-18.19,19.81-7.64,14.76,7.17,29.85,21.55,22.56,2.83-1.44,19.3-18.18,21.64-21.36,8.21-11.17-.52-27.62-14.2-26.28ZM310.03,344.3c-2.88.42-6.45,2.49-8.78,4.26-4.52,3.45-32.55,31.28-34.83,35.17-8.84,15.07,8.47,31.23,23.59,21.59,4.35-2.77,34.16-32.7,36.28-36.72,6.36-12.04-2.95-26.26-16.27-24.31Z"/><path fill="var(--card)" d="M500.27,860.09h-30v-336.5c0-1.05,13.11-12.99,15.36-14.43,1.65,2.06,14.64,13.4,14.64,14.43v336.5Z"/><path fill="var(--card)" d="M470.27,112.09c8.45-.22,18.13-2.04,26.55-1.05,1.01.12,3.45.46,3.45,1.55v335c0,1.03-12.98,12.37-14.64,14.43-2.26-1.44-15.36-13.37-15.36-14.43V112.09Z"/><path fill="var(--card)" d="M109.27,500.09c-.24-9.91-1.81-19.06,0-29h338.5c1,0,13.54,13.5,13.54,14.5-1.17,2.06-12.54,14.5-13.54,14.5H109.27Z"/><path fill="var(--card)" d="M861.27,471.09c.9,9.78.9,19.22,0,29h-338.5c-.6,0-6.34-7.44-7.97-9.03-1.96-1.9-5.04-2.72-5.57-5.47,0-1,12.54-14.5,13.54-14.5h338.5Z"/><path fill="currentColor" d="M324.93,239.42c4.32,4.32,5.78,12.32,3.07,17.9-26.62,30.07-56.7,57.37-84.78,86.22-11.18,6.94-25.95-1.58-24.98-14.93.38-5.24,3.79-9.22,7-13.05,22.8-27.16,55.56-50.73,79.07-77.93,6.13-4.01,15.39-3.43,20.62,1.79Z"/><path fill="currentColor" d="M310.03,344.3c13.33-1.95,22.63,12.27,16.27,24.31-2.12,4.02-31.93,33.94-36.28,36.72-15.12,9.64-32.44-6.52-23.59-21.59,2.28-3.89,30.31-31.72,34.83-35.17,2.33-1.78,5.9-3.84,8.78-4.26Z"/><path fill="currentColor" d="M375.06,280.3c13.69-1.35,22.42,15.11,14.2,26.28-2.34,3.18-18.81,19.93-21.64,21.36-14.39,7.3-29.19-7.8-21.55-22.56,1.13-2.18,15.76-17.88,18.19-19.81,3.16-2.51,6.65-4.86,10.8-5.27Z"/></svg>
152
143
  <h1>__NAME__</h1>
153
144
  <p class="sub">TypeScript, compiled to a native binary. No Rust, no Node, no Electron.</p>
154
145
  </header>
@@ -64,14 +64,7 @@ export default function App() {
64
64
  return (
65
65
  <main>
66
66
  <header>
67
- <div className="mark" aria-hidden="true">
68
- <div className="bar">
69
- <i />
70
- <i />
71
- <i />
72
- </div>
73
- <div className="pane" />
74
- </div>
67
+ <svg class="mark" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 970.64 970.64" role="img" aria-label="janela"><path fill="currentColor" d="M970.64,485.32c0,268.03-217.28,485.32-485.32,485.32S0,753.35,0,485.32,217.28,0,485.32,0s485.32,217.28,485.32,485.32ZM938.11,485.34c0-250.08-202.73-452.8-452.8-452.8S32.51,235.26,32.51,485.34s202.73,452.8,452.8,452.8,452.8-202.73,452.8-452.8Z" /><path fill="currentColor" d="M892.79,485.25c0,225.05-182.44,407.49-407.49,407.49S77.82,710.3,77.82,485.25,260.26,77.77,485.31,77.77s407.49,182.44,407.49,407.49ZM470.27,112.09v335.5c0,1.05,13.11,12.99,15.36,14.43,1.65-2.06,14.64-13.4,14.64-14.43V112.59c0-1.1-2.44-1.43-3.45-1.55-8.42-.99-18.1.83-26.55,1.05ZM858.27,438.09c-20.14-166.94-158.46-303.59-325-324v324h325ZM437.27,115.09c-21.65,1.62-44.22,7.07-65,13.5-137.62,42.61-242.41,165.92-260,309.5h325V115.09ZM109.27,500.09h338.5c1,0,12.36-12.44,13.54-14.5,0-1-12.54-14.5-13.54-14.5H109.27c-1.81,9.94-.24,19.09,0,29ZM861.27,471.09h-338.5c-1,0-13.53,13.5-13.54,14.5.52,2.76,3.61,3.57,5.57,5.47,1.64,1.59,7.37,9.03,7.97,9.03h338.5c.9-9.78.9-19.22,0-29ZM500.27,860.09v-336.5c0-1.03-12.98-12.37-14.64-14.43-2.26,1.44-15.36,13.37-15.36,14.43v336.5h30ZM437.27,533.09H112.27c19.95,166.03,159.46,303.1,325,323v-323ZM858.27,533.09h-325v324c165.97-22.51,304.48-156.63,325-324Z" /><path fill="var(--accent)" d="M858.27,438.09h-325V114.09c166.54,20.41,304.86,157.06,325,324Z" /><path fill="var(--accent)" d="M858.27,533.09c-20.52,167.37-159.03,301.49-325,324v-324h325Z" /><path fill="var(--accent)" d="M437.27,533.09v323c-165.54-19.9-305.05-156.97-325-323h325Z" /><path fill="var(--accent)" d="M437.27,115.09v323H112.27c17.6-143.57,122.38-266.89,260-309.5,20.78-6.43,43.34-11.88,65-13.5ZM324.93,239.42c-5.22-5.22-14.48-5.8-20.62-1.79-23.51,27.2-56.28,50.77-79.07,77.93-3.21,3.83-6.62,7.81-7,13.05-.97,13.35,13.8,21.88,24.98,14.93,28.09-28.84,58.16-56.14,84.78-86.22,2.71-5.58,1.25-13.58-3.07-17.9ZM375.06,280.3c-4.15.41-7.65,2.76-10.8,5.27-2.42,1.93-17.06,17.64-18.19,19.81-7.64,14.76,7.17,29.85,21.55,22.56,2.83-1.44,19.3-18.18,21.64-21.36,8.21-11.17-.52-27.62-14.2-26.28ZM310.03,344.3c-2.88.42-6.45,2.49-8.78,4.26-4.52,3.45-32.55,31.28-34.83,35.17-8.84,15.07,8.47,31.23,23.59,21.59,4.35-2.77,34.16-32.7,36.28-36.72,6.36-12.04-2.95-26.26-16.27-24.31Z" /><path fill="var(--card)" d="M500.27,860.09h-30v-336.5c0-1.05,13.11-12.99,15.36-14.43,1.65,2.06,14.64,13.4,14.64,14.43v336.5Z" /><path fill="var(--card)" d="M470.27,112.09c8.45-.22,18.13-2.04,26.55-1.05,1.01.12,3.45.46,3.45,1.55v335c0,1.03-12.98,12.37-14.64,14.43-2.26-1.44-15.36-13.37-15.36-14.43V112.09Z" /><path fill="var(--card)" d="M109.27,500.09c-.24-9.91-1.81-19.06,0-29h338.5c1,0,13.54,13.5,13.54,14.5-1.17,2.06-12.54,14.5-13.54,14.5H109.27Z" /><path fill="var(--card)" d="M861.27,471.09c.9,9.78.9,19.22,0,29h-338.5c-.6,0-6.34-7.44-7.97-9.03-1.96-1.9-5.04-2.72-5.57-5.47,0-1,12.54-14.5,13.54-14.5h338.5Z" /><path fill="currentColor" d="M324.93,239.42c4.32,4.32,5.78,12.32,3.07,17.9-26.62,30.07-56.7,57.37-84.78,86.22-11.18,6.94-25.95-1.58-24.98-14.93.38-5.24,3.79-9.22,7-13.05,22.8-27.16,55.56-50.73,79.07-77.93,6.13-4.01,15.39-3.43,20.62,1.79Z" /><path fill="currentColor" d="M310.03,344.3c13.33-1.95,22.63,12.27,16.27,24.31-2.12,4.02-31.93,33.94-36.28,36.72-15.12,9.64-32.44-6.52-23.59-21.59,2.28-3.89,30.31-31.72,34.83-35.17,2.33-1.78,5.9-3.84,8.78-4.26Z" /><path fill="currentColor" d="M375.06,280.3c13.69-1.35,22.42,15.11,14.2,26.28-2.34,3.18-18.81,19.93-21.64,21.36-14.39,7.3-29.19-7.8-21.55-22.56,1.13-2.18,15.76-17.88,18.19-19.81,3.16-2.51,6.65-4.86,10.8-5.27Z" /></svg>
75
68
  <h1>__NAME__</h1>
76
69
  <p className="sub">TypeScript, compiled to a native binary. No Rust, no Node, no Electron.</p>
77
70
  </header>
@@ -1,10 +1,6 @@
1
1
  /* The janela starter's look. Shared by every template so a project scaffolded
2
2
  with --template vue looks the same as one scaffolded without a framework.
3
- All of it is yours to replace.
4
-
5
- The window mark in App is drawn in CSS rather than shipped as an image: a
6
- janela frontend is embedded in the binary, so every asset has to be inlined
7
- anyway, and a window needs no artwork. */
3
+ All of it is yours to replace. */
8
4
 
9
5
  :root {
10
6
  color-scheme: light dark;
@@ -13,7 +9,7 @@
13
9
  --text: #18181b;
14
10
  --muted: #71717a;
15
11
  --line: #e4e4e7;
16
- --accent: #2563eb;
12
+ --accent: #1c6fd4;
17
13
  --accent-soft: #eff4ff;
18
14
  --shadow: 0 1px 2px rgb(0 0 0 / 0.04), 0 8px 24px -12px rgb(0 0 0 / 0.12);
19
15
  }
@@ -24,7 +20,7 @@
24
20
  --text: #fafafa;
25
21
  --muted: #a1a1aa;
26
22
  --line: #2e2e34;
27
- --accent: #60a5fa;
23
+ --accent: #58a6ff;
28
24
  --accent-soft: #1d2433;
29
25
  --shadow: 0 1px 2px rgb(0 0 0 / 0.3), 0 8px 24px -12px rgb(0 0 0 / 0.5);
30
26
  }
@@ -45,18 +41,14 @@ main { width: 100%; max-width: 760px; padding: 26px 22px 32px; }
45
41
 
46
42
  /* --- header ------------------------------------------------------- */
47
43
  header { text-align: center; margin-bottom: 20px; }
48
- .mark {
49
- width: 56px; height: 44px; margin: 0 auto 14px;
50
- border: 2px solid var(--text); border-radius: 8px;
51
- display: flex; flex-direction: column; overflow: hidden;
52
- }
53
- .mark .bar {
54
- height: 13px; flex: none;
55
- border-bottom: 2px solid var(--text);
56
- display: flex; align-items: center; gap: 3px; padding: 0 4px;
57
- }
58
- .mark .bar i { width: 3px; height: 3px; border-radius: 50%; background: var(--text); }
59
- .mark .pane { flex: 1; background: linear-gradient(135deg, var(--accent), transparent 70%); opacity: 0.85; }
44
+ /* The mark: a round window, which is what janela means. Inlined rather
45
+ than linked, because a janela frontend is embedded in the binary and
46
+ every asset has to be inlined anyway — and inline rather than a CSS
47
+ data URI so ONE copy serves both themes: the frame follows the text
48
+ colour, the glass follows the accent. Percent-encoding a data URI
49
+ would also have cost 4x the bytes. Delete it and the header still
50
+ works. */
51
+ .mark { display: block; width: 56px; height: 56px; margin: 0 auto 14px; }
60
52
  h1 { margin: 0; font-size: 26px; letter-spacing: -0.02em; font-weight: 650; }
61
53
  .sub { margin: 5px 0 0; color: var(--muted); font-size: 13.5px; }
62
54
 
@@ -59,14 +59,7 @@ export default function App() {
59
59
  return (
60
60
  <main>
61
61
  <header>
62
- <div class="mark" aria-hidden="true">
63
- <div class="bar">
64
- <i />
65
- <i />
66
- <i />
67
- </div>
68
- <div class="pane" />
69
- </div>
62
+ <svg class="mark" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 970.64 970.64" role="img" aria-label="janela"><path fill="currentColor" d="M970.64,485.32c0,268.03-217.28,485.32-485.32,485.32S0,753.35,0,485.32,217.28,0,485.32,0s485.32,217.28,485.32,485.32ZM938.11,485.34c0-250.08-202.73-452.8-452.8-452.8S32.51,235.26,32.51,485.34s202.73,452.8,452.8,452.8,452.8-202.73,452.8-452.8Z"/><path fill="currentColor" d="M892.79,485.25c0,225.05-182.44,407.49-407.49,407.49S77.82,710.3,77.82,485.25,260.26,77.77,485.31,77.77s407.49,182.44,407.49,407.49ZM470.27,112.09v335.5c0,1.05,13.11,12.99,15.36,14.43,1.65-2.06,14.64-13.4,14.64-14.43V112.59c0-1.1-2.44-1.43-3.45-1.55-8.42-.99-18.1.83-26.55,1.05ZM858.27,438.09c-20.14-166.94-158.46-303.59-325-324v324h325ZM437.27,115.09c-21.65,1.62-44.22,7.07-65,13.5-137.62,42.61-242.41,165.92-260,309.5h325V115.09ZM109.27,500.09h338.5c1,0,12.36-12.44,13.54-14.5,0-1-12.54-14.5-13.54-14.5H109.27c-1.81,9.94-.24,19.09,0,29ZM861.27,471.09h-338.5c-1,0-13.53,13.5-13.54,14.5.52,2.76,3.61,3.57,5.57,5.47,1.64,1.59,7.37,9.03,7.97,9.03h338.5c.9-9.78.9-19.22,0-29ZM500.27,860.09v-336.5c0-1.03-12.98-12.37-14.64-14.43-2.26,1.44-15.36,13.37-15.36,14.43v336.5h30ZM437.27,533.09H112.27c19.95,166.03,159.46,303.1,325,323v-323ZM858.27,533.09h-325v324c165.97-22.51,304.48-156.63,325-324Z"/><path fill="var(--accent)" d="M858.27,438.09h-325V114.09c166.54,20.41,304.86,157.06,325,324Z"/><path fill="var(--accent)" d="M858.27,533.09c-20.52,167.37-159.03,301.49-325,324v-324h325Z"/><path fill="var(--accent)" d="M437.27,533.09v323c-165.54-19.9-305.05-156.97-325-323h325Z"/><path fill="var(--accent)" d="M437.27,115.09v323H112.27c17.6-143.57,122.38-266.89,260-309.5,20.78-6.43,43.34-11.88,65-13.5ZM324.93,239.42c-5.22-5.22-14.48-5.8-20.62-1.79-23.51,27.2-56.28,50.77-79.07,77.93-3.21,3.83-6.62,7.81-7,13.05-.97,13.35,13.8,21.88,24.98,14.93,28.09-28.84,58.16-56.14,84.78-86.22,2.71-5.58,1.25-13.58-3.07-17.9ZM375.06,280.3c-4.15.41-7.65,2.76-10.8,5.27-2.42,1.93-17.06,17.64-18.19,19.81-7.64,14.76,7.17,29.85,21.55,22.56,2.83-1.44,19.3-18.18,21.64-21.36,8.21-11.17-.52-27.62-14.2-26.28ZM310.03,344.3c-2.88.42-6.45,2.49-8.78,4.26-4.52,3.45-32.55,31.28-34.83,35.17-8.84,15.07,8.47,31.23,23.59,21.59,4.35-2.77,34.16-32.7,36.28-36.72,6.36-12.04-2.95-26.26-16.27-24.31Z"/><path fill="var(--card)" d="M500.27,860.09h-30v-336.5c0-1.05,13.11-12.99,15.36-14.43,1.65,2.06,14.64,13.4,14.64,14.43v336.5Z"/><path fill="var(--card)" d="M470.27,112.09c8.45-.22,18.13-2.04,26.55-1.05,1.01.12,3.45.46,3.45,1.55v335c0,1.03-12.98,12.37-14.64,14.43-2.26-1.44-15.36-13.37-15.36-14.43V112.09Z"/><path fill="var(--card)" d="M109.27,500.09c-.24-9.91-1.81-19.06,0-29h338.5c1,0,13.54,13.5,13.54,14.5-1.17,2.06-12.54,14.5-13.54,14.5H109.27Z"/><path fill="var(--card)" d="M861.27,471.09c.9,9.78.9,19.22,0,29h-338.5c-.6,0-6.34-7.44-7.97-9.03-1.96-1.9-5.04-2.72-5.57-5.47,0-1,12.54-14.5,13.54-14.5h338.5Z"/><path fill="currentColor" d="M324.93,239.42c4.32,4.32,5.78,12.32,3.07,17.9-26.62,30.07-56.7,57.37-84.78,86.22-11.18,6.94-25.95-1.58-24.98-14.93.38-5.24,3.79-9.22,7-13.05,22.8-27.16,55.56-50.73,79.07-77.93,6.13-4.01,15.39-3.43,20.62,1.79Z"/><path fill="currentColor" d="M310.03,344.3c13.33-1.95,22.63,12.27,16.27,24.31-2.12,4.02-31.93,33.94-36.28,36.72-15.12,9.64-32.44-6.52-23.59-21.59,2.28-3.89,30.31-31.72,34.83-35.17,2.33-1.78,5.9-3.84,8.78-4.26Z"/><path fill="currentColor" d="M375.06,280.3c13.69-1.35,22.42,15.11,14.2,26.28-2.34,3.18-18.81,19.93-21.64,21.36-14.39,7.3-29.19-7.8-21.55-22.56,1.13-2.18,15.76-17.88,18.19-19.81,3.16-2.51,6.65-4.86,10.8-5.27Z"/></svg>
70
63
  <h1>__NAME__</h1>
71
64
  <p class="sub">TypeScript, compiled to a native binary. No Rust, no Node, no Electron.</p>
72
65
  </header>
@@ -1,10 +1,6 @@
1
1
  /* The janela starter's look. Shared by every template so a project scaffolded
2
2
  with --template vue looks the same as one scaffolded without a framework.
3
- All of it is yours to replace.
4
-
5
- The window mark in App is drawn in CSS rather than shipped as an image: a
6
- janela frontend is embedded in the binary, so every asset has to be inlined
7
- anyway, and a window needs no artwork. */
3
+ All of it is yours to replace. */
8
4
 
9
5
  :root {
10
6
  color-scheme: light dark;
@@ -13,7 +9,7 @@
13
9
  --text: #18181b;
14
10
  --muted: #71717a;
15
11
  --line: #e4e4e7;
16
- --accent: #2563eb;
12
+ --accent: #1c6fd4;
17
13
  --accent-soft: #eff4ff;
18
14
  --shadow: 0 1px 2px rgb(0 0 0 / 0.04), 0 8px 24px -12px rgb(0 0 0 / 0.12);
19
15
  }
@@ -24,7 +20,7 @@
24
20
  --text: #fafafa;
25
21
  --muted: #a1a1aa;
26
22
  --line: #2e2e34;
27
- --accent: #60a5fa;
23
+ --accent: #58a6ff;
28
24
  --accent-soft: #1d2433;
29
25
  --shadow: 0 1px 2px rgb(0 0 0 / 0.3), 0 8px 24px -12px rgb(0 0 0 / 0.5);
30
26
  }
@@ -45,18 +41,14 @@ main { width: 100%; max-width: 760px; padding: 26px 22px 32px; }
45
41
 
46
42
  /* --- header ------------------------------------------------------- */
47
43
  header { text-align: center; margin-bottom: 20px; }
48
- .mark {
49
- width: 56px; height: 44px; margin: 0 auto 14px;
50
- border: 2px solid var(--text); border-radius: 8px;
51
- display: flex; flex-direction: column; overflow: hidden;
52
- }
53
- .mark .bar {
54
- height: 13px; flex: none;
55
- border-bottom: 2px solid var(--text);
56
- display: flex; align-items: center; gap: 3px; padding: 0 4px;
57
- }
58
- .mark .bar i { width: 3px; height: 3px; border-radius: 50%; background: var(--text); }
59
- .mark .pane { flex: 1; background: linear-gradient(135deg, var(--accent), transparent 70%); opacity: 0.85; }
44
+ /* The mark: a round window, which is what janela means. Inlined rather
45
+ than linked, because a janela frontend is embedded in the binary and
46
+ every asset has to be inlined anyway — and inline rather than a CSS
47
+ data URI so ONE copy serves both themes: the frame follows the text
48
+ colour, the glass follows the accent. Percent-encoding a data URI
49
+ would also have cost 4x the bytes. Delete it and the header still
50
+ works. */
51
+ .mark { display: block; width: 56px; height: 56px; margin: 0 auto 14px; }
60
52
  h1 { margin: 0; font-size: 26px; letter-spacing: -0.02em; font-weight: 650; }
61
53
  .sub { margin: 5px 0 0; color: var(--muted); font-size: 13.5px; }
62
54
 
@@ -58,10 +58,7 @@
58
58
 
59
59
  <main>
60
60
  <header>
61
- <div class="mark" aria-hidden="true">
62
- <div class="bar"><i></i><i></i><i></i></div>
63
- <div class="pane"></div>
64
- </div>
61
+ <svg class="mark" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 970.64 970.64" role="img" aria-label="janela"><path fill="currentColor" d="M970.64,485.32c0,268.03-217.28,485.32-485.32,485.32S0,753.35,0,485.32,217.28,0,485.32,0s485.32,217.28,485.32,485.32ZM938.11,485.34c0-250.08-202.73-452.8-452.8-452.8S32.51,235.26,32.51,485.34s202.73,452.8,452.8,452.8,452.8-202.73,452.8-452.8Z"/><path fill="currentColor" d="M892.79,485.25c0,225.05-182.44,407.49-407.49,407.49S77.82,710.3,77.82,485.25,260.26,77.77,485.31,77.77s407.49,182.44,407.49,407.49ZM470.27,112.09v335.5c0,1.05,13.11,12.99,15.36,14.43,1.65-2.06,14.64-13.4,14.64-14.43V112.59c0-1.1-2.44-1.43-3.45-1.55-8.42-.99-18.1.83-26.55,1.05ZM858.27,438.09c-20.14-166.94-158.46-303.59-325-324v324h325ZM437.27,115.09c-21.65,1.62-44.22,7.07-65,13.5-137.62,42.61-242.41,165.92-260,309.5h325V115.09ZM109.27,500.09h338.5c1,0,12.36-12.44,13.54-14.5,0-1-12.54-14.5-13.54-14.5H109.27c-1.81,9.94-.24,19.09,0,29ZM861.27,471.09h-338.5c-1,0-13.53,13.5-13.54,14.5.52,2.76,3.61,3.57,5.57,5.47,1.64,1.59,7.37,9.03,7.97,9.03h338.5c.9-9.78.9-19.22,0-29ZM500.27,860.09v-336.5c0-1.03-12.98-12.37-14.64-14.43-2.26,1.44-15.36,13.37-15.36,14.43v336.5h30ZM437.27,533.09H112.27c19.95,166.03,159.46,303.1,325,323v-323ZM858.27,533.09h-325v324c165.97-22.51,304.48-156.63,325-324Z"/><path fill="var(--accent)" d="M858.27,438.09h-325V114.09c166.54,20.41,304.86,157.06,325,324Z"/><path fill="var(--accent)" d="M858.27,533.09c-20.52,167.37-159.03,301.49-325,324v-324h325Z"/><path fill="var(--accent)" d="M437.27,533.09v323c-165.54-19.9-305.05-156.97-325-323h325Z"/><path fill="var(--accent)" d="M437.27,115.09v323H112.27c17.6-143.57,122.38-266.89,260-309.5,20.78-6.43,43.34-11.88,65-13.5ZM324.93,239.42c-5.22-5.22-14.48-5.8-20.62-1.79-23.51,27.2-56.28,50.77-79.07,77.93-3.21,3.83-6.62,7.81-7,13.05-.97,13.35,13.8,21.88,24.98,14.93,28.09-28.84,58.16-56.14,84.78-86.22,2.71-5.58,1.25-13.58-3.07-17.9ZM375.06,280.3c-4.15.41-7.65,2.76-10.8,5.27-2.42,1.93-17.06,17.64-18.19,19.81-7.64,14.76,7.17,29.85,21.55,22.56,2.83-1.44,19.3-18.18,21.64-21.36,8.21-11.17-.52-27.62-14.2-26.28ZM310.03,344.3c-2.88.42-6.45,2.49-8.78,4.26-4.52,3.45-32.55,31.28-34.83,35.17-8.84,15.07,8.47,31.23,23.59,21.59,4.35-2.77,34.16-32.7,36.28-36.72,6.36-12.04-2.95-26.26-16.27-24.31Z"/><path fill="var(--card)" d="M500.27,860.09h-30v-336.5c0-1.05,13.11-12.99,15.36-14.43,1.65,2.06,14.64,13.4,14.64,14.43v336.5Z"/><path fill="var(--card)" d="M470.27,112.09c8.45-.22,18.13-2.04,26.55-1.05,1.01.12,3.45.46,3.45,1.55v335c0,1.03-12.98,12.37-14.64,14.43-2.26-1.44-15.36-13.37-15.36-14.43V112.09Z"/><path fill="var(--card)" d="M109.27,500.09c-.24-9.91-1.81-19.06,0-29h338.5c1,0,13.54,13.5,13.54,14.5-1.17,2.06-12.54,14.5-13.54,14.5H109.27Z"/><path fill="var(--card)" d="M861.27,471.09c.9,9.78.9,19.22,0,29h-338.5c-.6,0-6.34-7.44-7.97-9.03-1.96-1.9-5.04-2.72-5.57-5.47,0-1,12.54-14.5,13.54-14.5h338.5Z"/><path fill="currentColor" d="M324.93,239.42c4.32,4.32,5.78,12.32,3.07,17.9-26.62,30.07-56.7,57.37-84.78,86.22-11.18,6.94-25.95-1.58-24.98-14.93.38-5.24,3.79-9.22,7-13.05,22.8-27.16,55.56-50.73,79.07-77.93,6.13-4.01,15.39-3.43,20.62,1.79Z"/><path fill="currentColor" d="M310.03,344.3c13.33-1.95,22.63,12.27,16.27,24.31-2.12,4.02-31.93,33.94-36.28,36.72-15.12,9.64-32.44-6.52-23.59-21.59,2.28-3.89,30.31-31.72,34.83-35.17,2.33-1.78,5.9-3.84,8.78-4.26Z"/><path fill="currentColor" d="M375.06,280.3c13.69-1.35,22.42,15.11,14.2,26.28-2.34,3.18-18.81,19.93-21.64,21.36-14.39,7.3-29.19-7.8-21.55-22.56,1.13-2.18,15.76-17.88,18.19-19.81,3.16-2.51,6.65-4.86,10.8-5.27Z"/></svg>
65
62
  <h1>__NAME__</h1>
66
63
  <p class="sub">TypeScript, compiled to a native binary. No Rust, no Node, no Electron.</p>
67
64
  </header>
@@ -1,10 +1,6 @@
1
1
  /* The janela starter's look. Shared by every template so a project scaffolded
2
2
  with --template vue looks the same as one scaffolded without a framework.
3
- All of it is yours to replace.
4
-
5
- The window mark in App is drawn in CSS rather than shipped as an image: a
6
- janela frontend is embedded in the binary, so every asset has to be inlined
7
- anyway, and a window needs no artwork. */
3
+ All of it is yours to replace. */
8
4
 
9
5
  :root {
10
6
  color-scheme: light dark;
@@ -13,7 +9,7 @@
13
9
  --text: #18181b;
14
10
  --muted: #71717a;
15
11
  --line: #e4e4e7;
16
- --accent: #2563eb;
12
+ --accent: #1c6fd4;
17
13
  --accent-soft: #eff4ff;
18
14
  --shadow: 0 1px 2px rgb(0 0 0 / 0.04), 0 8px 24px -12px rgb(0 0 0 / 0.12);
19
15
  }
@@ -24,7 +20,7 @@
24
20
  --text: #fafafa;
25
21
  --muted: #a1a1aa;
26
22
  --line: #2e2e34;
27
- --accent: #60a5fa;
23
+ --accent: #58a6ff;
28
24
  --accent-soft: #1d2433;
29
25
  --shadow: 0 1px 2px rgb(0 0 0 / 0.3), 0 8px 24px -12px rgb(0 0 0 / 0.5);
30
26
  }
@@ -45,18 +41,14 @@ main { width: 100%; max-width: 760px; padding: 26px 22px 32px; }
45
41
 
46
42
  /* --- header ------------------------------------------------------- */
47
43
  header { text-align: center; margin-bottom: 20px; }
48
- .mark {
49
- width: 56px; height: 44px; margin: 0 auto 14px;
50
- border: 2px solid var(--text); border-radius: 8px;
51
- display: flex; flex-direction: column; overflow: hidden;
52
- }
53
- .mark .bar {
54
- height: 13px; flex: none;
55
- border-bottom: 2px solid var(--text);
56
- display: flex; align-items: center; gap: 3px; padding: 0 4px;
57
- }
58
- .mark .bar i { width: 3px; height: 3px; border-radius: 50%; background: var(--text); }
59
- .mark .pane { flex: 1; background: linear-gradient(135deg, var(--accent), transparent 70%); opacity: 0.85; }
44
+ /* The mark: a round window, which is what janela means. Inlined rather
45
+ than linked, because a janela frontend is embedded in the binary and
46
+ every asset has to be inlined anyway — and inline rather than a CSS
47
+ data URI so ONE copy serves both themes: the frame follows the text
48
+ colour, the glass follows the accent. Percent-encoding a data URI
49
+ would also have cost 4x the bytes. Delete it and the header still
50
+ works. */
51
+ .mark { display: block; width: 56px; height: 56px; margin: 0 auto 14px; }
60
52
  h1 { margin: 0; font-size: 26px; letter-spacing: -0.02em; font-weight: 650; }
61
53
  .sub { margin: 5px 0 0; color: var(--muted); font-size: 13.5px; }
62
54
 
@@ -58,10 +58,7 @@ async function pick() {
58
58
  <template>
59
59
  <main>
60
60
  <header>
61
- <div class="mark" aria-hidden="true">
62
- <div class="bar"><i /><i /><i /></div>
63
- <div class="pane" />
64
- </div>
61
+ <svg class="mark" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 970.64 970.64" role="img" aria-label="janela"><path fill="currentColor" d="M970.64,485.32c0,268.03-217.28,485.32-485.32,485.32S0,753.35,0,485.32,217.28,0,485.32,0s485.32,217.28,485.32,485.32ZM938.11,485.34c0-250.08-202.73-452.8-452.8-452.8S32.51,235.26,32.51,485.34s202.73,452.8,452.8,452.8,452.8-202.73,452.8-452.8Z"/><path fill="currentColor" d="M892.79,485.25c0,225.05-182.44,407.49-407.49,407.49S77.82,710.3,77.82,485.25,260.26,77.77,485.31,77.77s407.49,182.44,407.49,407.49ZM470.27,112.09v335.5c0,1.05,13.11,12.99,15.36,14.43,1.65-2.06,14.64-13.4,14.64-14.43V112.59c0-1.1-2.44-1.43-3.45-1.55-8.42-.99-18.1.83-26.55,1.05ZM858.27,438.09c-20.14-166.94-158.46-303.59-325-324v324h325ZM437.27,115.09c-21.65,1.62-44.22,7.07-65,13.5-137.62,42.61-242.41,165.92-260,309.5h325V115.09ZM109.27,500.09h338.5c1,0,12.36-12.44,13.54-14.5,0-1-12.54-14.5-13.54-14.5H109.27c-1.81,9.94-.24,19.09,0,29ZM861.27,471.09h-338.5c-1,0-13.53,13.5-13.54,14.5.52,2.76,3.61,3.57,5.57,5.47,1.64,1.59,7.37,9.03,7.97,9.03h338.5c.9-9.78.9-19.22,0-29ZM500.27,860.09v-336.5c0-1.03-12.98-12.37-14.64-14.43-2.26,1.44-15.36,13.37-15.36,14.43v336.5h30ZM437.27,533.09H112.27c19.95,166.03,159.46,303.1,325,323v-323ZM858.27,533.09h-325v324c165.97-22.51,304.48-156.63,325-324Z"/><path fill="var(--accent)" d="M858.27,438.09h-325V114.09c166.54,20.41,304.86,157.06,325,324Z"/><path fill="var(--accent)" d="M858.27,533.09c-20.52,167.37-159.03,301.49-325,324v-324h325Z"/><path fill="var(--accent)" d="M437.27,533.09v323c-165.54-19.9-305.05-156.97-325-323h325Z"/><path fill="var(--accent)" d="M437.27,115.09v323H112.27c17.6-143.57,122.38-266.89,260-309.5,20.78-6.43,43.34-11.88,65-13.5ZM324.93,239.42c-5.22-5.22-14.48-5.8-20.62-1.79-23.51,27.2-56.28,50.77-79.07,77.93-3.21,3.83-6.62,7.81-7,13.05-.97,13.35,13.8,21.88,24.98,14.93,28.09-28.84,58.16-56.14,84.78-86.22,2.71-5.58,1.25-13.58-3.07-17.9ZM375.06,280.3c-4.15.41-7.65,2.76-10.8,5.27-2.42,1.93-17.06,17.64-18.19,19.81-7.64,14.76,7.17,29.85,21.55,22.56,2.83-1.44,19.3-18.18,21.64-21.36,8.21-11.17-.52-27.62-14.2-26.28ZM310.03,344.3c-2.88.42-6.45,2.49-8.78,4.26-4.52,3.45-32.55,31.28-34.83,35.17-8.84,15.07,8.47,31.23,23.59,21.59,4.35-2.77,34.16-32.7,36.28-36.72,6.36-12.04-2.95-26.26-16.27-24.31Z"/><path fill="var(--card)" d="M500.27,860.09h-30v-336.5c0-1.05,13.11-12.99,15.36-14.43,1.65,2.06,14.64,13.4,14.64,14.43v336.5Z"/><path fill="var(--card)" d="M470.27,112.09c8.45-.22,18.13-2.04,26.55-1.05,1.01.12,3.45.46,3.45,1.55v335c0,1.03-12.98,12.37-14.64,14.43-2.26-1.44-15.36-13.37-15.36-14.43V112.09Z"/><path fill="var(--card)" d="M109.27,500.09c-.24-9.91-1.81-19.06,0-29h338.5c1,0,13.54,13.5,13.54,14.5-1.17,2.06-12.54,14.5-13.54,14.5H109.27Z"/><path fill="var(--card)" d="M861.27,471.09c.9,9.78.9,19.22,0,29h-338.5c-.6,0-6.34-7.44-7.97-9.03-1.96-1.9-5.04-2.72-5.57-5.47,0-1,12.54-14.5,13.54-14.5h338.5Z"/><path fill="currentColor" d="M324.93,239.42c4.32,4.32,5.78,12.32,3.07,17.9-26.62,30.07-56.7,57.37-84.78,86.22-11.18,6.94-25.95-1.58-24.98-14.93.38-5.24,3.79-9.22,7-13.05,22.8-27.16,55.56-50.73,79.07-77.93,6.13-4.01,15.39-3.43,20.62,1.79Z"/><path fill="currentColor" d="M310.03,344.3c13.33-1.95,22.63,12.27,16.27,24.31-2.12,4.02-31.93,33.94-36.28,36.72-15.12,9.64-32.44-6.52-23.59-21.59,2.28-3.89,30.31-31.72,34.83-35.17,2.33-1.78,5.9-3.84,8.78-4.26Z"/><path fill="currentColor" d="M375.06,280.3c13.69-1.35,22.42,15.11,14.2,26.28-2.34,3.18-18.81,19.93-21.64,21.36-14.39,7.3-29.19-7.8-21.55-22.56,1.13-2.18,15.76-17.88,18.19-19.81,3.16-2.51,6.65-4.86,10.8-5.27Z"/></svg>
65
62
  <h1>__NAME__</h1>
66
63
  <p class="sub">TypeScript, compiled to a native binary. No Rust, no Node, no Electron.</p>
67
64
  </header>
@@ -1,10 +1,6 @@
1
1
  /* The janela starter's look. Shared by every template so a project scaffolded
2
2
  with --template vue looks the same as one scaffolded without a framework.
3
- All of it is yours to replace.
4
-
5
- The window mark in App is drawn in CSS rather than shipped as an image: a
6
- janela frontend is embedded in the binary, so every asset has to be inlined
7
- anyway, and a window needs no artwork. */
3
+ All of it is yours to replace. */
8
4
 
9
5
  :root {
10
6
  color-scheme: light dark;
@@ -13,7 +9,7 @@
13
9
  --text: #18181b;
14
10
  --muted: #71717a;
15
11
  --line: #e4e4e7;
16
- --accent: #2563eb;
12
+ --accent: #1c6fd4;
17
13
  --accent-soft: #eff4ff;
18
14
  --shadow: 0 1px 2px rgb(0 0 0 / 0.04), 0 8px 24px -12px rgb(0 0 0 / 0.12);
19
15
  }
@@ -24,7 +20,7 @@
24
20
  --text: #fafafa;
25
21
  --muted: #a1a1aa;
26
22
  --line: #2e2e34;
27
- --accent: #60a5fa;
23
+ --accent: #58a6ff;
28
24
  --accent-soft: #1d2433;
29
25
  --shadow: 0 1px 2px rgb(0 0 0 / 0.3), 0 8px 24px -12px rgb(0 0 0 / 0.5);
30
26
  }
@@ -45,18 +41,14 @@ main { width: 100%; max-width: 760px; padding: 26px 22px 32px; }
45
41
 
46
42
  /* --- header ------------------------------------------------------- */
47
43
  header { text-align: center; margin-bottom: 20px; }
48
- .mark {
49
- width: 56px; height: 44px; margin: 0 auto 14px;
50
- border: 2px solid var(--text); border-radius: 8px;
51
- display: flex; flex-direction: column; overflow: hidden;
52
- }
53
- .mark .bar {
54
- height: 13px; flex: none;
55
- border-bottom: 2px solid var(--text);
56
- display: flex; align-items: center; gap: 3px; padding: 0 4px;
57
- }
58
- .mark .bar i { width: 3px; height: 3px; border-radius: 50%; background: var(--text); }
59
- .mark .pane { flex: 1; background: linear-gradient(135deg, var(--accent), transparent 70%); opacity: 0.85; }
44
+ /* The mark: a round window, which is what janela means. Inlined rather
45
+ than linked, because a janela frontend is embedded in the binary and
46
+ every asset has to be inlined anyway — and inline rather than a CSS
47
+ data URI so ONE copy serves both themes: the frame follows the text
48
+ colour, the glass follows the accent. Percent-encoding a data URI
49
+ would also have cost 4x the bytes. Delete it and the header still
50
+ works. */
51
+ .mark { display: block; width: 56px; height: 56px; margin: 0 auto 14px; }
60
52
  h1 { margin: 0; font-size: 26px; letter-spacing: -0.02em; font-weight: 650; }
61
53
  .sub { margin: 5px 0 0; color: var(--muted); font-size: 13.5px; }
62
54