mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-23 19:08:22 -06:00
fix(canvas): stop self-closing embed from starting a greedy block match
extractCanvasShortcodes used a block regex whose open-tag attrs group allowed a trailing slash, so a self-closing "[embed ... /]" open tag could start a block embed match. The block pattern then greedily swallowed all visible text up to a later stray "[/embed]", deleting that text from channel delivery. Anchor the block open-tag group so the attrs cannot end with a slash, which prevents a self-closing open tag from initiating a block match. The self-closing shortcode is now matched only by selfClosingRe and the surrounding visible text is preserved. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
// Canvas-render tests cover [embed] shortcode extraction and text stripping.
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { extractCanvasShortcodes } from "./canvas-render.ts";
|
||||
|
||||
describe("extractCanvasShortcodes", () => {
|
||||
it("does not let a self-closing embed start a greedy block match", () => {
|
||||
// Regression: the block regex used to greedily swallow the span from a
|
||||
// self-closing "[embed ... /]" open tag up to a later stray "[/embed]",
|
||||
// deleting the visible text in between (" keep me ") from channel delivery.
|
||||
const input = '[embed url="https://a.com" /] keep me [/embed]';
|
||||
const { text, previews } = extractCanvasShortcodes(input);
|
||||
|
||||
expect(previews).toHaveLength(1);
|
||||
expect(previews[0]?.url).toBe("https://a.com");
|
||||
// The visible text between the self-closing embed and the stray close
|
||||
// marker must be preserved, not silently stripped.
|
||||
expect(text).toContain("keep me");
|
||||
expect(text).toBe("keep me [/embed]");
|
||||
});
|
||||
|
||||
it("still extracts a normal block embed and strips only the shortcode span", () => {
|
||||
const input = 'before [embed ref="doc1"] hi [/embed] after';
|
||||
const { text, previews } = extractCanvasShortcodes(input);
|
||||
|
||||
expect(previews).toHaveLength(1);
|
||||
expect(previews[0]?.viewId).toBe("doc1");
|
||||
expect(text).toBe("before after");
|
||||
});
|
||||
|
||||
it("still extracts a plain self-closing embed and keeps surrounding text", () => {
|
||||
const input = 'see [embed url="https://b.com" /] end';
|
||||
const { text, previews } = extractCanvasShortcodes(input);
|
||||
|
||||
expect(previews).toHaveLength(1);
|
||||
expect(previews[0]?.url).toBe("https://b.com");
|
||||
expect(text).toBe("see end");
|
||||
});
|
||||
});
|
||||
@@ -203,7 +203,10 @@ export function extractCanvasShortcodes(text: string | undefined): {
|
||||
attrs: Record<string, string>;
|
||||
body?: string;
|
||||
}> = [];
|
||||
const blockRe = /\[embed\s+([^\]]*?)\]([\s\S]*?)\[\/embed\]/gi;
|
||||
// Exclude a self-closing open tag ("[embed ... /]") from starting a block
|
||||
// match by requiring the attrs group not to end with a slash; otherwise the
|
||||
// block regex greedily swallows visible text up to a later stray [/embed].
|
||||
const blockRe = /\[embed\s+([^\]]*?[^\]/]|)\]([\s\S]*?)\[\/embed\]/gi;
|
||||
const selfClosingRe = /\[embed\s+([^\]]*?)\/\]/gi;
|
||||
for (const re of [blockRe, selfClosingRe]) {
|
||||
let match: RegExpExecArray | null;
|
||||
|
||||
Reference in New Issue
Block a user