Skip to content

Adaptive Cards: app text is never Markdown; linear htmlToText - #4

Merged
oblomov-dev merged 1 commit into
mainfrom
claude/wizardly-ritchie-7sj2br
Oct 7, 2026
Merged

oblomov-dev merged 1 commit into
mainfrom
claude/wizardly-ritchie-7sj2br

Conversation

@oblomov-dev

Copy link
Copy Markdown
Member

Two renderer fixes. Both turned up in a review of abap2UI5/mcp-server, which vendors this renderer for format: "adaptive-card".

App text could render as Markdown links

A TextBlock and a Fact render their text as Markdown, and that text is the app's. Before this PR, a model value like Customer [click me](https://evil.example) or a toast like **[verify your account](https://evil.example/login)** reached the card unchanged. In Teams and Copilot it became a live link; the UI5 frontend shows the same text as it is.

  • Text blocks: a text with Markdown syntax is now a RichTextBlock of one TextRun, which is never Markdown. The error container already worked this way. It keeps weight, size, color and subtlety; maxLines and the heading style are lost for such texts.
  • Fact sets: a FactSet with such a fact becomes a Container of one RichTextBlock per fact, with the title in bold.
  • Plain text stays a TextBlock, so ordinary cards do not change. The only golden changes are the two [... - not rendered] placeholders.

htmlToText was quadratic

/<[^>]*>/ scanned from every < to the end of the text, and the lazy <script>…</script> pattern searched for a closing tag after every <script. 100k characters of < in a message box's details took seconds.

  • Tag patterns now stop at the next <.
  • <script> / <style> blocks are now removed by a scan that gives up on a tag name once one of its blocks has no closing tag.
  • A lone < (a < b) is now kept as text. Before, it swallowed everything up to the next >.

Tests

  • Markdown: a text, a title, a list fact and a toast with Markdown come out as TextRuns, and plain text stays a TextBlock.
  • htmlToText: checked for its output, and for speed on 100k characters of <, <script and <br.
  • Golden cards: nest and sampler were rewritten; only their placeholders changed.

PROTOCOL_SKIP_BACKENDS=1 PROTOCOL_SKIP_BROWSER=1 npm test: 80 passed, 3 skipped, 0 failed.

After merge, mcp-server re-vendors the renderer (scripts/vendor-adaptive-cards.mjs).

🤖 Generated with Claude Code

https://claude.ai/code/session_013nvd2W2zUT5ZyemG91n2oq


Generated by Claude Code

A TextBlock and a Fact render their text as Markdown, and their text is the
app's: a model value or a toast like "[verify your account](https://...)"
was a live link in Teams and Copilot, where the UI5 frontend shows the text
as it is. A text with Markdown syntax is now a RichTextBlock of one TextRun
(weight, size, color, subtlety kept), a FactSet with such a fact a
Container of RichTextBlocks; plain text stays a TextBlock.

htmlToText scanned from every "<" to the end of the text, and the lazy
<script> pattern to every later closing tag: 100k characters of "<" in a
message box's details took seconds. Every pattern now stops at the next
"<"; script/style blocks are dropped by a scan that gives up on a name
once it has no closing tag.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nvd2W2zUT5ZyemG91n2oq
@oblomov-dev
oblomov-dev merged commit 29aad60 into main Oct 7, 2026
3 checks passed
oblomov-dev added a commit to abap2UI5/mcp-server that referenced this pull request Oct 7, 2026
…same draft id on eviction (#49)

* Re-vendor the Adaptive Cards renderer from abap2UI5/protocol#4

The renderer now shares renderers/common/{view,request}.mjs with the
protocol's other renderers, and the view half reads the portable profile:
the vendor script copies them too (lib/vendor/adaptive-cards/common/,
profiles/) and rewrites every relative path to the vendored copy or, for
the agent modules, to lib/ - a path to anything else fails the vendoring.

Brings in: app text with Markdown syntax is a TextRun (no live links from
app data in Teams/Copilot); a linear htmlToText.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nvd2W2zUT5ZyemG91n2oq

* appclient: evicting a session keeps a newer one with the same draft id

Sessions started from one screen share its draft id - the playground's
Pilot starts one from its mirror on every change of the reader's typing -
and remember( ) deleted every id of the evicted session from the index,
the newest session's included: "unknown session 'D1' - open sessions: D1".
Only the ids that still name the evicted session are dropped now.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nvd2W2zUT5ZyemG91n2oq

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants