Public
Edited
Feb 1
3 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
layout_state = layout.on(
"stateChanged",
() => (mutable config = gl.LayoutConfig.fromResolved(layout.toConfig()))
)
Insert cell
layout = {
const layout = new gl.GoldenLayout(mutable config, page);
layout.registerComponent("panel", panel);
layout.init();
invalidation.then(() => layout.destroy()); // avoid layouts stacking up
return layout;
}
Insert cell
panel = function (container, component) {
let node = undefined;
if (component.label == "header") {
node = header;
} else if (component.label == "content") {
node = content;
} else if (component.label == "left_sidebar") {
node = left_sidebar;
} else if (component.label == "right_sidebar") {
node = right_sidebar;
} else if (component.label == "footer") {
//node = footer;
}

// maintain scroll position in the state
const c_element = container.getElement();
const element = c_element.appendChild(node);
debugger;
container.on("open", () => {
// Ugly this requires 2 animation frames to settle :/
requestAnimationFrame(() => {
requestAnimationFrame(() => {
requestAnimationFrame(() => {
// actually needs 3 for mobile :s
if (component.scrollTop) c_element.scrollTop = component.scrollTop;
if (component.scrollLeft) c_element.scrollLeft = component.scrollLeft;
});
});
});
});
c_element.addEventListener("scroll", (event) => {
container.extendState({
scrollTop: c_element.scrollTop,
scrollLeft: c_element.scrollLeft
});
});
}
Insert cell
isOnObservableCom = () => location.href.includes("observableusercontent.com") &&
!location.href.includes("blob:")
Insert cell
apply_layout = {
console.log("apply_layout");
if (isOnObservableCom())
return "Layout not applied when hosted on Observable";

document.body.appendChild(base_css);
document.body.appendChild(light_theme_css);
document.body.appendChild(page_style_css);
document.body.appendChild(lopeviz_handle_css);
document.body.appendChild(minicellStyle);
document.body.appendChild(page);
}
Insert cell
headerStartDelimiter = Symbol()
Insert cell
Insert cell
title = md`# Moldable Webfile (prototype II)`
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
minimap = visualizer(runtime, {
inspector: minicellInto,
invalidation,
detachNodes: false,
classList: "minimap"
})
Insert cell
Insert cell
Insert cell
## [Export](https://observablehq.com/@tomlarkworthy/exporter)
Insert cell
exporter_ref = exporter({ headless: true })
Insert cell
## [Edit](https://observablehq.com/@tomlarkworthy/cell-editor)
Insert cell
editor_view = viewof editor
Insert cell
Insert cell
content = visualizer(runtime, {
inspector: Inspector.into,
invalidation,
detachNodes: true,
filter: between(rightSidebarEndDelimiter, contentEndDelimiter),
classList: "content"
})
Insert cell
Insert cell
# <span class="fire_text">[HYTRADBOI 2025?](https://www.hytradboi.com/2025)</span>

🤞 this is accepted for the online conference "Have You Tried Rubbing a Database On It" as an interesting programming language topic
Insert cell
## How to use

You can drag cells around the page using the dotted square handle.

You can click a cell to get make it editable in the editor, or click it on the minimap on the left.

At the bottom of the editors cells chooser drop down is `<new cell>`. This will add a new cell underneath the last click one.

The header/sidepanel/content panels are resizable and dockable.

The exporter has a "preview" button which exports the page to an ephemeral tab, and a "download" which copies the whole thing including the latest modifications to an offline-file.

The editing experience is not there yet. Probably wiser to fork the [original Observable](https://observablehq.com/@tomlarkworthy/moldable-webpage) notebook if you seriously want to create a webpage.

Observable is a single linear list of cells, we map those to different panel sections using `delimiter` cells. It is possible to create cells outside those delimiters, and then they will not appear in a panel. The left sidepanel has a complete list of cells which are also draggable and clickable.
Insert cell
## TODO
- On mobile clicking the apply button loses the editor focus. We should refocus it automatically (editor)
- Editor does not load on safari :(
Insert cell
Insert cell
Insert cell
between = (symbolStart, symbolEnd) => (variable, index, state) => {
if (variable._value === symbolStart) {
state.between = true;
} else if (variable._value === symbolEnd) {
state.between = undefined;
} else {
return state.between;
}
}
Insert cell
## Imports
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
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