@rainbow-robotics/preview-host 1.10.2 → 1.10.3-dev.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 (29) hide show
  1. package/build.mjs +1 -1
  2. package/dist/assets/{AmrCapabilityControl.B4CgW0HQ.js → AmrCapabilityControl.DA5qEK-i.js} +1 -1
  3. package/dist/assets/{__federation_expose_App.acYJ0rMi.js → __federation_expose_App.BcKopQK3.js} +274 -274
  4. package/dist/assets/__federation_fn_import.DdkEuT6t.js +2 -2
  5. package/dist/assets/{cssMode.B7Sa5SmO.js → cssMode.TQiMpZrg.js} +1 -1
  6. package/dist/assets/{freemarker2.Boj5BbHW.js → freemarker2.DbolQG0W.js} +1 -1
  7. package/dist/assets/{handlebars.RfhGysmZ.js → handlebars.BN8p09Qr.js} +1 -1
  8. package/dist/assets/{html.B6s09jLr.js → html.tlNvU0m7.js} +1 -1
  9. package/dist/assets/{htmlMode.QTGNAiQh.js → htmlMode.PdnlViXL.js} +1 -1
  10. package/dist/assets/{index.DHJstkPN.js → index.Bah31zT_.js} +1 -1
  11. package/dist/assets/{index.DFTXe41N.js → index.BddoWb3-.js} +1 -1
  12. package/dist/assets/{index.2D6SvrX5.js → index.CyLeIbBs.js} +6 -6
  13. package/dist/assets/{javascript.BbnO7qD2.js → javascript.KlHHiAmd.js} +1 -1
  14. package/dist/assets/{jsonMode.CiczPnZT.js → jsonMode.BKWXxzrb.js} +1 -1
  15. package/dist/assets/{liquid.BHcURP5g.js → liquid.Dhh4R480.js} +1 -1
  16. package/dist/assets/{lspLanguageFeatures.CJQEwrRm.js → lspLanguageFeatures.CcelPyIj.js} +1 -1
  17. package/dist/assets/{mdx.DY4L30L7.js → mdx.BZ2TO2U9.js} +1 -1
  18. package/dist/assets/{python.FP1f8EM8.js → python.bwQQUg46.js} +1 -1
  19. package/dist/assets/{razor.BUhAJkAv.js → razor.DBishBMi.js} +1 -1
  20. package/dist/assets/remoteEntry.js +1 -1
  21. package/dist/assets/{tsMode.D2Wi64Ys.js → tsMode.C1-reqV1.js} +1 -1
  22. package/dist/assets/{typescript.C_s-gcW-.js → typescript.BEEY2Nts.js} +1 -1
  23. package/dist/assets/{useFileDownload.BcvMcbUi.js → useFileDownload.Cbk5Bf_C.js} +198 -198
  24. package/dist/assets/{xml.DuyXcwQ2.js → xml.B87S4wD8.js} +1 -1
  25. package/dist/assets/{yaml.DKD2F3Qf.js → yaml.CsjBCiTN.js} +1 -1
  26. package/dist/index.html +5 -5
  27. package/package.json +1 -1
  28. package/serve.mjs +81 -14
  29. /package/dist/assets/{RbLocalizationProvider.GKeO3IYX.js → RbLocalizationProvider.BX3b4qG3.js} +0 -0
@@ -1 +1 @@
1
- import{l as e}from"./__federation_expose_App.acYJ0rMi.js";const n={comments:{blockComment:["<!--","-->"]},brackets:[["<",">"]],autoClosingPairs:[{open:"<",close:">"},{open:"'",close:"'"},{open:'"',close:'"'}],surroundingPairs:[{open:"<",close:">"},{open:"'",close:"'"},{open:'"',close:'"'}],onEnterRules:[{beforeText:new RegExp("<([_:\\w][_:\\w-.\\d]*)([^/>]*(?!/)>)[^<]*$","i"),afterText:/^<\/([_:\w][_:\w-.\d]*)\s*>$/i,action:{indentAction:e.IndentAction.IndentOutdent}},{beforeText:new RegExp("<(\\w[\\w\\d]*)([^/>]*(?!/)>)[^<]*$","i"),action:{indentAction:e.IndentAction.Indent}}]},o={defaultToken:"",tokenPostfix:".xml",ignoreCase:!0,qualifiedName:/(?:[\w\.\-]+:)?[\w\.\-]+/,tokenizer:{root:[[/[^<&]+/,""],{include:"@whitespace"},[/(<)(@qualifiedName)/,[{token:"delimiter"},{token:"tag",next:"@tag"}]],[/(<\/)(@qualifiedName)(\s*)(>)/,[{token:"delimiter"},{token:"tag"},"",{token:"delimiter"}]],[/(<\?)(@qualifiedName)/,[{token:"delimiter"},{token:"metatag",next:"@tag"}]],[/(<\!)(@qualifiedName)/,[{token:"delimiter"},{token:"metatag",next:"@tag"}]],[/<\!\[CDATA\[/,{token:"delimiter.cdata",next:"@cdata"}],[/&\w+;/,"string.escape"]],cdata:[[/[^\]]+/,""],[/\]\]>/,{token:"delimiter.cdata",next:"@pop"}],[/\]/,""]],tag:[[/[ \t\r\n]+/,""],[/(@qualifiedName)(\s*=\s*)("[^"]*"|'[^']*')/,["attribute.name","","attribute.value"]],[/(@qualifiedName)(\s*=\s*)("[^">?\/]*|'[^'>?\/]*)(?=[\?\/]\>)/,["attribute.name","","attribute.value"]],[/(@qualifiedName)(\s*=\s*)("[^">]*|'[^'>]*)/,["attribute.name","","attribute.value"]],[/@qualifiedName/,"attribute.name"],[/\?>/,{token:"delimiter",next:"@pop"}],[/(\/)(>)/,[{token:"tag"},{token:"delimiter",next:"@pop"}]],[/>/,{token:"delimiter",next:"@pop"}]],whitespace:[[/[ \t\r\n]+/,""],[/<!--/,{token:"comment",next:"@comment"}]],comment:[[/[^<\-]+/,"comment.content"],[/-->/,{token:"comment",next:"@pop"}],[/<!--/,"comment.content.invalid"],[/[<\-]/,"comment.content"]]}};export{n as conf,o as language};
1
+ import{l as e}from"./__federation_expose_App.BcKopQK3.js";const n={comments:{blockComment:["<!--","-->"]},brackets:[["<",">"]],autoClosingPairs:[{open:"<",close:">"},{open:"'",close:"'"},{open:'"',close:'"'}],surroundingPairs:[{open:"<",close:">"},{open:"'",close:"'"},{open:'"',close:'"'}],onEnterRules:[{beforeText:new RegExp("<([_:\\w][_:\\w-.\\d]*)([^/>]*(?!/)>)[^<]*$","i"),afterText:/^<\/([_:\w][_:\w-.\d]*)\s*>$/i,action:{indentAction:e.IndentAction.IndentOutdent}},{beforeText:new RegExp("<(\\w[\\w\\d]*)([^/>]*(?!/)>)[^<]*$","i"),action:{indentAction:e.IndentAction.Indent}}]},o={defaultToken:"",tokenPostfix:".xml",ignoreCase:!0,qualifiedName:/(?:[\w\.\-]+:)?[\w\.\-]+/,tokenizer:{root:[[/[^<&]+/,""],{include:"@whitespace"},[/(<)(@qualifiedName)/,[{token:"delimiter"},{token:"tag",next:"@tag"}]],[/(<\/)(@qualifiedName)(\s*)(>)/,[{token:"delimiter"},{token:"tag"},"",{token:"delimiter"}]],[/(<\?)(@qualifiedName)/,[{token:"delimiter"},{token:"metatag",next:"@tag"}]],[/(<\!)(@qualifiedName)/,[{token:"delimiter"},{token:"metatag",next:"@tag"}]],[/<\!\[CDATA\[/,{token:"delimiter.cdata",next:"@cdata"}],[/&\w+;/,"string.escape"]],cdata:[[/[^\]]+/,""],[/\]\]>/,{token:"delimiter.cdata",next:"@pop"}],[/\]/,""]],tag:[[/[ \t\r\n]+/,""],[/(@qualifiedName)(\s*=\s*)("[^"]*"|'[^']*')/,["attribute.name","","attribute.value"]],[/(@qualifiedName)(\s*=\s*)("[^">?\/]*|'[^'>?\/]*)(?=[\?\/]\>)/,["attribute.name","","attribute.value"]],[/(@qualifiedName)(\s*=\s*)("[^">]*|'[^'>]*)/,["attribute.name","","attribute.value"]],[/@qualifiedName/,"attribute.name"],[/\?>/,{token:"delimiter",next:"@pop"}],[/(\/)(>)/,[{token:"tag"},{token:"delimiter",next:"@pop"}]],[/>/,{token:"delimiter",next:"@pop"}]],whitespace:[[/[ \t\r\n]+/,""],[/<!--/,{token:"comment",next:"@comment"}]],comment:[[/[^<\-]+/,"comment.content"],[/-->/,{token:"comment",next:"@pop"}],[/<!--/,"comment.content.invalid"],[/[<\-]/,"comment.content"]]}};export{n as conf,o as language};
@@ -1 +1 @@
1
- import{l as e}from"./__federation_expose_App.acYJ0rMi.js";const t={comments:{lineComment:"#"},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}],folding:{offSide:!0},onEnterRules:[{beforeText:/:\s*$/,action:{indentAction:e.IndentAction.Indent}}]},o={tokenPostfix:".yaml",brackets:[{token:"delimiter.bracket",open:"{",close:"}"},{token:"delimiter.square",open:"[",close:"]"}],keywords:["true","True","TRUE","false","False","FALSE","null","Null","Null","~"],numberInteger:/(?:0|[+-]?[0-9]+)/,numberFloat:/(?:0|[+-]?[0-9]+)(?:\.[0-9]+)?(?:e[-+][1-9][0-9]*)?/,numberOctal:/0o[0-7]+/,numberHex:/0x[0-9a-fA-F]+/,numberInfinity:/[+-]?\.(?:inf|Inf|INF)/,numberNaN:/\.(?:nan|Nan|NAN)/,numberDate:/\d{4}-\d\d-\d\d([Tt ]\d\d:\d\d:\d\d(\.\d+)?(( ?[+-]\d\d?(:\d\d)?)|Z)?)?/,escapes:/\\(?:[btnfr\\"']|[0-7][0-7]?|[0-3][0-7]{2})/,tokenizer:{root:[{include:"@whitespace"},{include:"@comment"},[/%[^ ]+.*$/,"meta.directive"],[/---/,"operators.directivesEnd"],[/\.{3}/,"operators.documentEnd"],[/[-?:](?= )/,"operators"],{include:"@anchor"},{include:"@tagHandle"},{include:"@flowCollections"},{include:"@blockStyle"},[/@numberInteger(?![ \t]*\S+)/,"number"],[/@numberFloat(?![ \t]*\S+)/,"number.float"],[/@numberOctal(?![ \t]*\S+)/,"number.octal"],[/@numberHex(?![ \t]*\S+)/,"number.hex"],[/@numberInfinity(?![ \t]*\S+)/,"number.infinity"],[/@numberNaN(?![ \t]*\S+)/,"number.nan"],[/@numberDate(?![ \t]*\S+)/,"number.date"],[/(".*?"|'.*?'|[^#'"]*?)([ \t]*)(:)( |$)/,["type","white","operators","white"]],{include:"@flowScalars"},[/.+?(?=(\s+#|$))/,{cases:{"@keywords":"keyword","@default":"string"}}]],object:[{include:"@whitespace"},{include:"@comment"},[/\}/,"@brackets","@pop"],[/,/,"delimiter.comma"],[/:(?= )/,"operators"],[/(?:".*?"|'.*?'|[^,\{\[]+?)(?=: )/,"type"],{include:"@flowCollections"},{include:"@flowScalars"},{include:"@tagHandle"},{include:"@anchor"},{include:"@flowNumber"},[/[^\},]+/,{cases:{"@keywords":"keyword","@default":"string"}}]],array:[{include:"@whitespace"},{include:"@comment"},[/\]/,"@brackets","@pop"],[/,/,"delimiter.comma"],{include:"@flowCollections"},{include:"@flowScalars"},{include:"@tagHandle"},{include:"@anchor"},{include:"@flowNumber"},[/[^\],]+/,{cases:{"@keywords":"keyword","@default":"string"}}]],multiString:[[/^( +).+$/,"string","@multiStringContinued.$1"]],multiStringContinued:[[/^( *).+$/,{cases:{"$1==$S2":"string","@default":{token:"@rematch",next:"@popall"}}}]],whitespace:[[/[ \t\r\n]+/,"white"]],comment:[[/#.*$/,"comment"]],flowCollections:[[/\[/,"@brackets","@array"],[/\{/,"@brackets","@object"]],flowScalars:[[/"([^"\\]|\\.)*$/,"string.invalid"],[/'([^'\\]|\\.)*$/,"string.invalid"],[/'[^']*'/,"string"],[/"/,"string","@doubleQuotedString"]],doubleQuotedString:[[/[^\\"]+/,"string"],[/@escapes/,"string.escape"],[/\\./,"string.escape.invalid"],[/"/,"string","@pop"]],blockStyle:[[/[>|][0-9]*[+-]?$/,"operators","@multiString"]],flowNumber:[[/@numberInteger(?=[ \t]*[,\]\}])/,"number"],[/@numberFloat(?=[ \t]*[,\]\}])/,"number.float"],[/@numberOctal(?=[ \t]*[,\]\}])/,"number.octal"],[/@numberHex(?=[ \t]*[,\]\}])/,"number.hex"],[/@numberInfinity(?=[ \t]*[,\]\}])/,"number.infinity"],[/@numberNaN(?=[ \t]*[,\]\}])/,"number.nan"],[/@numberDate(?=[ \t]*[,\]\}])/,"number.date"]],tagHandle:[[/\![^ ]*/,"tag"]],anchor:[[/[&*][^ ]+/,"namespace"]]}};export{t as conf,o as language};
1
+ import{l as e}from"./__federation_expose_App.BcKopQK3.js";const t={comments:{lineComment:"#"},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}],folding:{offSide:!0},onEnterRules:[{beforeText:/:\s*$/,action:{indentAction:e.IndentAction.Indent}}]},o={tokenPostfix:".yaml",brackets:[{token:"delimiter.bracket",open:"{",close:"}"},{token:"delimiter.square",open:"[",close:"]"}],keywords:["true","True","TRUE","false","False","FALSE","null","Null","Null","~"],numberInteger:/(?:0|[+-]?[0-9]+)/,numberFloat:/(?:0|[+-]?[0-9]+)(?:\.[0-9]+)?(?:e[-+][1-9][0-9]*)?/,numberOctal:/0o[0-7]+/,numberHex:/0x[0-9a-fA-F]+/,numberInfinity:/[+-]?\.(?:inf|Inf|INF)/,numberNaN:/\.(?:nan|Nan|NAN)/,numberDate:/\d{4}-\d\d-\d\d([Tt ]\d\d:\d\d:\d\d(\.\d+)?(( ?[+-]\d\d?(:\d\d)?)|Z)?)?/,escapes:/\\(?:[btnfr\\"']|[0-7][0-7]?|[0-3][0-7]{2})/,tokenizer:{root:[{include:"@whitespace"},{include:"@comment"},[/%[^ ]+.*$/,"meta.directive"],[/---/,"operators.directivesEnd"],[/\.{3}/,"operators.documentEnd"],[/[-?:](?= )/,"operators"],{include:"@anchor"},{include:"@tagHandle"},{include:"@flowCollections"},{include:"@blockStyle"},[/@numberInteger(?![ \t]*\S+)/,"number"],[/@numberFloat(?![ \t]*\S+)/,"number.float"],[/@numberOctal(?![ \t]*\S+)/,"number.octal"],[/@numberHex(?![ \t]*\S+)/,"number.hex"],[/@numberInfinity(?![ \t]*\S+)/,"number.infinity"],[/@numberNaN(?![ \t]*\S+)/,"number.nan"],[/@numberDate(?![ \t]*\S+)/,"number.date"],[/(".*?"|'.*?'|[^#'"]*?)([ \t]*)(:)( |$)/,["type","white","operators","white"]],{include:"@flowScalars"},[/.+?(?=(\s+#|$))/,{cases:{"@keywords":"keyword","@default":"string"}}]],object:[{include:"@whitespace"},{include:"@comment"},[/\}/,"@brackets","@pop"],[/,/,"delimiter.comma"],[/:(?= )/,"operators"],[/(?:".*?"|'.*?'|[^,\{\[]+?)(?=: )/,"type"],{include:"@flowCollections"},{include:"@flowScalars"},{include:"@tagHandle"},{include:"@anchor"},{include:"@flowNumber"},[/[^\},]+/,{cases:{"@keywords":"keyword","@default":"string"}}]],array:[{include:"@whitespace"},{include:"@comment"},[/\]/,"@brackets","@pop"],[/,/,"delimiter.comma"],{include:"@flowCollections"},{include:"@flowScalars"},{include:"@tagHandle"},{include:"@anchor"},{include:"@flowNumber"},[/[^\],]+/,{cases:{"@keywords":"keyword","@default":"string"}}]],multiString:[[/^( +).+$/,"string","@multiStringContinued.$1"]],multiStringContinued:[[/^( *).+$/,{cases:{"$1==$S2":"string","@default":{token:"@rematch",next:"@popall"}}}]],whitespace:[[/[ \t\r\n]+/,"white"]],comment:[[/#.*$/,"comment"]],flowCollections:[[/\[/,"@brackets","@array"],[/\{/,"@brackets","@object"]],flowScalars:[[/"([^"\\]|\\.)*$/,"string.invalid"],[/'([^'\\]|\\.)*$/,"string.invalid"],[/'[^']*'/,"string"],[/"/,"string","@doubleQuotedString"]],doubleQuotedString:[[/[^\\"]+/,"string"],[/@escapes/,"string.escape"],[/\\./,"string.escape.invalid"],[/"/,"string","@pop"]],blockStyle:[[/[>|][0-9]*[+-]?$/,"operators","@multiString"]],flowNumber:[[/@numberInteger(?=[ \t]*[,\]\}])/,"number"],[/@numberFloat(?=[ \t]*[,\]\}])/,"number.float"],[/@numberOctal(?=[ \t]*[,\]\}])/,"number.octal"],[/@numberHex(?=[ \t]*[,\]\}])/,"number.hex"],[/@numberInfinity(?=[ \t]*[,\]\}])/,"number.infinity"],[/@numberNaN(?=[ \t]*[,\]\}])/,"number.nan"],[/@numberDate(?=[ \t]*[,\]\}])/,"number.date"]],tagHandle:[[/\![^ ]*/,"tag"]],anchor:[[/[&*][^ ]+/,"namespace"]]}};export{t as conf,o as language};
package/dist/index.html CHANGED
@@ -6,16 +6,16 @@
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <title>Rainbow Robotics System</title>
8
8
  <script type="importmap">{"imports":{"react":"/plugin-shared/react.js","react/jsx-runtime":"/plugin-shared/react_jsx-runtime.js","react/jsx-dev-runtime":"/plugin-shared/react_jsx-dev-runtime.js","react-dom":"/plugin-shared/react-dom.js","react-dom/client":"/plugin-shared/react-dom_client.js","@rainbow-robotics/plugin-ui/react":"/plugin-shared/plugin-ui_react.js","@rainbow-robotics/plugin-ui/sdk":"/plugin-shared/plugin-ui_sdk.js","@rainbow-robotics/plugin-ui/host-ui":"/plugin-shared/plugin-ui_host-ui.js","@rainbow-robotics/plugin-ui/ws":"/plugin-shared/plugin-ui_ws.js"}}</script>
9
- <script type="module" crossorigin src="/assets/index.DFTXe41N.js"></script>
9
+ <script type="module" crossorigin src="/assets/index.BddoWb3-.js"></script>
10
10
  <link rel="modulepreload" crossorigin href="/assets/__federation_fn_import.DdkEuT6t.js">
11
11
  <link rel="modulepreload" crossorigin href="/assets/index.P6snMalu.js">
12
12
  <link rel="modulepreload" crossorigin href="/assets/jsx-runtime.CBqCNPYB.js">
13
13
  <link rel="modulepreload" crossorigin href="/assets/emotion-styled-base.browser.esm.BIcjgOzg.js">
14
- <link rel="modulepreload" crossorigin href="/assets/RbLocalizationProvider.GKeO3IYX.js">
14
+ <link rel="modulepreload" crossorigin href="/assets/RbLocalizationProvider.BX3b4qG3.js">
15
15
  <link rel="modulepreload" crossorigin href="/assets/index.B3zY1kf7.js">
16
- <link rel="modulepreload" crossorigin href="/assets/useFileDownload.BcvMcbUi.js">
17
- <link rel="modulepreload" crossorigin href="/assets/index.DHJstkPN.js">
18
- <link rel="modulepreload" crossorigin href="/assets/__federation_expose_App.acYJ0rMi.js">
16
+ <link rel="modulepreload" crossorigin href="/assets/useFileDownload.Cbk5Bf_C.js">
17
+ <link rel="modulepreload" crossorigin href="/assets/index.Bah31zT_.js">
18
+ <link rel="modulepreload" crossorigin href="/assets/__federation_expose_App.BcKopQK3.js">
19
19
 
20
20
 
21
21
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rainbow-robotics/preview-host",
3
- "version": "1.10.2",
3
+ "version": "1.10.3-dev.2",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": ">=20"
package/serve.mjs CHANGED
@@ -2,8 +2,9 @@
2
2
  // Usage:
3
3
  // rb-preview-host --plugin <staged-dir> [--port 4180] [--dist <dir>]
4
4
  import { createServer } from "node:http";
5
- import { createReadStream, existsSync, readFileSync, statSync, watch } from "node:fs";
6
- import { dirname, extname, join, resolve } from "node:path";
5
+ import { createReadStream, existsSync, mkdirSync, readFileSync, renameSync, statSync, watch, writeFileSync } from "node:fs";
6
+ import { homedir } from "node:os";
7
+ import { dirname, extname, join, resolve, sep } from "node:path";
7
8
  import { fileURLToPath } from "node:url";
8
9
 
9
10
  const here = dirname(fileURLToPath(import.meta.url));
@@ -69,8 +70,7 @@ if (pluginDir) {
69
70
  const injectedLines = [
70
71
  " <script>",
71
72
  " window.__RB_PLUGIN_PREVIEW_HOST__ = true;",
72
- // With a robot, use its assets server (models/ — URDF + meshes — isn't bundled: too large for npm).
73
- ` window.__RB_ASSETS_BASE_URL__ = ${robotHost ? JSON.stringify(`http://${robotHost}:5177`) : "window.location.origin"};`,
73
+ " window.__RB_ASSETS_BASE_URL__ = window.location.origin;",
74
74
  ` window.__RB_PLUGIN_PREVIEW_BASE__ = ${JSON.stringify(PLUGIN_BASE)};`,
75
75
  ];
76
76
  if (previewPluginId) {
@@ -124,24 +124,83 @@ function tryServe(res, baseDir, relPath, opts) {
124
124
  }
125
125
 
126
126
 
127
+ // models/ (URDF + meshes, ~800MB) isn't bundled — too large for npm. Each file is fetched from the
128
+ // kit's S3 copy on first use and kept in ~/.muscat-pk/models, so the 3D robot view works with any
129
+ // robot or a local core SW, and offline once a model has been seen.
130
+ // ponytail: cached by path only; a changed model needs ~/.muscat-pk/models cleared.
131
+ // Tried in order: the kit's S3 copy, then the assets server (:5177) of the robot / local Muscat,
132
+ // which has the models that Muscat ships — so a model missing from S3 (or no internet) still shows.
133
+ const MODEL_SOURCES = [
134
+ ...new Set([
135
+ process.env.MUSCAT_PK_MODELS_URL || "https://rainbow-deploy.s3.ap-northeast-2.amazonaws.com/muscat-plugin-kit/models",
136
+ ...(robotHost ? [`http://${robotHost}:5177/models`] : []),
137
+ "http://localhost:5177/models",
138
+ ]),
139
+ ];
140
+ const MODELS_CACHE = resolve(homedir(), ".muscat-pk", "models");
141
+
142
+ const unreachable = new Set(); // a source that didn't answer is skipped from then on
143
+
144
+ async function fetchModel(rel) {
145
+ const path = rel.split("/").map(encodeURIComponent).join("/");
146
+ for (const base of MODEL_SOURCES) {
147
+ if (unreachable.has(base)) continue;
148
+ try {
149
+ const r = await fetch(`${base}/${path}`, { signal: AbortSignal.timeout(15_000) });
150
+ // S3 answers 403 for a missing key; a web server may answer 200 with an HTML fallback page.
151
+ if (r.ok && !(r.headers.get("content-type") ?? "").startsWith("text/html")) return Buffer.from(await r.arrayBuffer());
152
+ } catch {
153
+ unreachable.add(base);
154
+ }
155
+ }
156
+ return null;
157
+ }
158
+
159
+ async function serveModel(res, url) {
160
+ const rel = decodeURIComponent(url.slice("/models/".length));
161
+ const file = resolve(MODELS_CACHE, rel);
162
+ if (!file.startsWith(MODELS_CACHE + sep)) {
163
+ res.statusCode = 400;
164
+ res.end();
165
+ return;
166
+ }
167
+ if (!existsSync(file)) {
168
+ const data = await fetchModel(rel);
169
+ if (!data) {
170
+ res.statusCode = 404;
171
+ res.end(`model file not found: ${rel}`);
172
+ return;
173
+ }
174
+ mkdirSync(dirname(file), { recursive: true });
175
+ const part = `${file}.${process.pid}.part`;
176
+ writeFileSync(part, data);
177
+ renameSync(part, file);
178
+ }
179
+ sendFile(res, file);
180
+ }
181
+
127
182
  const reloadClients = new Set();
128
183
  if (pluginDir && existsSync(pluginDir)) {
129
184
  let timer = null;
130
- const notify = () => {
185
+ const notify = (_event, file) => {
186
+ if (String(file ?? "").startsWith("sysroot")) return;
131
187
  clearTimeout(timer);
132
188
  timer = setTimeout(() => {
133
189
  for (const c of reloadClients) c.write("data: reload\n\n");
134
190
  }, 100);
135
191
  };
136
- try {
137
- // Recursive: macOS/Windows always, Linux on Node 20.13+.
138
- watch(pluginDir, { recursive: true }, notify);
139
- } catch {
140
-
141
- watch(pluginDir, notify);
142
- console.warn(
143
- "[preview-host] recursive watch unavailable — live reload limited to plugin root (upgrade to Node 20.13+ for full watch).",
144
- );
192
+ // Only what the page uses: the staged root files and assets/. The same dir also holds the editor's
193
+ // C++ SDK headers (sysroot/), which a recursive watch would scan and trip over while it is replaced.
194
+ // A watcher error must not take the preview down.
195
+ watch(pluginDir, notify).on("error", () => {});
196
+ const assetsDir = join(pluginDir, "assets");
197
+ if (existsSync(assetsDir)) {
198
+ try {
199
+ // Recursive: macOS/Windows always, Linux on Node 20.13+.
200
+ watch(assetsDir, { recursive: true }, notify).on("error", () => {});
201
+ } catch {
202
+ watch(assetsDir, notify).on("error", () => {});
203
+ }
145
204
  }
146
205
  }
147
206
 
@@ -177,6 +236,14 @@ const server = createServer((req, res) => {
177
236
  return;
178
237
  }
179
238
 
239
+ if (url.startsWith("/models/")) {
240
+ serveModel(res, url).catch((e) => {
241
+ res.statusCode = 502;
242
+ res.end(`model download failed: ${e.message}`);
243
+ });
244
+ return;
245
+ }
246
+
180
247
  // Static bundle + rb assets
181
248
  if (url !== "/" && tryServe(res, distDir, url)) return;
182
249