ADR-020: Backend Output Format Rendering
- Status
-
Accepted
- Date
-
2025-12
- Authors
-
Netresearch DTT GmbH
Context
LLM responses can contain markdown, HTML, JSON, or plain text depending on the task's output format. Users need to view output in an appropriate rendering mode without re-executing the (potentially expensive) LLM call.
Decision
Store raw LLM output and handle format rendering entirely client-side. The toggle between formats is ephemeral (not persisted) and operates on the cached raw content.
Four rendering modes in Resources/:
renderOutput() {
const content = this._rawContent;
const escaped = this.escapeHtml(content);
switch (this._activeFormat) {
case 'html': this.renderHtmlOutput(content); break;
case 'markdown': this.renderMarkdownOutput(escaped); break;
case 'json': this.renderJsonOutput(content); break;
default: this.renderPlainOutput(); break;
}
}
Rendering modes
| Mode | Technique | Security |
|---|---|---|
| Plain | <pre> with textContent assignment | Fully escaped (DOM API) |
| Markdown | Regex transforms on HTML-escaped content | Pre-escaped before transform |
| JSON | JSON.stringify pretty-print in <pre> | textContent assignment |
| HTML | Sandboxed iframe (sandbox=\"\") | No script execution, no parent DOM access |
Security approach
LLM responses are untrusted external content. Each mode uses a different security strategy:
- Plain/JSON: Content set via
textContent(automatic HTML escaping by the DOM). - Markdown: Content is first HTML-escaped via
escapeHtml()(textContentassignment to a temporary element, then read back viainnerHTML). Markdown regex transforms operate on already-escaped content, making injection safe. - HTML: Rendered inside a fully sandboxed
<iframe sandbox="">which blocks all scripting, form submission, and parent page access. A fixed height of 400px is used sincecontentDocumentis inaccessible in sandbox mode.
escapeHtml(text) {
this._escapeEl.textContent = text;
return this._escapeEl.innerHTML;
}
Format toggle
The active format is initialized from the task's output_format setting
(returned by the server in the AJAX response) and can be switched by clicking
format toggle buttons. The toggle updates _activeFormat, re-renders from
_rawContent, and highlights the active button. Clipboard copy always uses
the raw content regardless of active rendering mode.
Consequences
Positive:
- ●● No server round-trip needed to switch display formats.
- ● XSS prevention for all four rendering modes via distinct security strategies.
- ● Raw content preserved for clipboard copy regardless of rendering.
- ◐ Format toggle state is ephemeral, avoiding unnecessary persistence.
- ◐ Markdown renderer is lightweight (regex-based, no external library).
Negative:
- ◑ Markdown regex renderer is simplified (no tables, no nested lists, no links).
- ◑ HTML iframe height is fixed at 400px (cannot auto-resize in sandboxed mode).
- ◑ No syntax highlighting for JSON or code blocks.
Net Score: +4.5 (Positive)
Files changed
Added:
Resources/Public/ Java Script/ Backend/ Task Execute. js
Modified:
Resources/-- Format toggle UI and output container.Private/ Templates/ Backend/ Task/ Execute. html Classes/-- ReturnsController/ Backend/ Task Controller. php outputFormatin AJAX response.Classes/-- Defines valid output formats with content types.Domain/ Enum/ Task Output Format. php