Streaming AI Markdown Responses to Javascript Function Calls
When using Chilkat to interact with AI providers (like OpenAI or Anthropic) in streaming mode, the AI response arrives in small "chunks" rather than all at once. This response is often formatted in Markdown (using asterisks for bold, backticks for code, etc.).
Chilkat acts as an intelligent bridge between the raw AI data stream and your browser display. It parses the incoming Markdown in real-time and acts as a state machine. Instead of waiting for the full response, Chilkat translates the Markdown syntax immediately into specific JavaScript calls:
- Markdown Parsing: As chunks arrive, Chilkat determines if the text represents formatting (like
**or###) or raw content. - Function Selection:
- If Chilkat detects Markdown formatting that needs to be rendered (like a
<b>tag, a list item<li>, or a code block), it generates a call toappendHtmlBySelector. - If Chilkat detects plain content, it generates a call to
appendTextBySelector.
- If Chilkat detects Markdown formatting that needs to be rendered (like a
- Real-Time Execution: These JavaScript calls are executed immediately by the browser (or WebView).
This allows the user to see the text "type out" and format itself on the screen instantly as the AI "thinks," rather than staring at a loading spinner until the request is finished.
The JavaScript Source Code
These are the client-side functions that Chilkat calls to (and then your application code runs the generated Javascript code). They must be present in the HTML loaded into your browser control.
window.appendHtmlBySelector = function(selector, html) {
try {
const targetElement = document.querySelector(selector);
if (targetElement) {
// Parses the string as HTML and inserts it inside the element
targetElement.insertAdjacentHTML('beforeend', html);
window.scrollTo(0, document.body.scrollHeight);
} else {
console.error('appendHtmlBySelector failed: Element matching selector "' + selector + '" not found.');
}
} catch (e) {
console.error('appendHtmlBySelector failed:', e);
}
};
window.appendTextBySelector = function(selector, text) {
try {
const targetElement = document.querySelector(selector);
if (targetElement) {
// Creates a safe text node (HTML tags are not rendered)
targetElement.appendChild(document.createTextNode(text));
window.scrollTo(0, document.body.scrollHeight);
} else {
console.error('appendTextBySelector failed: Element matching selector "' + selector + '" not found.');
}
} catch (e) {
console.error('appendTextBySelector failed:', e);
}
};
Understanding Selectors
Both functions require a selector argument. A selector is simply a string pattern used to tell the browser which HTML element you want to update.
- ID Selector (
#): Targets a specific element with a unique ID.- Example:
'#response-container'targets<div id="response-container"></div>.
- Example:
- Class Selector (
.): Targets elements with a specific class name.- Example:
'.ai-message'targets<div class="ai-message"></div>.
- Example:
- Tag Selector: Targets elements by their HTML tag.
- Example:
'body'targets the main body of the page.
- Example:
Real-World Example Analysis
Here is the breakdown of how Chilkat processes the following markdown.
The Input (Markdown)
The AI sends this Markdown structure:
Here are the **four largest cities in California**, each with a one-sentence description: 1. **Los Angeles** — The state’s largest city, known for Hollywood, diverse neighborhoods, and its role as a global center for entertainment and culture. 2. **San Diego** — A coastal city famous for its beaches, mild climate, and major naval presence. 3. **San Jose** — The heart of Silicon Valley and a major hub for technology and innovation. 4. **San Francisco** — A historic, densely packed city known for its iconic landmarks, steep hills, and vibrant cultural scene.
The Output (JavaScript Calls)
Chilkat translates that Markdown into these specific calls. Notice how the Selector changes to ensure the HTML is built with the correct nesting.
Step 1: Initialize the Container
window.appendHtmlBySelector("#content", "<div class=\"response-content\">");
- Action: Chilkat creates a new
divfor this specific response inside the main#contentarea.
Step 2: Add the Intro Paragraph
window.appendHtmlBySelector("div.response-content:last-of-type", "<p>Here are the <strong>four largest cities in California</strong>, each with a one-sentence description:");
- Selector:
div.response-content:last-of-type - Why: Chilkat targets the newest response div created in Step 1. It inserts the paragraph there.
Step 3: Create the List Structure
window.appendHtmlBySelector("div.response-content:last-of-type", "<ol>");
- Action: It opens an Ordered List (
<ol>) inside the current response div. At this point, the list is empty.
Step 4: Populate List Items (Deep Nesting)
window.appendHtmlBySelector("div.response-content:last-of-type > ol:last-child", "<li><strong>Los Angeles</strong> — The state’s largest city...");
window.appendHtmlBySelector("div.response-content:last-of-type > ol:last-child", "<li><strong>San Diego</strong> — A coastal city...");
// ... repeated for San Jose and San Francisco
- Selector:
div.response-content:last-of-type > ol:last-child - Why: This is the most critical part. Chilkat recognizes that these lines are items inside the list, not new paragraphs.
- It looks for the current response div (
last-of-type). - Inside that, it looks for the
olthat was just created (ol:last-child). - It appends the
<li>inside thatol.
- It looks for the current response div (
Summary
By dynamically adjusting the selector, Chilkat builds a complex, nested HTML structure in real-time. The window.scrollTo command at the end of every function call ensures the user follows this construction line-by-line as it happens.