@frontmcp/uipack 1.2.1 → 1.4.0
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/adapters/index.js +1046 -698
- package/adapters/template-renderer.d.ts +14 -0
- package/adapters/template-renderer.d.ts.map +1 -1
- package/bridge-runtime/iife-generator.d.ts.map +1 -1
- package/bridge-runtime/index.js +149 -0
- package/component/index.d.ts +1 -0
- package/component/index.d.ts.map +1 -1
- package/component/index.js +468 -145
- package/component/loader.d.ts +21 -2
- package/component/loader.d.ts.map +1 -1
- package/component/renderer.d.ts +2 -2
- package/component/renderer.d.ts.map +1 -1
- package/component/transpiler.d.ts +16 -1
- package/component/transpiler.d.ts.map +1 -1
- package/component/types.d.ts +19 -0
- package/component/types.d.ts.map +1 -1
- package/component/ui-availability.d.ts +27 -0
- package/component/ui-availability.d.ts.map +1 -0
- package/esm/adapters/index.mjs +1046 -698
- package/esm/bridge-runtime/index.mjs +149 -0
- package/esm/component/index.mjs +468 -145
- package/esm/index.mjs +444 -109
- package/esm/package.json +2 -2
- package/esm/shell/index.mjs +420 -171
- package/index.d.ts +1 -1
- package/index.d.ts.map +1 -1
- package/index.js +445 -109
- package/package.json +2 -2
- package/shell/builder.d.ts.map +1 -1
- package/shell/data-injector.d.ts +27 -1
- package/shell/data-injector.d.ts.map +1 -1
- package/shell/index.d.ts +3 -2
- package/shell/index.d.ts.map +1 -1
- package/shell/index.js +423 -171
- package/shell/sizing-css.d.ts +27 -0
- package/shell/sizing-css.d.ts.map +1 -0
- package/shell/types.d.ts +102 -0
- package/shell/types.d.ts.map +1 -1
- package/types/index.d.ts +1 -1
- package/types/index.d.ts.map +1 -1
- package/types/ui-config.d.ts +105 -11
- package/types/ui-config.d.ts.map +1 -1
- package/types/ui-runtime.d.ts +23 -2
- package/types/ui-runtime.d.ts.map +1 -1
package/index.js
CHANGED
|
@@ -46,6 +46,7 @@ __export(index_exports, {
|
|
|
46
46
|
buildCSPDirectives: () => buildCSPDirectives,
|
|
47
47
|
buildCSPMetaTag: () => buildCSPMetaTag,
|
|
48
48
|
buildChartHtml: () => buildChartHtml,
|
|
49
|
+
buildCustomDataInjectionScript: () => buildCustomDataInjectionScript,
|
|
49
50
|
buildDataInjectionScript: () => buildDataInjectionScript,
|
|
50
51
|
buildMermaidHtml: () => buildMermaidHtml,
|
|
51
52
|
buildPdfHtml: () => buildPdfHtml,
|
|
@@ -297,6 +298,8 @@ function generateBridgeIIFE(options = {}) {
|
|
|
297
298
|
parts.push("});");
|
|
298
299
|
parts.push("");
|
|
299
300
|
parts.push("window.FrontMcpBridge = bridge;");
|
|
301
|
+
parts.push("");
|
|
302
|
+
parts.push(generateAutoResize());
|
|
300
303
|
parts.push("function __showLoading() {");
|
|
301
304
|
parts.push(' var root = document.getElementById("root");');
|
|
302
305
|
parts.push(" if (root && !root.hasChildNodes()) {");
|
|
@@ -317,6 +320,112 @@ function generateBridgeIIFE(options = {}) {
|
|
|
317
320
|
}
|
|
318
321
|
return code;
|
|
319
322
|
}
|
|
323
|
+
function generateAutoResize() {
|
|
324
|
+
return `
|
|
325
|
+
function __applySizingCss(sizing) {
|
|
326
|
+
if (typeof document === 'undefined' || !document.documentElement) return;
|
|
327
|
+
function toLen(v) { return typeof v === 'number' ? v + 'px' : v; }
|
|
328
|
+
var de = document.documentElement;
|
|
329
|
+
var body = document.body;
|
|
330
|
+
var root = document.getElementById('root');
|
|
331
|
+
if (sizing.preferredHeight != null) {
|
|
332
|
+
var ph = toLen(sizing.preferredHeight);
|
|
333
|
+
de.style.height = ph;
|
|
334
|
+
if (body) body.style.height = ph;
|
|
335
|
+
if (root && !root.style.minHeight) root.style.minHeight = ph;
|
|
336
|
+
}
|
|
337
|
+
if (sizing.minHeight != null) {
|
|
338
|
+
var mh = toLen(sizing.minHeight);
|
|
339
|
+
de.style.minHeight = mh;
|
|
340
|
+
if (body) body.style.minHeight = mh;
|
|
341
|
+
if (root) root.style.minHeight = mh;
|
|
342
|
+
}
|
|
343
|
+
if (sizing.maxHeight != null) {
|
|
344
|
+
var mx = toLen(sizing.maxHeight);
|
|
345
|
+
de.style.maxHeight = mx;
|
|
346
|
+
if (body) body.style.maxHeight = mx;
|
|
347
|
+
if (root) root.style.maxHeight = mx;
|
|
348
|
+
}
|
|
349
|
+
if (sizing.aspectRatio != null && root) {
|
|
350
|
+
root.style.aspectRatio = String(sizing.aspectRatio);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function __initAutoResize() {
|
|
355
|
+
if (typeof window === 'undefined') return;
|
|
356
|
+
// Idempotent: a re-injected IIFE must not stack observers.
|
|
357
|
+
if (window.__mcpAutoResizeInit) return;
|
|
358
|
+
window.__mcpAutoResizeInit = true;
|
|
359
|
+
var sizing = window.__mcpWidgetSizing;
|
|
360
|
+
if (!sizing || typeof sizing !== 'object') return;
|
|
361
|
+
|
|
362
|
+
// Apply CSS as a runtime fallback (the static <style> may be absent on some
|
|
363
|
+
// render paths). Wait for the body if it isn't ready yet.
|
|
364
|
+
function apply() { try { __applySizingCss(sizing); } catch (e) {} }
|
|
365
|
+
if (typeof document !== 'undefined' && document.readyState === 'loading') {
|
|
366
|
+
document.addEventListener('DOMContentLoaded', apply);
|
|
367
|
+
} else {
|
|
368
|
+
apply();
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Auto-resize defaults ON; opt out with autoResize:false.
|
|
372
|
+
if (sizing.autoResize === false) return;
|
|
373
|
+
if (typeof ResizeObserver === 'undefined') return;
|
|
374
|
+
|
|
375
|
+
function startObserving() {
|
|
376
|
+
var target = document.getElementById('root') || document.body;
|
|
377
|
+
if (!target) return;
|
|
378
|
+
|
|
379
|
+
var rafId = null;
|
|
380
|
+
var lastReported = -1;
|
|
381
|
+
function report() {
|
|
382
|
+
rafId = null;
|
|
383
|
+
try {
|
|
384
|
+
var rect = target.getBoundingClientRect();
|
|
385
|
+
var height = Math.ceil(rect.height);
|
|
386
|
+
var width = Math.ceil(rect.width);
|
|
387
|
+
if (height === lastReported || height <= 0) return;
|
|
388
|
+
lastReported = height;
|
|
389
|
+
var payload = { height: height, width: width };
|
|
390
|
+
if (sizing.aspectRatio != null) payload.aspectRatio = sizing.aspectRatio;
|
|
391
|
+
if (window.FrontMcpBridge && typeof window.FrontMcpBridge.setSize === 'function') {
|
|
392
|
+
window.FrontMcpBridge.setSize(payload).catch(function() {});
|
|
393
|
+
}
|
|
394
|
+
window.dispatchEvent(new CustomEvent('widget:resize', { detail: payload }));
|
|
395
|
+
} catch (e) {}
|
|
396
|
+
}
|
|
397
|
+
function schedule() {
|
|
398
|
+
// Debounce via rAF; fall back to setTimeout if rAF is unavailable.
|
|
399
|
+
if (rafId != null) return;
|
|
400
|
+
if (typeof requestAnimationFrame === 'function') {
|
|
401
|
+
rafId = requestAnimationFrame(report);
|
|
402
|
+
} else {
|
|
403
|
+
rafId = setTimeout(report, 100);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
try {
|
|
408
|
+
// Disconnect any prior observer before creating a new one (no leaks/dupes).
|
|
409
|
+
if (window.__mcpResizeObserver && typeof window.__mcpResizeObserver.disconnect === 'function') {
|
|
410
|
+
window.__mcpResizeObserver.disconnect();
|
|
411
|
+
}
|
|
412
|
+
var ro = new ResizeObserver(function() { schedule(); });
|
|
413
|
+
ro.observe(target);
|
|
414
|
+
window.__mcpResizeObserver = ro;
|
|
415
|
+
} catch (e) {}
|
|
416
|
+
// Report once on init so the host gets an initial measurement.
|
|
417
|
+
schedule();
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
if (typeof document !== 'undefined' && document.readyState === 'loading') {
|
|
421
|
+
document.addEventListener('DOMContentLoaded', startObserving);
|
|
422
|
+
} else {
|
|
423
|
+
startObserving();
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
__initAutoResize();
|
|
427
|
+
`.trim();
|
|
428
|
+
}
|
|
320
429
|
function generateContextDetection() {
|
|
321
430
|
return `
|
|
322
431
|
function detectTheme() {
|
|
@@ -424,6 +533,19 @@ var OpenAIAdapter = {
|
|
|
424
533
|
requestDisplayMode: function(context, mode) {
|
|
425
534
|
return Promise.resolve();
|
|
426
535
|
},
|
|
536
|
+
setSize: function(context, size) {
|
|
537
|
+
// OpenAI Apps SDK measures DOM height itself; if a sizing API surfaces on
|
|
538
|
+
// window.openai, forward to it, otherwise no-op (CSS drives layout).
|
|
539
|
+
try {
|
|
540
|
+
if (window.openai && typeof window.openai.requestDisplayMode === 'function' && size && size.displayMode) {
|
|
541
|
+
window.openai.requestDisplayMode(size.displayMode);
|
|
542
|
+
}
|
|
543
|
+
if (window.openai && typeof window.openai.setWidgetHeight === 'function' && size && typeof size.height === 'number') {
|
|
544
|
+
window.openai.setWidgetHeight(size.height);
|
|
545
|
+
}
|
|
546
|
+
} catch (e) {}
|
|
547
|
+
return Promise.resolve();
|
|
548
|
+
},
|
|
427
549
|
requestClose: function(context) {
|
|
428
550
|
return Promise.resolve();
|
|
429
551
|
}
|
|
@@ -668,6 +790,15 @@ var ExtAppsAdapter = {
|
|
|
668
790
|
requestDisplayMode: function(context, mode) {
|
|
669
791
|
return this.sendRequest('ui/setDisplayMode', { mode: mode });
|
|
670
792
|
},
|
|
793
|
+
setSize: function(context, size) {
|
|
794
|
+
// FrontMCP sizing channel \u2014 parallels ui/setDisplayMode. Reports the
|
|
795
|
+
// measured/desired widget dimensions to the host.
|
|
796
|
+
return this.sendRequest('ui/setSize', {
|
|
797
|
+
height: size && size.height,
|
|
798
|
+
width: size && size.width,
|
|
799
|
+
aspectRatio: size && size.aspectRatio
|
|
800
|
+
});
|
|
801
|
+
},
|
|
671
802
|
requestClose: function(context) {
|
|
672
803
|
return this.sendRequest('ui/close', {});
|
|
673
804
|
},
|
|
@@ -756,6 +887,10 @@ var ClaudeAdapter = {
|
|
|
756
887
|
requestDisplayMode: function() {
|
|
757
888
|
return Promise.resolve();
|
|
758
889
|
},
|
|
890
|
+
setSize: function() {
|
|
891
|
+
// Claude measures the rendered DOM height itself \u2014 CSS-only, no reporting.
|
|
892
|
+
return Promise.resolve();
|
|
893
|
+
},
|
|
759
894
|
requestClose: function() {
|
|
760
895
|
return Promise.resolve();
|
|
761
896
|
}
|
|
@@ -807,6 +942,9 @@ var GeminiAdapter = {
|
|
|
807
942
|
requestDisplayMode: function() {
|
|
808
943
|
return Promise.resolve();
|
|
809
944
|
},
|
|
945
|
+
setSize: function() {
|
|
946
|
+
return Promise.resolve();
|
|
947
|
+
},
|
|
810
948
|
requestClose: function() {
|
|
811
949
|
return Promise.resolve();
|
|
812
950
|
}
|
|
@@ -842,6 +980,9 @@ var GenericAdapter = {
|
|
|
842
980
|
requestDisplayMode: function() {
|
|
843
981
|
return Promise.resolve();
|
|
844
982
|
},
|
|
983
|
+
setSize: function() {
|
|
984
|
+
return Promise.resolve();
|
|
985
|
+
},
|
|
845
986
|
requestClose: function() {
|
|
846
987
|
return Promise.resolve();
|
|
847
988
|
}
|
|
@@ -1076,6 +1217,15 @@ FrontMcpBridge.prototype.requestDisplayMode = function(mode) {
|
|
|
1076
1217
|
});
|
|
1077
1218
|
};
|
|
1078
1219
|
|
|
1220
|
+
// Report a desired widget size to the host. \`size\` is { height?, width?, aspectRatio? }.
|
|
1221
|
+
// Per-adapter behaviour: Claude/generic no-op (host measures the DOM),
|
|
1222
|
+
// ext-apps sends ui/setSize, OpenAI forwards to its SDK when available.
|
|
1223
|
+
FrontMcpBridge.prototype.setSize = function(size) {
|
|
1224
|
+
if (!this._adapter) return Promise.reject(new Error('Not initialized'));
|
|
1225
|
+
if (!this._adapter.setSize) return Promise.resolve();
|
|
1226
|
+
return this._adapter.setSize(this._context, size || {});
|
|
1227
|
+
};
|
|
1228
|
+
|
|
1079
1229
|
FrontMcpBridge.prototype.requestClose = function() {
|
|
1080
1230
|
if (!this._adapter) return Promise.reject(new Error('Not initialized'));
|
|
1081
1231
|
return this._adapter.requestClose(this._context);
|
|
@@ -2830,9 +2980,61 @@ function escapeAttribute(str) {
|
|
|
2830
2980
|
return str.replace(/&/g, "&").replace(/"/g, """).replace(/'/g, "'").replace(/</g, "<").replace(/>/g, ">");
|
|
2831
2981
|
}
|
|
2832
2982
|
|
|
2983
|
+
// libs/uipack/src/shell/custom-shell-types.ts
|
|
2984
|
+
var SHELL_PLACEHOLDER_NAMES = ["CSP", "DATA", "BRIDGE", "CONTENT", "TITLE"];
|
|
2985
|
+
var SHELL_PLACEHOLDERS = {
|
|
2986
|
+
CSP: "{{CSP}}",
|
|
2987
|
+
DATA: "{{DATA}}",
|
|
2988
|
+
BRIDGE: "{{BRIDGE}}",
|
|
2989
|
+
CONTENT: "{{CONTENT}}",
|
|
2990
|
+
TITLE: "{{TITLE}}"
|
|
2991
|
+
};
|
|
2992
|
+
var REQUIRED_PLACEHOLDERS = ["CONTENT"];
|
|
2993
|
+
var OPTIONAL_PLACEHOLDERS = ["CSP", "DATA", "BRIDGE", "TITLE"];
|
|
2994
|
+
function isInlineShellSource(source) {
|
|
2995
|
+
return typeof source === "object" && source !== null && "inline" in source;
|
|
2996
|
+
}
|
|
2997
|
+
function isUrlShellSource(source) {
|
|
2998
|
+
return typeof source === "object" && source !== null && "url" in source;
|
|
2999
|
+
}
|
|
3000
|
+
function isNpmShellSource(source) {
|
|
3001
|
+
return typeof source === "object" && source !== null && "npm" in source;
|
|
3002
|
+
}
|
|
3003
|
+
|
|
3004
|
+
// libs/uipack/src/shell/custom-shell-applier.ts
|
|
3005
|
+
function applyShellTemplate(template, values) {
|
|
3006
|
+
let result = template;
|
|
3007
|
+
result = result.replaceAll(SHELL_PLACEHOLDERS.CSP, values.csp);
|
|
3008
|
+
result = result.replaceAll(SHELL_PLACEHOLDERS.DATA, values.data);
|
|
3009
|
+
result = result.replaceAll(SHELL_PLACEHOLDERS.BRIDGE, values.bridge);
|
|
3010
|
+
result = result.replaceAll(SHELL_PLACEHOLDERS.CONTENT, values.content);
|
|
3011
|
+
result = result.replaceAll(SHELL_PLACEHOLDERS.TITLE, values.title);
|
|
3012
|
+
return result;
|
|
3013
|
+
}
|
|
3014
|
+
|
|
3015
|
+
// libs/uipack/src/shell/custom-shell-validator.ts
|
|
3016
|
+
function validateShellTemplate(template) {
|
|
3017
|
+
const found = {};
|
|
3018
|
+
for (const name of SHELL_PLACEHOLDER_NAMES) {
|
|
3019
|
+
found[name] = template.includes(SHELL_PLACEHOLDERS[name]);
|
|
3020
|
+
}
|
|
3021
|
+
const missingRequired = REQUIRED_PLACEHOLDERS.filter((name) => !found[name]);
|
|
3022
|
+
const missingOptional = OPTIONAL_PLACEHOLDERS.filter((name) => !found[name]);
|
|
3023
|
+
return {
|
|
3024
|
+
valid: missingRequired.length === 0,
|
|
3025
|
+
found,
|
|
3026
|
+
missingRequired,
|
|
3027
|
+
missingOptional
|
|
3028
|
+
};
|
|
3029
|
+
}
|
|
3030
|
+
|
|
2833
3031
|
// libs/uipack/src/shell/data-injector.ts
|
|
3032
|
+
function hasSizing(sizing) {
|
|
3033
|
+
if (!sizing) return false;
|
|
3034
|
+
return sizing.preferredHeight !== void 0 || sizing.minHeight !== void 0 || sizing.maxHeight !== void 0 || sizing.aspectRatio !== void 0 || sizing.autoResize !== void 0;
|
|
3035
|
+
}
|
|
2834
3036
|
function buildDataInjectionScript(options) {
|
|
2835
|
-
const { toolName, input, output, structuredContent } = options;
|
|
3037
|
+
const { toolName, input, output, structuredContent, sizing } = options;
|
|
2836
3038
|
const lines = [
|
|
2837
3039
|
`window.__mcpAppsEnabled = true;`,
|
|
2838
3040
|
`window.__mcpToolName = ${safeJsonForScript(toolName)};`,
|
|
@@ -2840,10 +3042,24 @@ function buildDataInjectionScript(options) {
|
|
|
2840
3042
|
`window.__mcpToolOutput = ${safeJsonForScript(output ?? null)};`,
|
|
2841
3043
|
`window.__mcpStructuredContent = ${safeJsonForScript(structuredContent ?? null)};`
|
|
2842
3044
|
];
|
|
3045
|
+
if (hasSizing(sizing)) {
|
|
3046
|
+
lines.push(`window.__mcpWidgetSizing = ${safeJsonForScript(sizing)};`);
|
|
3047
|
+
}
|
|
2843
3048
|
return `<script>
|
|
2844
3049
|
${lines.join("\n")}
|
|
2845
3050
|
</script>`;
|
|
2846
3051
|
}
|
|
3052
|
+
function buildCustomDataInjectionScript(descriptor) {
|
|
3053
|
+
if (descriptor.script !== void 0) {
|
|
3054
|
+
return descriptor.script;
|
|
3055
|
+
}
|
|
3056
|
+
if (descriptor.globalKey !== void 0) {
|
|
3057
|
+
return `<script>window[${safeJsonForScript(descriptor.globalKey)}] = ${safeJsonForScript(
|
|
3058
|
+
descriptor.value ?? null
|
|
3059
|
+
)};</script>`;
|
|
3060
|
+
}
|
|
3061
|
+
return "";
|
|
3062
|
+
}
|
|
2847
3063
|
var _uniqueIdCounter = 0;
|
|
2848
3064
|
function createTemplateHelpers() {
|
|
2849
3065
|
return {
|
|
@@ -2871,59 +3087,79 @@ function createTemplateHelpers() {
|
|
|
2871
3087
|
};
|
|
2872
3088
|
}
|
|
2873
3089
|
|
|
2874
|
-
// libs/uipack/src/shell/
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
CSP: "{{CSP}}",
|
|
2878
|
-
DATA: "{{DATA}}",
|
|
2879
|
-
BRIDGE: "{{BRIDGE}}",
|
|
2880
|
-
CONTENT: "{{CONTENT}}",
|
|
2881
|
-
TITLE: "{{TITLE}}"
|
|
2882
|
-
};
|
|
2883
|
-
var REQUIRED_PLACEHOLDERS = ["CONTENT"];
|
|
2884
|
-
var OPTIONAL_PLACEHOLDERS = ["CSP", "DATA", "BRIDGE", "TITLE"];
|
|
2885
|
-
function isInlineShellSource(source) {
|
|
2886
|
-
return typeof source === "object" && source !== null && "inline" in source;
|
|
3090
|
+
// libs/uipack/src/shell/sizing-css.ts
|
|
3091
|
+
function sanitizeCssValue(value) {
|
|
3092
|
+
return value.replace(/[<>{};]/g, "").trim();
|
|
2887
3093
|
}
|
|
2888
|
-
function
|
|
2889
|
-
return typeof
|
|
3094
|
+
function toCssLength(value) {
|
|
3095
|
+
return typeof value === "number" ? `${value}px` : sanitizeCssValue(value);
|
|
2890
3096
|
}
|
|
2891
|
-
function
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
}
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
3097
|
+
function buildSizingStyleTag(sizing) {
|
|
3098
|
+
if (!hasSizing(sizing)) return "";
|
|
3099
|
+
const hasCssSizing = sizing.preferredHeight !== void 0 || sizing.minHeight !== void 0 || sizing.maxHeight !== void 0 || sizing.aspectRatio !== void 0;
|
|
3100
|
+
if (!hasCssSizing) return "";
|
|
3101
|
+
const rootRules = [];
|
|
3102
|
+
const docRules = ["margin: 0;"];
|
|
3103
|
+
if (sizing.preferredHeight !== void 0) {
|
|
3104
|
+
const h = toCssLength(sizing.preferredHeight);
|
|
3105
|
+
docRules.push(`height: ${h};`);
|
|
3106
|
+
rootRules.push(`min-height: ${h};`);
|
|
3107
|
+
}
|
|
3108
|
+
if (sizing.minHeight !== void 0) {
|
|
3109
|
+
const mh = toCssLength(sizing.minHeight);
|
|
3110
|
+
docRules.push(`min-height: ${mh};`);
|
|
3111
|
+
rootRules.push(`min-height: ${mh};`);
|
|
3112
|
+
}
|
|
3113
|
+
if (sizing.maxHeight !== void 0) {
|
|
3114
|
+
const mx = toCssLength(sizing.maxHeight);
|
|
3115
|
+
docRules.push(`max-height: ${mx};`);
|
|
3116
|
+
rootRules.push(`max-height: ${mx};`);
|
|
3117
|
+
}
|
|
3118
|
+
if (sizing.aspectRatio !== void 0) {
|
|
3119
|
+
const ar = typeof sizing.aspectRatio === "number" ? String(sizing.aspectRatio) : sanitizeCssValue(sizing.aspectRatio);
|
|
3120
|
+
if (ar) rootRules.push(`aspect-ratio: ${ar};`);
|
|
3121
|
+
}
|
|
3122
|
+
const parts = [`html, body { ${docRules.join(" ")} }`];
|
|
3123
|
+
if (rootRules.length > 0) {
|
|
3124
|
+
parts.push(`#root { ${rootRules.join(" ")} }`);
|
|
3125
|
+
}
|
|
3126
|
+
return `<style>${parts.join("\n")}</style>`;
|
|
2920
3127
|
}
|
|
2921
3128
|
|
|
2922
3129
|
// libs/uipack/src/shell/builder.ts
|
|
3130
|
+
function resolveDataInjectionScript(args) {
|
|
3131
|
+
if (args.dataInjection) {
|
|
3132
|
+
return buildCustomDataInjectionScript(args.dataInjection);
|
|
3133
|
+
}
|
|
3134
|
+
return buildDataInjectionScript({
|
|
3135
|
+
toolName: args.toolName,
|
|
3136
|
+
input: args.input,
|
|
3137
|
+
output: args.output,
|
|
3138
|
+
structuredContent: args.structuredContent,
|
|
3139
|
+
sizing: args.sizing
|
|
3140
|
+
});
|
|
3141
|
+
}
|
|
2923
3142
|
function buildShell(content, config) {
|
|
2924
|
-
const {
|
|
2925
|
-
|
|
2926
|
-
|
|
3143
|
+
const {
|
|
3144
|
+
toolName,
|
|
3145
|
+
csp,
|
|
3146
|
+
withShell = true,
|
|
3147
|
+
input,
|
|
3148
|
+
output,
|
|
3149
|
+
structuredContent,
|
|
3150
|
+
includeBridge = true,
|
|
3151
|
+
title,
|
|
3152
|
+
sizing
|
|
3153
|
+
} = config;
|
|
3154
|
+
const { customShell, dataInjection } = config;
|
|
3155
|
+
const dataScript = resolveDataInjectionScript({
|
|
3156
|
+
dataInjection,
|
|
3157
|
+
toolName,
|
|
3158
|
+
input,
|
|
3159
|
+
output,
|
|
3160
|
+
structuredContent,
|
|
3161
|
+
sizing
|
|
3162
|
+
});
|
|
2927
3163
|
if (!withShell) {
|
|
2928
3164
|
const html2 = `${dataScript}
|
|
2929
3165
|
${content}`;
|
|
@@ -2941,7 +3177,9 @@ ${content}`;
|
|
|
2941
3177
|
output,
|
|
2942
3178
|
structuredContent,
|
|
2943
3179
|
includeBridge,
|
|
2944
|
-
title
|
|
3180
|
+
title,
|
|
3181
|
+
sizing,
|
|
3182
|
+
dataInjection
|
|
2945
3183
|
});
|
|
2946
3184
|
}
|
|
2947
3185
|
const headParts = [
|
|
@@ -2953,6 +3191,10 @@ ${content}`;
|
|
|
2953
3191
|
}
|
|
2954
3192
|
headParts.push(buildCSPMetaTag(csp));
|
|
2955
3193
|
headParts.push(dataScript);
|
|
3194
|
+
const sizingStyle = buildSizingStyleTag(sizing);
|
|
3195
|
+
if (sizingStyle) {
|
|
3196
|
+
headParts.push(sizingStyle);
|
|
3197
|
+
}
|
|
2956
3198
|
if (includeBridge) {
|
|
2957
3199
|
const bridgeScript = generateBridgeIIFE({ minify: true });
|
|
2958
3200
|
headParts.push(`<script>${bridgeScript}</script>`);
|
|
@@ -2986,12 +3228,17 @@ function buildCustomShell(content, customShell, ctx) {
|
|
|
2986
3228
|
template = customShell.template;
|
|
2987
3229
|
}
|
|
2988
3230
|
const cspTag = buildCSPMetaTag(ctx.csp);
|
|
2989
|
-
const dataScript =
|
|
3231
|
+
const dataScript = resolveDataInjectionScript({
|
|
3232
|
+
dataInjection: ctx.dataInjection,
|
|
2990
3233
|
toolName: ctx.toolName,
|
|
2991
3234
|
input: ctx.input,
|
|
2992
3235
|
output: ctx.output,
|
|
2993
|
-
structuredContent: ctx.structuredContent
|
|
3236
|
+
structuredContent: ctx.structuredContent,
|
|
3237
|
+
sizing: ctx.sizing
|
|
2994
3238
|
});
|
|
3239
|
+
const sizingStyle = buildSizingStyleTag(ctx.sizing);
|
|
3240
|
+
const dataWithSizing = sizingStyle ? `${dataScript}
|
|
3241
|
+
${sizingStyle}` : dataScript;
|
|
2995
3242
|
let bridgeHtml = "";
|
|
2996
3243
|
if (ctx.includeBridge) {
|
|
2997
3244
|
const bridgeScript = generateBridgeIIFE({ minify: true });
|
|
@@ -2999,7 +3246,7 @@ function buildCustomShell(content, customShell, ctx) {
|
|
|
2999
3246
|
}
|
|
3000
3247
|
const html = applyShellTemplate(template, {
|
|
3001
3248
|
csp: cspTag,
|
|
3002
|
-
data:
|
|
3249
|
+
data: dataWithSizing,
|
|
3003
3250
|
bridge: bridgeHtml,
|
|
3004
3251
|
content,
|
|
3005
3252
|
title: ctx.title ? escapeHtmlForTag(ctx.title) : ""
|
|
@@ -3146,6 +3393,29 @@ function isFunctionSource(source) {
|
|
|
3146
3393
|
}
|
|
3147
3394
|
var FRONTMCP_META_KEY = "__frontmcp_meta";
|
|
3148
3395
|
|
|
3396
|
+
// libs/uipack/src/component/ui-availability.ts
|
|
3397
|
+
function isFrontmcpUiResolvable(...candidatePaths) {
|
|
3398
|
+
try {
|
|
3399
|
+
const nodeFs = require("fs");
|
|
3400
|
+
const nodePath = require("path");
|
|
3401
|
+
const ORG = "@frontmcp";
|
|
3402
|
+
const PKG = "ui";
|
|
3403
|
+
for (const candidate of candidatePaths) {
|
|
3404
|
+
let dir = candidate;
|
|
3405
|
+
while (true) {
|
|
3406
|
+
const pkgJson = nodePath.join(dir, "node_modules", ORG, PKG, "package.json");
|
|
3407
|
+
if (nodeFs.existsSync(pkgJson)) return true;
|
|
3408
|
+
const parent = nodePath.dirname(dir);
|
|
3409
|
+
if (parent === dir) break;
|
|
3410
|
+
dir = parent;
|
|
3411
|
+
}
|
|
3412
|
+
}
|
|
3413
|
+
return false;
|
|
3414
|
+
} catch {
|
|
3415
|
+
return false;
|
|
3416
|
+
}
|
|
3417
|
+
}
|
|
3418
|
+
|
|
3149
3419
|
// libs/uipack/src/component/transpiler.ts
|
|
3150
3420
|
function transpileReactSource(source, filename) {
|
|
3151
3421
|
const esbuild = require("esbuild");
|
|
@@ -3161,7 +3431,12 @@ function transpileReactSource(source, filename) {
|
|
|
3161
3431
|
});
|
|
3162
3432
|
return result.code;
|
|
3163
3433
|
}
|
|
3164
|
-
function bundleFileSource(source, filename, resolveDir, componentName) {
|
|
3434
|
+
function bundleFileSource(source, filename, resolveDir, componentName, options = {}) {
|
|
3435
|
+
if (!isFrontmcpUiResolvable(resolveDir, process.cwd())) {
|
|
3436
|
+
throw new Error(
|
|
3437
|
+
`FileSource widget "${filename}" requires the @frontmcp/ui package, which provides the React bridge mount that's injected at bundle time. Install it (e.g. \`npm install @frontmcp/ui\` or \`yarn add @frontmcp/ui\`) and try again.`
|
|
3438
|
+
);
|
|
3439
|
+
}
|
|
3165
3440
|
const esbuild = require("esbuild");
|
|
3166
3441
|
const mountCode = `
|
|
3167
3442
|
// --- Auto-generated mount ---
|
|
@@ -3243,7 +3518,11 @@ if (__root) {
|
|
|
3243
3518
|
format: "esm",
|
|
3244
3519
|
target: "es2020",
|
|
3245
3520
|
jsx: "automatic",
|
|
3246
|
-
|
|
3521
|
+
// When `bundleReact` is set (resourceMode: 'inline'), React itself is
|
|
3522
|
+
// bundled — required for hosts that block external script execution
|
|
3523
|
+
// such as Claude (#454). Otherwise React stays external and is loaded
|
|
3524
|
+
// via the import map emitted by the renderer.
|
|
3525
|
+
external: options.bundleReact ? [] : ["react", "react-dom", "react/jsx-runtime", "react/jsx-dev-runtime"],
|
|
3247
3526
|
alias,
|
|
3248
3527
|
define: { "process.env.NODE_ENV": '"production"' },
|
|
3249
3528
|
platform: "browser",
|
|
@@ -3255,7 +3534,7 @@ if (__root) {
|
|
|
3255
3534
|
} catch (err) {
|
|
3256
3535
|
const message = err instanceof Error ? err.message : String(err);
|
|
3257
3536
|
throw new Error(
|
|
3258
|
-
`Failed to bundle FileSource "${filename}": ${message}.
|
|
3537
|
+
`Failed to bundle FileSource "${filename}": ${message}. If the error mentions @frontmcp/ui or @frontmcp/uipack, ensure both packages are installed in the consuming project.`
|
|
3259
3538
|
);
|
|
3260
3539
|
}
|
|
3261
3540
|
}
|
|
@@ -3273,6 +3552,9 @@ var DEFAULT_META = {
|
|
|
3273
3552
|
renderer: "auto"
|
|
3274
3553
|
};
|
|
3275
3554
|
function resolveUISource(source, options) {
|
|
3555
|
+
if (options?.inlineReact && options?.transformOnly) {
|
|
3556
|
+
throw new Error("resolveUISource: `inlineReact` and `transformOnly` are mutually exclusive \u2014 set at most one.");
|
|
3557
|
+
}
|
|
3276
3558
|
const resolver = options?.resolver ?? createEsmShResolver();
|
|
3277
3559
|
if (isNpmSource(source)) {
|
|
3278
3560
|
return resolveNpmSource(source, resolver);
|
|
@@ -3281,7 +3563,7 @@ function resolveUISource(source, options) {
|
|
|
3281
3563
|
return resolveImportSource(source);
|
|
3282
3564
|
}
|
|
3283
3565
|
if (isFileSource(source)) {
|
|
3284
|
-
return resolveFileSource(source);
|
|
3566
|
+
return resolveFileSource(source, { inlineReact: options?.inlineReact, transformOnly: options?.transformOnly });
|
|
3285
3567
|
}
|
|
3286
3568
|
if (isFunctionSource(source)) {
|
|
3287
3569
|
return resolveFunctionSource(source, options?.input, options?.output);
|
|
@@ -3314,15 +3596,42 @@ function resolveImportSource(source) {
|
|
|
3314
3596
|
peerDependencies: []
|
|
3315
3597
|
};
|
|
3316
3598
|
}
|
|
3317
|
-
function resolveFileSource(source) {
|
|
3599
|
+
function resolveFileSource(source, options = {}) {
|
|
3318
3600
|
const path = require("path");
|
|
3319
3601
|
const ext = path.extname(source.file).toLowerCase();
|
|
3320
3602
|
if (ext === ".tsx" || ext === ".jsx") {
|
|
3321
3603
|
const fs = require("fs");
|
|
3322
|
-
const
|
|
3323
|
-
const
|
|
3604
|
+
const wasRelative = !path.isAbsolute(source.file);
|
|
3605
|
+
const filePath = wasRelative ? path.resolve(process.cwd(), source.file) : source.file;
|
|
3606
|
+
let rawSource;
|
|
3607
|
+
try {
|
|
3608
|
+
rawSource = fs.readFileSync(filePath, "utf-8");
|
|
3609
|
+
} catch (err) {
|
|
3610
|
+
const isNotFound = err?.code === "ENOENT";
|
|
3611
|
+
if (isNotFound && wasRelative) {
|
|
3612
|
+
throw new Error(
|
|
3613
|
+
`FileSource widget "${source.file}" not found at "${filePath}". Relative paths are resolved against process.cwd() ("${process.cwd()}"), not the tool file's directory. To anchor the path to the tool file, pass an absolute path \u2014 e.g. \`{ file: fileURLToPath(new URL('./widget.tsx', import.meta.url)) }\` from \`node:url\` (see issue #444).`
|
|
3614
|
+
);
|
|
3615
|
+
}
|
|
3616
|
+
throw err;
|
|
3617
|
+
}
|
|
3324
3618
|
const componentName = source.exportName || extractDefaultExportName(rawSource) || "Component";
|
|
3325
|
-
|
|
3619
|
+
if (options.transformOnly === true) {
|
|
3620
|
+
const code = transpileReactSource(rawSource, path.basename(filePath));
|
|
3621
|
+
const parsed2 = parseImports(code);
|
|
3622
|
+
return {
|
|
3623
|
+
mode: "module",
|
|
3624
|
+
code,
|
|
3625
|
+
imports: [...new Set(parsed2.externalImports.map((i) => i.specifier))],
|
|
3626
|
+
exportName: componentName,
|
|
3627
|
+
meta: { mcpAware: true, renderer: "react" },
|
|
3628
|
+
peerDependencies: source.peerDependencies || ["react", "react-dom"],
|
|
3629
|
+
bundled: false
|
|
3630
|
+
};
|
|
3631
|
+
}
|
|
3632
|
+
const bundled = bundleFileSource(rawSource, source.file, path.dirname(filePath), componentName, {
|
|
3633
|
+
bundleReact: options.inlineReact === true
|
|
3634
|
+
});
|
|
3326
3635
|
const parsed = parseImports(bundled.code);
|
|
3327
3636
|
return {
|
|
3328
3637
|
mode: "module",
|
|
@@ -3391,12 +3700,18 @@ function generateMappedPropsCode(mapping) {
|
|
|
3391
3700
|
}
|
|
3392
3701
|
|
|
3393
3702
|
// libs/uipack/src/component/renderer.ts
|
|
3703
|
+
var DEFAULT_WIDGET_MOUNT = {
|
|
3704
|
+
moduleSpecifier: "@frontmcp/ui/react",
|
|
3705
|
+
wrapperImportName: "McpBridgeProvider"
|
|
3706
|
+
};
|
|
3394
3707
|
function renderComponent(config, shellConfig) {
|
|
3395
3708
|
const resolver = shellConfig.resolver ?? createEsmShResolver();
|
|
3396
3709
|
const resolved = resolveUISource(config.source, {
|
|
3397
3710
|
resolver,
|
|
3398
3711
|
input: shellConfig.input,
|
|
3399
|
-
output: shellConfig.output
|
|
3712
|
+
output: shellConfig.output,
|
|
3713
|
+
inlineReact: config.inlineReact === true,
|
|
3714
|
+
transformOnly: config.transformOnly === true
|
|
3400
3715
|
});
|
|
3401
3716
|
const mergedShellConfig = {
|
|
3402
3717
|
...shellConfig,
|
|
@@ -3407,10 +3722,10 @@ function renderComponent(config, shellConfig) {
|
|
|
3407
3722
|
if (resolved.mode === "inline") {
|
|
3408
3723
|
return buildShell(resolved.html ?? "", mergedShellConfig);
|
|
3409
3724
|
}
|
|
3410
|
-
const content = buildModuleContent(resolved, resolver, config.props);
|
|
3725
|
+
const content = buildModuleContent(resolved, resolver, config.props, shellConfig.mount);
|
|
3411
3726
|
return buildShell(content, mergedShellConfig);
|
|
3412
3727
|
}
|
|
3413
|
-
function buildModuleContent(resolved, resolver, propsMapping) {
|
|
3728
|
+
function buildModuleContent(resolved, resolver, propsMapping, mount = DEFAULT_WIDGET_MOUNT) {
|
|
3414
3729
|
const parts = [];
|
|
3415
3730
|
if (resolved.code && resolved.bundled) {
|
|
3416
3731
|
const importEntries = {};
|
|
@@ -3434,14 +3749,16 @@ ${resolved.code}
|
|
|
3434
3749
|
...resolved.imports || [],
|
|
3435
3750
|
"react-dom/client",
|
|
3436
3751
|
// needed by mount script
|
|
3437
|
-
|
|
3438
|
-
//
|
|
3752
|
+
mount.moduleSpecifier,
|
|
3753
|
+
// mounter/provider package (e.g. @frontmcp/ui/react)
|
|
3439
3754
|
// React subpath entries needed by esm.sh externalized modules:
|
|
3440
3755
|
"react/jsx-runtime",
|
|
3441
|
-
"react/jsx-dev-runtime"
|
|
3442
|
-
"react-dom/server",
|
|
3443
|
-
"react-dom/static"
|
|
3756
|
+
"react/jsx-dev-runtime"
|
|
3444
3757
|
]);
|
|
3758
|
+
if (mount === DEFAULT_WIDGET_MOUNT) {
|
|
3759
|
+
allSpecifiers.add("react-dom/server");
|
|
3760
|
+
allSpecifiers.add("react-dom/static");
|
|
3761
|
+
}
|
|
3445
3762
|
const coreDeps = /* @__PURE__ */ new Set([
|
|
3446
3763
|
"react",
|
|
3447
3764
|
"react-dom",
|
|
@@ -3472,8 +3789,9 @@ ${resolved.code}
|
|
|
3472
3789
|
const importMap = createImportMapFromResolved(importEntries);
|
|
3473
3790
|
parts.push(generateImportMapScriptTag(importMap));
|
|
3474
3791
|
}
|
|
3475
|
-
|
|
3476
|
-
|
|
3792
|
+
const mountNodeId = mount.mountNodeId ?? "root";
|
|
3793
|
+
parts.push(`<div id="${mountNodeId}">${mount.mountNodeInnerHtml ?? ""}</div>`);
|
|
3794
|
+
const mountCode = generateInlineMountCode(resolved.exportName, mount);
|
|
3477
3795
|
parts.push(`<script type="module">
|
|
3478
3796
|
${resolved.code}
|
|
3479
3797
|
${mountCode}
|
|
@@ -3510,15 +3828,21 @@ function addExternalParam(url, externals) {
|
|
|
3510
3828
|
const sep = url.includes("?") ? "&" : "?";
|
|
3511
3829
|
return `${url}${sep}external=${externals.join(",")}`;
|
|
3512
3830
|
}
|
|
3513
|
-
function generateInlineMountCode(componentName) {
|
|
3831
|
+
function generateInlineMountCode(componentName, mount) {
|
|
3832
|
+
if (mount.generate) {
|
|
3833
|
+
return mount.generate(componentName);
|
|
3834
|
+
}
|
|
3835
|
+
const wrapperName = mount.wrapperImportName ?? "McpBridgeProvider";
|
|
3836
|
+
const wrapperAlias = `__${wrapperName}`;
|
|
3837
|
+
const mountNodeId = mount.mountNodeId ?? "root";
|
|
3514
3838
|
return `
|
|
3515
3839
|
// --- Mount ---
|
|
3516
3840
|
import { createRoot as __createRoot } from 'react-dom/client';
|
|
3517
|
-
import {
|
|
3518
|
-
const __root = document.getElementById('
|
|
3841
|
+
import { ${wrapperName} as ${wrapperAlias} } from '${mount.moduleSpecifier}';
|
|
3842
|
+
const __root = document.getElementById('${mountNodeId}');
|
|
3519
3843
|
if (__root) {
|
|
3520
3844
|
__createRoot(__root).render(
|
|
3521
|
-
React.createElement(
|
|
3845
|
+
React.createElement(${wrapperAlias}, null,
|
|
3522
3846
|
React.createElement(${componentName})
|
|
3523
3847
|
)
|
|
3524
3848
|
);
|
|
@@ -3594,38 +3918,6 @@ function isUIRenderFailure(result) {
|
|
|
3594
3918
|
return typeof rec["reason"] === "string" && !("meta" in rec);
|
|
3595
3919
|
}
|
|
3596
3920
|
|
|
3597
|
-
// libs/uipack/src/adapters/type-detector.ts
|
|
3598
|
-
function detectUIType(template) {
|
|
3599
|
-
if (template === null || template === void 0) {
|
|
3600
|
-
return "auto";
|
|
3601
|
-
}
|
|
3602
|
-
if (typeof template === "object" && template !== null && "file" in template) {
|
|
3603
|
-
const file = template.file;
|
|
3604
|
-
if (/\.(tsx|jsx)$/i.test(file)) return "react";
|
|
3605
|
-
}
|
|
3606
|
-
if (typeof template === "function") {
|
|
3607
|
-
const proto = template.prototype;
|
|
3608
|
-
if (proto && typeof proto.render === "function") {
|
|
3609
|
-
return "react";
|
|
3610
|
-
}
|
|
3611
|
-
const asRecord = template;
|
|
3612
|
-
if (asRecord["$$typeof"] !== void 0) {
|
|
3613
|
-
return "react";
|
|
3614
|
-
}
|
|
3615
|
-
if (template.name && /^[A-Z]/.test(template.name)) {
|
|
3616
|
-
return "react";
|
|
3617
|
-
}
|
|
3618
|
-
return "html";
|
|
3619
|
-
}
|
|
3620
|
-
if (typeof template === "string") {
|
|
3621
|
-
if (template.includes("<") && template.includes(">")) {
|
|
3622
|
-
return "html";
|
|
3623
|
-
}
|
|
3624
|
-
return "markdown";
|
|
3625
|
-
}
|
|
3626
|
-
return "auto";
|
|
3627
|
-
}
|
|
3628
|
-
|
|
3629
3921
|
// libs/uipack/src/adapters/content-detector.ts
|
|
3630
3922
|
var CHART_TYPES = /* @__PURE__ */ new Set(["bar", "line", "pie", "area", "scatter", "doughnut", "radar", "polarArea", "bubble"]);
|
|
3631
3923
|
var MERMAID_PREFIXES = [
|
|
@@ -3766,6 +4058,38 @@ function wrapDetectedContent(value) {
|
|
|
3766
4058
|
}
|
|
3767
4059
|
}
|
|
3768
4060
|
|
|
4061
|
+
// libs/uipack/src/adapters/type-detector.ts
|
|
4062
|
+
function detectUIType(template) {
|
|
4063
|
+
if (template === null || template === void 0) {
|
|
4064
|
+
return "auto";
|
|
4065
|
+
}
|
|
4066
|
+
if (typeof template === "object" && template !== null && "file" in template) {
|
|
4067
|
+
const file = template.file;
|
|
4068
|
+
if (/\.(tsx|jsx)$/i.test(file)) return "react";
|
|
4069
|
+
}
|
|
4070
|
+
if (typeof template === "function") {
|
|
4071
|
+
const proto = template.prototype;
|
|
4072
|
+
if (proto && typeof proto.render === "function") {
|
|
4073
|
+
return "react";
|
|
4074
|
+
}
|
|
4075
|
+
const asRecord = template;
|
|
4076
|
+
if (asRecord["$$typeof"] !== void 0) {
|
|
4077
|
+
return "react";
|
|
4078
|
+
}
|
|
4079
|
+
if (template.name && /^[A-Z]/.test(template.name)) {
|
|
4080
|
+
return "react";
|
|
4081
|
+
}
|
|
4082
|
+
return "html";
|
|
4083
|
+
}
|
|
4084
|
+
if (typeof template === "string") {
|
|
4085
|
+
if (template.includes("<") && template.includes(">")) {
|
|
4086
|
+
return "html";
|
|
4087
|
+
}
|
|
4088
|
+
return "markdown";
|
|
4089
|
+
}
|
|
4090
|
+
return "auto";
|
|
4091
|
+
}
|
|
4092
|
+
|
|
3769
4093
|
// libs/uipack/src/adapters/template-renderer.ts
|
|
3770
4094
|
function buildCspConfig(resolver) {
|
|
3771
4095
|
const cspResourceDomains = ["https://esm.sh"];
|
|
@@ -3786,21 +4110,26 @@ function buildCspConfig(resolver) {
|
|
|
3786
4110
|
return { resourceDomains: cspResourceDomains, connectDomains: cspConnectDomains };
|
|
3787
4111
|
}
|
|
3788
4112
|
function renderToolTemplate(options) {
|
|
3789
|
-
const { toolName, input, output, template, resolver } = options;
|
|
4113
|
+
const { toolName, input, output, template, resolver, platformType, sizing } = options;
|
|
3790
4114
|
const uiType = detectUIType(template);
|
|
4115
|
+
const resourceMode = options.resourceMode ?? (platformType === "claude" ? "inline" : "cdn");
|
|
3791
4116
|
const shellConfig = {
|
|
3792
4117
|
toolName,
|
|
3793
4118
|
input,
|
|
3794
4119
|
output,
|
|
3795
4120
|
includeBridge: true,
|
|
3796
|
-
resolver
|
|
4121
|
+
resolver,
|
|
4122
|
+
sizing
|
|
3797
4123
|
};
|
|
3798
4124
|
let html;
|
|
3799
4125
|
let hash = "";
|
|
3800
4126
|
let size = 0;
|
|
3801
4127
|
if (typeof template === "object" && template !== null && "file" in template) {
|
|
3802
4128
|
const cspConfig = buildCspConfig(resolver);
|
|
3803
|
-
const result = renderComponent(
|
|
4129
|
+
const result = renderComponent(
|
|
4130
|
+
{ source: template, inlineReact: resourceMode === "inline" },
|
|
4131
|
+
{ ...shellConfig, csp: cspConfig }
|
|
4132
|
+
);
|
|
3804
4133
|
html = result.html;
|
|
3805
4134
|
hash = result.hash;
|
|
3806
4135
|
size = result.size;
|
|
@@ -3842,6 +4171,12 @@ function renderToolTemplate(options) {
|
|
|
3842
4171
|
"ui/type": uiType,
|
|
3843
4172
|
"ui/mimeType": MCP_APPS_MIME_TYPE
|
|
3844
4173
|
};
|
|
4174
|
+
if (hasSizing(sizing)) {
|
|
4175
|
+
if (sizing.preferredHeight !== void 0) meta["ui/preferredHeight"] = sizing.preferredHeight;
|
|
4176
|
+
if (sizing.minHeight !== void 0) meta["ui/minHeight"] = sizing.minHeight;
|
|
4177
|
+
if (sizing.maxHeight !== void 0) meta["ui/maxHeight"] = sizing.maxHeight;
|
|
4178
|
+
if (sizing.aspectRatio !== void 0) meta["ui/aspectRatio"] = sizing.aspectRatio;
|
|
4179
|
+
}
|
|
3845
4180
|
return {
|
|
3846
4181
|
html,
|
|
3847
4182
|
uiType,
|
|
@@ -3947,6 +4282,7 @@ function buildCDNInfoForUIType(uiType) {
|
|
|
3947
4282
|
buildCSPDirectives,
|
|
3948
4283
|
buildCSPMetaTag,
|
|
3949
4284
|
buildChartHtml,
|
|
4285
|
+
buildCustomDataInjectionScript,
|
|
3950
4286
|
buildDataInjectionScript,
|
|
3951
4287
|
buildMermaidHtml,
|
|
3952
4288
|
buildPdfHtml,
|