@nervekit/desktop 0.1.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.
Files changed (87) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +5 -0
  3. package/build/README.md +1 -0
  4. package/build/icons/1024x1024.png +0 -0
  5. package/build/icons/128x128.png +0 -0
  6. package/build/icons/16x16.png +0 -0
  7. package/build/icons/24x24.png +0 -0
  8. package/build/icons/256x256.png +0 -0
  9. package/build/icons/32x32.png +0 -0
  10. package/build/icons/48x48.png +0 -0
  11. package/build/icons/512x512.png +0 -0
  12. package/build/icons/64x64.png +0 -0
  13. package/build/tray/tray-dark-16.png +0 -0
  14. package/build/tray/tray-dark-24.png +0 -0
  15. package/build/tray/tray-dark.png +0 -0
  16. package/build/tray/tray-light-16.png +0 -0
  17. package/build/tray/tray-light-24.png +0 -0
  18. package/build/tray/tray-light.png +0 -0
  19. package/build/tray/tray-template-16.png +0 -0
  20. package/build/tray/tray-template-24.png +0 -0
  21. package/build/tray/tray-template.png +0 -0
  22. package/dist/app/cli-options.d.ts +7 -0
  23. package/dist/app/cli-options.d.ts.map +1 -0
  24. package/dist/app/cli-options.js +101 -0
  25. package/dist/app/cli-options.js.map +1 -0
  26. package/dist/bin.d.ts +3 -0
  27. package/dist/bin.d.ts.map +1 -0
  28. package/dist/bin.js +75 -0
  29. package/dist/bin.js.map +1 -0
  30. package/dist/daemon-helpers.d.ts +31 -0
  31. package/dist/daemon-helpers.d.ts.map +1 -0
  32. package/dist/daemon-helpers.js +224 -0
  33. package/dist/daemon-helpers.js.map +1 -0
  34. package/dist/daemon.d.ts +38 -0
  35. package/dist/daemon.d.ts.map +1 -0
  36. package/dist/daemon.js +494 -0
  37. package/dist/daemon.js.map +1 -0
  38. package/dist/electron.d.ts +3 -0
  39. package/dist/electron.d.ts.map +1 -0
  40. package/dist/electron.js +4 -0
  41. package/dist/electron.js.map +1 -0
  42. package/dist/ipc/notifications-ipc.d.ts +4 -0
  43. package/dist/ipc/notifications-ipc.d.ts.map +1 -0
  44. package/dist/ipc/notifications-ipc.js +42 -0
  45. package/dist/ipc/notifications-ipc.js.map +1 -0
  46. package/dist/ipc/window-ipc.d.ts +15 -0
  47. package/dist/ipc/window-ipc.d.ts.map +1 -0
  48. package/dist/ipc/window-ipc.js +71 -0
  49. package/dist/ipc/window-ipc.js.map +1 -0
  50. package/dist/logging.d.ts +7 -0
  51. package/dist/logging.d.ts.map +1 -0
  52. package/dist/logging.js +61 -0
  53. package/dist/logging.js.map +1 -0
  54. package/dist/main.d.ts +2 -0
  55. package/dist/main.d.ts.map +1 -0
  56. package/dist/main.js +328 -0
  57. package/dist/main.js.map +1 -0
  58. package/dist/preload.cjs +44 -0
  59. package/dist/settings/desktop-settings.d.ts +4 -0
  60. package/dist/settings/desktop-settings.d.ts.map +1 -0
  61. package/dist/settings/desktop-settings.js +45 -0
  62. package/dist/settings/desktop-settings.js.map +1 -0
  63. package/dist/shared/ozone-platform.d.ts +3 -0
  64. package/dist/shared/ozone-platform.d.ts.map +1 -0
  65. package/dist/shared/ozone-platform.js +16 -0
  66. package/dist/shared/ozone-platform.js.map +1 -0
  67. package/dist/tray/tray.d.ts +21 -0
  68. package/dist/tray/tray.d.ts.map +1 -0
  69. package/dist/tray/tray.js +122 -0
  70. package/dist/tray/tray.js.map +1 -0
  71. package/dist/types.d.ts +26 -0
  72. package/dist/types.d.ts.map +1 -0
  73. package/dist/types.js +2 -0
  74. package/dist/types.js.map +1 -0
  75. package/dist/window/loading-pages.d.ts +4 -0
  76. package/dist/window/loading-pages.d.ts.map +1 -0
  77. package/dist/window/loading-pages.js +137 -0
  78. package/dist/window/loading-pages.js.map +1 -0
  79. package/dist/window/navigation-guards.d.ts +3 -0
  80. package/dist/window/navigation-guards.d.ts.map +1 -0
  81. package/dist/window/navigation-guards.js +53 -0
  82. package/dist/window/navigation-guards.js.map +1 -0
  83. package/dist/window/preload-paths.d.ts +5 -0
  84. package/dist/window/preload-paths.d.ts.map +1 -0
  85. package/dist/window/preload-paths.js +31 -0
  86. package/dist/window/preload-paths.js.map +1 -0
  87. package/package.json +54 -0
@@ -0,0 +1,21 @@
1
+ import type { ManagedDaemon } from "../daemon.js";
2
+ import type { BrowserWindowType } from "../electron.js";
3
+ import type { QuitOptions } from "../types.js";
4
+ interface TrayControllerDependencies {
5
+ getMainWindow: () => BrowserWindowType | undefined;
6
+ getManagedDaemon: () => ManagedDaemon | undefined;
7
+ showMainWindow: () => void | Promise<void>;
8
+ hideWindow: (window: BrowserWindowType) => void;
9
+ requestQuit: (options?: QuitOptions) => void;
10
+ restartDaemon: () => void;
11
+ }
12
+ export interface TrayController {
13
+ hasTray(): boolean;
14
+ ensureTray(): void;
15
+ updateTrayMenu(): void;
16
+ updateTrayIcon(): void;
17
+ }
18
+ export declare function createTrayController(dependencies: TrayControllerDependencies): TrayController;
19
+ export declare function installTrayThemeRefresh(updateTrayIcon: () => void): void;
20
+ export {};
21
+ //# sourceMappingURL=tray.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tray.d.ts","sourceRoot":"","sources":["../../src/tray/tray.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,iBAAiB,EAAyB,MAAM,gBAAgB,CAAC;AAS/E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAG/C,UAAU,0BAA0B;IAClC,aAAa,EAAE,MAAM,iBAAiB,GAAG,SAAS,CAAC;IACnD,gBAAgB,EAAE,MAAM,aAAa,GAAG,SAAS,CAAC;IAClD,cAAc,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,UAAU,EAAE,CAAC,MAAM,EAAE,iBAAiB,KAAK,IAAI,CAAC;IAChD,WAAW,EAAE,CAAC,OAAO,CAAC,EAAE,WAAW,KAAK,IAAI,CAAC;IAC7C,aAAa,EAAE,MAAM,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,IAAI,OAAO,CAAC;IACnB,UAAU,IAAI,IAAI,CAAC;IACnB,cAAc,IAAI,IAAI,CAAC;IACvB,cAAc,IAAI,IAAI,CAAC;CACxB;AAED,wBAAgB,oBAAoB,CAClC,YAAY,EAAE,0BAA0B,GACvC,cAAc,CAsGhB;AAwBD,wBAAgB,uBAAuB,CAAC,cAAc,EAAE,MAAM,IAAI,GAAG,IAAI,CAExE"}
@@ -0,0 +1,122 @@
1
+ import { clipboard, Menu, nativeImage, nativeTheme, shell, Tray, } from "../electron.js";
2
+ import { resolveTrayIconPath } from "../window/preload-paths.js";
3
+ export function createTrayController(dependencies) {
4
+ let tray;
5
+ function ensureTray() {
6
+ if (tray)
7
+ return;
8
+ tray = new Tray(createTrayIcon());
9
+ tray.setToolTip("Nerve");
10
+ tray.on("click", () => {
11
+ void dependencies.showMainWindow();
12
+ });
13
+ updateTrayMenu();
14
+ }
15
+ function updateTrayMenu() {
16
+ if (!tray)
17
+ return;
18
+ const mainWindow = dependencies.getMainWindow();
19
+ const managedDaemon = dependencies.getManagedDaemon();
20
+ tray.setContextMenu(Menu.buildFromTemplate([
21
+ {
22
+ label: "Show Nerve",
23
+ click: () => {
24
+ void dependencies.showMainWindow();
25
+ },
26
+ },
27
+ {
28
+ label: "Hide to Tray",
29
+ enabled: Boolean(mainWindow?.isVisible()),
30
+ click: () => {
31
+ if (mainWindow)
32
+ dependencies.hideWindow(mainWindow);
33
+ },
34
+ },
35
+ {
36
+ label: daemonTargetLabel(managedDaemon),
37
+ enabled: false,
38
+ },
39
+ {
40
+ label: "Restart Daemon",
41
+ enabled: Boolean(managedDaemon?.owned),
42
+ click: () => dependencies.restartDaemon(),
43
+ },
44
+ {
45
+ label: "Open in Browser",
46
+ enabled: Boolean(managedDaemon?.url),
47
+ click: () => {
48
+ if (managedDaemon?.url)
49
+ void shell.openExternal(managedDaemon.url);
50
+ },
51
+ },
52
+ {
53
+ label: managedDaemon?.mobileSetupUrl
54
+ ? "Copy Mobile Setup URL"
55
+ : managedDaemon?.shareUrl
56
+ ? "Copy Mobile URL"
57
+ : "Copy Mobile URL (unavailable)",
58
+ enabled: Boolean(managedDaemon?.mobileSetupUrl ?? managedDaemon?.shareUrl),
59
+ click: () => {
60
+ const url = managedDaemon?.mobileSetupUrl ?? managedDaemon?.shareUrl;
61
+ if (url)
62
+ clipboard.writeText(url);
63
+ },
64
+ },
65
+ {
66
+ label: "Copy HTTPS App URL",
67
+ enabled: Boolean(managedDaemon?.secureShareUrl),
68
+ click: () => {
69
+ if (managedDaemon?.secureShareUrl)
70
+ clipboard.writeText(managedDaemon.secureShareUrl);
71
+ },
72
+ },
73
+ {
74
+ label: "Copy CA Certificate URL",
75
+ enabled: Boolean(managedDaemon?.caCertUrl),
76
+ click: () => {
77
+ if (managedDaemon?.caCertUrl)
78
+ clipboard.writeText(managedDaemon.caCertUrl);
79
+ },
80
+ },
81
+ { type: "separator" },
82
+ {
83
+ label: "Quit",
84
+ click: () => dependencies.requestQuit({
85
+ source: "tray-quit",
86
+ hideWindows: true,
87
+ }),
88
+ },
89
+ ]));
90
+ }
91
+ function updateTrayIcon() {
92
+ if (!tray)
93
+ return;
94
+ tray.setImage(createTrayIcon());
95
+ }
96
+ function hasTray() {
97
+ return Boolean(tray);
98
+ }
99
+ return { ensureTray, hasTray, updateTrayIcon, updateTrayMenu };
100
+ }
101
+ function daemonTargetLabel(managedDaemon) {
102
+ if (!managedDaemon)
103
+ return "Daemon: starting";
104
+ const status = managedDaemon.getStatus();
105
+ const suffix = status === "restarting"
106
+ ? " — reconnecting…"
107
+ : status === "failed"
108
+ ? " — unavailable"
109
+ : "";
110
+ if (managedDaemon.mode === "remote")
111
+ return `Remote daemon: ${managedDaemon.url}${suffix}`;
112
+ return `${managedDaemon.owned ? "Local daemon: owned" : "Local daemon: existing"}${suffix}`;
113
+ }
114
+ function createTrayIcon() {
115
+ const image = nativeImage.createFromPath(resolveTrayIconPath());
116
+ image.setTemplateImage(process.platform === "darwin");
117
+ return image;
118
+ }
119
+ export function installTrayThemeRefresh(updateTrayIcon) {
120
+ nativeTheme.on("updated", updateTrayIcon);
121
+ }
122
+ //# sourceMappingURL=tray.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tray.js","sourceRoot":"","sources":["../../src/tray/tray.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ export interface DesktopWindowState {
2
+ maximized: boolean;
3
+ focused: boolean;
4
+ }
5
+ export interface DesktopNotificationPayload {
6
+ title: string;
7
+ body?: string;
8
+ urgency?: "normal" | "attention";
9
+ }
10
+ export type QuitSource = "startup-error" | "titlebar-close" | "native-window-close" | "tray-quit" | "signal" | "unknown";
11
+ export interface QuitOptions {
12
+ source?: QuitSource;
13
+ hideWindows?: boolean;
14
+ signal?: NodeJS.Signals;
15
+ }
16
+ export interface DesktopCliOptions {
17
+ mode?: "local" | "remote";
18
+ remoteUrl?: string;
19
+ token?: string;
20
+ host?: string;
21
+ port?: number;
22
+ httpsPort?: number;
23
+ allowRemote?: boolean;
24
+ mobileHttps?: boolean;
25
+ }
26
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,0BAA0B;IACzC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,QAAQ,GAAG,WAAW,CAAC;CAClC;AAED,MAAM,MAAM,UAAU,GAClB,eAAe,GACf,gBAAgB,GAChB,qBAAqB,GACrB,WAAW,GACX,QAAQ,GACR,SAAS,CAAC;AAEd,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,4 @@
1
+ export declare function createDataUrl(html: string): string;
2
+ export declare function loadingHtml(statusText?: string): string;
3
+ export declare function errorHtml(error: unknown): string;
4
+ //# sourceMappingURL=loading-pages.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loading-pages.d.ts","sourceRoot":"","sources":["../../src/window/loading-pages.ts"],"names":[],"mappings":"AAEA,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAElD;AAED,wBAAgB,WAAW,CAAC,UAAU,SAA2B,GAAG,MAAM,CAgBzE;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAkBhD"}
@@ -0,0 +1,137 @@
1
+ import { escapeHtml } from "@nervekit/shared";
2
+ export function createDataUrl(html) {
3
+ return `data:text/html;charset=utf-8,${encodeURIComponent(html)}`;
4
+ }
5
+ export function loadingHtml(statusText = "Starting local daemon…") {
6
+ return `<!doctype html>
7
+ <html lang="en">
8
+ <head>
9
+ <meta charset="UTF-8" />
10
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
11
+ <title>Nerve</title>
12
+ <style>${shellStyles()}</style>
13
+ </head>
14
+ <body>
15
+ <main class="loading" aria-live="polite" aria-label="Starting Nerve">
16
+ <div class="spinner" aria-hidden="true"></div>
17
+ <p class="status">${escapeHtml(statusText)}</p>
18
+ </main>
19
+ </body>
20
+ </html>`;
21
+ }
22
+ export function errorHtml(error) {
23
+ const message = error instanceof Error ? error.message : String(error);
24
+ return `<!doctype html>
25
+ <html lang="en">
26
+ <head>
27
+ <meta charset="UTF-8" />
28
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
29
+ <title>Nerve startup error</title>
30
+ <style>${shellStyles()}</style>
31
+ </head>
32
+ <body>
33
+ <main class="error">
34
+ <h1 class="error-title">Daemon unavailable</h1>
35
+ <p class="status">Could not start the local daemon. Use the Nerve tray menu → “Restart Daemon” to try again.</p>
36
+ <pre>${escapeHtml(message)}</pre>
37
+ </main>
38
+ </body>
39
+ </html>`;
40
+ }
41
+ function shellStyles() {
42
+ // Mirrors the shadcn theme tokens from packages/web/src/app.css
43
+ // so the splash matches the workbench in both light and dark.
44
+ return `
45
+ :root {
46
+ color-scheme: light dark;
47
+ --background: oklch(1 0 0);
48
+ --foreground: oklch(0.147 0.004 49.3);
49
+ --muted-foreground: oklch(0.547 0.021 43.1);
50
+ --border: oklch(0.922 0.005 34.3);
51
+ --destructive: oklch(0.577 0.245 27.325);
52
+ --font-sans: Geist, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
53
+ --font-mono: "Iosevka", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
54
+ --text-xs: 0.75rem;
55
+ --text-sm: 0.875rem;
56
+ --text-xl: 1.25rem;
57
+ font-family: var(--font-sans);
58
+ }
59
+ @media (prefers-color-scheme: dark) {
60
+ :root {
61
+ --background: oklch(0.147 0.004 49.3);
62
+ --foreground: oklch(0.986 0.002 67.8);
63
+ --muted-foreground: oklch(0.714 0.014 41.2);
64
+ --border: oklch(1 0 0 / 10%);
65
+ --destructive: oklch(0.704 0.191 22.216);
66
+ }
67
+ }
68
+ * { box-sizing: border-box; }
69
+ body {
70
+ margin: 0;
71
+ min-height: 100vh;
72
+ display: grid;
73
+ place-items: center;
74
+ background: var(--background);
75
+ color: var(--foreground);
76
+ user-select: none;
77
+ -webkit-app-region: drag;
78
+ }
79
+ main {
80
+ width: min(420px, calc(100vw - 48px));
81
+ display: grid;
82
+ justify-items: center;
83
+ gap: 16px;
84
+ padding: 24px;
85
+ text-align: center;
86
+ }
87
+ .loading {
88
+ gap: 14px;
89
+ }
90
+ .spinner {
91
+ width: 32px;
92
+ height: 32px;
93
+ border: 2px solid var(--border);
94
+ border-top-color: var(--foreground);
95
+ border-radius: 999px;
96
+ animation: spin 0.9s linear infinite;
97
+ }
98
+ .error-title {
99
+ margin: 0;
100
+ color: var(--foreground);
101
+ font-size: var(--text-xl);
102
+ font-weight: 600;
103
+ letter-spacing: -0.01em;
104
+ }
105
+ .status {
106
+ margin: 0;
107
+ color: var(--muted-foreground);
108
+ font-size: var(--text-sm);
109
+ line-height: 1.5;
110
+ }
111
+ pre {
112
+ width: 100%;
113
+ max-height: 280px;
114
+ margin: 4px 0 0;
115
+ overflow: auto;
116
+ user-select: text;
117
+ -webkit-app-region: no-drag;
118
+ white-space: pre-wrap;
119
+ text-align: left;
120
+ border: 1px solid color-mix(in oklab, var(--destructive) 40%, transparent);
121
+ border-radius: 10px;
122
+ padding: 12px;
123
+ background: color-mix(in oklab, var(--destructive) 10%, transparent);
124
+ color: var(--destructive);
125
+ font-family: var(--font-mono);
126
+ font-size: var(--text-xs);
127
+ line-height: 1.5;
128
+ }
129
+ @keyframes spin {
130
+ to { transform: rotate(360deg); }
131
+ }
132
+ @media (prefers-reduced-motion: reduce) {
133
+ .spinner { animation: none; }
134
+ }
135
+ `;
136
+ }
137
+ //# sourceMappingURL=loading-pages.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loading-pages.js","sourceRoot":"","sources":["../../src/window/loading-pages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,OAAO,gCAAgC,kBAAkB,CAAC,IAAI,CAAC,EAAE,CAAC;AACpE,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,UAAU,GAAG,wBAAwB;IAC/D,OAAO;;;;;;aAMI,WAAW,EAAE;;;;;0BAKA,UAAU,CAAC,UAAU,CAAC;;;QAGxC,CAAC;AACT,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,KAAc;IACtC,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACvE,OAAO;;;;;;aAMI,WAAW,EAAE;;;;;;aAMb,UAAU,CAAC,OAAO,CAAC;;;QAGxB,CAAC;AACT,CAAC;AAED,SAAS,WAAW;IAClB,gEAAgE;IAChE,8DAA8D;IAC9D,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2FN,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { BrowserWindowType } from "../electron.js";
2
+ export declare function installNavigationGuards(window: BrowserWindowType, getDaemonUrl: () => string | undefined): void;
3
+ //# sourceMappingURL=navigation-guards.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"navigation-guards.d.ts","sourceRoot":"","sources":["../../src/window/navigation-guards.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAGxD,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,iBAAiB,EACzB,YAAY,EAAE,MAAM,MAAM,GAAG,SAAS,GACrC,IAAI,CAoBN"}
@@ -0,0 +1,53 @@
1
+ import { shell } from "../electron.js";
2
+ export function installNavigationGuards(window, getDaemonUrl) {
3
+ window.webContents.setWindowOpenHandler(({ url }) => {
4
+ if (isAllowedDaemonUrl(url, getDaemonUrl()))
5
+ void window.loadURL(url);
6
+ else
7
+ openExternallyIfSafe(url);
8
+ return { action: "deny" };
9
+ });
10
+ window.webContents.on("will-navigate", (event, url) => {
11
+ if (isInternalShellUrl(url) || isAllowedDaemonUrl(url, getDaemonUrl()))
12
+ return;
13
+ event.preventDefault();
14
+ openExternallyIfSafe(url);
15
+ });
16
+ window.webContents.on("will-redirect", (event, url) => {
17
+ if (isInternalShellUrl(url) || isAllowedDaemonUrl(url, getDaemonUrl()))
18
+ return;
19
+ event.preventDefault();
20
+ openExternallyIfSafe(url);
21
+ });
22
+ }
23
+ function isAllowedDaemonUrl(rawUrl, daemonUrl) {
24
+ if (!daemonUrl)
25
+ return false;
26
+ try {
27
+ return new URL(rawUrl).origin === new URL(daemonUrl).origin;
28
+ }
29
+ catch {
30
+ return false;
31
+ }
32
+ }
33
+ function isInternalShellUrl(rawUrl) {
34
+ try {
35
+ const url = new URL(rawUrl);
36
+ return url.protocol === "data:";
37
+ }
38
+ catch {
39
+ return false;
40
+ }
41
+ }
42
+ function openExternallyIfSafe(rawUrl) {
43
+ try {
44
+ const url = new URL(rawUrl);
45
+ if (url.protocol === "http:" || url.protocol === "https:") {
46
+ void shell.openExternal(url.toString());
47
+ }
48
+ }
49
+ catch {
50
+ // Ignore malformed navigation targets.
51
+ }
52
+ }
53
+ //# sourceMappingURL=navigation-guards.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"navigation-guards.js","sourceRoot":"","sources":["../../src/window/navigation-guards.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAEvC,MAAM,UAAU,uBAAuB,CACrC,MAAyB,EACzB,YAAsC;IAEtC,MAAM,CAAC,WAAW,CAAC,oBAAoB,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE;QAClD,IAAI,kBAAkB,CAAC,GAAG,EAAE,YAAY,EAAE,CAAC;YAAE,KAAK,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;;YACjE,oBAAoB,CAAC,GAAG,CAAC,CAAC;QAC/B,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IAC5B,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,eAAe,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QACpD,IAAI,kBAAkB,CAAC,GAAG,CAAC,IAAI,kBAAkB,CAAC,GAAG,EAAE,YAAY,EAAE,CAAC;YACpE,OAAO;QACT,KAAK,CAAC,cAAc,EAAE,CAAC;QACvB,oBAAoB,CAAC,GAAG,CAAC,CAAC;IAC5B,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,eAAe,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QACpD,IAAI,kBAAkB,CAAC,GAAG,CAAC,IAAI,kBAAkB,CAAC,GAAG,EAAE,YAAY,EAAE,CAAC;YACpE,OAAO;QACT,KAAK,CAAC,cAAc,EAAE,CAAC;QACvB,oBAAoB,CAAC,GAAG,CAAC,CAAC;IAC5B,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,kBAAkB,CACzB,MAAc,EACd,SAA6B;IAE7B,IAAI,CAAC,SAAS;QAAE,OAAO,KAAK,CAAC;IAC7B,IAAI,CAAC;QACH,OAAO,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,MAAM,KAAK,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC;IAC9D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,SAAS,kBAAkB,CAAC,MAAc;IACxC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;QAC5B,OAAO,GAAG,CAAC,QAAQ,KAAK,OAAO,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,SAAS,oBAAoB,CAAC,MAAc;IAC1C,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;QAC5B,IAAI,GAAG,CAAC,QAAQ,KAAK,OAAO,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;YAC1D,KAAK,KAAK,CAAC,YAAY,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC;QAC1C,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,uCAAuC;IACzC,CAAC;AACH,CAAC"}
@@ -0,0 +1,5 @@
1
+ export declare function resolvePreloadPath(): string;
2
+ export declare function resolvePackagedWebDistPath(): string | undefined;
3
+ export declare function resolveAppIconPath(): string;
4
+ export declare function resolveTrayIconPath(): string;
5
+ //# sourceMappingURL=preload-paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preload-paths.d.ts","sourceRoot":"","sources":["../../src/window/preload-paths.ts"],"names":[],"mappings":"AAKA,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAED,wBAAgB,0BAA0B,IAAI,MAAM,GAAG,SAAS,CAG/D;AAED,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAED,wBAAgB,mBAAmB,IAAI,MAAM,CAQ5C"}
@@ -0,0 +1,31 @@
1
+ import { existsSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { app, nativeTheme } from "../electron.js";
5
+ export function resolvePreloadPath() {
6
+ return join(dirname(fileURLToPath(import.meta.url)), "..", "preload.cjs");
7
+ }
8
+ export function resolvePackagedWebDistPath() {
9
+ if (!app.isPackaged)
10
+ return undefined;
11
+ return join(process.resourcesPath, "web-dist");
12
+ }
13
+ export function resolveAppIconPath() {
14
+ return resolveDesktopAssetPath("build", "icons", "512x512.png");
15
+ }
16
+ export function resolveTrayIconPath() {
17
+ const name = process.platform === "darwin"
18
+ ? "tray-template.png"
19
+ : nativeTheme.shouldUseDarkColors
20
+ ? "tray-dark.png"
21
+ : "tray-light.png";
22
+ return resolveDesktopAssetPath("build", "tray", name);
23
+ }
24
+ function resolveDesktopAssetPath(...segments) {
25
+ const moduleDir = dirname(fileURLToPath(import.meta.url));
26
+ const packageRelativePath = join(moduleDir, "..", "..", ...segments);
27
+ if (existsSync(packageRelativePath))
28
+ return packageRelativePath;
29
+ return join(process.resourcesPath, "app.asar.unpacked", ...segments);
30
+ }
31
+ //# sourceMappingURL=preload-paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preload-paths.js","sourceRoot":"","sources":["../../src/window/preload-paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAElD,MAAM,UAAU,kBAAkB;IAChC,OAAO,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,aAAa,CAAC,CAAC;AAC5E,CAAC;AAED,MAAM,UAAU,0BAA0B;IACxC,IAAI,CAAC,GAAG,CAAC,UAAU;QAAE,OAAO,SAAS,CAAC;IACtC,OAAO,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,UAAU,CAAC,CAAC;AACjD,CAAC;AAED,MAAM,UAAU,kBAAkB;IAChC,OAAO,uBAAuB,CAAC,OAAO,EAAE,OAAO,EAAE,aAAa,CAAC,CAAC;AAClE,CAAC;AAED,MAAM,UAAU,mBAAmB;IACjC,MAAM,IAAI,GACR,OAAO,CAAC,QAAQ,KAAK,QAAQ;QAC3B,CAAC,CAAC,mBAAmB;QACrB,CAAC,CAAC,WAAW,CAAC,mBAAmB;YAC/B,CAAC,CAAC,eAAe;YACjB,CAAC,CAAC,gBAAgB,CAAC;IACzB,OAAO,uBAAuB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;AACxD,CAAC;AAED,SAAS,uBAAuB,CAAC,GAAG,QAAkB;IACpD,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1D,MAAM,mBAAmB,GAAG,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,QAAQ,CAAC,CAAC;IACrE,IAAI,UAAU,CAAC,mBAAmB,CAAC;QAAE,OAAO,mBAAmB,CAAC;IAEhE,OAAO,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,mBAAmB,EAAE,GAAG,QAAQ,CAAC,CAAC;AACvE,CAAC"}
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@nervekit/desktop",
3
+ "version": "0.1.0",
4
+ "description": "UI-first personal AI coding harness.",
5
+ "author": "ThilinaTLM",
6
+ "license": "Apache-2.0",
7
+ "type": "module",
8
+ "publishConfig": {
9
+ "access": "public",
10
+ "registry": "https://registry.npmjs.org"
11
+ },
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/ThilinaTLM/nerve.git",
15
+ "directory": "packages/desktop"
16
+ },
17
+ "homepage": "https://github.com/ThilinaTLM/nerve#readme",
18
+ "desktopName": "nerve-desktop",
19
+ "main": "dist/main.js",
20
+ "bin": {
21
+ "nerve-desktop": "./dist/bin.js"
22
+ },
23
+ "files": [
24
+ "dist/**",
25
+ "build/**",
26
+ "package.json",
27
+ "LICENSE",
28
+ "NOTICE"
29
+ ],
30
+ "dependencies": {
31
+ "electron": "^42.0.0",
32
+ "@nervekit/orchestrator": "0.1.0",
33
+ "@nervekit/shared": "0.1.0"
34
+ },
35
+ "devDependencies": {
36
+ "@types/node": "24.10.1",
37
+ "electron-builder": "^26.15.0",
38
+ "sharp": "^0.34.5",
39
+ "typescript": "5.9.3"
40
+ },
41
+ "engines": {
42
+ "node": ">=24.0.0"
43
+ },
44
+ "scripts": {
45
+ "icons": "node scripts/generate-icons.mjs",
46
+ "build": "pnpm run icons && tsc -b && node scripts/copy-preload.mjs",
47
+ "check": "tsc -b --pretty false",
48
+ "start": "node scripts/start-electron.mjs",
49
+ "build:deps": "pnpm --filter @nervekit/shared --filter @nervekit/agent --filter @nervekit/tools build",
50
+ "dev": "pnpm run build:deps && pnpm --filter @nervekit/web build && pnpm --filter @nervekit/orchestrator build && node ../../scripts/copy-web-dist-to-orchestrator.mjs && pnpm --filter @nervekit/desktop build && node scripts/start-electron.mjs",
51
+ "build:package": "pnpm run build:deps && pnpm --filter @nervekit/web build && pnpm --filter @nervekit/orchestrator build && node ../../scripts/copy-web-dist-to-orchestrator.mjs && pnpm --filter @nervekit/desktop build && electron-builder --publish never",
52
+ "package:linux": "pnpm run build:deps && pnpm --filter @nervekit/web build && pnpm --filter @nervekit/orchestrator build && node ../../scripts/copy-web-dist-to-orchestrator.mjs && pnpm --filter @nervekit/desktop build && electron-builder --linux AppImage deb rpm --publish never"
53
+ }
54
+ }