@holochain/hc-spin 0.200.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 (48) hide show
  1. package/.editorconfig +9 -0
  2. package/.eslintignore +4 -0
  3. package/.eslintrc +31 -0
  4. package/.prettierignore +6 -0
  5. package/.prettierrc.yaml +3 -0
  6. package/.vscode/extensions.json +3 -0
  7. package/.vscode/launch.json +39 -0
  8. package/.vscode/settings.json +12 -0
  9. package/.yarnrc.yml +1 -0
  10. package/README.md +48 -0
  11. package/build/entitlements.mac.plist +12 -0
  12. package/build/icon.icns +0 -0
  13. package/build/icon.ico +0 -0
  14. package/build/icon.png +0 -0
  15. package/cli/cli.js +18 -0
  16. package/dist/cli.js +18 -0
  17. package/dist/main/index.js +13513 -0
  18. package/dist/preload/index.js +5 -0
  19. package/dist/renderer/assets/renderer-2UdJ5Bnz.js +1 -0
  20. package/dist/renderer/index.html +44 -0
  21. package/dist/renderer/indexNotFound1.html +44 -0
  22. package/dist/renderer/indexNotFound2.html +44 -0
  23. package/docs/DEVSETUP.md +36 -0
  24. package/electron.vite.config.ts +22 -0
  25. package/package.json +51 -0
  26. package/resources/icon.png +0 -0
  27. package/rust-utils/.yarnrc.yml +1 -0
  28. package/rust-utils/Cargo.toml +44 -0
  29. package/rust-utils/build.rs +5 -0
  30. package/rust-utils/index.d.ts +33 -0
  31. package/rust-utils/index.js +258 -0
  32. package/rust-utils/package.json +31 -0
  33. package/rust-utils/src/decode_webhapp.rs +112 -0
  34. package/rust-utils/src/lib.rs +7 -0
  35. package/rust-utils/src/types.rs +52 -0
  36. package/rust-utils/src/utils.rs +4 -0
  37. package/rust-utils/src/zome_call_signer.rs +99 -0
  38. package/src/main/index.ts +305 -0
  39. package/src/main/validateArgs.ts +90 -0
  40. package/src/main/windows.ts +178 -0
  41. package/src/preload/index.ts +8 -0
  42. package/src/renderer/index.html +44 -0
  43. package/src/renderer/indexNotFound1.html +44 -0
  44. package/src/renderer/indexNotFound2.html +44 -0
  45. package/src/renderer/src/renderer.ts +1 -0
  46. package/tsconfig.json +4 -0
  47. package/tsconfig.node.json +8 -0
  48. package/tsconfig.web.json +7 -0
@@ -0,0 +1,178 @@
1
+ import path from 'path';
2
+ import fs from 'fs';
3
+ import url from 'url';
4
+ import { InstalledAppId } from '@holochain/client';
5
+ import { BrowserWindow, NativeImage, nativeImage, net, session, shell } from 'electron';
6
+ import { is } from '@electron-toolkit/utils';
7
+ import { HappOrWebhappPath } from './validateArgs';
8
+
9
+ export type UISource =
10
+ | {
11
+ type: 'path';
12
+ path: string;
13
+ }
14
+ | {
15
+ type: 'port';
16
+ port: number;
17
+ };
18
+
19
+ export const createHappWindow = async (
20
+ uiSource: UISource,
21
+ happOrWebhappPath: HappOrWebhappPath,
22
+ appId: InstalledAppId,
23
+ agentNum: number,
24
+ appPort: number,
25
+ appDataRootDir: string,
26
+ ): Promise<BrowserWindow> => {
27
+ // TODO create mapping between installed-app-id's and window ids
28
+ if (!appPort) throw new Error('App port not defined.');
29
+
30
+ const partition = `persist:${agentNum}:${appId}`;
31
+
32
+ if (uiSource.type === 'path') {
33
+ const ses = session.fromPartition(partition);
34
+ ses.protocol.handle('webhapp', async (request) => {
35
+ const uriWithoutProtocol = request.url.slice('webhapp://'.length);
36
+ const filePathComponents = uriWithoutProtocol.split('/').slice(1);
37
+ const filePath = path.join(...filePathComponents);
38
+ return net.fetch(url.pathToFileURL(path.join(uiSource.path, filePath)).toString());
39
+ });
40
+ }
41
+
42
+ // Extend preload script to add window.__HC_LAUNCHER_ENV__
43
+ let preloadScript = fs.readFileSync(path.join(__dirname, '../preload/index.js')).toString();
44
+
45
+ preloadScript += `
46
+ electron.contextBridge.exposeInMainWorld("__HC_LAUNCHER_ENV__", {
47
+ APP_INTERFACE_PORT: ${appPort},
48
+ INSTALLED_APP_ID: "${appId}",
49
+ FRAMEWORK: "electron"
50
+ });
51
+ `;
52
+
53
+ const preloadPath = path.join(appDataRootDir, `preload-${agentNum}-${appId}.js`);
54
+
55
+ fs.writeFileSync(preloadPath, preloadScript);
56
+
57
+ let icon: NativeImage | undefined;
58
+
59
+ if (uiSource.type === 'path') {
60
+ const iconPath = path.join(uiSource.path, 'icon.png');
61
+ if (!fs.existsSync(iconPath) && agentNum === 1) {
62
+ console.warn(
63
+ '\n\n+++++ WARNING +++++\n[hc-spin] No icon.png found. It is recommended to put an icon.png file (1024x1024 pixel) in the root of your UI assets directory which can be used by the Holochain Launcher.\n+++++++++++++++++++\n\n',
64
+ );
65
+ }
66
+ icon = nativeImage.createFromPath(iconPath);
67
+ } else {
68
+ try {
69
+ const iconResponse = await net.fetch(`http://127.0.0.1:${uiSource.port}/icon.png`);
70
+ const buffer = await iconResponse.arrayBuffer();
71
+ if (buffer.byteLength === 0 && agentNum === 1) {
72
+ console.warn(
73
+ '\n\n+++++ WARNING +++++\n[hc-spin] No icon.png found. It is recommended to put an icon.png file (1024x1024 pixel) in the root of your UI assets directory which can be used by the Holochain Launcher.\n+++++++++++++++++++\n\n',
74
+ );
75
+ }
76
+ icon = nativeImage.createFromBuffer(Buffer.from(buffer));
77
+ } catch (e) {
78
+ console.error('Failed to get icon.png: ', e);
79
+ }
80
+ }
81
+
82
+ const happWindow = new BrowserWindow({
83
+ width: 1200,
84
+ height: 800,
85
+ show: false,
86
+ icon,
87
+ title: `Agent ${agentNum} - ${appId}`,
88
+ webPreferences: {
89
+ preload: preloadPath,
90
+ partition,
91
+ },
92
+ });
93
+
94
+ const [windowPositionX, windowPositionY] = happWindow.getPosition();
95
+ const windowPositionXMoved = windowPositionX + agentNum * 20;
96
+ const windowPositionYMoved = windowPositionY + agentNum * 20;
97
+ happWindow.setPosition(windowPositionXMoved, windowPositionYMoved);
98
+
99
+ happWindow.menuBarVisible = false;
100
+
101
+ setLinkOpenHandlers(happWindow);
102
+
103
+ happWindow.on('page-title-updated', (evt) => {
104
+ evt.preventDefault();
105
+ });
106
+
107
+ happWindow.webContents.openDevTools();
108
+
109
+ if (uiSource.type === 'port') {
110
+ try {
111
+ // Check whether dev server is responsive and index.html exists
112
+ await net.fetch(`http://127.0.0.1:${uiSource.port}/index.html`);
113
+ } catch (e) {
114
+ console.error(`No index.html file found at http://127.0.0.1:${uiSource.port}/index.html`, e);
115
+ if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
116
+ happWindow.loadURL(process.env['ELECTRON_RENDERER_URL']);
117
+ } else {
118
+ happWindow.loadFile(path.join(__dirname, '../renderer/index.html'));
119
+ }
120
+ happWindow.show();
121
+ return happWindow;
122
+ }
123
+ await happWindow.loadURL(`http://127.0.0.1:${uiSource.port}`);
124
+ } else if (uiSource.type === 'path') {
125
+ try {
126
+ await happWindow.loadURL(`webhapp://webhappwindow/index.html`);
127
+ } catch (e) {
128
+ console.error('[ERROR] Failed to fetch index.html');
129
+
130
+ if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
131
+ happWindow.loadURL(process.env['ELECTRON_RENDERER_URL']);
132
+ } else {
133
+ const notFoundPath =
134
+ happOrWebhappPath.type === 'webhapp'
135
+ ? path.join(__dirname, '../renderer/indexNotFound1.html')
136
+ : path.join(__dirname, '../renderer/indexNotFound2.html');
137
+ happWindow.loadFile(notFoundPath);
138
+ }
139
+
140
+ happWindow.show();
141
+ return happWindow;
142
+ }
143
+ } else {
144
+ throw new Error('Unsupported uiSource type: ', (uiSource as any).type);
145
+ }
146
+
147
+ happWindow.show();
148
+
149
+ return happWindow;
150
+ };
151
+
152
+ export function setLinkOpenHandlers(browserWindow: BrowserWindow): void {
153
+ // links in happ windows should open in the system default application
154
+ // instead of the webview
155
+ browserWindow.webContents.on('will-navigate', (e) => {
156
+ if (e.url.startsWith('http://localhost') || e.url.startsWith('http://127.0.0.1')) {
157
+ // ignore dev server reload
158
+ return;
159
+ }
160
+ if (
161
+ e.url.startsWith('http://') ||
162
+ e.url.startsWith('https://') ||
163
+ e.url.startsWith('mailto://')
164
+ ) {
165
+ e.preventDefault();
166
+ shell.openExternal(e.url);
167
+ }
168
+ });
169
+
170
+ // Links with target=_blank should open in the system default browser and
171
+ // happ windows are not allowed to spawn new electron windows
172
+ browserWindow.webContents.setWindowOpenHandler((details) => {
173
+ if (details.url.startsWith('http://') || details.url.startsWith('https://')) {
174
+ shell.openExternal(details.url);
175
+ }
176
+ return { action: 'deny' };
177
+ });
178
+ }
@@ -0,0 +1,8 @@
1
+ // See the Electron documentation for details on how to use preload scripts:
2
+ // https://www.electronjs.org/docs/latest/tutorial/process-model#preload-scripts
3
+ import { contextBridge, ipcRenderer } from 'electron';
4
+ import { ZomeCallUnsignedNapi } from 'hc-spin-rust-utils';
5
+
6
+ contextBridge.exposeInMainWorld('electronAPI', {
7
+ signZomeCall: (zomeCall: ZomeCallUnsignedNapi) => ipcRenderer.invoke('sign-zome-call', zomeCall),
8
+ });
@@ -0,0 +1,44 @@
1
+ <!doctype html>
2
+ <html>
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <title>Holochain dev CLI</title>
6
+ <meta
7
+ http-equiv="Content-Security-Policy"
8
+ content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'"
9
+ />
10
+ </head>
11
+
12
+ <body>
13
+ <div class="container">
14
+ <h1>index.html not found.</h1>
15
+ <div style="margin-bottom: 100px">
16
+ Is your dev server running at the port specified with the
17
+ <span class="code">--ui-port</span> option?
18
+ </div>
19
+ </div>
20
+ <script type="module" src="./src/renderer.ts"></script>
21
+ </body>
22
+ </html>
23
+ <style>
24
+ body {
25
+ font-family: Arial, Helvetica, sans-serif;
26
+ margin: 0;
27
+ }
28
+
29
+ .container {
30
+ display: flex;
31
+ height: 100vh;
32
+ margin: 0;
33
+ padding: 0;
34
+ flex-direction: column;
35
+ align-items: center;
36
+ justify-content: center;
37
+ }
38
+
39
+ .code {
40
+ background-color: lightgray;
41
+ border-radius: 3px;
42
+ padding: 1px 3px;
43
+ }
44
+ </style>
@@ -0,0 +1,44 @@
1
+ <!doctype html>
2
+ <html>
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <title>Holochain dev CLI</title>
6
+ <meta
7
+ http-equiv="Content-Security-Policy"
8
+ content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'"
9
+ />
10
+ </head>
11
+
12
+ <body>
13
+ <div class="container">
14
+ <h1>index.html not found.</h1>
15
+ <div style="margin-bottom: 100px">
16
+ Make sure the UI assets in your .webhapp file contain an index.html file at the asset
17
+ folder's root level.
18
+ </div>
19
+ </div>
20
+ <script type="module" src="./src/renderer.ts"></script>
21
+ </body>
22
+ </html>
23
+ <style>
24
+ body {
25
+ font-family: Arial, Helvetica, sans-serif;
26
+ margin: 0;
27
+ }
28
+
29
+ .container {
30
+ display: flex;
31
+ height: 100vh;
32
+ margin: 0;
33
+ padding: 0;
34
+ flex-direction: column;
35
+ align-items: center;
36
+ justify-content: center;
37
+ }
38
+
39
+ .code {
40
+ background-color: lightgray;
41
+ border-radius: 3px;
42
+ padding: 1px 3px;
43
+ }
44
+ </style>
@@ -0,0 +1,44 @@
1
+ <!doctype html>
2
+ <html>
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <title>Holochain dev CLI</title>
6
+ <meta
7
+ http-equiv="Content-Security-Policy"
8
+ content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'"
9
+ />
10
+ </head>
11
+
12
+ <body>
13
+ <div class="container">
14
+ <h1>index.html not found.</h1>
15
+ <div style="margin-bottom: 100px">
16
+ Make sure that the path you provided via the <span class="code">--ui-path</span> option
17
+ contains an index.html file.
18
+ </div>
19
+ </div>
20
+ <script type="module" src="./src/renderer.ts"></script>
21
+ </body>
22
+ </html>
23
+ <style>
24
+ body {
25
+ font-family: Arial, Helvetica, sans-serif;
26
+ margin: 0;
27
+ }
28
+
29
+ .container {
30
+ display: flex;
31
+ height: 100vh;
32
+ margin: 0;
33
+ padding: 0;
34
+ flex-direction: column;
35
+ align-items: center;
36
+ justify-content: center;
37
+ }
38
+
39
+ .code {
40
+ background-color: lightgray;
41
+ border-radius: 3px;
42
+ padding: 1px 3px;
43
+ }
44
+ </style>
@@ -0,0 +1 @@
1
+ console.error('index.html not found.');
package/tsconfig.json ADDED
@@ -0,0 +1,4 @@
1
+ {
2
+ "files": [],
3
+ "references": [{ "path": "./tsconfig.node.json" }, { "path": "./tsconfig.web.json" }]
4
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "extends": "@electron-toolkit/tsconfig/tsconfig.node.json",
3
+ "include": ["electron.vite.config.*", "src/main/*", "src/preload/*"],
4
+ "compilerOptions": {
5
+ "composite": true,
6
+ "types": ["electron-vite/node"]
7
+ }
8
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "extends": "@electron-toolkit/tsconfig/tsconfig.web.json",
3
+ "include": ["src/renderer/**/*.ts", "src/preload/*.d.ts"],
4
+ "compilerOptions": {
5
+ "composite": true
6
+ }
7
+ }