import * as DOMPurify from 'dompurify' ; import MarkdownIt from 'markdown-it' ; import type * as MarkdownItToken from 'markdown-it/lib/token' ; import type { ActivationFunction } from 'vscode-notebook-renderer' ; const sanitizerOptions = { ALLOWED_TAGS : [ 'a' , 'button' , 'blockquote' , 'code' , 'div' , 'h1' , 'h2' , 'h3' , 'h4' , 'h5' , 'h6' , 'hr' , 'img' , 'input' , 'label' , 'li' , 'p' , 'pre' , 'select' , 'small' , 'span' , 'strong' , 'textarea' , 'ul' , 'ol' ] , } ; export const activate = ( ctx ) => { const markdownIt = new MarkdownIt ( { html : true , linkify : true , highlight : ( str , lang ? ) => { if ( lang ) { return `<code class="vscode-code-block" data-vscode-code-block-lang="${ markdownIt . utils . escapeHtml ( lang ) }">${ markdownIt . utils . escapeHtml ( str ) }</code>` ; } return `<code>${ markdownIt . utils . escapeHtml ( str ) }</code>` ; } } ) ; markdownIt . linkify . set ( { fuzzyLink : false } ) ; addNamedHeaderRendering ( markdownIt ) ; const style = document . createElement ( 'style' ) ; style . textContent = `
		.emptyMarkdownCell::before {
			content: "${ document . documentElement . style . getPropertyValue ( '--notebook-cell-markup-empty-content' ) }";
			font-style: italic;
			opacity: 0.6;
		}

		img {
			max-width: 100%;
			max-height: 100%;
		}

		a {
			text-decoration: none;
		}

		a:hover {
			text-decoration: underline;
		}

		a:focus,
		input:focus,
		select:focus,
		textarea:focus {
			outline: 1px solid -webkit-focus-ring-color;
			outline-offset: -1px;
		}

		hr {
			border: 0;
			height: 2px;
			border-bottom: 2px solid;
		}

		h2, h3, h4, h5, h6 {
			font-weight: normal;
		}

		h1 {
			font-size: 2.3em;
		}

		h2 {
			font-size: 2em;
		}

		h3 {
			font-size: 1.7em;
		}

		h3 {
			font-size: 1.5em;
		}

		h4 {
			font-size: 1.3em;
		}

		h5 {
			font-size: 1.2em;
		}

		h1,
		h2,
		h3 {
			font-weight: normal;
		}

		div {
			width: 100%;
		}

		/* Adjust margin of first item in markdown cell */
		*:first-child {
			margin-top: 0px;
		}

		/* h1 tags don't need top margin */
		h1:first-child {
			margin-top: 0;
		}

		/* Removes bottom margin when only one item exists in markdown cell */
		*:only-child,
		*:last-child {
			margin-bottom: 0;
			padding-bottom: 0;
		}

		/* makes all markdown cells consistent */
		div {
			min-height: var(--notebook-markdown-min-height);
		}

		table {
			border-collapse: collapse;
			border-spacing: 0;
		}

		table th,
		table td {
			border: 1px solid;
		}

		table > thead > tr > th {
			text-align: left;
			border-bottom: 1px solid;
		}

		table > thead > tr > th,
		table > thead > tr > td,
		table > tbody > tr > th,
		table > tbody > tr > td {
			padding: 5px 10px;
		}

		table > tbody > tr + tr > td {
			border-top: 1px solid;
		}

		blockquote {
			margin: 0 7px 0 5px;
			padding: 0 16px 0 10px;
			border-left-width: 5px;
			border-left-style: solid;
		}

		code {
			font-size: 1em;
		}

		pre code {
			font-family: var(--vscode-editor-font-family);

			line-height: 1.357em;
			white-space: pre-wrap;
		}
	` ; const template = document . createElement ( 'template' ) ; template . classList . add ( 'markdown-style' ) ; template . content . appendChild ( style ) ; document . head . appendChild ( template ) ; return { renderOutputItem : ( outputInfo , element ) => { let previewNode ; if ( ! element . shadowRoot ) { const previewRoot = element . attachShadow ( { mode : 'open' } ) ; const defaultStyles = document . getElementById ( '_defaultStyles' ) as HTMLStyleElement ; previewRoot . appendChild ( defaultStyles . cloneNode ( true ) ) ; for ( const element of document . getElementsByClassName ( 'markdown-style' ) ) { if ( element instanceof HTMLTemplateElement ) { previewRoot . appendChild ( element . content . cloneNode ( true ) ) ; } else { previewRoot . appendChild ( element . cloneNode ( true ) ) ; } } previewNode = document . createElement ( 'div' ) ; previewNode . id = 'preview' ; previewRoot . appendChild ( previewNode ) ; } else { previewNode = element . shadowRoot . getElementById ( 'preview' ) ! ; } const text = outputInfo . text ( ) ; if ( text . trim ( ) . length === 0 ) { previewNode . innerText = '' ; previewNode . classList . add ( 'emptyMarkdownCell' ) ; } else { previewNode . classList . remove ( 'emptyMarkdownCell' ) ; const unsanitizedRenderedMarkdown = markdownIt . render ( text ) ; previewNode . innerHTML = ( ctx . workspace . isTrusted ? unsanitizedRenderedMarkdown : DOMPurify . sanitize ( unsanitizedRenderedMarkdown , sanitizerOptions ) ) as string ; } } , extendMarkdownIt : ( f ) => { f ( markdownIt ) ; } } ; } ; function addNamedHeaderRendering ( md ) { const slugCounter = new Map < string , number > ( ) ; const originalHeaderOpen = md . renderer . rules . heading_open ; md . renderer . rules . heading_open = ( tokens , idx , options , env , self ) => { const title = tokens [ idx + 1 ] . children ! . reduce < string > ( ( acc , t ) => acc + t . content , '' ) ; let slug = slugFromHeading ( title ) ; if ( slugCounter . has ( slug ) ) { const count = slugCounter . get ( slug ) ! ; slugCounter . set ( slug , count + 1 ) ; slug = slugFromHeading ( slug + '-' + ( count + 1 ) ) ; } else { slugCounter . set ( slug , 0 ) ; } tokens [ idx ] . attrSet ( 'id' , slug ) ; if ( originalHeaderOpen ) { return originalHeaderOpen ( tokens , idx , options , env , self ) ; } else { return self . renderToken ( tokens , idx , options ) ; } } ; const originalRender = md . render ; md . render = function ( ) { slugCounter . clear ( ) ; return originalRender . apply ( this , arguments as any ) ; } ; } function slugFromHeading ( heading ) { const slugifiedHeading = encodeURI ( heading . trim ( ) . toLowerCase ( ) . replace ( /\s+/g , '-' ) . replace ( /[\]\[\!\'\#\$\%\&\(\)\*\+\,\.\/\:\;\<\=\>\?\@\\\^\_\{\|\}\~\`。，、；：？！…—·ˉ¨‘’“”々～‖∶＂＇｀｜〃〔〕〈〉《》「」『』．〖〗【】（）［］｛｝]/g , '' ) . replace ( /^\-+/ , '' ) . replace ( /\-+$/ , '' ) ) ; return slugifiedHeading ; }