@bobfrankston/msger 0.1.32 → 0.1.34
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 +34 -15
- package/cli.d.ts.map +1 -0
- package/cli.js +10 -2
- package/cli.js.map +1 -1
- package/cli.ts +13 -2
- package/clihandler.d.ts +2 -0
- package/clihandler.d.ts.map +1 -0
- package/clihandler.js +14 -3
- package/clihandler.js.map +1 -1
- package/clihandler.ts +17 -4
- package/index.d.ts.map +1 -0
- package/msger-native/BUILD-TROUBLESHOOTING.md +161 -0
- package/msger-native/Cargo.toml +10 -0
- package/msger-native/build-on-pi.sh +35 -0
- package/msger-native/build-pi.ts.backup +94 -0
- package/msger-native/build-pi.ts.old +94 -0
- package/msger-native/build.ts +21 -25
- package/msger-native/package.json +20 -0
- package/msger-native/src/main.rs +395 -0
- package/msgernative-linux-x64 +0 -0
- package/msgernative-win32-x64.exe +0 -0
- package/package.json +2 -10
- package/postinstall.d.ts.map +1 -0
- package/tsconfig.json +30 -0
- package/apply-detach-fix.d.ts +0 -3
package/README.md
CHANGED
|
@@ -26,6 +26,13 @@ npm install -g @bobfrankston/msger
|
|
|
26
26
|
|
|
27
27
|
The correct binary is automatically selected based on your platform and architecture.
|
|
28
28
|
|
|
29
|
+
**Important: Linux/WSL Requirements**
|
|
30
|
+
- Requires a display server (X11 or Wayland)
|
|
31
|
+
- WSL users need WSLg (Windows 11) or an X server (VcXsrv, Xming)
|
|
32
|
+
- Headless systems cannot display GUI windows
|
|
33
|
+
- If you see "UnsupportedWindowHandle" error, there's no display server
|
|
34
|
+
- Alternative: Use the Windows binary directly instead of WSL
|
|
35
|
+
|
|
29
36
|
## Usage
|
|
30
37
|
|
|
31
38
|
### As a Library (Recommended)
|
|
@@ -55,20 +62,25 @@ msger -t "Hello" -m "Hello, World!"
|
|
|
55
62
|
# With HTML content
|
|
56
63
|
msger -t "Welcome" -html "<h1>Welcome!</h1><p>This is <strong>HTML</strong></p>"
|
|
57
64
|
|
|
58
|
-
# Load a URL (web page or local file)
|
|
65
|
+
# Load a URL (web page or local file) - defaults to 1024x768
|
|
59
66
|
msger -url "https://example.com"
|
|
60
67
|
msger -url "file:///path/to/page.html"
|
|
61
68
|
|
|
62
69
|
# With custom size
|
|
63
|
-
msger -t "Big Window" -m "Hello" -
|
|
70
|
+
msger -t "Big Window" -m "Hello" -size 800,600
|
|
64
71
|
|
|
65
72
|
# With timeout (auto-close after 5 seconds)
|
|
66
73
|
msger -t "Notification" -m "This will close in 5 seconds" -timeout 5
|
|
67
74
|
|
|
75
|
+
# Detached mode - CLI exits immediately, window stays open
|
|
76
|
+
msger -detach -t "Background Window" -m "This window stays open"
|
|
77
|
+
|
|
68
78
|
# See all options
|
|
69
79
|
msger --help
|
|
70
80
|
```
|
|
71
81
|
|
|
82
|
+
**Note on `-detach` mode:** When using `-detach` in the CLI, the message box window opens and the CLI exits immediately without waiting for user interaction. The window remains open independently. This is useful for fire-and-forget notifications. In library mode, `detach: true` makes the Promise resolve immediately with `{button: 'detached'}` while the window stays open.
|
|
83
|
+
|
|
72
84
|
## API
|
|
73
85
|
|
|
74
86
|
### `showMessageBox(options: MessageBoxOptions): Promise<MessageBoxResult>`
|
|
@@ -303,21 +315,28 @@ All binaries are bundled with the npm package, and the correct one is selected a
|
|
|
303
315
|
## CLI Options
|
|
304
316
|
|
|
305
317
|
```
|
|
306
|
-
Usage: msger [options]
|
|
318
|
+
Usage: msger [options] [message words...]
|
|
307
319
|
|
|
308
320
|
Options:
|
|
309
|
-
-
|
|
310
|
-
-
|
|
311
|
-
-html
|
|
312
|
-
-url
|
|
313
|
-
-
|
|
314
|
-
-
|
|
315
|
-
-
|
|
316
|
-
-
|
|
317
|
-
-
|
|
318
|
-
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
+
-message <text> Plain text message
|
|
322
|
+
-title <text> Window title
|
|
323
|
+
-html <html> HTML content
|
|
324
|
+
-url <url> URL to load (web or file://) - defaults to 1024x768
|
|
325
|
+
-size <width,height> Window size in pixels (default: 600x400, or 1024x768 for URLs)
|
|
326
|
+
-buttons <labels...> Button labels (space-separated)
|
|
327
|
+
-ok Add OK button
|
|
328
|
+
-cancel Add Cancel button
|
|
329
|
+
-input Enable input field
|
|
330
|
+
-default <value> Default input value
|
|
331
|
+
-timeout <seconds> Auto-close after N seconds
|
|
332
|
+
-detach Exit CLI immediately, leave window open
|
|
333
|
+
-help, --help, -? Show help message
|
|
334
|
+
|
|
335
|
+
Examples:
|
|
336
|
+
msger Hello World
|
|
337
|
+
msger -message "Save changes?" -buttons Yes No Cancel
|
|
338
|
+
msger -url "https://github.com/BobFrankston/msger"
|
|
339
|
+
msger -detach -message "Background notification"
|
|
321
340
|
```
|
|
322
341
|
|
|
323
342
|
## License
|
package/cli.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["cli.ts"],"names":[],"mappings":";AAKA,wBAA8B,IAAI,kBAwCjC"}
|
package/cli.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { showMessageBox } from './index.js';
|
|
3
|
-
import { parseCliArgs, showHelp } from './clihandler.js';
|
|
3
|
+
import { parseCliArgs, showHelp, showVersion } from './clihandler.js';
|
|
4
4
|
export default async function main() {
|
|
5
5
|
const args = process.argv.slice(2);
|
|
6
6
|
// No arguments - show help
|
|
@@ -9,13 +9,21 @@ export default async function main() {
|
|
|
9
9
|
process.exit(0);
|
|
10
10
|
}
|
|
11
11
|
// Parse command line arguments
|
|
12
|
-
const { options, showHelp: shouldShowHelp } = parseCliArgs(args);
|
|
12
|
+
const { options, showHelp: shouldShowHelp, showVersion: shouldShowVersion } = parseCliArgs(args);
|
|
13
13
|
if (shouldShowHelp) {
|
|
14
14
|
showHelp();
|
|
15
15
|
process.exit(0);
|
|
16
16
|
}
|
|
17
|
+
if (shouldShowVersion) {
|
|
18
|
+
showVersion();
|
|
19
|
+
process.exit(0);
|
|
20
|
+
}
|
|
17
21
|
try {
|
|
18
22
|
const result = await showMessageBox(options);
|
|
23
|
+
// If detached, exit immediately without printing result
|
|
24
|
+
if (options.detach) {
|
|
25
|
+
process.exit(0);
|
|
26
|
+
}
|
|
19
27
|
console.log(JSON.stringify(result, null, 2));
|
|
20
28
|
// Exit with code based on result
|
|
21
29
|
if (result.dismissed || result.closed) {
|
package/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAEtE,MAAM,CAAC,OAAO,CAAC,KAAK,UAAU,IAAI;IAC9B,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAEnC,2BAA2B;IAC3B,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACpB,QAAQ,EAAE,CAAC;QACX,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC;IAED,+BAA+B;IAC/B,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,WAAW,EAAE,iBAAiB,EAAE,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IAEjG,IAAI,cAAc,EAAE,CAAC;QACjB,QAAQ,EAAE,CAAC;QACX,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC;IAED,IAAI,iBAAiB,EAAE,CAAC;QACpB,WAAW,EAAE,CAAC;QACd,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC;IAED,IAAI,CAAC;QACD,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,OAAO,CAAC,CAAC;QAE7C,wDAAwD;QACxD,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;YACjB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACpB,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAE7C,iCAAiC;QACjC,IAAI,MAAM,CAAC,SAAS,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;YACpC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACpB,CAAC;IACL,CAAC;IAAC,OAAO,KAAU,EAAE,CAAC;QAClB,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;QACvC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC;AACL,CAAC;AAED,6CAA6C;AAC7C,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;IACnB,IAAI,EAAE,CAAC;AACX,CAAC"}
|
package/cli.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { showMessageBox } from './index.js';
|
|
4
|
-
import { parseCliArgs, showHelp } from './clihandler.js';
|
|
4
|
+
import { parseCliArgs, showHelp, showVersion } from './clihandler.js';
|
|
5
5
|
|
|
6
6
|
export default async function main() {
|
|
7
7
|
const args = process.argv.slice(2);
|
|
@@ -13,15 +13,26 @@ export default async function main() {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
// Parse command line arguments
|
|
16
|
-
const { options, showHelp: shouldShowHelp } = parseCliArgs(args);
|
|
16
|
+
const { options, showHelp: shouldShowHelp, showVersion: shouldShowVersion } = parseCliArgs(args);
|
|
17
17
|
|
|
18
18
|
if (shouldShowHelp) {
|
|
19
19
|
showHelp();
|
|
20
20
|
process.exit(0);
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
if (shouldShowVersion) {
|
|
24
|
+
showVersion();
|
|
25
|
+
process.exit(0);
|
|
26
|
+
}
|
|
27
|
+
|
|
23
28
|
try {
|
|
24
29
|
const result = await showMessageBox(options);
|
|
30
|
+
|
|
31
|
+
// If detached, exit immediately without printing result
|
|
32
|
+
if (options.detach) {
|
|
33
|
+
process.exit(0);
|
|
34
|
+
}
|
|
35
|
+
|
|
25
36
|
console.log(JSON.stringify(result, null, 2));
|
|
26
37
|
|
|
27
38
|
// Exit with code based on result
|
package/clihandler.d.ts
CHANGED
|
@@ -5,6 +5,8 @@ import { MessageBoxOptions } from './index.js';
|
|
|
5
5
|
export declare function parseCliArgs(args: string[]): {
|
|
6
6
|
options: MessageBoxOptions;
|
|
7
7
|
showHelp: boolean;
|
|
8
|
+
showVersion: boolean;
|
|
8
9
|
};
|
|
9
10
|
export declare function showHelp(): void;
|
|
11
|
+
export declare function showVersion(): void;
|
|
10
12
|
//# sourceMappingURL=clihandler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clihandler.d.ts","sourceRoot":"","sources":["clihandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AA4D/C;;GAEG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG;IAAE,OAAO,EAAE,iBAAiB,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAC;IAAC,WAAW,EAAE,OAAO,CAAA;CAAE,CA+HpH;AAED,wBAAgB,QAAQ,IAAI,IAAI,CAE/B;AAED,wBAAgB,WAAW,IAAI,IAAI,CAElC"}
|
package/clihandler.js
CHANGED
|
@@ -20,6 +20,7 @@ Options:
|
|
|
20
20
|
-size <width,height> Set window size (e.g., -size 800,600)
|
|
21
21
|
-timeout <seconds> Auto-close after specified seconds
|
|
22
22
|
-detach Leave window open after app exits (parent process returns immediately)
|
|
23
|
+
-v, -version, --version Show version number
|
|
23
24
|
-help, -?, --help Show this help message
|
|
24
25
|
|
|
25
26
|
Examples:
|
|
@@ -55,6 +56,10 @@ export function parseCliArgs(args) {
|
|
|
55
56
|
cli.help = true;
|
|
56
57
|
i++;
|
|
57
58
|
}
|
|
59
|
+
else if (arg === '-v' || arg === '-version' || arg === '--version') {
|
|
60
|
+
cli.version = true;
|
|
61
|
+
i++;
|
|
62
|
+
}
|
|
58
63
|
else if (arg === '-message' || arg === '--message') {
|
|
59
64
|
cli.message = args[++i];
|
|
60
65
|
i++;
|
|
@@ -157,6 +162,9 @@ export function parseCliArgs(args) {
|
|
|
157
162
|
if (cli.buttons.length === 0) {
|
|
158
163
|
cli.buttons = ['OK'];
|
|
159
164
|
}
|
|
165
|
+
// Default size for URL to larger window
|
|
166
|
+
const defaultWidth = url ? 1024 : 600;
|
|
167
|
+
const defaultHeight = url ? 768 : 400;
|
|
160
168
|
const options = {
|
|
161
169
|
title: cli.title,
|
|
162
170
|
message,
|
|
@@ -166,15 +174,18 @@ export function parseCliArgs(args) {
|
|
|
166
174
|
allowInput: cli.input,
|
|
167
175
|
defaultValue: cli.defaultValue,
|
|
168
176
|
size: {
|
|
169
|
-
width: cli.width,
|
|
170
|
-
height: cli.height,
|
|
177
|
+
width: cli.width || defaultWidth,
|
|
178
|
+
height: cli.height || defaultHeight,
|
|
171
179
|
},
|
|
172
180
|
timeout: cli.timeout,
|
|
173
181
|
detach: cli.detach
|
|
174
182
|
};
|
|
175
|
-
return { options, showHelp: cli.help || false };
|
|
183
|
+
return { options, showHelp: cli.help || false, showVersion: cli.version || false };
|
|
176
184
|
}
|
|
177
185
|
export function showHelp() {
|
|
178
186
|
console.log(HELP_TEXT);
|
|
179
187
|
}
|
|
188
|
+
export function showVersion() {
|
|
189
|
+
console.log(packageJson.version);
|
|
190
|
+
}
|
|
180
191
|
//# sourceMappingURL=clihandler.js.map
|
package/clihandler.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"clihandler.js","sourceRoot":"","sources":["clihandler.ts"],"names":[],"mappings":"AACA,OAAO,WAAW,MAAM,gBAAgB,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"clihandler.js","sourceRoot":"","sources":["clihandler.ts"],"names":[],"mappings":"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"}
|
package/clihandler.ts
CHANGED
|
@@ -14,6 +14,7 @@ interface CliOptions {
|
|
|
14
14
|
timeout?: number;
|
|
15
15
|
detach?: boolean;
|
|
16
16
|
help?: boolean;
|
|
17
|
+
version?: boolean;
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
const HELP_TEXT = `
|
|
@@ -37,6 +38,7 @@ Options:
|
|
|
37
38
|
-size <width,height> Set window size (e.g., -size 800,600)
|
|
38
39
|
-timeout <seconds> Auto-close after specified seconds
|
|
39
40
|
-detach Leave window open after app exits (parent process returns immediately)
|
|
41
|
+
-v, -version, --version Show version number
|
|
40
42
|
-help, -?, --help Show this help message
|
|
41
43
|
|
|
42
44
|
Examples:
|
|
@@ -59,7 +61,7 @@ Notes:
|
|
|
59
61
|
/**
|
|
60
62
|
* Parse command line arguments into message box options
|
|
61
63
|
*/
|
|
62
|
-
export function parseCliArgs(args: string[]): { options: MessageBoxOptions; showHelp: boolean } {
|
|
64
|
+
export function parseCliArgs(args: string[]): { options: MessageBoxOptions; showHelp: boolean; showVersion: boolean } {
|
|
63
65
|
const cli: CliOptions = {
|
|
64
66
|
buttons: [],
|
|
65
67
|
input: false,
|
|
@@ -75,6 +77,9 @@ export function parseCliArgs(args: string[]): { options: MessageBoxOptions; show
|
|
|
75
77
|
if (arg === '-help' || arg === '--help' || arg === '-?' || arg === '/?') {
|
|
76
78
|
cli.help = true;
|
|
77
79
|
i++;
|
|
80
|
+
} else if (arg === '-v' || arg === '-version' || arg === '--version') {
|
|
81
|
+
cli.version = true;
|
|
82
|
+
i++;
|
|
78
83
|
} else if (arg === '-message' || arg === '--message') {
|
|
79
84
|
cli.message = args[++i];
|
|
80
85
|
i++;
|
|
@@ -162,6 +167,10 @@ export function parseCliArgs(args: string[]): { options: MessageBoxOptions; show
|
|
|
162
167
|
cli.buttons = ['OK'];
|
|
163
168
|
}
|
|
164
169
|
|
|
170
|
+
// Default size for URL to larger window
|
|
171
|
+
const defaultWidth = url ? 1024 : 600;
|
|
172
|
+
const defaultHeight = url ? 768 : 400;
|
|
173
|
+
|
|
165
174
|
const options: MessageBoxOptions = {
|
|
166
175
|
title: cli.title,
|
|
167
176
|
message,
|
|
@@ -171,16 +180,20 @@ export function parseCliArgs(args: string[]): { options: MessageBoxOptions; show
|
|
|
171
180
|
allowInput: cli.input,
|
|
172
181
|
defaultValue: cli.defaultValue,
|
|
173
182
|
size: {
|
|
174
|
-
width: cli.width,
|
|
175
|
-
height: cli.height,
|
|
183
|
+
width: cli.width || defaultWidth,
|
|
184
|
+
height: cli.height || defaultHeight,
|
|
176
185
|
},
|
|
177
186
|
timeout: cli.timeout,
|
|
178
187
|
detach: cli.detach
|
|
179
188
|
};
|
|
180
189
|
|
|
181
|
-
return { options, showHelp: cli.help || false };
|
|
190
|
+
return { options, showHelp: cli.help || false, showVersion: cli.version || false };
|
|
182
191
|
}
|
|
183
192
|
|
|
184
193
|
export function showHelp(): void {
|
|
185
194
|
console.log(HELP_TEXT);
|
|
186
195
|
}
|
|
196
|
+
|
|
197
|
+
export function showVersion(): void {
|
|
198
|
+
console.log(packageJson.version);
|
|
199
|
+
}
|
package/index.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,iBAAiB;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC,CAAW,uDAAuD;IACjF,OAAO,CAAC,EAAE,MAAM,CAAC,CAAS,8EAA8E;IACxG,IAAI,CAAC,EAAE,MAAM,CAAC,CAAY,iDAAiD;IAC3E,GAAG,CAAC,EAAE,MAAM,CAAC,CAAa,0DAA0D;IAEpF,IAAI,CAAC,EAAE;QACH,KAAK,EAAE,MAAM,CAAC,CAAQ,mBAAmB;QACzC,MAAM,EAAE,MAAM,CAAC,CAAO,oBAAoB;KAC7C,CAAC;IAOF,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC,CAAO,0DAA0D;IACpF,YAAY,CAAC,EAAE,MAAM,CAAC,CAAI,4DAA4D;IACtF,UAAU,CAAC,EAAE,OAAO,CAAC,CAAK,gEAAgE;IAE1F,OAAO,CAAC,EAAE,MAAM,CAAC,CAAS,yDAAyD;IACnF,MAAM,CAAC,EAAE,OAAO,CAAC,CAAS,0DAA0D;CACvF;AAED,MAAM,WAAW,gBAAgB;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC3B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAmG1F"}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# ARM64 Pi Build Troubleshooting Notes
|
|
2
|
+
|
|
3
|
+
## Current Status (2025-10-22)
|
|
4
|
+
|
|
5
|
+
**Goal:** Automate ARM64 cross-compilation for Raspberry Pi via SSH using `build-pi.ts`
|
|
6
|
+
|
|
7
|
+
**Current Issue:** `pkg-config` PATH problem during Cargo build
|
|
8
|
+
|
|
9
|
+
### Problem Summary
|
|
10
|
+
|
|
11
|
+
The build script (`build-pi.ts`) successfully:
|
|
12
|
+
- ✅ Connects to Pi via SSH (pi4c)
|
|
13
|
+
- ✅ Copies source files to `/tmp/msger-native-build`
|
|
14
|
+
- ✅ Detects/installs Rust/Cargo
|
|
15
|
+
- ✅ Detects `pkg-config` is installed (via `command -v pkg-config`)
|
|
16
|
+
- ✅ Verifies GTK dependencies exist (via `pkg-config --exists gtk+-3.0 webkit2gtk-4.0`)
|
|
17
|
+
- ✅ Starts cargo build
|
|
18
|
+
|
|
19
|
+
BUT fails because:
|
|
20
|
+
- ❌ Cargo's build scripts cannot find `pkg-config` in PATH
|
|
21
|
+
- ❌ Even though `pkg-config` exists in the shell, cargo subprocesses don't inherit the PATH correctly
|
|
22
|
+
|
|
23
|
+
### Error Details
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
error: failed to run custom build command for `gdk-sys v0.18.2`
|
|
27
|
+
Could not run `PKG_CONFIG_PATH=... pkg-config --libs --cflags gdk-3.0 'gdk-3.0 >= 3.22'`
|
|
28
|
+
The pkg-config command could not be found.
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
This happens for multiple crates:
|
|
32
|
+
- `gdk-sys v0.18.2`
|
|
33
|
+
- `glib-sys v0.18.1`
|
|
34
|
+
- `gobject-sys v0.18.0`
|
|
35
|
+
- `gio-sys v0.18.1`
|
|
36
|
+
|
|
37
|
+
### Root Cause
|
|
38
|
+
|
|
39
|
+
When cargo runs build scripts (build.rs files), it spawns subprocesses that don't properly inherit the PATH from the SSH session. The current build command (line 70 in `build-pi.ts`):
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
ssh pi4c "cd /tmp/msger-native-build && export PATH=~/.cargo/bin:\$PATH && export PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig:/usr/share/pkgconfig && cargo build --release"
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
The PATH only prepends `~/.cargo/bin` but doesn't include `/usr/bin` where `pkg-config` is actually installed.
|
|
46
|
+
|
|
47
|
+
### Attempted Solutions
|
|
48
|
+
|
|
49
|
+
1. ✅ **Fixed dependency order** - Moved `pkg-config` installation check BEFORE GTK dependency check
|
|
50
|
+
- Lines 45-54: Now checks/installs `pkg-config` first
|
|
51
|
+
- Lines 56-65: Then checks/installs GTK dependencies
|
|
52
|
+
|
|
53
|
+
2. ⏳ **PATH fix needed** - Need to add `/usr/bin` to PATH in cargo build command
|
|
54
|
+
|
|
55
|
+
### Required Fix
|
|
56
|
+
|
|
57
|
+
**File:** `build-pi.ts` line 70
|
|
58
|
+
|
|
59
|
+
**Current:**
|
|
60
|
+
```typescript
|
|
61
|
+
execSync(`ssh ${PI_HOST} "cd ${PI_BUILD_DIR} && export PATH=~/.cargo/bin:\\$PATH && export PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig:/usr/share/pkgconfig && cargo build --release"`, { stdio: 'inherit' });
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**Change to:**
|
|
65
|
+
```typescript
|
|
66
|
+
execSync(`ssh ${PI_HOST} "cd ${PI_BUILD_DIR} && export PATH=/usr/bin:/usr/local/bin:~/.cargo/bin:\\$PATH && export PKG_CONFIG=/usr/bin/pkg-config && export PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig:/usr/share/pkgconfig && cargo build --release"`, { stdio: 'inherit' });
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**Changes:**
|
|
70
|
+
1. Add `/usr/bin:/usr/local/bin:` to the beginning of PATH
|
|
71
|
+
2. Add `export PKG_CONFIG=/usr/bin/pkg-config` to explicitly point to pkg-config binary
|
|
72
|
+
|
|
73
|
+
### Alternative Solutions to Try
|
|
74
|
+
|
|
75
|
+
If the above doesn't work, consider these alternatives:
|
|
76
|
+
|
|
77
|
+
1. **Use full path in PKG_CONFIG environment variable:**
|
|
78
|
+
```bash
|
|
79
|
+
export PKG_CONFIG=$(which pkg-config)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
2. **Install pkg-config to a location cargo can find:**
|
|
83
|
+
```bash
|
|
84
|
+
# Link pkg-config to /usr/local/bin if it's not there
|
|
85
|
+
sudo ln -s /usr/bin/pkg-config /usr/local/bin/pkg-config
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
3. **Use cargo's config.toml to set environment:**
|
|
89
|
+
Create `.cargo/config.toml` on the Pi with:
|
|
90
|
+
```toml
|
|
91
|
+
[env]
|
|
92
|
+
PKG_CONFIG = "/usr/bin/pkg-config"
|
|
93
|
+
PKG_CONFIG_PATH = "/usr/lib/aarch64-linux-gnu/pkgconfig:/usr/share/pkgconfig"
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
4. **SSH with login shell to inherit full PATH:**
|
|
97
|
+
```bash
|
|
98
|
+
ssh pi4c "bash -l -c 'cd /tmp/msger-native-build && cargo build --release'"
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Files Modified
|
|
102
|
+
|
|
103
|
+
- `Y:/dev/utils/msger/msger-native/build-pi.ts` - Main build script
|
|
104
|
+
- Fixed pkg-config dependency order (lines 45-65)
|
|
105
|
+
- Still needs PATH fix at line 70
|
|
106
|
+
|
|
107
|
+
### Pi Environment Info
|
|
108
|
+
|
|
109
|
+
- **Host:** pi4c
|
|
110
|
+
- **Architecture:** aarch64-unknown-linux-gnu
|
|
111
|
+
- **Rust Version:** 1.90.0 (1159e78c4 2025-09-14)
|
|
112
|
+
- **Build Directory:** `/tmp/msger-native-build`
|
|
113
|
+
|
|
114
|
+
### Dependencies Required
|
|
115
|
+
|
|
116
|
+
- `pkg-config` - ✅ Installed
|
|
117
|
+
- `libgtk-3-dev` - ✅ Installed
|
|
118
|
+
- `libwebkit2gtk-4.0-dev` - ✅ Installed
|
|
119
|
+
- `libsoup2.4-dev` - ✅ Installed
|
|
120
|
+
- Rust/Cargo - ✅ Installed
|
|
121
|
+
|
|
122
|
+
### Next Steps
|
|
123
|
+
|
|
124
|
+
1. Apply the PATH fix to line 70 of `build-pi.ts`
|
|
125
|
+
2. Test the build again
|
|
126
|
+
3. If still fails, try alternative solutions listed above
|
|
127
|
+
4. Consider creating a setup script on the Pi that persists environment variables
|
|
128
|
+
5. Document successful solution in copilot-session-summary.md
|
|
129
|
+
|
|
130
|
+
### Related Files
|
|
131
|
+
|
|
132
|
+
- `build-pi.ts` - Main build script
|
|
133
|
+
- `copilot-session-summary.md` - Project overview and session history
|
|
134
|
+
- `build-arm64.ts` - Alternative ARM64 build approach (cross-compilation)
|
|
135
|
+
- `Cargo.toml` - Rust dependencies (includes GTK/WebKit bindings)
|
|
136
|
+
|
|
137
|
+
### Debug Commands
|
|
138
|
+
|
|
139
|
+
To debug on the Pi directly:
|
|
140
|
+
```bash
|
|
141
|
+
# SSH to Pi
|
|
142
|
+
ssh pi4c
|
|
143
|
+
|
|
144
|
+
# Check pkg-config location
|
|
145
|
+
which pkg-config
|
|
146
|
+
# Should output: /usr/bin/pkg-config
|
|
147
|
+
|
|
148
|
+
# Check PATH during cargo build
|
|
149
|
+
cd /tmp/msger-native-build
|
|
150
|
+
export PATH=/usr/bin:/usr/local/bin:~/.cargo/bin:$PATH
|
|
151
|
+
export PKG_CONFIG=/usr/bin/pkg-config
|
|
152
|
+
export PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig:/usr/share/pkgconfig
|
|
153
|
+
cargo build --release --verbose
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Notes
|
|
157
|
+
|
|
158
|
+
- VS Code auto-save was interfering with file edits during troubleshooting
|
|
159
|
+
- The `command -v pkg-config` check succeeds, but cargo subprocesses can't find it
|
|
160
|
+
- This is a common issue with cargo build scripts and SSH environment inheritance
|
|
161
|
+
- The fix is well-documented in Rust community but needs to be applied to this specific setup
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Build script for ARM64/Raspberry Pi
|
|
3
|
+
# Run this directly on your Raspberry Pi
|
|
4
|
+
|
|
5
|
+
echo "🦾 Building msger for ARM64..."
|
|
6
|
+
|
|
7
|
+
# Check for Rust
|
|
8
|
+
if ! command -v cargo &> /dev/null; then
|
|
9
|
+
echo "📦 Installing Rust..."
|
|
10
|
+
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
|
11
|
+
source "$HOME/.cargo/env"
|
|
12
|
+
fi
|
|
13
|
+
|
|
14
|
+
# Check for build dependencies
|
|
15
|
+
echo "🔍 Checking for build dependencies..."
|
|
16
|
+
if ! pkg-config --exists gtk+-3.0 webkit2gtk-4.1; then
|
|
17
|
+
echo "📦 Installing build dependencies..."
|
|
18
|
+
sudo apt-get update
|
|
19
|
+
sudo apt-get install -y pkg-config libgtk-3-dev libwebkit2gtk-4.1-dev libsoup-3.0-dev
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
# Build
|
|
23
|
+
echo "📦 Building binary..."
|
|
24
|
+
cargo build --release
|
|
25
|
+
|
|
26
|
+
# Copy binary
|
|
27
|
+
echo "📋 Copying binary..."
|
|
28
|
+
mkdir -p bin
|
|
29
|
+
cp target/release/msgernative bin/msgernative-arm64
|
|
30
|
+
chmod +x bin/msgernative-arm64
|
|
31
|
+
|
|
32
|
+
echo "✅ Build complete! Binary is at: bin/msgernative-arm64"
|
|
33
|
+
echo ""
|
|
34
|
+
echo "To use it in the npm package, copy it to:"
|
|
35
|
+
echo " msger-native/bin/msgernative-arm64"
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { execSync } from 'child_process';
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
|
|
6
|
+
// Configuration - UPDATE THESE VALUES
|
|
7
|
+
const PI_HOST = 'pi4c'; // or 'pi@raspberrypi.local' or 'pi@192.168.1.xxx'
|
|
8
|
+
const PI_BUILD_DIR = '/tmp/msger-native-build';
|
|
9
|
+
|
|
10
|
+
console.log('🦾 Building ARM64 binary via SSH to Raspberry Pi...');
|
|
11
|
+
console.log(`📍 Target: ${PI_HOST}`);
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
// Test SSH connection
|
|
15
|
+
console.log('🔍 Testing SSH connection...');
|
|
16
|
+
execSync(`ssh ${PI_HOST} "echo Connected"`, { stdio: 'pipe' });
|
|
17
|
+
console.log('✅ SSH connection successful');
|
|
18
|
+
} catch (error) {
|
|
19
|
+
console.error('❌ Cannot connect to Pi via SSH');
|
|
20
|
+
console.error(' Make sure you can run: ssh ' + PI_HOST);
|
|
21
|
+
console.error(' You may need to set up SSH keys: ssh-copy-id ' + PI_HOST);
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Create build directory on Pi
|
|
26
|
+
console.log('📁 Creating build directory on Pi...');
|
|
27
|
+
execSync(`ssh ${PI_HOST} "mkdir -p ${PI_BUILD_DIR}/src"`, { stdio: 'inherit' });
|
|
28
|
+
|
|
29
|
+
// Copy source files to Pi
|
|
30
|
+
console.log('📤 Copying source files to Pi...');
|
|
31
|
+
execSync(`scp Cargo.toml ${PI_HOST}:${PI_BUILD_DIR}/`, { stdio: 'inherit', cwd: import.meta.dirname });
|
|
32
|
+
execSync(`scp -r src/* ${PI_HOST}:${PI_BUILD_DIR}/src/`, { stdio: 'inherit', cwd: import.meta.dirname });
|
|
33
|
+
|
|
34
|
+
// Check for Rust on Pi
|
|
35
|
+
console.log('🔍 Checking for Rust/Cargo on Pi...');
|
|
36
|
+
try {
|
|
37
|
+
execSync(`ssh ${PI_HOST} "command -v cargo"`, { stdio: 'pipe' });
|
|
38
|
+
console.log('✅ Rust/Cargo found');
|
|
39
|
+
} catch {
|
|
40
|
+
console.log('📦 Installing Rust on Pi...');
|
|
41
|
+
execSync(`ssh ${PI_HOST} "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y"`, { stdio: 'inherit' });
|
|
42
|
+
console.log('✅ Rust installed successfully');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Check for build dependencies on Pi
|
|
46
|
+
console.log('🔍 Checking for build dependencies on Pi...');
|
|
47
|
+
try {
|
|
48
|
+
execSync(`ssh ${PI_HOST} "pkg-config --exists gtk+-3.0 webkit2gtk-4.0"`, { stdio: 'pipe' });
|
|
49
|
+
console.log('✅ Build dependencies found');
|
|
50
|
+
} catch {
|
|
51
|
+
console.log('📦 Installing build dependencies on Pi...');
|
|
52
|
+
execSync(`ssh ${PI_HOST} "sudo apt-get update && sudo apt-get install -y pkg-config libgtk-3-dev libwebkit2gtk-4.0-dev libsoup2.4-dev"`, { stdio: 'inherit' });
|
|
53
|
+
console.log('✅ Build dependencies installed');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Ensure pkg-config is installed before building
|
|
57
|
+
console.log('🔧 Ensuring pkg-config is installed on Pi...');
|
|
58
|
+
execSync(`ssh ${PI_HOST} "sudo apt-get install -y pkg-config"`, { stdio: 'inherit' });
|
|
59
|
+
|
|
60
|
+
// Build on Pi
|
|
61
|
+
console.log('\n📦 Building ARM64 binary on Pi...');
|
|
62
|
+
try {
|
|
63
|
+
execSync(`ssh ${PI_HOST} "cd ${PI_BUILD_DIR} && export PATH=~/.cargo/bin:\\$PATH && export PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig:/usr/share/pkgconfig && cargo build --release"`, { stdio: 'inherit' });
|
|
64
|
+
console.log('✅ ARM64 build completed');
|
|
65
|
+
} catch (error: any) {
|
|
66
|
+
console.error('❌ Build failed:');
|
|
67
|
+
console.error(error.stdout?.toString());
|
|
68
|
+
console.error(error.stderr?.toString());
|
|
69
|
+
throw error;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Copy binary back
|
|
73
|
+
console.log('📥 Copying binary back from Pi...');
|
|
74
|
+
const destDir = path.join(import.meta.dirname, 'bin');
|
|
75
|
+
if (!fs.existsSync(destDir)) {
|
|
76
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const destPath = path.join(destDir, 'msgernative-arm64');
|
|
80
|
+
execSync(`scp ${PI_HOST}:${PI_BUILD_DIR}/target/release/msgernative ${destPath}`, { stdio: 'inherit' });
|
|
81
|
+
|
|
82
|
+
// Set execute permissions
|
|
83
|
+
fs.chmodSync(destPath, 0o755);
|
|
84
|
+
|
|
85
|
+
const stats = fs.statSync(destPath);
|
|
86
|
+
const sizeMB = (stats.size / (1024 * 1024)).toFixed(2);
|
|
87
|
+
console.log(`✅ Binary copied to: ${destPath}`);
|
|
88
|
+
console.log(`📊 Binary size: ${sizeMB} MB`);
|
|
89
|
+
|
|
90
|
+
// Cleanup on Pi
|
|
91
|
+
console.log('🧹 Cleaning up on Pi...');
|
|
92
|
+
execSync(`ssh ${PI_HOST} "rm -rf ${PI_BUILD_DIR}"`, { stdio: 'inherit' });
|
|
93
|
+
|
|
94
|
+
console.log('\n🎉 ARM64 build process completed!');
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { execSync } from 'child_process';
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
|
|
6
|
+
// Configuration - UPDATE THESE VALUES
|
|
7
|
+
const PI_HOST = 'pi4c'; // or 'pi@raspberrypi.local' or 'pi@192.168.1.xxx'
|
|
8
|
+
const PI_BUILD_DIR = '/tmp/msger-native-build';
|
|
9
|
+
|
|
10
|
+
console.log('🦾 Building ARM64 binary via SSH to Raspberry Pi...');
|
|
11
|
+
console.log(`📍 Target: ${PI_HOST}`);
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
// Test SSH connection
|
|
15
|
+
console.log('🔍 Testing SSH connection...');
|
|
16
|
+
execSync(`ssh ${PI_HOST} "echo Connected"`, { stdio: 'pipe' });
|
|
17
|
+
console.log('✅ SSH connection successful');
|
|
18
|
+
} catch (error) {
|
|
19
|
+
console.error('❌ Cannot connect to Pi via SSH');
|
|
20
|
+
console.error(' Make sure you can run: ssh ' + PI_HOST);
|
|
21
|
+
console.error(' You may need to set up SSH keys: ssh-copy-id ' + PI_HOST);
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Create build directory on Pi
|
|
26
|
+
console.log('📁 Creating build directory on Pi...');
|
|
27
|
+
execSync(`ssh ${PI_HOST} "mkdir -p ${PI_BUILD_DIR}/src"`, { stdio: 'inherit' });
|
|
28
|
+
|
|
29
|
+
// Copy source files to Pi
|
|
30
|
+
console.log('📤 Copying source files to Pi...');
|
|
31
|
+
execSync(`scp Cargo.toml ${PI_HOST}:${PI_BUILD_DIR}/`, { stdio: 'inherit', cwd: import.meta.dirname });
|
|
32
|
+
execSync(`scp -r src/* ${PI_HOST}:${PI_BUILD_DIR}/src/`, { stdio: 'inherit', cwd: import.meta.dirname });
|
|
33
|
+
|
|
34
|
+
// Check for Rust on Pi
|
|
35
|
+
console.log('🔍 Checking for Rust/Cargo on Pi...');
|
|
36
|
+
try {
|
|
37
|
+
execSync(`ssh ${PI_HOST} "command -v cargo"`, { stdio: 'pipe' });
|
|
38
|
+
console.log('✅ Rust/Cargo found');
|
|
39
|
+
} catch {
|
|
40
|
+
console.log('📦 Installing Rust on Pi...');
|
|
41
|
+
execSync(`ssh ${PI_HOST} "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y"`, { stdio: 'inherit' });
|
|
42
|
+
console.log('✅ Rust installed successfully');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Check for build dependencies on Pi
|
|
46
|
+
console.log('🔍 Checking for build dependencies on Pi...');
|
|
47
|
+
try {
|
|
48
|
+
execSync(`ssh ${PI_HOST} "pkg-config --exists gtk+-3.0 webkit2gtk-4.0"`, { stdio: 'pipe' });
|
|
49
|
+
console.log('✅ Build dependencies found');
|
|
50
|
+
} catch {
|
|
51
|
+
console.log('📦 Installing build dependencies on Pi...');
|
|
52
|
+
execSync(`ssh ${PI_HOST} "sudo apt-get update && sudo apt-get install -y pkg-config libgtk-3-dev libwebkit2gtk-4.0-dev libsoup2.4-dev"`, { stdio: 'inherit' });
|
|
53
|
+
console.log('✅ Build dependencies installed');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Ensure pkg-config is installed before building
|
|
57
|
+
console.log('🔧 Ensuring pkg-config is installed on Pi...');
|
|
58
|
+
execSync(`ssh ${PI_HOST} "sudo apt-get install -y pkg-config"`, { stdio: 'inherit' });
|
|
59
|
+
|
|
60
|
+
// Build on Pi
|
|
61
|
+
console.log('\n📦 Building ARM64 binary on Pi...');
|
|
62
|
+
try {
|
|
63
|
+
execSync(`ssh ${PI_HOST} "cd ${PI_BUILD_DIR} && export PATH=~/.cargo/bin:\\$PATH && export PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig:/usr/share/pkgconfig && cargo build --release"`, { stdio: 'inherit' });
|
|
64
|
+
console.log('✅ ARM64 build completed');
|
|
65
|
+
} catch (error: any) {
|
|
66
|
+
console.error('❌ Build failed:');
|
|
67
|
+
console.error(error.stdout?.toString());
|
|
68
|
+
console.error(error.stderr?.toString());
|
|
69
|
+
throw error;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Copy binary back
|
|
73
|
+
console.log('📥 Copying binary back from Pi...');
|
|
74
|
+
const destDir = path.join(import.meta.dirname, 'bin');
|
|
75
|
+
if (!fs.existsSync(destDir)) {
|
|
76
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const destPath = path.join(destDir, 'msgernative-arm64');
|
|
80
|
+
execSync(`scp ${PI_HOST}:${PI_BUILD_DIR}/target/release/msgernative ${destPath}`, { stdio: 'inherit' });
|
|
81
|
+
|
|
82
|
+
// Set execute permissions
|
|
83
|
+
fs.chmodSync(destPath, 0o755);
|
|
84
|
+
|
|
85
|
+
const stats = fs.statSync(destPath);
|
|
86
|
+
const sizeMB = (stats.size / (1024 * 1024)).toFixed(2);
|
|
87
|
+
console.log(`✅ Binary copied to: ${destPath}`);
|
|
88
|
+
console.log(`📊 Binary size: ${sizeMB} MB`);
|
|
89
|
+
|
|
90
|
+
// Cleanup on Pi
|
|
91
|
+
console.log('🧹 Cleaning up on Pi...');
|
|
92
|
+
execSync(`ssh ${PI_HOST} "rm -rf ${PI_BUILD_DIR}"`, { stdio: 'inherit' });
|
|
93
|
+
|
|
94
|
+
console.log('\n🎉 ARM64 build process completed!');
|
package/msger-native/build.ts
CHANGED
|
@@ -40,15 +40,9 @@ function main() {
|
|
|
40
40
|
const success = runCommand('cargo build --release', 'Building Windows x64 binary (release)');
|
|
41
41
|
|
|
42
42
|
if (success) {
|
|
43
|
-
// Copy binary to
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
mkdirSync(binDir, { recursive: true });
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const exeName = 'msgernative.exe';
|
|
50
|
-
const srcPath = `./target/release/${exeName}`;
|
|
51
|
-
const destPath = `${binDir}/${exeName}`;
|
|
43
|
+
// Copy binary to parent directory with platform-specific name
|
|
44
|
+
const srcPath = `./target/release/msgernative.exe`;
|
|
45
|
+
const destPath = `../msgernative-win32-x64.exe`;
|
|
52
46
|
|
|
53
47
|
if (existsSync(srcPath)) {
|
|
54
48
|
copyFileSync(srcPath, destPath);
|
|
@@ -74,18 +68,19 @@ function main() {
|
|
|
74
68
|
const success = runCommand('cargo build --release', 'Building Linux x64 binary');
|
|
75
69
|
|
|
76
70
|
if (success) {
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
mkdirSync(binDir, { recursive: true });
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const exeName = 'msgernative';
|
|
83
|
-
const srcPath = `./target/release/${exeName}`;
|
|
84
|
-
const destPath = `${binDir}/${exeName}`;
|
|
71
|
+
const srcPath = `./target/release/msgernative`;
|
|
72
|
+
const destPath = `../msgernative-linux-x64`;
|
|
85
73
|
|
|
86
74
|
if (existsSync(srcPath)) {
|
|
87
75
|
copyFileSync(srcPath, destPath);
|
|
76
|
+
// Make executable
|
|
77
|
+
chmodSync(destPath, 0o755);
|
|
88
78
|
console.log(`\n✅ Binary copied to: ${destPath}`);
|
|
79
|
+
|
|
80
|
+
// Show file size
|
|
81
|
+
const stats = statSync(destPath);
|
|
82
|
+
const sizeMB = (stats.size / (1024 * 1024)).toFixed(2);
|
|
83
|
+
console.log(`📊 Binary size: ${sizeMB} MB`);
|
|
89
84
|
}
|
|
90
85
|
}
|
|
91
86
|
} else {
|
|
@@ -93,18 +88,19 @@ function main() {
|
|
|
93
88
|
const success = runCommand('cargo build --release', 'Building native binary');
|
|
94
89
|
|
|
95
90
|
if (success) {
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
mkdirSync(binDir, { recursive: true });
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const exeName = 'msgernative';
|
|
102
|
-
const srcPath = `./target/release/${exeName}`;
|
|
103
|
-
const destPath = `${binDir}/${exeName}`;
|
|
91
|
+
const srcPath = `./target/release/msgernative`;
|
|
92
|
+
const destPath = `../msgernative-linux-x64`;
|
|
104
93
|
|
|
105
94
|
if (existsSync(srcPath)) {
|
|
106
95
|
copyFileSync(srcPath, destPath);
|
|
96
|
+
// Make executable
|
|
97
|
+
chmodSync(destPath, 0o755);
|
|
107
98
|
console.log(`\n✅ Binary copied to: ${destPath}`);
|
|
99
|
+
|
|
100
|
+
// Show file size
|
|
101
|
+
const stats = statSync(destPath);
|
|
102
|
+
const sizeMB = (stats.size / (1024 * 1024)).toFixed(2);
|
|
103
|
+
console.log(`📊 Binary size: ${sizeMB} MB`);
|
|
108
104
|
}
|
|
109
105
|
}
|
|
110
106
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@bobfrankston/msger-native",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Rust binary for msger - native message box implementation",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "node --experimental-strip-types build.ts",
|
|
8
|
+
"build:wsl": "node --experimental-strip-types build-wsl.ts",
|
|
9
|
+
"build:arm64": "node --experimental-strip-types build-arm64.ts",
|
|
10
|
+
"build:pi": "node --experimental-strip-types build-pi.ts"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"message-box",
|
|
14
|
+
"rust",
|
|
15
|
+
"native",
|
|
16
|
+
"webview"
|
|
17
|
+
],
|
|
18
|
+
"author": "Bob Frankston",
|
|
19
|
+
"license": "ISC"
|
|
20
|
+
}
|
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
use serde::{Deserialize, Serialize};
|
|
2
|
+
use std::io::{self, Read};
|
|
3
|
+
use tao::{
|
|
4
|
+
event::{Event, WindowEvent},
|
|
5
|
+
event_loop::{ControlFlow, EventLoop},
|
|
6
|
+
window::WindowBuilder,
|
|
7
|
+
};
|
|
8
|
+
use wry::WebViewBuilder;
|
|
9
|
+
|
|
10
|
+
#[cfg(target_os = "linux")]
|
|
11
|
+
use std::env;
|
|
12
|
+
|
|
13
|
+
#[derive(Deserialize, Debug)]
|
|
14
|
+
#[serde(rename_all = "camelCase")]
|
|
15
|
+
struct MessageBoxOptions {
|
|
16
|
+
#[serde(default = "default_title")]
|
|
17
|
+
title: String,
|
|
18
|
+
#[serde(default)]
|
|
19
|
+
message: Option<String>,
|
|
20
|
+
#[serde(default)]
|
|
21
|
+
html: Option<String>,
|
|
22
|
+
#[serde(default)]
|
|
23
|
+
url: Option<String>,
|
|
24
|
+
#[serde(default = "default_width")]
|
|
25
|
+
width: i32,
|
|
26
|
+
#[serde(default = "default_height")]
|
|
27
|
+
height: i32,
|
|
28
|
+
#[serde(default = "default_buttons")]
|
|
29
|
+
buttons: Vec<String>,
|
|
30
|
+
#[serde(default)]
|
|
31
|
+
default_value: Option<String>,
|
|
32
|
+
#[serde(default)]
|
|
33
|
+
allow_input: bool,
|
|
34
|
+
#[serde(default)]
|
|
35
|
+
timeout: Option<u64>,
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
#[derive(Serialize, Deserialize, Debug)]
|
|
39
|
+
struct MessageBoxResult {
|
|
40
|
+
button: String,
|
|
41
|
+
#[serde(skip_serializing_if = "Option::is_none")]
|
|
42
|
+
value: Option<String>,
|
|
43
|
+
#[serde(skip_serializing_if = "Option::is_none")]
|
|
44
|
+
form: Option<serde_json::Value>,
|
|
45
|
+
#[serde(skip_serializing_if = "Option::is_none")]
|
|
46
|
+
closed: Option<bool>,
|
|
47
|
+
#[serde(skip_serializing_if = "Option::is_none")]
|
|
48
|
+
dismissed: Option<bool>,
|
|
49
|
+
#[serde(skip_serializing_if = "Option::is_none")]
|
|
50
|
+
timeout: Option<bool>,
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
fn default_title() -> String {
|
|
54
|
+
"Message".to_string()
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
fn default_width() -> i32 {
|
|
58
|
+
600
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
fn default_height() -> i32 {
|
|
62
|
+
400
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
fn default_buttons() -> Vec<String> {
|
|
66
|
+
vec!["OK".to_string()]
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
fn generate_html(options: &MessageBoxOptions) -> String {
|
|
70
|
+
let html_content = if let Some(ref html) = options.html {
|
|
71
|
+
html.clone()
|
|
72
|
+
} else if let Some(ref message) = options.message {
|
|
73
|
+
format!("<p>{}</p>", escape_html(message))
|
|
74
|
+
} else {
|
|
75
|
+
String::new()
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
let buttons_html: String = options
|
|
79
|
+
.buttons
|
|
80
|
+
.iter()
|
|
81
|
+
.map(|btn| {
|
|
82
|
+
format!(
|
|
83
|
+
r#"<button onclick="sendResult('{}')">{}</button>"#,
|
|
84
|
+
escape_html(btn),
|
|
85
|
+
escape_html(btn)
|
|
86
|
+
)
|
|
87
|
+
})
|
|
88
|
+
.collect::<Vec<_>>()
|
|
89
|
+
.join("\n ");
|
|
90
|
+
|
|
91
|
+
let input_html = if options.allow_input {
|
|
92
|
+
format!(
|
|
93
|
+
r#"<input type="text" id="inputField" value="{}" />"#,
|
|
94
|
+
escape_html(options.default_value.as_deref().unwrap_or(""))
|
|
95
|
+
)
|
|
96
|
+
} else {
|
|
97
|
+
String::new()
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
format!(
|
|
101
|
+
r#"<!DOCTYPE html>
|
|
102
|
+
<html>
|
|
103
|
+
<head>
|
|
104
|
+
<meta charset="UTF-8">
|
|
105
|
+
<style>
|
|
106
|
+
body {{
|
|
107
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
108
|
+
padding: 20px;
|
|
109
|
+
margin: 0;
|
|
110
|
+
display: flex;
|
|
111
|
+
flex-direction: column;
|
|
112
|
+
height: 100vh;
|
|
113
|
+
box-sizing: border-box;
|
|
114
|
+
}}
|
|
115
|
+
#content {{
|
|
116
|
+
flex: 1;
|
|
117
|
+
overflow: auto;
|
|
118
|
+
margin-bottom: 20px;
|
|
119
|
+
}}
|
|
120
|
+
#inputField {{
|
|
121
|
+
width: 100%;
|
|
122
|
+
padding: 8px;
|
|
123
|
+
margin-bottom: 15px;
|
|
124
|
+
border: 1px solid #ccc;
|
|
125
|
+
border-radius: 4px;
|
|
126
|
+
font-size: 14px;
|
|
127
|
+
box-sizing: border-box;
|
|
128
|
+
}}
|
|
129
|
+
#buttons {{
|
|
130
|
+
display: flex;
|
|
131
|
+
gap: 10px;
|
|
132
|
+
justify-content: flex-end;
|
|
133
|
+
}}
|
|
134
|
+
button {{
|
|
135
|
+
padding: 8px 20px;
|
|
136
|
+
border: none;
|
|
137
|
+
border-radius: 4px;
|
|
138
|
+
background-color: #0066cc;
|
|
139
|
+
color: white;
|
|
140
|
+
font-size: 14px;
|
|
141
|
+
cursor: pointer;
|
|
142
|
+
min-width: 80px;
|
|
143
|
+
}}
|
|
144
|
+
button:hover {{
|
|
145
|
+
background-color: #0052a3;
|
|
146
|
+
}}
|
|
147
|
+
button:first-child {{
|
|
148
|
+
background-color: #6c757d;
|
|
149
|
+
}}
|
|
150
|
+
button:first-child:hover {{
|
|
151
|
+
background-color: #5a6268;
|
|
152
|
+
}}
|
|
153
|
+
</style>
|
|
154
|
+
</head>
|
|
155
|
+
<body>
|
|
156
|
+
<div id="content">
|
|
157
|
+
{}
|
|
158
|
+
</div>
|
|
159
|
+
{}
|
|
160
|
+
<div id="buttons">
|
|
161
|
+
{}
|
|
162
|
+
</div>
|
|
163
|
+
<script>
|
|
164
|
+
function sendResult(button) {{
|
|
165
|
+
const result = {{ button: button }};
|
|
166
|
+
|
|
167
|
+
// Get input field value if present
|
|
168
|
+
const inputField = document.getElementById('inputField');
|
|
169
|
+
if (inputField) {{
|
|
170
|
+
result.value = inputField.value;
|
|
171
|
+
}}
|
|
172
|
+
|
|
173
|
+
// Get all form data if forms are present
|
|
174
|
+
const forms = document.querySelectorAll('form');
|
|
175
|
+
if (forms.length > 0) {{
|
|
176
|
+
const formData = {{}};
|
|
177
|
+
forms.forEach(form => {{
|
|
178
|
+
const data = new FormData(form);
|
|
179
|
+
data.forEach((value, key) => {{
|
|
180
|
+
formData[key] = value;
|
|
181
|
+
}});
|
|
182
|
+
}});
|
|
183
|
+
if (Object.keys(formData).length > 0) {{
|
|
184
|
+
result.form = formData;
|
|
185
|
+
}}
|
|
186
|
+
}}
|
|
187
|
+
|
|
188
|
+
window.ipc.postMessage(JSON.stringify(result));
|
|
189
|
+
}}
|
|
190
|
+
|
|
191
|
+
// Zoom support with mouse wheel
|
|
192
|
+
let zoomLevel = 1.0;
|
|
193
|
+
document.addEventListener('wheel', function(e) {{
|
|
194
|
+
// Only zoom when Ctrl is held (or Cmd on Mac)
|
|
195
|
+
if (e.ctrlKey || e.metaKey) {{
|
|
196
|
+
e.preventDefault();
|
|
197
|
+
|
|
198
|
+
// Zoom in/out based on wheel direction
|
|
199
|
+
if (e.deltaY < 0) {{
|
|
200
|
+
zoomLevel = Math.min(zoomLevel + 0.1, 3.0); // Max 300%
|
|
201
|
+
}} else {{
|
|
202
|
+
zoomLevel = Math.max(zoomLevel - 0.1, 0.5); // Min 50%
|
|
203
|
+
}}
|
|
204
|
+
|
|
205
|
+
document.body.style.zoom = zoomLevel;
|
|
206
|
+
}}
|
|
207
|
+
}}, {{ passive: false }});
|
|
208
|
+
|
|
209
|
+
document.addEventListener('keydown', function(e) {{
|
|
210
|
+
if (e.key === 'Escape') {{
|
|
211
|
+
const result = {{ button: '{}', dismissed: true }};
|
|
212
|
+
window.ipc.postMessage(JSON.stringify(result));
|
|
213
|
+
}} else if (e.key === 'Enter') {{
|
|
214
|
+
const buttons = document.querySelectorAll('button');
|
|
215
|
+
if (buttons.length > 0) {{
|
|
216
|
+
buttons[buttons.length - 1].click();
|
|
217
|
+
}}
|
|
218
|
+
}} else if ((e.ctrlKey || e.metaKey) && e.key === '0') {{
|
|
219
|
+
// Reset zoom with Ctrl+0
|
|
220
|
+
e.preventDefault();
|
|
221
|
+
zoomLevel = 1.0;
|
|
222
|
+
document.body.style.zoom = zoomLevel;
|
|
223
|
+
}} else if ((e.ctrlKey || e.metaKey) && (e.key === '+' || e.key === '=')) {{
|
|
224
|
+
// Zoom in with Ctrl++
|
|
225
|
+
e.preventDefault();
|
|
226
|
+
zoomLevel = Math.min(zoomLevel + 0.1, 3.0);
|
|
227
|
+
document.body.style.zoom = zoomLevel;
|
|
228
|
+
}} else if ((e.ctrlKey || e.metaKey) && e.key === '-') {{
|
|
229
|
+
// Zoom out with Ctrl+-
|
|
230
|
+
e.preventDefault();
|
|
231
|
+
zoomLevel = Math.max(zoomLevel - 0.1, 0.5);
|
|
232
|
+
document.body.style.zoom = zoomLevel;
|
|
233
|
+
}}
|
|
234
|
+
}});
|
|
235
|
+
</script>
|
|
236
|
+
</body>
|
|
237
|
+
</html>"#,
|
|
238
|
+
html_content,
|
|
239
|
+
input_html,
|
|
240
|
+
buttons_html,
|
|
241
|
+
options.buttons.first().unwrap_or(&"Cancel".to_string())
|
|
242
|
+
)
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
fn escape_html(s: &str) -> String {
|
|
246
|
+
s.replace('&', "&")
|
|
247
|
+
.replace('<', "<")
|
|
248
|
+
.replace('>', ">")
|
|
249
|
+
.replace('"', """)
|
|
250
|
+
.replace('\'', "'")
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
fn main() {
|
|
254
|
+
// Check for display server on Linux
|
|
255
|
+
#[cfg(target_os = "linux")]
|
|
256
|
+
{
|
|
257
|
+
if env::var("DISPLAY").is_err() && env::var("WAYLAND_DISPLAY").is_err() {
|
|
258
|
+
eprintln!("Error: No display server detected.");
|
|
259
|
+
eprintln!("This program requires a graphical environment to run.");
|
|
260
|
+
eprintln!();
|
|
261
|
+
eprintln!("Solutions:");
|
|
262
|
+
eprintln!(" • On WSL: Use WSLg (Windows 11) or install an X server (VcXsrv, Xming)");
|
|
263
|
+
eprintln!(" • On headless Linux: Use 'DISPLAY=:0 msger ...' with a running X server");
|
|
264
|
+
eprintln!(" • Alternative: Run the Windows binary directly instead of WSL");
|
|
265
|
+
std::process::exit(1);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Read JSON from stdin
|
|
270
|
+
let mut input = String::new();
|
|
271
|
+
io::stdin()
|
|
272
|
+
.read_to_string(&mut input)
|
|
273
|
+
.expect("Failed to read stdin");
|
|
274
|
+
|
|
275
|
+
let options: MessageBoxOptions =
|
|
276
|
+
serde_json::from_str(&input).expect("Failed to parse JSON input");
|
|
277
|
+
|
|
278
|
+
// Create event loop and window
|
|
279
|
+
let event_loop = EventLoop::new();
|
|
280
|
+
let window = WindowBuilder::new()
|
|
281
|
+
.with_title(&options.title)
|
|
282
|
+
.with_inner_size(tao::dpi::LogicalSize::new(
|
|
283
|
+
options.width as f64,
|
|
284
|
+
options.height as f64,
|
|
285
|
+
))
|
|
286
|
+
.with_resizable(true)
|
|
287
|
+
.build(&event_loop)
|
|
288
|
+
.expect("Failed to create window");
|
|
289
|
+
|
|
290
|
+
// Generate HTML
|
|
291
|
+
let html = generate_html(&options);
|
|
292
|
+
|
|
293
|
+
// Create result holder
|
|
294
|
+
let result = std::sync::Arc::new(std::sync::Mutex::new(None::<MessageBoxResult>));
|
|
295
|
+
let result_clone = result.clone();
|
|
296
|
+
|
|
297
|
+
// Set up timeout if specified
|
|
298
|
+
let timeout_instant = options.timeout.map(|secs| {
|
|
299
|
+
std::time::Instant::now() + std::time::Duration::from_secs(secs)
|
|
300
|
+
});
|
|
301
|
+
let timeout_result = result.clone();
|
|
302
|
+
let timeout_buttons = options.buttons.clone();
|
|
303
|
+
|
|
304
|
+
// Create webview with IPC handler
|
|
305
|
+
let _webview = if let Some(ref url) = options.url {
|
|
306
|
+
// Load URL
|
|
307
|
+
WebViewBuilder::new()
|
|
308
|
+
.with_url(url)
|
|
309
|
+
.with_devtools(true)
|
|
310
|
+
.with_ipc_handler(move |msg| {
|
|
311
|
+
// Parse the result from JavaScript (msg is now a Request<String>)
|
|
312
|
+
if let Ok(res) = serde_json::from_str::<MessageBoxResult>(msg.body()) {
|
|
313
|
+
let mut result_lock = result_clone.lock().unwrap();
|
|
314
|
+
*result_lock = Some(res);
|
|
315
|
+
}
|
|
316
|
+
})
|
|
317
|
+
.build(&window)
|
|
318
|
+
.expect("Failed to create webview")
|
|
319
|
+
} else {
|
|
320
|
+
// Use HTML content
|
|
321
|
+
WebViewBuilder::new()
|
|
322
|
+
.with_html(&html)
|
|
323
|
+
.with_devtools(true)
|
|
324
|
+
.with_ipc_handler(move |msg| {
|
|
325
|
+
// Parse the result from JavaScript (msg is now a Request<String>)
|
|
326
|
+
if let Ok(res) = serde_json::from_str::<MessageBoxResult>(msg.body()) {
|
|
327
|
+
let mut result_lock = result_clone.lock().unwrap();
|
|
328
|
+
*result_lock = Some(res);
|
|
329
|
+
}
|
|
330
|
+
})
|
|
331
|
+
.build(&window)
|
|
332
|
+
.expect("Failed to create webview")
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
// Run event loop
|
|
336
|
+
event_loop.run(move |event, _,control_flow| {
|
|
337
|
+
// Check timeout
|
|
338
|
+
if let Some(deadline) = timeout_instant {
|
|
339
|
+
if std::time::Instant::now() >= deadline {
|
|
340
|
+
let mut result_lock = timeout_result.lock().unwrap();
|
|
341
|
+
if result_lock.is_none() {
|
|
342
|
+
*result_lock = Some(MessageBoxResult {
|
|
343
|
+
button: timeout_buttons.first().unwrap_or(&"Cancel".to_string()).clone(),
|
|
344
|
+
value: None,
|
|
345
|
+
form: None,
|
|
346
|
+
closed: None,
|
|
347
|
+
dismissed: None,
|
|
348
|
+
timeout: Some(true),
|
|
349
|
+
});
|
|
350
|
+
*control_flow = ControlFlow::Exit;
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
*control_flow = ControlFlow::WaitUntil(deadline);
|
|
355
|
+
} else {
|
|
356
|
+
*control_flow = ControlFlow::Wait;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
match event {
|
|
360
|
+
Event::WindowEvent {
|
|
361
|
+
event: WindowEvent::CloseRequested,
|
|
362
|
+
..
|
|
363
|
+
} => {
|
|
364
|
+
// Window closed without button click
|
|
365
|
+
let mut result_lock = result.lock().unwrap();
|
|
366
|
+
if result_lock.is_none() {
|
|
367
|
+
*result_lock = Some(MessageBoxResult {
|
|
368
|
+
button: options.buttons.first().unwrap_or(&"Cancel".to_string()).clone(),
|
|
369
|
+
value: None,
|
|
370
|
+
form: None,
|
|
371
|
+
closed: Some(true),
|
|
372
|
+
dismissed: None,
|
|
373
|
+
timeout: None,
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
*control_flow = ControlFlow::Exit;
|
|
377
|
+
}
|
|
378
|
+
Event::MainEventsCleared => {
|
|
379
|
+
// Check if result is set
|
|
380
|
+
let result_lock = result.lock().unwrap();
|
|
381
|
+
if result_lock.is_some() {
|
|
382
|
+
*control_flow = ControlFlow::Exit;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
Event::LoopDestroyed => {
|
|
386
|
+
// Output result to stdout
|
|
387
|
+
let result_lock = result.lock().unwrap();
|
|
388
|
+
if let Some(ref res) = *result_lock {
|
|
389
|
+
println!("{}", serde_json::to_string(res).unwrap());
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
_ => {}
|
|
393
|
+
}
|
|
394
|
+
});
|
|
395
|
+
}
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bobfrankston/msger",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.34",
|
|
4
4
|
"description": "Fast, lightweight, cross-platform message box - Rust-powered alternative to msgview",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./index.js",
|
|
@@ -48,13 +48,5 @@
|
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"@types/node": "^24.9.1"
|
|
51
|
-
}
|
|
52
|
-
"files": [
|
|
53
|
-
"*.js",
|
|
54
|
-
"*.d.ts",
|
|
55
|
-
"*.js.map",
|
|
56
|
-
"*.ts",
|
|
57
|
-
"msger-native/bin/",
|
|
58
|
-
"README.md"
|
|
59
|
-
]
|
|
51
|
+
}
|
|
60
52
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postinstall.d.ts","sourceRoot":"","sources":["postinstall.js"],"names":[],"mappings":""}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "esnext",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"allowSyntheticDefaultImports": true,
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"allowJs": true,
|
|
9
|
+
"strict": true,
|
|
10
|
+
"forceConsistentCasingInFileNames": true,
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"declaration": true,
|
|
13
|
+
"declarationMap": true,
|
|
14
|
+
"sourceMap": true,
|
|
15
|
+
"strictNullChecks": false,
|
|
16
|
+
"noImplicitAny": true,
|
|
17
|
+
"noImplicitReturns": false,
|
|
18
|
+
"noImplicitThis": true,
|
|
19
|
+
"newLine": "lf"
|
|
20
|
+
},
|
|
21
|
+
"exclude": [
|
|
22
|
+
"node_modules",
|
|
23
|
+
"msger-native",
|
|
24
|
+
"cruft",
|
|
25
|
+
".git",
|
|
26
|
+
"tests",
|
|
27
|
+
"prev",
|
|
28
|
+
"postinstall.js"
|
|
29
|
+
]
|
|
30
|
+
}
|
package/apply-detach-fix.d.ts
DELETED