Public
Edited
Jan 15, 2023
Importers
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
editor
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
loadCodeMirrorModules = async entries => {
const repo = Object.create(null)
const loadToRepo = async key => {
const module = await esmCodeMirror(key)
repo[key] = module
}
await Promise.all(entries.map(loadToRepo))
return repo
}
Insert cell
cmImports = loadCodeMirrorModules([
"view",
"state",
//"history",
//"fold",
"language",
//"gutter",
"commands",
"search",
"autocomplete",
// "language",
// "highlight"
"lang-javascript"
])
Insert cell
EditorView = cmImports.view.EditorView
Insert cell
EditorState = cmImports.state.EditorState
Insert cell
Insert cell
CodeMirror = async (doc = "", config = {}) => {
const extensions = config.extensions ?? [];
const keymaps = config.keymaps ?? [];

const { highlightSpecialChars, keymap, drawSelection, highlightActiveLine } =
cmImports.view;
const { Prec } = cmImports.state;
let state = EditorState.create({
doc,
extensions: [
cmReactiveViewof,
cmImports.view.lineNumbers(),
highlightSpecialChars(),
drawSelection(),
highlightActiveLine(),
keymap.of([
...cmImports.commands.defaultKeymap,
...cmImports.search.searchKeymap,
...keymaps
]),
...extensions
]
});
const view = new cmImports.view.EditorView({ state });
const el = view.dom;
el.value = view.state.doc.toString();
return el;
}
Insert cell
Insert cell
cmReactiveViewof = {
// prevent input events coming from the contenteditable div that
// propagates and triggers a cell refresh; we shall handle it ourselves.
const blocker = cmImports.view.ViewPlugin.fromClass(class {
constructor(view) {
this.elContent = null
return true
}
update({view}) {
if (this.elContent) return;
const el = view.dom.querySelector('.cm-content')
if (el) {
this.elContent = el
this.elContent.addEventListener('input', this.handler)
}
}
destroy() {
if (!this.elContent) return;
this.elContent.removeEventListener('input', this.handler)
this.elContent = null
}
handler(evt) {
evt.stopImmediatePropagation()
}
})
const handler = EditorView.updateListener.of(update => {
const { dom } = update.view
if (!update.docChanged) return
dom.value = update.state.doc.toString()
dom.dispatchEvent(new CustomEvent('input'))
})
return [blocker, handler]
}
Insert cell
Insert cell
Insert cell
//loadBasicSetup = () => esmCodeMirror('basic-setup').then(m => m.basicSetup)
Insert cell
Insert cell
cmOneDark = esmCodeMirror('theme-one-dark')
Insert cell
myDefaultTheme = EditorView.theme({
'&': {
fontFamily: 'Consolas, "Roboto Mono", monospace',
fontSize: '14px',
height: '200px',
border: '1px solid #ddd',
},
})
Insert cell
Insert cell
esmImport = pkg => import(`https://cdn.skypack.dev/${pkg}`)
Insert cell
esmCodeMirror = (mod) =>
esmImport(
"@codemirror/" +
(mod.indexOf("@") >= 0
? mod
: // mod == "state"? "state@^0.19.0":
`${mod}@${CODEMIRROR_VERSION}`)
)
Insert cell
//basicSetup = cm.basicSetup
Insert cell
Insert cell
Insert cell
MarkdownIt = require('markdown-it@12/dist/markdown-it.js')
Insert cell
mdit = MarkdownIt('commonmark').enable('strikethrough')
Insert cell
import { md as mdi } with { mdit } from '@andy0130tw/my-markdown-it'
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