Public
Edited
Feb 16, 2024
3 forks
Importers
48 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
text
Insert cell
Insert cell
Insert cell
query
Insert cell
Insert cell
Insert cell
js
Insert cell
Insert cell
Insert cell
python
Insert cell
Insert cell
viewof instantText = Editor({
value: "Hello World",
instant: true,
})
Insert cell
instantText
Insert cell
Insert cell
viewof delayed = Editor({
value: "Hello Debounced",
debounced: 1000
})
Insert cell
delayed
Insert cell
Insert cell
Editor({
value: "Hello CMU\nHello line two\nHello line three",
lineNumbers: true
})
Insert cell
Insert cell
Editor({
value: "Hello World",
label: "Text"
})
Insert cell
Insert cell
function metalSyntax(hljs) {
return {
name: "Metal",
keywords: "false true break case continue default discard do else for if return switch while kernel vertex fragment static typedef namespace const constant device constexpr vertex_id instance_id draw_id stage_in float4x4 sampler array void float float2 float3 float4 bool bool2 bool3 bool4 char char2 char3 char4 short short2 short3 short4 int int1 int3 int4 long long2 long3 long4 uchar uchar2 uchar3 uchar4 ushort ushort2 ushort3 ushort4 uint uint2 uint3 uint4 ulong ulong2 ulong3 ulong4 half half2 half3 half4 vector_float vector_float2 vector_float3 vector_float4 texture texture2 texture3 texture4"
,
contains: [
hljs.C_LINE_COMMENT_MODE,
hljs.C_BLOCK_COMMENT_MODE,
hljs.C_NUMBER_MODE,
]
}
}
Insert cell
function customHighlighter(editor) {
editor.textContent = editor.textContent;
highlightjs.registerLanguage("metal", metalSyntax)
highlightjs.highlightElement(editor)
}
Insert cell
Insert cell
Editor({
highlight: customHighlighter,
language: 'metal',
value: `// Fragment shader that samples a texture and outputs the sampled color.
fragment float4 textureFragmentShader(TexturePipelineRasterizerData in [[stage_in]],
texture2d<float> texture
[[texture(AAPLTextureInputIndexColor)]])
{
sampler simpleSampler;

// Sample data from the texture.
float4 colorSample = texture.sample(simpleSampler, in.texcoord);

// Return the color sample as the final color.
return colorSample;
}`
})
Insert cell
Insert cell
function set(input, value) {
input.value = value;
input.dispatchEvent(new Event("input", {bubbles: true}));
}
Insert cell
viewof programmableEditor = Editor()
Insert cell
Inputs.button([
["Set to a sample value", () => set(viewof programmableEditor, 'a sample value')],
["Reset", () => set(viewof programmableEditor, '')]
])
Insert cell
Insert cell
import {localStorageView} from '@tomlarkworthy/local-storage-view'

Insert cell
viewof persistentEditor = Editor()
Insert cell
viewof sourceStorage = localStorageView("persistentEditor")
Insert cell
Inputs.bind(viewof persistentEditor, viewof sourceStorage)
Insert cell
Insert cell
function Editor(opt = {}) {
const {language, styles, value, label, lineNumbers, codeJarOpts, instant, debounced, highlight} = {
language: 'plaintext',
styles: defaultStyles,
value: '',
label: '',
lineNumbers: false,
instant: false,
highlight: highlighter,
debounced: 0,
// TODO: figure out a way to make shift+enter work on indented lines without this
codeJarOpts: { preserveIdent: false },
...opt
};

const container = html`<div>`

const run = html`<div style="display: ${"none"}; position: absolute; right: 10px; cursor: pointer; top: ${label && typeof label == "string" ? "32px" : "7px"}; z-index: 1;">${svg`<svg width=12 height=12 viewbox="0 0 14 14">${runButton}`}`;

container.addEventListener('mouseenter', (e) => {
run.style.display = "inline-block";
})

container.addEventListener('mouseleave', (e) => {
run.style.display = "none";
})
container.append(run)
let shiftDown = false;

let lb;
if (label) {
const isString = typeof label == "string";
lb = html`${isString ? `<label style="display: block; font-family: sans-serif; color: #333; font-size: 0.9em;">${label}:` : label}`;
container.append(lb);
}

container.append(html`<span style="${languageLabelStyles}
top: ${label && typeof label == "string" ? `35px` : "10px"}">${language}`)

const ed = html`<div style="${styles} padding-left: ${lineNumbers ? '45px' : '10px'};" class="language-${language}">${value}`;
container.appendChild(ed);
container.value = value;
let jar = CodeJar(ed, lineNumbers ? withLineNumbers(highlight, {
// unclear why but this makes line numbers black
color: '#fff'
}) : highlight, codeJarOpts);


let settingInternally = false
// support sending and receiving updates as per https://observablehq.com/@tophtucker/custom-input-example
Object.defineProperty(container, "value", {
get() {
// console.log("editor.get called:", ed.textContent)
return ed.textContent;
},
set(v) {
// console.log("editor.set called with: ",v)
// Only set this when we are receiving an external message, otherwise it moves the cursor within the input
if (!settingInternally) {
ed.textContent = v;
jar.updateCode(v);
}
settingInternally = false
}
});
if (lineNumbers) {
const lineNumberContainer = container.querySelector('.codejar-linenumbers');
lineNumberContainer.style.textAlign = "center";
lineNumberContainer.style.fontFamily = "monospace";
lineNumberContainer.style.paddingTop = "10px";
lineNumberContainer.style.fontSize = "13px";
}

ed.addEventListener('input', (e) => {
e.stopPropagation();
})

ed.addEventListener('keydown', (e) => {
if (e.key == "Shift") shiftDown = true;
if (e.key == "Enter") {
if (shiftDown) {
e.preventDefault();
container.value = ed.textContent;
container.dispatchEvent(new CustomEvent("input"));
}
}
})

run.addEventListener('click', (e) => {
container.value = ed.textContent;
container.dispatchEvent(new CustomEvent("input"));
})
ed.addEventListener('keyup', (e) => {
if (e.key == "Shift") shiftDown = false;
})

const debouncedUpdate = debounce((code) => {
settingInternally = true;
container.value = code;
container.dispatchEvent(new CustomEvent("input"));
}, debounced);

jar.onUpdate(code => {
if (instant || debounced > 0) {
debouncedUpdate(code);
}
})

const wrap = container.querySelector('.codejar-wrap');

return container;
}
Insert cell
function highlighter(editor) {
editor.textContent = editor.textContent;
highlightjs.highlightElement(editor)
}
Insert cell
function makeEditor(defaultOpt) {
return (opt = {}) => Editor({...defaultOpt, ...opt})
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
import {Editor as CmudigEditor} from '@cmudig/editor'
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