Szymon Kaliski

  1. Main
  2. Projects
  3. Notes
  4. Music
  5. Bio

Editable

editable-cli is a command line tool piggybacking on observable ↗ internals which provides file-based interactive notebooks.

User has access to new top-level function: def. This function is used to define constants and functions which are live-reloaded, with persisted state (unless the body of the function has changed):

def("a", 10);
def("b", 20);
def("sum", (a, b) => a + b); // sum is "tied" to defs "a" and "b" here

This environment is ideal for quick sketches and explorations. It provides all that observable exposes, including DOM access and more.

File-based notebooks have few additional nice properties:

editable-cli is open sourced: szymonkaliski/editable-cli ↗, and can be installed via npm: npm install -g editable-cli.

def("chart", (DOM, data, margin, d3, yAxis, xAxis, y, x, width, height) => {
  const svg = d3.select(DOM.svg(width, height));

  svg
    .append("g")
    .attr("fill", "#444")
    .selectAll("rect")
    .data(data)
    .enter()
    .append("rect")
    .attr("x", x(0))
    .attr("y", (d) => y(d.name))
    .attr("width", (d) => x(d.value) - x(0))
    .attr("height", y.bandwidth());

  svg
    .append("g")
    .attr("fill", "white")
    .attr("text-anchor", "end")
    .style("font", "12px sans-serif")
    .selectAll("text")
    .data(data)
    .enter()
    .append("text")
    .attr("x", (d) => x(d.value) - 4)
    .attr("y", (d) => y(d.name) + y.bandwidth() / 2)
    .attr("dy", "0.35em")
    .text((d) => d3.format(".3f")(d.value));

  svg.append("g").call(xAxis);
  svg.append("g").call(yAxis);

  return svg.node();
});

def("margin", { top: 30, right: 0, left: 30, bottom: 10 });
def("height", (data, margin) => data.length * 18 + margin.top + margin.bottom);
def("width", 600);

def("alphabet", (require) => require("@observablehq/alphabet"));
def("d3", (require) => require("d3"));

def("yAxis", (d3, margin, y) => (g) => {
  return g
    .attr("transform", `translate(${margin.left},0)`)
    .call(d3.axisLeft(y).tickSizeOuter(0));
});

def("xAxis", (d3, margin, x, width) => (g) => {
  return g
    .attr("transform", `translate(0,${margin.top})`)
    .call(d3.axisTop(x).ticks(width / 80))
    .call((g) => g.select(".domain").remove());
});

def("y", (d3, margin, data, height) => {
  return d3
    .scaleBand()
    .domain(data.map((d) => d.name))
    .range([margin.top, height - margin.bottom])
    .padding(0.1);
});

def("x", (d3, margin, data, width) => {
  return d3
    .scaleLinear()
    .domain([0, d3.max(data, (d) => d.value)])
    .range([margin.left, width - margin.right]);
});

def("data", (alphabet) => {
  return (alphabet || [])
    .slice()
    .sort((a, b) => b.frequency - a.frequency)
    .map(({ letter, frequency }) => ({ name: letter, value: frequency }));
});

Backlinks

  1. 2024-05-17Live-Programming Issues1
  2. 2022-01-03A Dog, Short Ramble on "Programming", MIDI→CV, and a Rabbit-Holing Web Browser1
  3. 2021-12-22CartographistWeb Browser Optimized for Rabbit-Holing1

436 words published on 2018-08-19let me know what you think