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

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more