@mcp-html-bridge/ui-engine 0.5.1 → 0.6.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/dist/bridge.d.ts +3 -0
- package/dist/bridge.d.ts.map +1 -0
- package/{src/bridge.ts → dist/bridge.js} +6 -3
- package/dist/bridge.js.map +1 -0
- package/dist/engine.d.ts +26 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +92 -0
- package/dist/engine.js.map +1 -0
- package/dist/html-builder.d.ts +16 -0
- package/dist/html-builder.d.ts.map +1 -0
- package/dist/html-builder.js +54 -0
- package/dist/html-builder.js.map +1 -0
- package/{src/index.ts → dist/index.d.ts} +3 -26
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +36 -0
- package/dist/index.js.map +1 -0
- package/dist/llm-renderer.d.ts +36 -0
- package/dist/llm-renderer.d.ts.map +1 -0
- package/dist/llm-renderer.js +103 -0
- package/dist/llm-renderer.js.map +1 -0
- package/dist/playground.d.ts +4 -0
- package/dist/playground.d.ts.map +1 -0
- package/{src/playground.ts → dist/playground.js} +12 -11
- package/dist/playground.js.map +1 -0
- package/dist/renderer.d.ts +7 -0
- package/dist/renderer.d.ts.map +1 -0
- package/dist/renderer.js +345 -0
- package/dist/renderer.js.map +1 -0
- package/dist/renderers/form.d.ts +4 -0
- package/dist/renderers/form.d.ts.map +1 -0
- package/dist/renderers/form.js +181 -0
- package/dist/renderers/form.js.map +1 -0
- package/dist/theme.d.ts +2 -0
- package/dist/theme.d.ts.map +1 -0
- package/{src/theme.ts → dist/theme.js} +6 -3
- package/dist/theme.js.map +1 -0
- package/dist/types.d.ts +50 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/utilities.d.ts +19 -0
- package/dist/utilities.d.ts.map +1 -0
- package/{src/utilities.ts → dist/utilities.js} +6 -3
- package/dist/utilities.js.map +1 -0
- package/package.json +4 -1
- package/src/engine.ts +0 -118
- package/src/html-builder.ts +0 -61
- package/src/llm-renderer.ts +0 -129
- package/src/renderer.ts +0 -380
- package/src/renderers/form.ts +0 -200
- package/src/types.ts +0 -60
- package/tsconfig.json +0 -8
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tailwind-like atomic utility classes.
|
|
3
|
+
*
|
|
4
|
+
* Maps familiar Tailwind class names to our CSS variable system.
|
|
5
|
+
* LLMs already know Tailwind syntax from training data — by providing
|
|
6
|
+
* these utilities, we ensure any model produces visually consistent HTML
|
|
7
|
+
* that automatically respects light/dark mode.
|
|
8
|
+
*
|
|
9
|
+
* This is NOT a full Tailwind build. It's a curated subset:
|
|
10
|
+
* - Layout (flex, grid, gap)
|
|
11
|
+
* - Spacing (p-*, m-*)
|
|
12
|
+
* - Typography (text-*, font-*)
|
|
13
|
+
* - Colors (text-*, bg-*, border-*)
|
|
14
|
+
* - Borders & radius
|
|
15
|
+
* - Shadows & effects
|
|
16
|
+
* - Sizing & overflow
|
|
17
|
+
*/
|
|
18
|
+
export declare function generateUtilityCSS(): string;
|
|
19
|
+
//# sourceMappingURL=utilities.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utilities.d.ts","sourceRoot":"","sources":["../src/utilities.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,wBAAgB,kBAAkB,IAAI,MAAM,CA4S3C"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
/**
|
|
2
3
|
* Tailwind-like atomic utility classes.
|
|
3
4
|
*
|
|
@@ -15,9 +16,10 @@
|
|
|
15
16
|
* - Shadows & effects
|
|
16
17
|
* - Sizing & overflow
|
|
17
18
|
*/
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
exports.generateUtilityCSS = generateUtilityCSS;
|
|
21
|
+
function generateUtilityCSS() {
|
|
22
|
+
return `
|
|
21
23
|
/* ══════════════════════════════════════════
|
|
22
24
|
Tailwind-compatible utility classes
|
|
23
25
|
Mapped to CSS variable theming system
|
|
@@ -317,3 +319,4 @@ export function generateUtilityCSS(): string {
|
|
|
317
319
|
}
|
|
318
320
|
`;
|
|
319
321
|
}
|
|
322
|
+
//# sourceMappingURL=utilities.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utilities.js","sourceRoot":"","sources":["../src/utilities.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;GAgBG;;AAEH,gDA4SC;AA5SD,SAAgB,kBAAkB;IAChC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0SR,CAAC;AACF,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mcp-html-bridge/ui-engine",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Core UI rendering engine — schema/data → self-contained HTML",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -10,6 +10,9 @@
|
|
|
10
10
|
"default": "./dist/index.js"
|
|
11
11
|
}
|
|
12
12
|
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist/"
|
|
15
|
+
],
|
|
13
16
|
"scripts": {
|
|
14
17
|
"build": "tsc -p tsconfig.json",
|
|
15
18
|
"clean": "rm -rf dist"
|
package/src/engine.ts
DELETED
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
// ── Engine Orchestrator: JSON → self-contained HTML document ──
|
|
2
|
-
import type { EngineInput, RenderOptions, JSONSchema } from './types.js';
|
|
3
|
-
import type { LLMConfig } from './llm-renderer.js';
|
|
4
|
-
import { document as htmlDocument } from './html-builder.js';
|
|
5
|
-
import { generateThemeCSS } from './theme.js';
|
|
6
|
-
import { generateBridgeJS } from './bridge.js';
|
|
7
|
-
import { renderJSON, getRendererCSS, getRendererJS } from './renderer.js';
|
|
8
|
-
import { renderWithLLM } from './llm-renderer.js';
|
|
9
|
-
import { renderForm, getFormCSS } from './renderers/form.js';
|
|
10
|
-
import { generatePlaygroundHTML, getPlaygroundCSS, getPlaygroundJS } from './playground.js';
|
|
11
|
-
import { generateUtilityCSS } from './utilities.js';
|
|
12
|
-
|
|
13
|
-
/** Options for rendering */
|
|
14
|
-
interface DataRenderOptions extends RenderOptions {
|
|
15
|
-
toolName?: string;
|
|
16
|
-
toolDescription?: string;
|
|
17
|
-
/** LLM config for semantic rendering. Omit for structural fallback. */
|
|
18
|
-
llm?: LLMConfig;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/** Build the document wrapper around an HTML body fragment */
|
|
22
|
-
function buildDocument(body: string, options: DataRenderOptions, extraCSS = '', extraJS = ''): string {
|
|
23
|
-
const cssParts = [generateThemeCSS(), generateUtilityCSS(), extraCSS];
|
|
24
|
-
const jsParts = [generateBridgeJS(), extraJS];
|
|
25
|
-
|
|
26
|
-
if (options.debug) {
|
|
27
|
-
cssParts.push(getPlaygroundCSS());
|
|
28
|
-
jsParts.push(getPlaygroundJS());
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const playgroundHTML = options.debug ? generatePlaygroundHTML() : '';
|
|
32
|
-
|
|
33
|
-
return htmlDocument({
|
|
34
|
-
title: options.title ?? options.toolName ?? 'MCP Result',
|
|
35
|
-
css: cssParts.join('\n'),
|
|
36
|
-
body: body + playgroundHTML,
|
|
37
|
-
js: jsParts.join('\n'),
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Render any JSON data as a full HTML document.
|
|
43
|
-
*
|
|
44
|
-
* - If `options.llm` is provided: calls the LLM for semantic rendering (async).
|
|
45
|
-
* - If omitted: uses the structural renderer (sync, returned as resolved Promise).
|
|
46
|
-
*/
|
|
47
|
-
export async function renderFromData(
|
|
48
|
-
data: unknown,
|
|
49
|
-
options: DataRenderOptions = {}
|
|
50
|
-
): Promise<string> {
|
|
51
|
-
if (options.llm) {
|
|
52
|
-
const body = await renderWithLLM(data, options.llm);
|
|
53
|
-
// Always include structural CSS/JS — the LLM fallback path may produce structural HTML
|
|
54
|
-
return buildDocument(body, options, getRendererCSS(), getRendererJS());
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const body = renderJSON(data);
|
|
58
|
-
return buildDocument(body, options, getRendererCSS(), getRendererJS());
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Sync structural rendering (no LLM). Use when you know you don't need LLM.
|
|
63
|
-
*/
|
|
64
|
-
export function renderFromDataSync(
|
|
65
|
-
data: unknown,
|
|
66
|
-
options: Omit<DataRenderOptions, 'llm'> = {}
|
|
67
|
-
): string {
|
|
68
|
-
const body = renderJSON(data);
|
|
69
|
-
return buildDocument(body, options, getRendererCSS(), getRendererJS());
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/** Render a form from a JSON Schema (for tool input). Always sync. */
|
|
73
|
-
export function renderFromSchema(
|
|
74
|
-
schema: JSONSchema,
|
|
75
|
-
options: DataRenderOptions = {}
|
|
76
|
-
): string {
|
|
77
|
-
const body = renderForm(schema, {
|
|
78
|
-
toolName: options.toolName,
|
|
79
|
-
toolDescription: options.toolDescription,
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
const playground = options.debug ? generatePlaygroundHTML() : '';
|
|
83
|
-
|
|
84
|
-
const css = [
|
|
85
|
-
generateThemeCSS(),
|
|
86
|
-
generateUtilityCSS(),
|
|
87
|
-
getFormCSS(),
|
|
88
|
-
options.debug ? getPlaygroundCSS() : '',
|
|
89
|
-
].join('\n');
|
|
90
|
-
|
|
91
|
-
const js = [
|
|
92
|
-
generateBridgeJS(),
|
|
93
|
-
options.debug ? getPlaygroundJS() : '',
|
|
94
|
-
].join('\n');
|
|
95
|
-
|
|
96
|
-
return htmlDocument({
|
|
97
|
-
title: options.title ?? options.toolName ?? 'MCP Tool',
|
|
98
|
-
css,
|
|
99
|
-
body: body + playground,
|
|
100
|
-
js,
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/** Unified API */
|
|
105
|
-
export async function render(input: EngineInput, options: DataRenderOptions = {}): Promise<string> {
|
|
106
|
-
if (input.mode === 'schema') {
|
|
107
|
-
return renderFromSchema(input.schema, {
|
|
108
|
-
...options,
|
|
109
|
-
toolName: input.toolName,
|
|
110
|
-
toolDescription: input.toolDescription,
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
return renderFromData(input.data, {
|
|
114
|
-
...options,
|
|
115
|
-
toolName: input.toolName,
|
|
116
|
-
toolDescription: input.toolDescription,
|
|
117
|
-
});
|
|
118
|
-
}
|
package/src/html-builder.ts
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
// ── Safe HTML string builder with XSS prevention ──
|
|
2
|
-
|
|
3
|
-
const ESCAPE_MAP: Record<string, string> = {
|
|
4
|
-
'&': '&',
|
|
5
|
-
'<': '<',
|
|
6
|
-
'>': '>',
|
|
7
|
-
'"': '"',
|
|
8
|
-
"'": ''',
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
/** Escape HTML special characters to prevent XSS */
|
|
12
|
-
export function escapeHtml(str: string): string {
|
|
13
|
-
return str.replace(/[&<>"']/g, (ch) => ESCAPE_MAP[ch] ?? ch);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/** Build an HTML opening tag with attributes */
|
|
17
|
-
export function tag(
|
|
18
|
-
name: string,
|
|
19
|
-
attrs: Record<string, string | boolean | undefined> = {},
|
|
20
|
-
selfClosing = false
|
|
21
|
-
): string {
|
|
22
|
-
const attrStr = Object.entries(attrs)
|
|
23
|
-
.filter(([, v]) => v !== undefined && v !== false)
|
|
24
|
-
.map(([k, v]) => (v === true ? k : `${k}="${escapeHtml(String(v))}"`))
|
|
25
|
-
.join(' ');
|
|
26
|
-
const open = attrStr ? `<${name} ${attrStr}` : `<${name}`;
|
|
27
|
-
return selfClosing ? `${open} />` : `${open}>`;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/** Wrap content in a style tag */
|
|
31
|
-
export function style(css: string): string {
|
|
32
|
-
return `<style>\n${css}\n</style>`;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/** Wrap content in a script tag */
|
|
36
|
-
export function script(js: string): string {
|
|
37
|
-
return `<script>\n${js}\n</script>`;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/** Wrap content in a complete HTML document */
|
|
41
|
-
export function document(opts: {
|
|
42
|
-
title?: string;
|
|
43
|
-
css?: string;
|
|
44
|
-
js?: string;
|
|
45
|
-
body: string;
|
|
46
|
-
}): string {
|
|
47
|
-
const titleTag = opts.title ? `<title>${escapeHtml(opts.title)}</title>` : '';
|
|
48
|
-
return `<!DOCTYPE html>
|
|
49
|
-
<html lang="en">
|
|
50
|
-
<head>
|
|
51
|
-
<meta charset="UTF-8">
|
|
52
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
53
|
-
${titleTag}
|
|
54
|
-
${opts.css ? style(opts.css) : ''}
|
|
55
|
-
</head>
|
|
56
|
-
<body>
|
|
57
|
-
${opts.body}
|
|
58
|
-
${opts.js ? script(opts.js) : ''}
|
|
59
|
-
</body>
|
|
60
|
-
</html>`;
|
|
61
|
-
}
|
package/src/llm-renderer.ts
DELETED
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* LLM-powered semantic renderer.
|
|
3
|
-
*
|
|
4
|
-
* The structural renderer (renderer.ts) maps JSON shapes to HTML mechanically.
|
|
5
|
-
* This module sends JSON to an LLM with a rendering prompt,
|
|
6
|
-
* letting the model understand semantics and produce the best HTML.
|
|
7
|
-
*
|
|
8
|
-
* Supports any OpenAI-compatible API.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
/** Configuration for the LLM endpoint */
|
|
12
|
-
export interface LLMConfig {
|
|
13
|
-
/** API base URL (e.g. "http://localhost:11434/v1") */
|
|
14
|
-
apiUrl: string;
|
|
15
|
-
/** API key (optional for local models) */
|
|
16
|
-
apiKey?: string;
|
|
17
|
-
/** Model name */
|
|
18
|
-
model: string;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* The rendering prompt.
|
|
23
|
-
*
|
|
24
|
-
* This is intentionally short and non-prescriptive. We tell the model
|
|
25
|
-
* WHAT to do (produce the best HTML for this data), not HOW to do it
|
|
26
|
-
* (don't enumerate SVG, markdown, etc.). The model should figure out
|
|
27
|
-
* the semantics on its own.
|
|
28
|
-
*/
|
|
29
|
-
const SYSTEM_PROMPT = `You are a JSON-to-HTML renderer. You receive JSON data and produce an HTML fragment that visualizes it in the best way possible.
|
|
30
|
-
|
|
31
|
-
Rules:
|
|
32
|
-
1. Output ONLY an HTML fragment. No <html>, <head>, <body> wrappers — those exist already.
|
|
33
|
-
2. Understand what the data MEANS, not just its shape. Render content in its native form — if something is meant to be seen, show it; if it's meant to be read, format it; if it's structured, organize it.
|
|
34
|
-
3. Use these CSS variables for theming (light/dark mode is handled automatically):
|
|
35
|
-
Colors: --bg-primary, --bg-secondary, --bg-tertiary, --bg-elevated, --text-primary, --text-secondary, --text-tertiary, --accent, --accent-subtle, --success, --warning, --danger, --info (each has a -subtle variant), --border, --border-strong
|
|
36
|
-
Typography: --font-sans, --font-mono, --text-xs to --text-3xl
|
|
37
|
-
Spacing: --sp-1 (4px) to --sp-12 (48px)
|
|
38
|
-
Radius: --radius-sm, --radius-md, --radius-lg, --radius-full
|
|
39
|
-
Shadows: --shadow-sm, --shadow-md, --shadow-lg
|
|
40
|
-
Utility classes: .card, .badge, .badge-success, .badge-warning, .badge-danger, .badge-info, .section-title
|
|
41
|
-
4. Tailwind-compatible utility classes are available. Use them freely: flex, grid, gap-*, p-*, m-*, text-*, bg-*, border, rounded-*, shadow-*, font-*, items-*, justify-*, w-full, h-full, overflow-*, relative/absolute, etc. Responsive prefixes sm: and md: work too.
|
|
42
|
-
5. Do not invent data. Do not add commentary. Just the HTML.
|
|
43
|
-
6. Do not wrap output in markdown code fences.`;
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Call an OpenAI-compatible chat completions API.
|
|
47
|
-
*/
|
|
48
|
-
async function callLLM(
|
|
49
|
-
data: unknown,
|
|
50
|
-
config: LLMConfig
|
|
51
|
-
): Promise<string> {
|
|
52
|
-
const url = config.apiUrl.replace(/\/+$/, '') + '/chat/completions';
|
|
53
|
-
|
|
54
|
-
const headers: Record<string, string> = {
|
|
55
|
-
'Content-Type': 'application/json',
|
|
56
|
-
};
|
|
57
|
-
if (config.apiKey) {
|
|
58
|
-
headers['Authorization'] = `Bearer ${config.apiKey}`;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const body = {
|
|
62
|
-
model: config.model,
|
|
63
|
-
messages: [
|
|
64
|
-
{ role: 'system', content: SYSTEM_PROMPT },
|
|
65
|
-
{ role: 'user', content: JSON.stringify(data, null, 2) },
|
|
66
|
-
],
|
|
67
|
-
temperature: 0.2,
|
|
68
|
-
max_tokens: 16384,
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
const response = await fetch(url, {
|
|
72
|
-
method: 'POST',
|
|
73
|
-
headers,
|
|
74
|
-
body: JSON.stringify(body),
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
if (!response.ok) {
|
|
78
|
-
const text = await response.text();
|
|
79
|
-
throw new Error(`LLM API ${response.status}: ${text.slice(0, 300)}`);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const result = await response.json() as {
|
|
83
|
-
choices?: Array<{ message?: { content?: string } }>;
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
const content = result.choices?.[0]?.message?.content;
|
|
87
|
-
if (!content) {
|
|
88
|
-
throw new Error('LLM returned empty response');
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
return stripCodeFences(content);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/** Remove markdown code fences if the LLM wrapped its output */
|
|
95
|
-
function stripCodeFences(html: string): string {
|
|
96
|
-
const trimmed = html.trim();
|
|
97
|
-
const fenceMatch = trimmed.match(/^```(?:html)?\s*\n([\s\S]*?)\n\s*```$/);
|
|
98
|
-
if (fenceMatch) {
|
|
99
|
-
return fenceMatch[1].trim();
|
|
100
|
-
}
|
|
101
|
-
return trimmed;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Render JSON data using an LLM for semantic understanding.
|
|
106
|
-
* Returns an HTML fragment (not a full document).
|
|
107
|
-
* Falls back to structural renderer on failure.
|
|
108
|
-
*/
|
|
109
|
-
export async function renderWithLLM(
|
|
110
|
-
data: unknown,
|
|
111
|
-
config: LLMConfig
|
|
112
|
-
): Promise<string> {
|
|
113
|
-
try {
|
|
114
|
-
const html = await callLLM(data, config);
|
|
115
|
-
return `<div class="mcp-root">${html}</div>`;
|
|
116
|
-
} catch (err) {
|
|
117
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
118
|
-
console.error(` LLM rendering failed: ${message}`);
|
|
119
|
-
console.error(' Falling back to structural renderer.');
|
|
120
|
-
|
|
121
|
-
const { renderJSON } = await import('./renderer.js');
|
|
122
|
-
const fallbackHTML = renderJSON(data);
|
|
123
|
-
const errorBanner = `<div style="padding:var(--sp-3);margin-bottom:var(--sp-4);background:var(--warning-subtle);border:1px solid var(--warning);border-radius:var(--radius-sm);font-size:var(--text-sm);color:var(--text-primary)">LLM rendering failed: ${message.replace(/</g, '<')}. Showing structural fallback.</div>`;
|
|
124
|
-
return `<div class="mcp-root">${errorBanner}${fallbackHTML}</div>`;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/** Export the system prompt for inspection/customization */
|
|
129
|
-
export { SYSTEM_PROMPT as RENDERING_PROMPT };
|