@adia-ai/web-modules 0.8.2 → 0.8.4

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/CHANGELOG.md CHANGED
@@ -1,4 +1,17 @@
1
1
  # Changelog — @adia-ai/web-modules
2
+
3
+ ## [Unreleased]
4
+
5
+ ### Added
6
+ - **`chat-shell.appendMessage({ html: true })`** — assistant messages can carry trusted app-authored component markup (light-DOM upgrade in place); the `render` (markdown) path escapes inline HTML by design, which made gen-ui's error bubbles show raw `<col-ui …>` source text. The html mode persists on the message record and survives `stopStreaming()` and `conversation` getter/setter round-trips.
7
+
8
+ ### Maintenance
9
+ - **`dist/` bundles rebuilt** (`chat/chat-shell.min.js`, `everything.min.js` — the chat-shell change plus the inlined @adia-ai/llm provider-routing fixes).
10
+
11
+ ## [0.8.3] — 2026-07-16
12
+
13
+ ### Maintenance
14
+ - **Lockstep version bump only.** No source changes in this package; bumped to maintain the 11-package version coherence enforced by `scripts/release/check-lockstep.mjs`. Substantive v0.8.3 work shipped in the two Claude Code plugins — the adia-tokens knowledge pack + command refresh (adia-ui-factory) and the release-tooling invariant-3 reconciliation (adia-ui-forge); see `packages/plugins/adia-ui-factory/CHANGELOG.md#083--2026-07-16`.
2
15
  ## [0.8.2] — 2026-07-16
3
16
 
4
17
  ### Maintenance
@@ -66,13 +66,15 @@ class ChatShell extends UIElement {
66
66
  get conversation() {
67
67
  return this.#messages
68
68
  .filter(m => m.role === 'user' || m.role === 'assistant')
69
- .map(m => ({ role: m.role, content: m.content }));
69
+ .map(m => (m.html ? { role: m.role, content: m.content, html: true } : { role: m.role, content: m.content }));
70
70
  }
71
71
 
72
72
  set conversation(msgs) {
73
73
  this.clear();
74
74
  for (const m of msgs) {
75
- this.appendMessage({ role: m.role, content: m.content, render: true });
75
+ // Round-trip the html mode re-rendering an html message through
76
+ // the markdown path would escape its component markup.
77
+ this.appendMessage({ role: m.role, content: m.content, render: !m.html, html: !!m.html });
76
78
  }
77
79
  }
78
80
 
@@ -122,9 +124,22 @@ class ChatShell extends UIElement {
122
124
 
123
125
  // ── Message management ──
124
126
 
125
- appendMessage({ role, content = '', render: renderMd = false }) {
127
+ /**
128
+ * @param {object} msg
129
+ * @param {string} msg.role — 'user' | 'assistant' | 'error'
130
+ * @param {string} [msg.content]
131
+ * @param {boolean} [msg.render] — treat content as MARKDOWN (inline HTML
132
+ * is escaped — component markup shows as literal text under this flag)
133
+ * @param {boolean} [msg.html] — assistant only: insert content as raw
134
+ * HTML (light-DOM component markup upgrades in place). TRUSTED
135
+ * app-authored strings only — never LLM output or user input; the
136
+ * caller owns escaping of any interpolated values.
137
+ */
138
+ appendMessage({ role, content = '', render: renderMd = false, html = false }) {
126
139
  const id = `msg_${++msgId}`;
127
- this.#messages.push({ id, role, content });
140
+ // html persists on the record: stopStreaming() and the conversation
141
+ // setter must never push an html message back through the markdown path.
142
+ this.#messages.push({ id, role, content, html });
128
143
 
129
144
  const el = document.createElement('div');
130
145
  el.setAttribute('data-role', role);
@@ -133,10 +148,13 @@ class ChatShell extends UIElement {
133
148
  if (role === 'user') {
134
149
  el.innerHTML = `<div data-bubble>${escapeHTML(content)}</div>`;
135
150
  } else if (role === 'assistant') {
136
- const rendered = renderMd && content ? renderMarkdown(content) : escapeHTML(content);
151
+ const done = renderMd || html;
152
+ const rendered = html ? content
153
+ : renderMd && content ? renderMarkdown(content)
154
+ : escapeHTML(content);
137
155
  el.innerHTML = `
138
156
  <span data-avatar>AI</span>
139
- <div data-bubble><div data-content>${rendered}</div>${!renderMd ? '<span data-cursor></span>' : ''}</div>
157
+ <div data-bubble><div data-content>${rendered}</div>${!done ? '<span data-cursor></span>' : ''}</div>
140
158
  `;
141
159
  } else if (role === 'error') {
142
160
  el.innerHTML = `<icon-ui name="warning"></icon-ui><span>${escapeHTML(content)}</span>`;
@@ -202,9 +220,11 @@ class ChatShell extends UIElement {
202
220
  // Remove cursor
203
221
  this.#messagesEl?.querySelector('[data-role]:last-child [data-cursor]')?.remove();
204
222
 
205
- // Render markdown in last assistant bubble
223
+ // Render markdown in last assistant bubble — never an html-mode
224
+ // message: its component markup is already live DOM, and the markdown
225
+ // path would re-escape it to source text.
206
226
  const last = this.#messages[this.#messages.length - 1];
207
- if (last?.role === 'assistant' && last.content) {
227
+ if (last?.role === 'assistant' && last.content && !last.html) {
208
228
  const contentEl = this.#messagesEl?.querySelector('[data-role]:last-child [data-content]');
209
229
  if (contentEl) {
210
230
  contentEl.innerHTML = renderMarkdown(last.content);