@await-widget/runtime 0.0.31 → 0.0.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/bin/await-widget.js +72 -10
  2. package/package.json +1 -1
@@ -6,6 +6,7 @@ import http from 'node:http';
6
6
  import os from 'node:os';
7
7
  import path from 'node:path';
8
8
  import process from 'node:process';
9
+ import readline from 'node:readline';
9
10
 
10
11
  const defaultPort = 4343;
11
12
  const maxBodyBytes = 100 * 1024 * 1024;
@@ -16,6 +17,7 @@ const crcTable = makeCrcTable();
16
17
  const bridgeInfoDirectory = '.await';
17
18
  const bridgeInfoFileName = 'bridge.json';
18
19
  const singleWidgetPath = '.';
20
+ const widgetDirectoryError = 'Run await-widget from a package root or first-level widget folder whose package.json includes @await-widget/runtime.';
19
21
 
20
22
  function main() {
21
23
  const options = parseArgs(process.argv.slice(2));
@@ -128,10 +130,10 @@ function parseAppArgs(args) {
128
130
  };
129
131
  }
130
132
 
131
- function startWorkspaceServer(options) {
133
+ async function startWorkspaceServer(options) {
132
134
  let roots;
133
135
  try {
134
- roots = resolveWidgetServerRoots(process.cwd());
136
+ roots = await resolveWidgetServerRoots(process.cwd());
135
137
  } catch (error) {
136
138
  console.error(error.message);
137
139
  process.exit(1);
@@ -394,22 +396,81 @@ function findPackageRoot(start) {
394
396
  }
395
397
  }
396
398
 
397
- function resolveWidgetServerRoots(start) {
399
+ async function resolveWidgetServerRoots(start) {
398
400
  const widgetRoot = path.resolve(start);
399
401
  const packageRoot = findPackageRoot(widgetRoot);
400
402
  if (
401
403
  !packageRoot
402
- || packageRoot === widgetRoot
403
- || path.dirname(widgetRoot) !== packageRoot
404
- || !isValidTopLevelName(path.basename(widgetRoot))
405
404
  || !hasRuntimeDependency(packageRoot)
406
405
  ) {
407
- throw new Error('Run await-widget from a first-level widget folder inside a package whose package.json includes @await-widget/runtime.');
406
+ throw new Error(widgetDirectoryError);
407
+ }
408
+
409
+ if (packageRoot === widgetRoot) {
410
+ return {packageRoot, widgetRoot: await selectWidgetRoot(packageRoot)};
411
+ }
412
+
413
+ if (path.dirname(widgetRoot) !== packageRoot || !isValidTopLevelName(path.basename(widgetRoot))) {
414
+ throw new Error(widgetDirectoryError);
408
415
  }
409
416
 
410
417
  return {packageRoot, widgetRoot};
411
418
  }
412
419
 
420
+ function selectWidgetRoot(packageRoot) {
421
+ const options = [
422
+ {name: `${path.basename(packageRoot)} (root)`, path: packageRoot},
423
+ ...fs.readdirSync(packageRoot, {withFileTypes: true})
424
+ .filter(entry => entry.isDirectory() && isValidTopLevelName(entry.name))
425
+ .sort((left, right) => left.name.localeCompare(right.name))
426
+ .map(entry => ({name: entry.name, path: path.join(packageRoot, entry.name)})),
427
+ ];
428
+ let selected = 0;
429
+ let rendered = false;
430
+ readline.emitKeypressEvents(process.stdin);
431
+ process.stdin.setRawMode(true);
432
+ process.stdin.resume();
433
+ console.log('Select a directory to connect:');
434
+
435
+ const render = () => {
436
+ if (rendered) {
437
+ readline.moveCursor(process.stdout, 0, -options.length);
438
+ readline.clearScreenDown(process.stdout);
439
+ }
440
+
441
+ process.stdout.write(`${options.map((option, index) => `${index === selected ? '❯' : ' '} ${option.name}`).join('\n')}\n`);
442
+ rendered = true;
443
+ };
444
+ render();
445
+
446
+ return new Promise(resolve => {
447
+ const cleanup = () => {
448
+ process.stdin.off('keypress', onKeypress);
449
+ process.stdin.setRawMode(false);
450
+ process.stdin.pause();
451
+ };
452
+ const onKeypress = (_character, key) => {
453
+ if (key.name === 'up' || key.name === 'down') {
454
+ selected = (selected + (key.name === 'up' ? options.length - 1 : 1)) % options.length;
455
+ render();
456
+ return;
457
+ }
458
+
459
+ if (key.name === 'return' || key.name === 'enter') {
460
+ cleanup();
461
+ resolve(options[selected].path);
462
+ return;
463
+ }
464
+
465
+ if (key.ctrl && key.name === 'c') {
466
+ cleanup();
467
+ process.exit(130);
468
+ }
469
+ };
470
+ process.stdin.on('keypress', onKeypress);
471
+ });
472
+ }
473
+
413
474
  function hasRuntimeDependency(packageRoot) {
414
475
  const manifest = JSON.parse(fs.readFileSync(path.join(packageRoot, 'package.json'), 'utf8'));
415
476
  return Boolean(manifest.dependencies?.['@await-widget/runtime'] ?? manifest.devDependencies?.['@await-widget/runtime']);
@@ -948,11 +1009,12 @@ function printHelp() {
948
1009
  await-widget [--port ${defaultPort}]
949
1010
  await-widget app <command> [options]
950
1011
 
951
- Start the Await computer connection from a first-level widget folder under a
952
- package whose package.json includes @await-widget/runtime.
1012
+ Start the Await computer connection from a package root or first-level widget
1013
+ folder whose package.json includes @await-widget/runtime. Starting from the
1014
+ package root opens a directory selector.
953
1015
 
954
1016
  What it does:
955
- - Serves the current widget folder to Await for live sync.
1017
+ - Serves the selected widget folder to Await for live sync.
956
1018
  - Prints a URL to paste into Await's Connect Computer sheet.
957
1019
  - Writes .await/bridge.json in the package root for app commands.
958
1020
  - Sends one-shot JSON app commands with await-widget app <command>.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@await-widget/runtime",
3
- "version": "0.0.31",
3
+ "version": "0.0.32",
4
4
  "description": "TypeScript declarations for Await widgets.",
5
5
  "license": "MIT",
6
6
  "author": "Maundy",