diff --git a/packages/shared/src/markdown/copyToClipboard/attachCopyClickListener.ts b/packages/shared/src/markdown/copyToClipboard/attachCopyClickListener.ts index b73bee2d..8a183764 100644 --- a/packages/shared/src/markdown/copyToClipboard/attachCopyClickListener.ts +++ b/packages/shared/src/markdown/copyToClipboard/attachCopyClickListener.ts @@ -1,35 +1,46 @@ -export const attachCopyClickListener = (markdownContainer: HTMLElement) => { - markdownContainer.querySelectorAll('.nlux-comp-copyButton').forEach((copyButton) => { - if (!(copyButton instanceof HTMLButtonElement)) { - return; - } +const addListenersToCopyButton = (copyButton: Element) => { + if (!(copyButton instanceof HTMLButtonElement)) { + return; + } - // If button has click event listener, do not add another one - if (copyButton.dataset.clickListenerSet === 'true') { + // If button has click event listener, do not add another one + if (copyButton.dataset.clickListenerSet === 'true') { + return; + } + + let clicked = false; + const codeBlock = copyButton.nextElementSibling as HTMLElement; + copyButton.addEventListener('click', () => { + if (clicked || !codeBlock) { return; } - let clicked = false; - const codeBlock = copyButton.nextElementSibling as HTMLElement; - copyButton.addEventListener('click', () => { - if (clicked || !codeBlock) { - return; - } + // Copy code to clipboard + const code = codeBlock.innerText; + navigator.clipboard.writeText(code ?? ''); - // Copy code to clipboard - const code = codeBlock.innerText; - navigator.clipboard.writeText(code ?? ''); + // Mark button as clicked for 1 second + clicked = true; + copyButton.classList.add('clicked'); + setTimeout(() => { + clicked = false; + copyButton.classList.remove('clicked'); + }, 1000); + }); - // Mark button as clicked for 1 second - clicked = true; - copyButton.classList.add('clicked'); - setTimeout(() => { - clicked = false; - copyButton.classList.remove('clicked'); - }, 1000); - }); + // Set data attribute to indicate that click event listener has been set + copyButton.dataset.clickListenerSet = 'true'; +}; - // Set data attribute to indicate that click event listener has been set - copyButton.dataset.clickListenerSet = 'true'; - }); +export const attachCopyClickListener = (markdownContainer: HTMLElement) => { + const copyButtonCssClass = 'nlux-comp-copyButton'; + if ( + markdownContainer instanceof HTMLButtonElement && + markdownContainer.classList.contains(copyButtonCssClass) + ) { + addListenersToCopyButton(markdownContainer); + return; + } + + markdownContainer.querySelectorAll(`.${copyButtonCssClass}`).forEach(addListenersToCopyButton); }; diff --git a/packages/shared/src/markdown/stream/streamParser.ts b/packages/shared/src/markdown/stream/streamParser.ts index 8670266f..9c966ba2 100644 --- a/packages/shared/src/markdown/stream/streamParser.ts +++ b/packages/shared/src/markdown/stream/streamParser.ts @@ -1,6 +1,7 @@ import {StandardStreamParser} from '../../types/markdown/streamParser'; import {warn} from '../../utils/warn'; import {parseMdSnapshot} from '../snapshot/snapshotParser'; +import {attachCopyClickListener} from '../copyToClipboard/attachCopyClickListener'; const defaultDelayInMsBeforeComplete = 2000; const defaultDelayInMsBetweenBufferChecks = 2; @@ -44,7 +45,12 @@ export const createMdStreamRenderer: StandardStreamParser = ( // const commitWipContent = () => { while (wipContainer.firstChild) { - wipContainer.before(wipContainer.firstChild); + const childToCommit = wipContainer.firstChild; + if (childToCommit instanceof HTMLElement) { + attachCopyClickListener(childToCommit); + } + + wipContainer.before(childToCommit); } }; @@ -60,14 +66,14 @@ export const createMdStreamRenderer: StandardStreamParser = ( onComplete?.(); }; + const delayBetweenBufferChecks = ( + !options?.skipStreamingAnimation && options?.streamingAnimationSpeed && options.streamingAnimationSpeed >= 0 + ) ? options.streamingAnimationSpeed : defaultDelayInMsBetweenBufferChecks; + let timeSinceLastProcessing: number | undefined = undefined; let currentMarkdown = ''; let previousHtml: string | undefined = undefined; - const parsingIntervalDelay = ( - !options?.skipStreamingAnimation && options?.streamingAnimationSpeed && options.streamingAnimationSpeed >= 0 - ) ? options.streamingAnimationSpeed : defaultDelayInMsBetweenBufferChecks; - let parsingInterval: number | undefined = setInterval(() => { const nowTime = new Date().getTime(); if (buffer.length === 0) { @@ -110,9 +116,7 @@ export const createMdStreamRenderer: StandardStreamParser = ( // Which means that the last parsed markdown is complete and should be committed to the DOM // Commit the last parsed content to the DOM - while (wipContainer.children.length > 0) { - wipContainer.before(wipContainer.children[0]); - } + commitWipContent(); // Extract new HTML and insert it into WIP container const currentHtml = parsedHtml.slice(previousHtml.length).trim(); @@ -133,7 +137,7 @@ export const createMdStreamRenderer: StandardStreamParser = ( previousHtml = parsedHtml; } }); - }, parsingIntervalDelay) as unknown as number; + }, delayBetweenBufferChecks) as unknown as number; return { next: (chunk: string) => { @@ -154,7 +158,9 @@ export const createMdStreamRenderer: StandardStreamParser = ( streamIsComplete = true; }, error: () => { - // No error handling for now + // No special handling for errors + // Just complete the stream + streamIsComplete = true; }, }; };