Skip to content

Commit 55d1cc9

Browse files
CopilotOEvgeny
andauthored
fix: split committed/active blocks at micromark-event level instead of DOM lastElementChild
Agent-Logs-Url: https://github.com/microsoft/BotFramework-WebChat/sessions/0cc28430-8345-487c-a77b-dd62abdb9973 Co-authored-by: OEvgeny <2841858+OEvgeny@users.noreply.github.com>
1 parent c48c4a6 commit 55d1cc9

1 file changed

Lines changed: 36 additions & 17 deletions

File tree

packages/bundle/src/markdown/createStreamingRenderer.ts

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -198,19 +198,24 @@ export default function createStreamingRenderer(
198198
} else {
199199
// New block boundary in tail: commit newly-finished blocks, replace active.
200200
const newActiveOffsetInTail = tailBlocks[tailBlocks.length - 1].startOffset;
201-
const committedTailEvents = tailEvents.filter(([, token]) => token.start.offset < newActiveOffsetInTail);
202-
const committedTailHTML = compile(micromarkOptions)(committedTailEvents);
203201

204202
activeBlockStartOffset += newActiveOffsetInTail;
205203

204+
// Split at the micromark-event level so that blocks which compile to
205+
// multiple sibling DOM nodes (e.g. htmlFlow) are kept whole.
206+
const committedTailEvents = tailEvents.filter(([, token]) => token.start.offset < newActiveOffsetInTail);
207+
const activeTailEvents = tailEvents.filter(([, token]) => token.start.offset >= newActiveOffsetInTail);
208+
209+
const committedTailHTML = compile(micromarkOptions)(committedTailEvents);
210+
const activeTailHTML = compile(micromarkOptions)(activeTailEvents);
211+
206212
const committedDoc = domParser.parseFromString(committedTailHTML, 'text/html');
207213
const committedFragment = committedDoc.createDocumentFragment();
208214

209215
committedFragment.append(...Array.from(committedDoc.body.childNodes));
210216
betterLinkDocumentMod(committedFragment, createDecorate(emptyDefinitions, externalLinkAlt));
211217

212-
const remainingHTML = tailHTML.slice(committedTailHTML.length);
213-
const activeDoc = domParser.parseFromString(remainingHTML, 'text/html');
218+
const activeDoc = domParser.parseFromString(activeTailHTML, 'text/html');
214219
const activeFragment = activeDoc.createDocumentFragment();
215220

216221
activeFragment.append(...Array.from(activeDoc.body.childNodes));
@@ -243,27 +248,35 @@ export default function createStreamingRenderer(
243248

244249
// Full reparse path.
245250
const fullEvents = parseEvents(processedMarkdown);
246-
const rawHTML = compile(micromarkOptions)(fullEvents);
247-
const parsedDocument = domParser.parseFromString(rawHTML, 'text/html');
248-
const fragment = parsedDocument.createDocumentFragment();
249-
250-
fragment.append(...Array.from(parsedDocument.body.childNodes));
251-
252251
const blocks = findTopLevelBlocks(fullEvents);
253252

254253
if (blocks.length >= 2) {
255-
activeBlockStartOffset = blocks[blocks.length - 1].startOffset;
254+
const lastBlock = blocks[blocks.length - 1];
256255

257-
const range = document.createRange();
256+
activeBlockStartOffset = lastBlock.startOffset;
258257

259-
range.setStartBefore(fragment.firstChild!);
260-
range.setEndBefore(fragment.lastElementChild!);
258+
// Split at the micromark-event level so that blocks which compile to
259+
// multiple sibling DOM nodes (e.g. htmlFlow) are kept whole.
260+
const committedEvents = fullEvents.filter(([, token]) => token.start.offset < lastBlock.startOffset);
261+
const activeEvents = fullEvents.filter(([, token]) => token.start.offset >= lastBlock.startOffset);
262+
263+
const committedHTML = compile(micromarkOptions)(committedEvents);
264+
const activeHTML = compile(micromarkOptions)(activeEvents);
265+
266+
const committedDoc = domParser.parseFromString(committedHTML, 'text/html');
267+
const committedFragment = committedDoc.createDocumentFragment();
268+
269+
committedFragment.append(...Array.from(committedDoc.body.childNodes));
270+
271+
const activeDoc = domParser.parseFromString(activeHTML, 'text/html');
272+
const activeFragment = activeDoc.createDocumentFragment();
273+
274+
activeFragment.append(...Array.from(activeDoc.body.childNodes));
261275

262-
const committedFragment = range.extractContents();
263276
const decorate = createDecorate(emptyDefinitions, externalLinkAlt);
264277

265278
betterLinkDocumentMod(committedFragment, decorate);
266-
betterLinkDocumentMod(fragment, decorate);
279+
betterLinkDocumentMod(activeFragment, decorate);
267280

268281
const wrapper = ensureWrapper(options.container, options.containerClassName);
269282

@@ -272,7 +285,7 @@ export default function createStreamingRenderer(
272285
wrapper.replaceChildren(
273286
applyTransform(committedFragment, options.transformFragment),
274287
activeSentinel,
275-
applyTransform(fragment, options.transformFragment)
288+
applyTransform(activeFragment, options.transformFragment)
276289
);
277290

278291
return;
@@ -282,6 +295,12 @@ export default function createStreamingRenderer(
282295
activeBlockStartOffset = 0;
283296
activeSentinel = null;
284297

298+
const rawHTML = compile(micromarkOptions)(fullEvents);
299+
const parsedDocument = domParser.parseFromString(rawHTML, 'text/html');
300+
const fragment = parsedDocument.createDocumentFragment();
301+
302+
fragment.append(...Array.from(parsedDocument.body.childNodes));
303+
285304
betterLinkDocumentMod(fragment, createDecorate(emptyDefinitions, externalLinkAlt));
286305

287306
const wrapper = ensureWrapper(options.container, options.containerClassName);

0 commit comments

Comments
 (0)