@21stware/rpui 0.4.4 → 0.5.5

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/dist/serve.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { createServer } from "node:http";
3
- import { existsSync, statSync, readFileSync, writeFileSync, readdirSync } from "node:fs";
3
+ import { existsSync, statSync, readFileSync, watch, writeFileSync, readdirSync } from "node:fs";
4
4
  import { dirname, join, resolve, sep, basename, relative } from "node:path";
5
5
  import { fileURLToPath } from "node:url";
6
6
  import { spawn } from "node:child_process";
@@ -25,9 +25,10 @@ function collectRpml(dir) {
25
25
  function escapeHtml(s) {
26
26
  return s.replace(/[&<>]/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;" })[c]);
27
27
  }
28
- function buildHtml(dir, title, galleryJs) {
29
- const docs = collectRpml(dir);
30
- const data = JSON.stringify(docs).replace(/<\/script/gi, "<\\/script").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
28
+ function safeJson(docs) {
29
+ return JSON.stringify(docs).replace(/<\/script/gi, "<\\/script").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
30
+ }
31
+ function buildHtml(docs, title, galleryJs, live = false) {
31
32
  return `<!doctype html>
32
33
  <html lang="zh-CN">
33
34
  <head>
@@ -37,7 +38,7 @@ function buildHtml(dir, title, galleryJs) {
37
38
  <style>html,body{margin:0;height:100%}</style>
38
39
  </head>
39
40
  <body>
40
- <script>globalThis.__RPML_DOCS__ = ${data};<\/script>
41
+ <script>globalThis.__RPML_DOCS__ = ${safeJson(docs)};${live ? "globalThis.__RPML_LIVE__ = true;" : ""}<\/script>
41
42
  <script type="module">
42
43
  ${galleryJs}
43
44
  <\/script>
@@ -121,12 +122,24 @@ async function serve(argv) {
121
122
  }
122
123
  const galleryJs = readFileSync(GALLERY_JS, "utf8");
123
124
  const title = root.split(sep).pop() || "RPML";
125
+ const clients = /* @__PURE__ */ new Set();
124
126
  const server = createServer((req, res) => {
125
127
  const url = (req.url || "/").split("?")[0];
126
128
  try {
129
+ if (url === "/~live") {
130
+ res.writeHead(200, {
131
+ "content-type": "text/event-stream; charset=utf-8",
132
+ "cache-control": "no-cache, no-transform",
133
+ connection: "keep-alive"
134
+ });
135
+ res.write("retry: 1000\n\n");
136
+ clients.add(res);
137
+ req.on("close", () => clients.delete(res));
138
+ return;
139
+ }
127
140
  if (url === "/" || !url.endsWith(".rpml")) {
128
141
  res.writeHead(200, { "content-type": "text/html; charset=utf-8" });
129
- res.end(buildHtml(root, title, galleryJs));
142
+ res.end(buildHtml(collectRpml(root), title, galleryJs, true));
130
143
  return;
131
144
  }
132
145
  const file = join(root, decodeURIComponent(url.replace(/^\/+/, "")));
@@ -142,15 +155,42 @@ async function serve(argv) {
142
155
  res.end("Error: " + e.message);
143
156
  }
144
157
  });
158
+ let lastJson = "";
159
+ const pushUpdate = () => {
160
+ let docs;
161
+ try {
162
+ docs = collectRpml(root);
163
+ } catch {
164
+ return;
165
+ }
166
+ const json = safeJson(docs);
167
+ if (json === lastJson) return;
168
+ lastJson = json;
169
+ const frame = `data: ${json}
170
+
171
+ `;
172
+ for (const res of clients) res.write(frame);
173
+ };
174
+ let debounce;
175
+ const onChange = () => {
176
+ clearTimeout(debounce);
177
+ debounce = setTimeout(pushUpdate, 80);
178
+ };
179
+ try {
180
+ watch(root, { recursive: true }, onChange);
181
+ } catch {
182
+ setInterval(pushUpdate, 700);
183
+ }
145
184
  const actualPort = await listen(server, port, host);
146
185
  const count = collectRpml(root).length;
186
+ lastJson = safeJson(collectRpml(root));
147
187
  const addr = `http://${host}:${actualPort}`;
148
188
  console.log(``);
149
189
  console.log(` RPUI serving ${count} .rpml file${count === 1 ? "" : "s"} from ${root}`);
150
190
  console.log(``);
151
191
  console.log(` Local: ${addr}`);
152
192
  console.log(``);
153
- console.log(` Press Ctrl+C to stop`);
193
+ console.log(` Live reload on — edits render in place. Press Ctrl+C to stop`);
154
194
  if (open) openBrowser(addr);
155
195
  }
156
196
  function build(argv) {
@@ -186,7 +226,7 @@ function build(argv) {
186
226
  console.error(`✗ No .rpml files found in ${root}`);
187
227
  process.exit(1);
188
228
  }
189
- writeFileSync(out, buildHtml(root, title, galleryJs));
229
+ writeFileSync(out, buildHtml(collectRpml(root), title, galleryJs));
190
230
  console.log(`✓ compiled ${count} .rpml file${count === 1 ? "" : "s"} → ${out}`);
191
231
  }
192
232
  const [sub, ...rest] = process.argv.slice(2);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@21stware/rpui",
3
- "version": "0.4.4",
3
+ "version": "0.5.5",
4
4
  "description": "RPUI: static UI prototype renderer (RPML Web Components runtime)",
5
5
  "type": "module",
6
6
  "main": "dist/rpui.js",
@@ -14,9 +14,11 @@
14
14
  "types": "./dist/rpui.d.ts",
15
15
  "default": "./dist/rpui.js"
16
16
  },
17
- "./dist/rpui.js": "./dist/rpui.js",
18
- "./llms.txt": "../../llms.txt",
19
- "./SKILL.md": "../../rapid-prototype-implement/SKILL.md"
17
+ "./gallery": {
18
+ "types": "./dist/gallery.d.ts",
19
+ "default": "./dist/gallery.js"
20
+ },
21
+ "./dist/rpui.js": "./dist/rpui.js"
20
22
  },
21
23
  "sideEffects": true,
22
24
  "files": [