@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 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" -w 800 -h 600
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
- -t, --title <title> Window title
310
- -m, --message <message> Plain text message
311
- -html, --html <html> HTML content
312
- -url, --url <url> URL to load (web or file://)
313
- -w, --width <width> Window width in pixels (default: 600)
314
- -h, --height <height> Window height in pixels (default: 400)
315
- -b, --buttons <buttons> Comma-separated button labels (default: "OK")
316
- -i, --allowInput Enable input field
317
- -d, --defaultValue <value> Default input value
318
- -timeout, --timeout <sec> Auto-close after N seconds
319
- --help Show help
320
- --version Show version
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;AAEzD,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,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IAEjE,IAAI,cAAc,EAAE,CAAC;QACjB,QAAQ,EAAE,CAAC;QACX,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC;IAED,IAAI,CAAC;QACD,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,OAAO,CAAC,CAAC;QAC7C,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"}
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;AAiB/D,MAAM,SAAS,GAAG;SACT,WAAW,CAAC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqC3B,CAAC;AAEF;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,IAAc;IACvC,MAAM,GAAG,GAAe;QACpB,OAAO,EAAE,EAAE;QACX,KAAK,EAAE,KAAK;QACZ,IAAI,EAAE,KAAK;KACd,CAAC;IAEF,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,MAAM,YAAY,GAAa,EAAE,CAAC;IAElC,OAAO,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;QACrB,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QAEpB,IAAI,GAAG,KAAK,OAAO,IAAI,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;YACtE,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC;YAChB,CAAC,EAAE,CAAC;QACR,CAAC;aAAM,IAAI,GAAG,KAAK,UAAU,IAAI,GAAG,KAAK,WAAW,EAAE,CAAC;YACnD,GAAG,CAAC,OAAO,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;YACxB,CAAC,EAAE,CAAC;QACR,CAAC;aAAM,IAAI,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;YAC/C,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;YACtB,CAAC,EAAE,CAAC;QACR,CAAC;aAAM,IAAI,GAAG,KAAK,OAAO,IAAI,GAAG,KAAK,QAAQ,EAAE,CAAC;YAC7C,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;YACrB,CAAC,EAAE,CAAC;QACR,CAAC;aAAM,IAAI,GAAG,KAAK,MAAM,IAAI,GAAG,KAAK,OAAO,EAAE,CAAC;YAC3C,GAAG,CAAC,GAAG,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;YACpB,CAAC,EAAE,CAAC;QACR,CAAC;aAAM,IAAI,GAAG,KAAK,UAAU,IAAI,GAAG,KAAK,WAAW,EAAE,CAAC;YACnD,iDAAiD;YACjD,CAAC,EAAE,CAAC;YACJ,OAAO,CAAC,GAAG,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;gBACjD,GAAG,CAAC,OAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC3B,CAAC,EAAE,CAAC;YACR,CAAC;QACL,CAAC;aAAM,IAAI,GAAG,KAAK,KAAK,IAAI,GAAG,KAAK,MAAM,EAAE,CAAC;YACzC,IAAI,CAAC,GAAG,CAAC,OAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC/B,GAAG,CAAC,OAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC5B,CAAC;YACD,CAAC,EAAE,CAAC;QACR,CAAC;aAAM,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,UAAU,EAAE,CAAC;YACjD,IAAI,CAAC,GAAG,CAAC,OAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;gBACnC,GAAG,CAAC,OAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAChC,CAAC;YACD,CAAC,EAAE,CAAC;QACR,CAAC;aAAM,IAAI,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;YAC/C,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC;YACjB,CAAC,EAAE,CAAC;QACR,CAAC;aAAM,IAAI,GAAG,KAAK,UAAU,IAAI,GAAG,KAAK,WAAW,EAAE,CAAC;YACnD,GAAG,CAAC,YAAY,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;YAC7B,CAAC,EAAE,CAAC;QACR,CAAC;aAAM,IAAI,GAAG,KAAK,UAAU,IAAI,GAAG,KAAK,WAAW,EAAE,CAAC;YACnD,GAAG,CAAC,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACtC,CAAC,EAAE,CAAC;QACR,CAAC;aAAM,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,UAAU,EAAE,CAAC;YACjD,GAAG,CAAC,MAAM,GAAG,IAAI,CAAC;YAClB,CAAC,EAAE,CAAC;QACR,CAAC;aAAM,IAAI,GAAG,KAAK,OAAO,IAAI,GAAG,KAAK,QAAQ,EAAE,CAAC;YAC7C,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;YACvB,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;YAChE,IAAI,CAAC;gBAAE,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC;YACrB,IAAI,CAAC;gBAAE,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC;YACtB,CAAC,EAAE,CAAC;QACR,CAAC;aAAM,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YAC9B,4CAA4C;YAC5C,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACvB,CAAC,EAAE,CAAC;QACR,CAAC;aAAM,CAAC;YACJ,0BAA0B;YAC1B,OAAO,CAAC,IAAI,CAAC,mBAAmB,GAAG,EAAE,CAAC,CAAC;YACvC,CAAC,EAAE,CAAC;QACR,CAAC;IACL,CAAC;IAED,oBAAoB;IACpB,IAAI,OAAe,CAAC;IACpB,IAAI,IAAwB,CAAC;IAC7B,IAAI,GAAuB,CAAC;IAE5B,IAAI,GAAG,CAAC,GAAG,EAAE,CAAC;QACV,gBAAgB;QAChB,OAAO,GAAG,GAAG,CAAC,GAAG,CAAC;QAClB,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;IAClB,CAAC;SAAM,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC;QAClB,yBAAyB;QACzB,OAAO,GAAG,GAAG,CAAC,IAAI,CAAC;QACnB,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC;IACpB,CAAC;SAAM,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;QACrB,wBAAwB;QACxB,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC;IAC1B,CAAC;SAAM,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACjC,uCAAuC;QACvC,OAAO,GAAG,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACrC,CAAC;SAAM,CAAC;QACJ,sBAAsB;QACtB,OAAO,GAAG,qBAAqB,CAAC;IACpC,CAAC;IAED,oCAAoC;IACpC,IAAI,GAAG,CAAC,OAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5B,GAAG,CAAC,OAAO,GAAG,CAAC,IAAI,CAAC,CAAC;IACzB,CAAC;IAED,MAAM,OAAO,GAAsB;QAC/B,KAAK,EAAE,GAAG,CAAC,KAAK;QAChB,OAAO;QACP,IAAI;QACJ,GAAG;QACH,OAAO,EAAE,GAAG,CAAC,OAAO;QACpB,UAAU,EAAE,GAAG,CAAC,KAAK;QACrB,YAAY,EAAE,GAAG,CAAC,YAAY;QAC9B,IAAI,EAAE;YACF,KAAK,EAAE,GAAG,CAAC,KAAK;YAChB,MAAM,EAAE,GAAG,CAAC,MAAM;SACrB;QACD,OAAO,EAAE,GAAG,CAAC,OAAO;QACpB,MAAM,EAAE,GAAG,CAAC,MAAM;KACrB,CAAC;IAEF,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,CAAC,IAAI,IAAI,KAAK,EAAE,CAAC;AACpD,CAAC;AAED,MAAM,UAAU,QAAQ;IACpB,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;AAC3B,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,10 @@
1
+ [package]
2
+ name = "msgernative"
3
+ version = "0.1.0"
4
+ edition = "2021"
5
+
6
+ [dependencies]
7
+ wry = "0.47"
8
+ tao = "0.30"
9
+ serde = { version = "1.0", features = ["derive"] }
10
+ serde_json = "1.0"
@@ -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!');
@@ -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 bin directory for easy access
44
- const binDir = './bin';
45
- if (!existsSync(binDir)) {
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 binDir = './bin';
78
- if (!existsSync(binDir)) {
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 binDir = './bin';
97
- if (!existsSync(binDir)) {
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('&', "&amp;")
247
+ .replace('<', "&lt;")
248
+ .replace('>', "&gt;")
249
+ .replace('"', "&quot;")
250
+ .replace('\'', "&#39;")
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.32",
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
+ }
@@ -1,3 +0,0 @@
1
- #!/usr/bin/env node
2
- export {};
3
- //# sourceMappingURL=apply-detach-fix.d.ts.map