Public
Edited
May 24, 2023
1 star
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
unistUtilVisitParents = import("unist-util-visit-parents")
Insert cell
updateEditorStyle = debounce(
250,
(editor, viewer) => requestIdleCallback(() => {
const startMark = performanceNow()
const mdastTree = mdastUtilFromMarkdown.fromMarkdown(editor.value)

const style = html`
<style>
::highlight(mdast-heading) {
background-color: yellow;
text-decoration: underline;
}

::highlight(mdast-comment) {
color: gray;
}
</style>
`
viewer.appendChild(style)

const root = viewer.childNodes[0].childNodes
const nodes = []
unistUtilVisitParents.visitParents(mdastTree, "heading", (node) => {
nodes.push(node)
})
const ranges = nodes.slice(0, 100).map(node => {
const range = document.createRange()
range.setStart(root[node.position.start.line - 1], 0)
range.setEnd(root[node.position.end.line - 1], 1)
return range
})

const nodes2 = []
unistUtilVisitParents.visitParents(mdastTree, "html", (node) => {
nodes2.push(node)
})
unistUtilVisitParents.visitParents(mdastTree, "code", (node) => {
nodes2.push(node)
})
const ranges2 = nodes2.slice(0, 100).map(node => {
const range = document.createRange()
range.setStart(root[node.position.start.line - 1], 0)
range.setEnd(root[node.position.end.line - 1], 1)
return range
})

console.log("mdast tree:", mdastTree)
console.log(performanceNow() - startMark)
window.CSS.highlights.clear()
const highlight = new window.Highlight(...ranges)
window.CSS.highlights.set("mdast-heading", highlight);

const highlight2 = new window.Highlight(...ranges2)
window.CSS.highlights.set("mdast-comment", highlight2);

//const lezerTree = lezerMarkdown.parser.parse(editor.value)
//console.log("lezer tree:", lezerTree)
//console.log(performanceNow() - startMark)
}),
)
Insert cell
Insert cell
updateEditor = (() => {
const render = text => {
const lines = text.split(/\n|\r\n/)
return snabbdom.h("div", null, lines.map((line) => {
const hash = cyrb53(line, line.length ? 0 : Math.random())
return snabbdom.h("span", { key: hash.toString() }, line + "\n")
}))
}

let tree = null
return {
init(editor, viewer) {
const dom = document.createElement("div")
viewer.appendChild(dom)
const nextTree = render(editor.value)
snabbdom.patch(dom, nextTree)
tree = nextTree
},
update(editor, viewer) {
//viewer.textContent = editor.value
const nextTree = render(editor.value)
snabbdom.patch(tree, nextTree)
tree = nextTree
}
}
})()
Insert cell
cyrb53 = (str, seed = 0) => {
let h1 = 0xdeadbeef ^ seed, h2 = 0x41c6ce57 ^ seed;
for(let i = 0, ch; i < str.length; i++) {
ch = str.charCodeAt(i);
h1 = Math.imul(h1 ^ ch, 2654435761);
h2 = Math.imul(h2 ^ ch, 1597334677);
}
h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507);
h1 ^= Math.imul(h2 ^ (h2 >>> 13), 3266489909);
h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507);
h2 ^= Math.imul(h1 ^ (h1 >>> 13), 3266489909);

return 4294967296 * (2097151 & h2) + (h1 >>> 0);
};
Insert cell
vdom = import("https://cdn.skypack.dev/virtual-dom@2.1.1")
Insert cell
snabbdom = import("snabbdom").then(module => ({
...module,
patch: module.init([]),
}))
Insert cell
lezerMarkdown = import("@lezer/markdown")
Insert cell
mdastUtilFromMarkdown = import("mdast-util-from-markdown")
Insert cell
debounce = (delayMillis, action) => _.debounce(action, delayMillis, { leading: false })
Insert cell
onEditorInput = (editor, viewer, renderIndex) => {
const startMark = performanceNow()
if (renderIndex === 0) {
updateEditor.init(editor, viewer)
}
updateEditor.update(editor, viewer)
updateEditorStyle(editor, viewer)
const updateMark = performanceNow() - startMark
requestAnimationFrame(() => {
const renderMark = performanceNow() - startMark
mutable timeMarks = { updateMark, renderMark }
})
}
Insert cell
onEditorScroll = (editor, viewer) => {
viewer.scrollTop = editor.scrollTop
}
Insert cell
createTextEditor = (initialText) => {
const editor = html`
<textarea style="${styles.content} ${styles.editor}" />
`
const viewer = html`
<div style="${styles.content} ${styles.viewer}"></div>
`

let renderIndex = 0
editor.addEventListener("scroll", event => {
onEditorScroll(editor, viewer)
})
editor.addEventListener("input", event => {
onEditorInput(editor, viewer, renderIndex)
renderIndex += 1
})
editor.value = initialText
onEditorInput(editor, viewer, renderIndex)
renderIndex += 1
return html`
<div style="${styles.container}">
${viewer}
${editor}
</div>
`
}
Insert cell
styles = ({
content: `
margin: 0;
padding: 10px;
width: 720px;
height: 360px;
white-space: break-spaces;
word-break: break-word;
overflow-wrap: anywhere;
font-family: var(--monospace), monospace;
font-family: ${fonts.sans};
font-kerning: none;
font-size: 14px;
line-height: 21px;
font-weight: 400;
outline: none;
overscroll-behavior: none;
`,
container: `
position: relative;
`,
editor: `
position: absolute;
top: 0;
border: 1px solid transparent;
background: ${options.includes("editor transparent") ? "transparent" : "transparent"};
color: ${options.includes("editor transparent") ? "transparent" : "#000"};
caret-color: #000;
overflow-y: scroll;
overflow-x: hidden;
resize: none;
`,
viewer: `
border: 1px solid #aaa;
background: ${options.includes("viewer transparent") ? "transparent" : "#fff"};
color: ${options.includes("viewer transparent") ? "transparent" : "#000"};
overflow: hidden;
pointer-events: none;
user-select: none;
`
})
Insert cell
Insert cell
Insert cell
texts = {
return {
markdown: await fetchText(source),
}
}
Insert cell
fetchText = url => fetch(url).then(response => response.text())
Insert cell
performanceNow = () => window.performance ? window.performance.now() : Date.now()
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more