@21stware/rpui 0.3.1 → 0.3.2

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 (3) hide show
  1. package/README.md +34 -6
  2. package/dist/serve.js +69 -9
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -32,16 +32,20 @@ import { parseToPage } from '@21stware/rpui';
32
32
  document.body.appendChild(parseToPage(rpmlSource));
33
33
  ```
34
34
 
35
- ## `rpui serve` — host a directory of prototypes
35
+ ## `rpui` CLI
36
36
 
37
- The package ships an `rpui` bin that hosts a directory of `.rpml` files as one navigable gallery (collapsible sidebar, hash routing, `index.rpml` as the default home) and prints a local URL:
37
+ The package ships an `rpui` bin with two commands for working with a directory of `.rpml` files. Both render the directory as one navigable gallery (collapsible sidebar, hash routing, `index.rpml` as the default home) and share the same zero-dependency runtime.
38
+
39
+ ### `rpui serve` — host a directory of prototypes
40
+
41
+ Hosts the directory on a local server, prints the URL, and opens the browser:
38
42
 
39
43
  ```bash
40
44
  npx @21stware/rpui serve .
41
45
  ```
42
46
 
43
47
  ```
44
- RPUI serving 9 .rpml files from /Users/me/prototypes
48
+ RPUI serving 8 .rpml files from /Users/me/prototypes
45
49
 
46
50
  Local: http://localhost:3000
47
51
 
@@ -55,10 +59,34 @@ The directory is re-scanned on every page load, so editing a `.rpml` and refresh
55
59
  | `[dir]` | Directory to serve (default: current directory) |
56
60
  | `-p, --port` | Port (default `3000`; auto-increments if busy) |
57
61
  | `--host` | Host (default `localhost`) |
62
+ | `--no-open` | Don't open the browser on start (headless / CI) |
63
+
64
+ ```bash
65
+ # serve a specific directory on a chosen port, without opening a browser
66
+ npx @21stware/rpui serve ./prototypes --port 4000 --no-open
67
+ ```
68
+
69
+ ### `rpui build` — compile to one HTML file
70
+
71
+ Compiles the directory into a single self-contained HTML file (inlined runtime + gallery) that works offline from `file://`:
72
+
73
+ ```bash
74
+ npx @21stware/rpui build .
75
+ ```
76
+
77
+ ```
78
+ ✓ compiled 8 .rpml files → prototypes.html
79
+ ```
80
+
81
+ | Argument | Description |
82
+ |----------|-------------|
83
+ | `[dir]` | Directory of `.rpml` (recursive; default: current directory) |
84
+ | `-o, --out` | Output HTML path (default: `<dir>.html`) |
85
+ | `--title` | Gallery title (default: directory name) |
58
86
 
59
87
  ```bash
60
- # serve a specific directory on a chosen port
61
- npx @21stware/rpui serve ./prototypes --port 4000
88
+ # compile to a named file with a custom title
89
+ npx @21stware/rpui build ./prototypes -o prototypes.html --title "Prototypes"
62
90
  ```
63
91
 
64
92
  ## Exports
@@ -70,4 +98,4 @@ npx @21stware/rpui serve ./prototypes --port 4000
70
98
  | `parseToPage(source)` | parse `.rpml` text into a ready-to-mount `<page>` element |
71
99
  | `rewriteTags(source)` | rewrite bare RPML language tags to the registered component tags |
72
100
 
73
- See the repo root for the full [RPML spec](../../spec), [`llms.txt`](../../llms.txt) component reference, and the [`SKILL.md`](../../SKILL.md) authoring workflow.
101
+ See the repo root for the full [RPML spec](../../spec), [`llms.txt`](../../llms.txt) component reference, and the [`SKILL.md`](../../rapid-prototype-implement/SKILL.md) authoring workflow.
package/dist/serve.js CHANGED
@@ -1,8 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import { createServer } from "node:http";
3
- import { existsSync, statSync, readFileSync, readdirSync } from "node:fs";
4
- import { dirname, join, resolve, sep, relative } from "node:path";
3
+ import { existsSync, statSync, readFileSync, writeFileSync, readdirSync } from "node:fs";
4
+ import { dirname, join, resolve, sep, basename, relative } from "node:path";
5
5
  import { fileURLToPath } from "node:url";
6
+ import { spawn } from "node:child_process";
6
7
  const __dirname$1 = dirname(fileURLToPath(import.meta.url));
7
8
  const GALLERY_JS = join(__dirname$1, "gallery.js");
8
9
  function collectRpml(dir) {
@@ -44,31 +45,51 @@ ${galleryJs}
44
45
  </html>
45
46
  `;
46
47
  }
48
+ function openBrowser(url) {
49
+ const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open";
50
+ const args = process.platform === "win32" ? ["/c", "start", "", url] : [url];
51
+ try {
52
+ const child = spawn(cmd, args, { stdio: "ignore", detached: true });
53
+ child.on("error", () => {
54
+ });
55
+ child.unref();
56
+ } catch {
57
+ }
58
+ }
47
59
  function parseArgs(argv) {
48
60
  let dir = ".";
49
61
  let port = 3e3;
50
62
  let host = "localhost";
63
+ let open = true;
51
64
  let sawPositional = false;
52
65
  for (let i = 0; i < argv.length; i++) {
53
66
  const a = argv[i];
54
67
  if (a === "-p" || a === "--port") port = Number(argv[++i]) || port;
55
68
  else if (a === "--host") host = argv[++i] || host;
69
+ else if (a === "--no-open") open = false;
70
+ else if (a === "--open") open = true;
56
71
  else if (a === "-h" || a === "--help") usage();
57
72
  else if (!a.startsWith("-") && !sawPositional) {
58
73
  dir = a;
59
74
  sawPositional = true;
60
75
  }
61
76
  }
62
- return { dir, port, host };
77
+ return { dir, port, host, open };
63
78
  }
64
79
  function usage() {
65
- console.error(`Usage: rpui serve [dir] [--port 3000] [--host localhost]
80
+ console.error(`Usage:
81
+ rpui serve [dir] [--port 3000] [--host localhost] [--no-open]
82
+ rpui build [dir] [-o out.html] [--title "..."]
66
83
 
67
- Host a directory of .rpml files as one navigable gallery.
84
+ serve — host a directory of .rpml files as one navigable gallery (opens the browser).
85
+ build — compile a directory of .rpml files into one self-contained HTML.
68
86
 
69
- dir directory to serve (default: current directory)
70
- -p, --port port (default 3000; auto-increments if busy)
71
- --host host (default localhost)`);
87
+ dir directory (default: current directory)
88
+ -p, --port serve: port (default 3000; auto-increments if busy)
89
+ --host serve: host (default localhost)
90
+ --no-open serve: do not open the browser
91
+ -o, --out build: output HTML path (default: <dir>.html)
92
+ --title build: gallery title (default: directory name)`);
72
93
  process.exit(1);
73
94
  }
74
95
  function listen(server, port, host) {
@@ -88,7 +109,7 @@ function listen(server, port, host) {
88
109
  });
89
110
  }
90
111
  async function serve(argv) {
91
- const { dir, port, host } = parseArgs(argv);
112
+ const { dir, port, host, open } = parseArgs(argv);
92
113
  const root = resolve(dir);
93
114
  if (!existsSync(root) || !statSync(root).isDirectory()) {
94
115
  console.error(`✗ Not a directory: ${dir}`);
@@ -130,10 +151,49 @@ async function serve(argv) {
130
151
  console.log(` Local: ${addr}`);
131
152
  console.log(``);
132
153
  console.log(` Press Ctrl+C to stop`);
154
+ if (open) openBrowser(addr);
155
+ }
156
+ function build(argv) {
157
+ let dir = ".";
158
+ let out = "";
159
+ let title = "";
160
+ let sawPositional = false;
161
+ for (let i = 0; i < argv.length; i++) {
162
+ const a = argv[i];
163
+ if (a === "-o" || a === "--out") out = argv[++i] || out;
164
+ else if (a === "--title") title = argv[++i] || title;
165
+ else if (a === "-h" || a === "--help") usage();
166
+ else if (!a.startsWith("-") && !sawPositional) {
167
+ dir = a;
168
+ sawPositional = true;
169
+ }
170
+ }
171
+ const root = resolve(dir);
172
+ if (!existsSync(root) || !statSync(root).isDirectory()) {
173
+ console.error(`✗ Not a directory: ${dir}`);
174
+ process.exit(1);
175
+ }
176
+ if (!existsSync(GALLERY_JS)) {
177
+ console.error(`✗ Missing runtime bundle (gallery.js). Reinstall @21stware/rpui or run the build.`);
178
+ process.exit(1);
179
+ }
180
+ const name = basename(root) || "RPML";
181
+ if (!out) out = `${name}.html`;
182
+ if (!title) title = name;
183
+ const galleryJs = readFileSync(GALLERY_JS, "utf8");
184
+ const count = collectRpml(root).length;
185
+ if (!count) {
186
+ console.error(`✗ No .rpml files found in ${root}`);
187
+ process.exit(1);
188
+ }
189
+ writeFileSync(out, buildHtml(root, title, galleryJs));
190
+ console.log(`✓ compiled ${count} .rpml file${count === 1 ? "" : "s"} → ${out}`);
133
191
  }
134
192
  const [sub, ...rest] = process.argv.slice(2);
135
193
  if (sub === "serve") {
136
194
  serve(rest);
195
+ } else if (sub === "build") {
196
+ build(rest);
137
197
  } else if (sub === "-h" || sub === "--help" || sub === void 0) {
138
198
  usage();
139
199
  } else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@21stware/rpui",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "description": "RPUI: static UI prototype renderer (RPML Web Components runtime)",
5
5
  "type": "module",
6
6
  "main": "dist/rpui.js",
@@ -16,7 +16,7 @@
16
16
  },
17
17
  "./dist/rpui.js": "./dist/rpui.js",
18
18
  "./llms.txt": "../../llms.txt",
19
- "./SKILL.md": "../../SKILL.md"
19
+ "./SKILL.md": "../../rapid-prototype-implement/SKILL.md"
20
20
  },
21
21
  "sideEffects": true,
22
22
  "files": [