mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-21 17:00:45 +00:00
Fix HTML escaping in markdown code blocks
Move HTML tag escaping from pre-parse to custom renderers. This preserves < in code blocks while still escaping in text content.
This commit is contained in:
parent
dccdf91b8c
commit
0b31884385
3 changed files with 18 additions and 105 deletions
|
|
@ -1,12 +0,0 @@
|
||||||
---
|
|
||||||
description: Review a file for issues
|
|
||||||
---
|
|
||||||
Please review the following file for potential issues, bugs, or improvements:
|
|
||||||
|
|
||||||
$1
|
|
||||||
|
|
||||||
Focus on:
|
|
||||||
- Logic errors
|
|
||||||
- Edge cases
|
|
||||||
- Code style
|
|
||||||
- Performance concerns
|
|
||||||
|
|
@ -1,86 +0,0 @@
|
||||||
/**
|
|
||||||
* Test hook demonstrating custom commands, message rendering, and before_agent_start.
|
|
||||||
*/
|
|
||||||
import type { BeforeAgentStartEvent, HookAPI } from "@mariozechner/pi-coding-agent";
|
|
||||||
import { Box, Spacer, Text } from "@mariozechner/pi-tui";
|
|
||||||
|
|
||||||
export default function (pi: HookAPI) {
|
|
||||||
// Track whether injection is enabled
|
|
||||||
let injectEnabled = false;
|
|
||||||
|
|
||||||
// Register a custom message renderer for our "test-info" type
|
|
||||||
pi.registerMessageRenderer("test-info", (message, options, theme) => {
|
|
||||||
const box = new Box(1, 1, (t) => theme.bg("customMessageBg", t));
|
|
||||||
|
|
||||||
const label = theme.fg("success", "[TEST INFO]");
|
|
||||||
box.addChild(new Text(label, 0, 0));
|
|
||||||
box.addChild(new Spacer(1));
|
|
||||||
|
|
||||||
const content =
|
|
||||||
typeof message.content === "string"
|
|
||||||
? message.content
|
|
||||||
: message.content.map((c) => (c.type === "text" ? c.text : "[image]")).join("");
|
|
||||||
|
|
||||||
box.addChild(new Text(theme.fg("text", content), 0, 0));
|
|
||||||
|
|
||||||
if (options.expanded && message.details) {
|
|
||||||
box.addChild(new Spacer(1));
|
|
||||||
box.addChild(new Text(theme.fg("dim", `Details: ${JSON.stringify(message.details)}`), 0, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
return box;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Register /test-msg command
|
|
||||||
pi.registerCommand("test-msg", {
|
|
||||||
description: "Send a test custom message",
|
|
||||||
handler: async () => {
|
|
||||||
pi.sendMessage(
|
|
||||||
{
|
|
||||||
customType: "test-info",
|
|
||||||
content: "This is a test message with custom rendering!",
|
|
||||||
display: true,
|
|
||||||
details: { timestamp: Date.now(), source: "test-command hook" },
|
|
||||||
},
|
|
||||||
true, // triggerTurn: start agent run
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// Register /test-hidden command
|
|
||||||
pi.registerCommand("test-hidden", {
|
|
||||||
description: "Send a hidden message (display: false)",
|
|
||||||
handler: async (ctx) => {
|
|
||||||
pi.sendMessage({
|
|
||||||
customType: "test-info",
|
|
||||||
content: "This message is in context but not displayed",
|
|
||||||
display: false,
|
|
||||||
});
|
|
||||||
ctx.ui.notify("Sent hidden message (check session file)");
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// Register /test-inject command to toggle before_agent_start injection
|
|
||||||
pi.registerCommand("test-inject", {
|
|
||||||
description: "Toggle context injection before agent starts",
|
|
||||||
handler: async (ctx) => {
|
|
||||||
injectEnabled = !injectEnabled;
|
|
||||||
ctx.ui.notify(`Context injection ${injectEnabled ? "enabled" : "disabled"}`);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// Demonstrate before_agent_start: inject context when enabled
|
|
||||||
pi.on("before_agent_start", async (event: BeforeAgentStartEvent) => {
|
|
||||||
if (!injectEnabled) return;
|
|
||||||
|
|
||||||
// Return a message to inject before the user's prompt
|
|
||||||
return {
|
|
||||||
message: {
|
|
||||||
customType: "test-info",
|
|
||||||
content: `[Injected context for prompt: "${event.prompt.slice(0, 50)}..."]`,
|
|
||||||
display: true,
|
|
||||||
details: { injectedAt: Date.now() },
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
@ -1050,19 +1050,17 @@
|
||||||
// INITIALIZATION
|
// INITIALIZATION
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
// Wrapper for marked that escapes HTML before parsing
|
// Escape HTML tags in text (but not code blocks)
|
||||||
// Escapes all < that look like tag starts (followed by letter or /)
|
function escapeHtmlTags(text) {
|
||||||
// Code blocks are safe because marked processes them before our escaping affects display
|
return text.replace(/<(?=[a-zA-Z\/])/g, '<');
|
||||||
function safeMarkedParse(text) {
|
|
||||||
const escaped = text.replace(/<(?=[a-zA-Z\/])/g, '<');
|
|
||||||
return marked.parse(escaped);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure marked with syntax highlighting
|
// Configure marked with syntax highlighting and HTML escaping for text
|
||||||
marked.use({
|
marked.use({
|
||||||
breaks: true,
|
breaks: true,
|
||||||
gfm: true,
|
gfm: true,
|
||||||
renderer: {
|
renderer: {
|
||||||
|
// Code blocks: syntax highlight, no HTML escaping
|
||||||
code(token) {
|
code(token) {
|
||||||
const code = token.text;
|
const code = token.text;
|
||||||
const lang = token.lang;
|
const lang = token.lang;
|
||||||
|
|
@ -1082,10 +1080,23 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return `<pre><code class="hljs">${highlighted}</code></pre>`;
|
return `<pre><code class="hljs">${highlighted}</code></pre>`;
|
||||||
|
},
|
||||||
|
// Text content: escape HTML tags
|
||||||
|
text(token) {
|
||||||
|
return escapeHtmlTags(escapeHtml(token.text));
|
||||||
|
},
|
||||||
|
// Inline code: escape HTML
|
||||||
|
codespan(token) {
|
||||||
|
return `<code>${escapeHtml(token.text)}</code>`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Simple marked parse (escaping handled in renderers)
|
||||||
|
function safeMarkedParse(text) {
|
||||||
|
return marked.parse(text);
|
||||||
|
}
|
||||||
|
|
||||||
// Search input
|
// Search input
|
||||||
const searchInput = document.getElementById('tree-search');
|
const searchInput = document.getElementById('tree-search');
|
||||||
searchInput.addEventListener('input', (e) => {
|
searchInput.addEventListener('input', (e) => {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue