diff --git a/README.md b/README.md index 0e3bc06bc..34dabd75a 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Observable CLI +# Observable Framework - [Documentation](https://cli.observablehq.com/) - [Issues](https://github.com/observablehq/cli/issues) diff --git a/docs/components.md b/docs/components.md index 67722f1d5..1c0b7eae7 100644 --- a/docs/components.md +++ b/docs/components.md @@ -2,7 +2,7 @@ You don’t have to start from scratch: components are reusable pieces of code (functions, themes, snippets, etc.) that make it quicker to update page layout and appearance, and add common page content. -The Observable CLI offers three flavors of components: [layout helpers](#layout-helpers), [Observable Plot snippets](#observable-plot-snippets), and [Observable Inputs](#observable-inputs). +Observable Framework offers three flavors of components: [layout helpers](#layout-helpers), [Observable Plot snippets](#observable-plot-snippets), and [Observable Inputs](#observable-inputs). ## Layout helpers @@ -140,4 +140,4 @@ The [radio input](./inputs/radio) prompts a user to select a penguin species: const pickSpecies = view(Inputs.radio(["Adelie", "Chinstrap", "Gentoo"], {value: "Gentoo", label: "Penguin species:"})) ``` -The value of `pickSpecies` (="${pickSpecies}") can then be accessed elsewhere in the page, as a parameter in other computations, and to create interactive charts, tables or text with [inline expressions](./javascript#inline-expressions). +The value of `pickSpecies` (="${pickSpecies}") can then be accessed elsewhere in the page, as a parameter in other computations, and to create interactive charts, tables or text with [inline expressions](./javascript#inline-expressions). diff --git a/docs/contributing.md b/docs/contributing.md index 5a5d77c63..19c6a2a16 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -1,6 +1,6 @@ # Contributing -If you’d like to contribute to the Observable CLI, here’s how. First clone the [git repo](https://github.com/observablehq/cli) and run [Yarn (1.x)](https://classic.yarnpkg.com/lang/en/docs/install/) to install dependencies: +If you’d like to contribute to Observable Framework, here’s how. First clone the [git repo](https://github.com/observablehq/cli) and run [Yarn (1.x)](https://classic.yarnpkg.com/lang/en/docs/install/) to install dependencies: ```sh git clone git@github.com:observablehq/cli.git @@ -30,7 +30,7 @@ This creates the `dist` folder. View the site using your preferred web server, s http-server dist ``` -This documentation site is built on GitHub using the Observable CLI; see the [deploy workflow](https://github.com/observablehq/cli/blob/main/.github/workflows/deploy.yml). Please open a pull request if you’d like to contribute to the documentation or to CLI features. Contributors are expected to follow our [code of conduct](https://github.com/observablehq/.github/blob/master/CODE_OF_CONDUCT.md). 🙏 +This documentation site is built on GitHub using Observable Framework; see the [deploy workflow](https://github.com/observablehq/cli/blob/main/.github/workflows/deploy.yml). Please open a pull request if you’d like to contribute. Contributors are expected to follow our [code of conduct](https://github.com/observablehq/.github/blob/master/CODE_OF_CONDUCT.md). 🙏 A test coverage report can be generated with [c8](https://github.com/bcoe/c8), in text and lcov formats, to help you identify which lines of code are not (yet!) covered by tests. Just run: @@ -42,7 +42,7 @@ yarn test:coverage
These instructions are intended for Observable staff.
-To release a new version of the CLI, first update the [package.json](https://github.com/observablehq/cli/blob/main/package.json) file by following the standard process for committing code changes: +To release a new version, first update the [package.json](https://github.com/observablehq/cli/blob/main/package.json) file by following the standard process for committing code changes: 1. Create a new branch. 2. Edit the `version` field in the [package.json](https://github.com/observablehq/cli/blob/main/package.json) file as desired. diff --git a/docs/data/forecast.json.js b/docs/data/forecast.json.js new file mode 100644 index 000000000..2f33a6d67 --- /dev/null +++ b/docs/data/forecast.json.js @@ -0,0 +1,13 @@ +const longitude = -122.47; +const latitude = 37.80; + +async function json(url) { + const response = await fetch(url); + if (!response.ok) throw new Error(`fetch failed: ${response.status}`); + return await response.json(); +} + +const station = await json(`https://api.weather.gov/points/${latitude},${longitude}`); +const forecast = await json(station.properties.forecastHourly); + +process.stdout.write(JSON.stringify(forecast)); diff --git a/docs/data/forecast.json.py b/docs/data/forecast.json.py new file mode 100644 index 000000000..395facb3f --- /dev/null +++ b/docs/data/forecast.json.py @@ -0,0 +1,11 @@ +import json +import requests +import sys + +longitude = -122.47 +latitude = 37.80 + +station = requests.get(f"https://api.weather.gov/points/{latitude},{longitude}").json() +forecast = requests.get(station["properties"]["forecastHourly"]).json() + +json.dump(forecast, sys.stdout) diff --git a/docs/getting-started.md b/docs/getting-started.md index ccc3aaebb..6bff064a1 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -1,153 +1,571 @@ + + # Getting started -The Observable CLI is a Node.js application. As the name suggests, the CLI lives on the command line; the instructions below are intended to run in your [terminal](https://support.apple.com/guide/terminal/open-or-quit-terminal-apd5265185d-f365-44cb-8b09-71a064a42125/mac). You’ll need to install [Node.js 20.6 or later](https://nodejs.org/) before you can install the CLI. +Welcome! This tutorial will guide your first steps with Observable Framework by way of a hands-on exercise creating a dashboard of local weather. 🌦️ + +Observable Framework — or “Framework” for short — is an open-source system for building data apps, dashboards, and reports that combines the power of JavaScript on the front-end for interactive graphics with any language you want on the back-end for data preparation and analysis. + +Framework is three things in one: + +- a **local development server** that you use to preview projects locally during development, with instant updates as you save changes, +- a **static site generator** that compiles Markdown, JavaScript, and other sources and static assets — alongside data snapshots generated by loaders — into a static site that you can host anywhere, and +- a **command-line interface** to Observable so that you can quickly and securely share your site with whomever you like (your boss, your team, the world). -## Installing +We’ll touch on each of these parts in this tutorial. It’ll go something like this: -We recommend starting with our default project template which currently requires Yarn 1.x. If you already have Yarn installed, you can check the version like so: +```js +const digraph = dot`digraph { + rankdir=LR -```sh -yarn --version + create -> edit -> preview -> build -> deploy -> view + preview -> edit + + subgraph cluster_develop { + label = "develop" + color = "gray" + edit + preview + } + + subgraph cluster_publish { + label = "publish" + color = "gray" + build + deploy + } +}`; ``` -Otherwise, or if some other version of Yarn is installed, you can install Yarn via npm: +
+ ${digraph} +
An overview of developing with Framework.
+
+ +First you’ll setup your local development environment by [**creating**](#1.-create) a project. Next you’ll [**develop**](#2.-develop): an iterative process where you save changes to source files in your editor while previewing the result in your browser. When you’re ready to share, it’s time to [**publish**](#3.-publish): you can either build a static site for self-hosting or deploy directly to Observable. Lastly, you can invite people to view your project! + +These are just first steps. You can continue to develop projects after publishing, and republish as needed. You can also setup continuous deployment to publish your site automatically on commit or on schedule. We’ll cover these [next steps](#next-steps) briefly below. + +## 1. Create + +Framework includes a helper script (`observable create`) for creating new projects. After a few quick prompts — where to create the project, your preferred package manager, *etc.* — it will stamp out a fresh project from a template. + +
+

Framework is a Node.js application published to npm. You must have Node.js 20.6 or later installed before you can install Framework. Framework is a command-line interface (CLI) and runs in the terminal.

+

If you run into difficulty following this tutorial, we’re happy to help! Please visit the Observable forum or our GitHub discussions.

+
+ +To create a new project with npm, run: + +
npm init @observablehq
+ +If you prefer Yarn, run: + +
yarn create @observablehq
+ +
The commands above will not work until Framework is published to npm and made publicly available. Until then, you’ll need to install Yarn 1.x and run this instead: +
yarn global add https://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@github.com/observablehq/cli
+
$(yarn global bin)/observable create
+ +You can run the above command anywhere, but you may want to `cd` to your `~/Development` directory first (or wherever you do local development). + +The first prompt asks where to create your new project. Enter `./hello-framework` to create a directory named `hello-framework` within the current directory. Or just hit Enter, as this is conveniently the default. (The `./` is implied, so `./hello-framework` is equivalent to `hello-framework`. You can create a project in a different directory by entering a path that starts with `../` or `~/` or `/`.) + +
   observable create 
+
+  Where to create your project?
+  ./hello-framework 
+
+ +Next you’ll enter the project’s title. A project’s title appears in the sidebar as well as on all pages. You can hit Enter here to accept the default title derived from the directory name. + +
   observable create 
+
+  Where to create your project?
+  ./hello-framework
+
+  What to title your project?
+  Hello Framework
+
+ +Next, decide whether you want sample files in your new project. These files demonstrate common techniques and are handy for learning — you can edit the code and see what happens. But if you’d prefer a more minimal starter project with less to delete later, you can omit them. They’re not needed for this tutorial. + +
   observable create 
+
+  Where to create your project?
+  ./hello-framework
+
+  What to title your project?
+  Hello Framework
+
+  Include sample files to help you get started?
+   Yes, include sample files (recommended)
+  ○ No, create an empty project
+
+ +If you use npm or Yarn as your preferred package manager, declare your allegiance now. The package manager you used to launch `observable create` will be selected by default, so you can just hit Enter again to continue. If you prefer a different package manager (say pnpm), choose `No`; you can always install dependencies after the project is created. + +
   observable create 
+
+  Where to create your project?
+  ./hello-framework
+
+  What to title your project?
+  Hello Framework
+
+  Include sample files to help you get started?
+  Yes, include sample files
+
+  Install dependencies?
+  ○ Yes, via npm
+   Yes, via yarn (recommended)
+  ○ No
+
+ +If you’ll continue developing your project after you finish this tutorial and want source control, answer `Yes` to initialize a git repository. Or say `No` — you can always do it later by running `git init`. + +
   observable create 
+
+  Where to create your project?
+  ./hello-framework
+
+  What to title your project?
+  Hello Framework
+
+  Include sample files to help you get started?
+  Yes, include sample files
+
+  Install dependencies?
+  Yes, via yarn
+
+  Initialize a git repository?
+   Yes / ○ No
+
+ +And that’s it! After some downloading, copying, and installing, your new project is ready to go. 🎉 + +
   observable create 
+
+  Where to create your project?
+  ./hello-framework
+
+  What to title your project?
+  Hello Framework
+
+  Include sample files to help you get started?
+  Yes, include sample files
+
+  Install dependencies?
+  Yes, via yarn
+
+  Initialize a git repository?
+  Yes
+
+  Installed! 🎉
+
+  Next steps…
+
+  cd ./hello-framework
+  yarn dev
+
+  Problems? https://cli.observablehq.com/getting-started
+ +## 2. Develop + +Next, `cd` into your new project folder. + +
cd hello-framework
+ +Framework’s local development server lets you preview your site in the browser as you make rapid changes. The preview server generates pages on-the-fly: as you edit files in your editor, changes are instantly streamed to your browser. + +
You can work offline with the preview server, but you must be connected to the internet to import libraries from npm. In the future, we intend to support self-hosting imported libraries; please upvote #20 and #360 if you are interested in this feature.
+ +To start the preview server using npm: + +
npm run dev
+ +Or with Yarn: + +
yarn dev
+ +You should see something like this: + +
Observable Framework     v1.0.0
+↳ http://127.0.0.1:3000/
+ +
+

If port 3000 is in use, the preview server will choose the next available port, so your actual port may vary. To specify port 4321 (and similarly for any other port), use --port 4321.

+

For security, the preview server is by default only accessible on your local machine using the loopback address 127.0.0.1. To allow remote connections, use --host 0.0.0.0.

+
+ +Now visit in your browser, which should look like: + +
+ +
The default home page (docs/index.md) after creating a new project.
+
+ +### Test live preview + +Live preview means that as you save changes, your in-browser preview updates instantly. Live preview applies to Markdown pages, imported JavaScript modules (so-called *hot module replacement*), data loaders, and file attachments. This feature is implemented by the preview server watching files and pushing changes to the browser over a socket. + +To experience live preview, open docs/index.md in your preferred text editor — below we show Visual Studio Code — and position your browser window so that you can see your editor and browser side-by-side. If you then replace the text “Hello, Observable Framework” with “Hi, Mom!” and save, you should see: + +
+ +
No seriously — hi, Mom! Thanks for supporting me all these years.
+
+ +
If you don’t see an update after saving, try reloading. The preview socket may disconnect if you’re idle. Please upvote #50 if you run into this issue.
+ +### Create a new page + +Now let’s add a page for our weather dashboard. Create a new file `docs/weather.md` and paste in the following snippet: + +````md run=false +# Weather report + +```js +1 + 2 +``` +```` + +To see the new page in the sidebar, you must restart the preview server. In the terminal, use Control-C (⌃C) to kill the preview server. Then use up arrow (↑) to re-run the command to start the preview server (`npm run dev` or `yarn dev`). Lastly, reload your browser. A bit of rigamarole, but you won’t have to do it often… 😓 + +If you click on the **Weather report** link in the sidebar, it’ll take you to , where you should see: + +
+ +
The humble beginnings of a local weather dashboard.
+
+ +
The sidebar is hidden by default in narrow windows. If you don’t see the sidebar, you can show it by making the window wider, or using Command-B (⌘B) or Option-B (⌥B) on Firefox and non-macOS, or clicking the right-pointing arrow ↦ on the left edge of the window.
+ +As evidenced by the code 1 + 2 rendered as 3, JavaScript fenced code blocks (```js) are *live*: the code runs in the browser. Try replacing 2 with Math.random(), and the code will re-run automatically on save. In a bit, we’ll write code to render a chart. We can also use code to debug as we develop, say to inspect data. + +### Data loader + +Next, let’s load some data. The [National Weather Service (NWS)](https://www.weather.gov/documentation/services-web-api) provides an excellent and free API for local weather data within the United States. We’ll use the `/points/{latitude},{longitude}` endpoint to get metadata for the closest grid point to the given location, and then fetch the corresponding hourly forecast. + +Create a new file docs/data/forecast.json.js and paste in the following snippet: + +
const longitude = ${html`${longitude.toFixed(2)}`};
+const latitude = ${html`${latitude.toFixed(2)}`};
+
+async function json(url) {
+  const response = await fetch(url);
+  if (!response.ok) throw new Error(`fetch failed: ${response.status}`);
+  return await response.json();
+}
+
+const station = await json(`https://api.weather.gov/points/${latitude},${longitude}`);
+const forecast = await json(station.properties.forecastHourly);
+
+process.stdout.write(JSON.stringify(forecast));
+ +```js +const location = view(Locator([-122.47, 37.8])); + +function Locator(initialValue) { + const form = html`
+ + +
`; + form.b.onclick = async event => { + form.value = await new Promise((resolve, reject) => { + navigator.geolocation.getCurrentPosition( + ({coords: {longitude, latitude}}) => { + form.o.value = "Located!"; + resolve([longitude, latitude]); + }, + (error) => { + form.o.value = "Error!"; + reject(error); + } + ); + form.o.value = "Locating…"; + }); + form.dispatchEvent(new CustomEvent("input", {bubbles: true})); + }; + form.value = initialValue; + return form; +} +``` -```sh -npm install --global yarn +```js +const [longitude, latitude] = location; ``` -See the [Yarn 1.x installation instructions](https://classic.yarnpkg.com/docs/install) for details. +To personalize this code snippet to your current location, edit the longitude and latitude values above, or click the **Locate me** button above. + +
NWS does not provide forecasts for points outside the United States, so if you specify such a location the API will return an error and the data loader will fail.
+ +
If you would rather write your data loader in Python, R, or some other language, take a peek at the next steps below before continuing.
+ +Your data loader should look like this: + +
+ +
A JavaScript data loader for fetching a local forecast from weather.gov.
+
+ +If you like, you can run your data loader manually in the terminal: + +
node docs/data/forecast.json.js
-Once Yarn is installed, you can install `observablehq-create`, our project template. This package won’t be made publicly available until the Observable CLI is released, so the command below uses an access token to download it from our private repo. Please do not share this token with anyone outside the Early Access program. +If this barfs a bunch of JSON in the terminal, it’s working as intended. 😅 Normally you don’t run data loaders by hand — Framework runs them automatically, as needed — but data loaders are “just” programs so you can run them manually if you want. Conversely, any executable or shell script that runs on your machine and outputs something to stdout can be a data loader! -```sh -yarn global add https://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@github.com/observablehq/create +### File attachments + +Framework uses [file-based routing](./routing) not just for pages but for data loaders as well: the data loader forecast.json.js serves the file forecast.json. To load this file from docs/weather.md we use the relative path ./data/forecast.json. In effect, data loaders are simply a naming convention for generating “static” files — a big advantage of which is that you can edit a data loader and the changes immediately propagate to the live preview without needing a reload. + +To load a file in JavaScript, use the built-in [`FileAttachment`](./javascript/files). In `weather.md`, replace the contents of the JavaScript code block (the parts inside the triple backticks ```) with the following code: + +```js run=false +const forecast = FileAttachment("./data/forecast.json").json(); ``` -Once installed, create a new project with the following command: +
FileAttachment is a special function that can only be passed a static string literal as an argument. This restriction enables static analysis, allowing Framework to determine which data loaders to run on build and improving security by only including referenced files in the published site.
-```sh -observablehq-create +You can now reference the variable `forecast` from other code. For example, you can add another code block that displays the `forecast` data. + +```js run=false +display(forecast); ``` -If Yarn doesn’t install onto your `$PATH`, instead try: +This looks like: + +
+ +
Using FileAttachment to load data.
+
+ +The built-in [`display`](./javascript/display) function displays the specified value, a bit like `console.log` in the browser’s console. As you may have noticed above with 1 + 2, `display` is called implicitly when a code block contains an expression. -```sh -$(yarn global bin)/observablehq-create +For convenience, here’s a copy of the data so you can explore it here: + +```js +forecast ``` -After answering a few questions, this command will create a new project folder in the current working directory. +This is a GeoJSON `Feature` object of a `Polygon` geometry representing the grid square. The `properties` object within contains the hourly forecast data. You can display it on a map with Leaflet, if you like. -## Project structure +
+
+
This grid point covers the south end of the Golden Gate Bridge.
+
-A typical project looks like this: +```js +const map = L.map(document.querySelector("#map")); +const tile = L.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png").addTo(map); +const geo = L.geoJSON().addData(forecast).addTo(map); +map.fitBounds(geo.getBounds(), {padding: [50, 50]}); +invalidation.then(() => map.remove()); +``` +```js +const forecast = FileAttachment("./data/forecast.json").json(); ``` -. -├─ docs -│ ├─ .observablehq -│ │ └─ cache -│ ├─ components -│ │ └─ dotmap.js -│ ├─ data -│ │ └─ quakes.csv.ts -│ ├─ quakes.md -│ └─ index.md -├─ .gitignore -├─ README.md -├─ observablehq.config.ts -├─ yarn.lock -└─ package.json + +### Plots + +Now let’s add a chart using Observable Plot. Framework includes a variety of recommended libraries by default, including `Plot`, and you can always import more from npm. Replace the `display(forecast)` code block with the following code: + +```js run=false +Plot.plot({ + title: "Hourly temperature forecast", + x: {type: "utc", ticks: "day", label: null}, + y: {grid: true, inset: 10, label: "Degrees (F)"}, + marks: [ + Plot.lineY(forecast.properties.periods, { + x: "startTime", + y: "temperature", + z: null, // varying color, not series + stroke: "temperature", + curve: "step-after" + }) + ] +}) ``` -#### `docs` +
Because this is JSON data, startTime is a string rather than a Date. Setting the type of the x scale to utc tells Plot to interpret these values as temporal rather than ordinal.
-This is the “source root” — where your source files live. It doesn’t have to be named `docs`, but that’s the default; you can change it using the **root** [config option](./config). Pages go here. Each page is a Markdown file. The Observable CLI uses [file-based routing](./routing), which means that the name of the file controls where the page is served. You can create as many pages as you like. Use folders to organize your pages. +You should now see: -#### `docs/.observablehq/cache` +
+ +
Using Plot to make a chart.
+
-This is where the [data loader](./loaders) cache lives. You don’t typically have to worry about this since it’s autogenerated when the first data loader is referenced. You can `rm -rf docs/.observablehq/cache` to clean the cache and force data loaders to re-run. +
Try editing forecast.json.js to change the longitude and latitude to a different location! After you save, Framework will run the data loader again and push the new data to the client to update the chart. For example, to see the current forecast at the White House:
const longitude = -77.04;
+const latitude = 38.90;
-#### `docs/.observablehq/deploy.json` +As before, the code block contains an expression (a call to `Plot.plot`) and hence `display` is called implicitly. And since this expression evaluates to a DOM element (a `
` containing an ``), `display` inserts the element directly into the page. We didn’t have to touch the DOM API! -This file is autogenerated. If you deploy your project to the Observable platform, we’ll save some information here to make it easier to redeploy next time. +### Components -#### `docs/components` +As pages grow, complex inline JavaScript may become unwieldy and repetitive. Tidy code by moving it into functions. In Framework, a function that returns a DOM element is called a *component*. -You can put shared [JavaScript modules](./javascript/imports) anywhere in your source root, but we recommend putting them here. This helps you pull code out of Markdown files and into JavaScript, making it easier to reuse code across pages, write tests and run linters, and even share code with vanilla web applications. +To turn the chart above into a component, wrap it in a function and promote the `data` to a required argument. Accept any named options (such as `width`) as an optional second argument with destructuring. -#### `docs/data` +```js echo +function temperaturePlot(data, {width} = {}) { + return Plot.plot({ + title: "Hourly temperature forecast", + width, + x: {type: "utc", ticks: "day", label: null}, + y: {grid: true, inset: 10, label: "Degrees (F)"}, + marks: [ + Plot.lineY(data.properties.periods, { + x: "startTime", + y: "temperature", + z: null, // varying color, not series + stroke: "temperature", + curve: "step-after" + }) + ] + }); +} +``` -You can put [data loaders](./loaders) or static files anywhere in your source root, but we recommend putting them here. +Now you can call `temperaturePlot` to display the forecast anywhere on the page: -#### `docs/index.md` +```js run=false +temperaturePlot(forecast) +``` + +
JavaScript can be extracted into standalone modules (.js files) that you can import into Markdown. This lets you share code across pages, write unit tests for components, and more.
-This is the home page for your site. You can have as many additional pages as you’d like, but you should always have a home page, too. +### Layout -#### `observablehq.config.ts` +Let’s put some finishing touches on and wrap up this tutorial. -This is the [project configuration](./config) file, such as the pages and sections in the sidebar navigation, and the project’s title. The config file can be written in either TypeScript (`.ts`) or JavaScript (`.js`). +While this nascent dashboard only has a single chart on it, most dashboards will have many charts, tables, values, and other elements. To assist layout, Framework includes simple `grid` and `card` CSS classes with 1, 2, 3, or 4 columns. (You can write more elaborate custom styles if needed, or load your preferred CSS framework.) -## Preview +For example, here’s a two-column grid with three cards: -After you’ve initialized your project, you can start developing locally. In preview mode, the Observable CLI generates HTML pages on-demand as you view a local version of your site in the browser. As you edit files, changes will be instantly reflected in the browser. +```html echo +
+
one–two
+
three
+
four
+
+``` -To start the preview server: +
Framework’s grid is responsive: on narrow windows, the two-column grid will automatically collapse to a one-column grid. Cells in a grid have the same height by default (using grid-auto-rows), so consider separate <div class="grid"> containers if you want to vary row height.
-```sh -yarn dev +When placing charts in a grid, you typically want to render responsively based on the width (and sometimes height) of the containing cell. Framework’s `resize` helper takes a render function returning a DOM element and re-renders whenever the container resizes. It looks like this: + +```html echo +
+
${resize((width) => temperaturePlot(forecast, {width}))}
+
``` -Then visit to preview. +Lastly, let’s apply the `dashboard` [theme](./themes) and disable the table of contents (`toc`) using [YAML front matter](./markdown). The `dashboard` theme allows the main column to span the full width of the window; without it, the main column width is limited to 1152px as appropriate for documentation or a report. -By default, the preview server is only visible to you on your local machine using the loopback address `127.0.0.1`. You can open access to remote connections using `--host 0.0.0.0`. The preview server runs on port 3000 by default (or the next available port if the former is already in use); you can specify the port with the `--port` flag. +```yaml run=false +--- +theme: dashboard +toc: false +--- +``` -## Build +
+ +
Adopting a grid layout and the dashboard theme.
+
-When you’re ready to deploy your project, use the `build` command to generate the output root (`dist`). You can then copy the `dist` folder to your static site server. +_Ta-da!_ 🎉 Perhaps not the most exciting dashboard yet, but it has potential! Try exploring other data in the NWS forecast and adding more charts. For example, you could visualize precipitation probability. -To generate your static site: +## 3. Publish -```sh -yarn build -``` +When you’re ready to share your project — whether privately with specific people you want to invite, or publicly with the world — you can quickly deploy it to [Observable](https://observablehq.com) using the `deploy` command: -You can then use `npx http-server dist` to preview your built site. +
npm run deploy
-## Deploy +Or with Yarn: -If you’d like to host your project on the [Observable platform](https://observablehq.com) and share it securely with your team, use the `deploy` command. +
yarn deploy
-To deploy your project to Observable: +
If you don’t have an Observable account yet, the first time you deploy you’ll be prompted to sign-up and create an account. It’s free for individuals and small teams, and we offer paid tiers for larger teams.
-```sh -yarn deploy -``` +When the deploy finishes, Framework will show your project’s URL on observablehq.cloud. And from there you can invite people to see your private project, or make your project private so anyone can see it. -Once done, the command will print the URL where you can view your project on the Observable Cloud. It will follow this pattern: +### Self hosting -``` -https://observablehq.com/@/ -``` +Of course, you don’t have to deploy to Observable — Framework projects are simply static sites, so you can host them anywhere. For example, if you’re visualizing sensitive or proprietary data, you can self-host projects and keep your data and analysis entirely within your own network. -## Advanced usage +To build your static site, run: + +
npm run build
+ +Or with Yarn: + +
yarn build
+ +This generates the `dist` directory; you can then copy this directory to your static site server or preferred hosting service. To preview your built site locally, you can use a local static HTTP server such as [http-server](https://github.com/http-party/http-server): + +
npx http-server dist
+ +## Next steps Here are a few more tips. -### Deploying via GitHub Actions +### Write a data loader in Python, R, or other language -You can schedule the Observable CLI to build and deploy your project automatically on commit, or on a schedule. We’ll share example source code soon, but please reach out and ask if you have questions on how to setup continuous deployment. +We coded exclusively in JavaScript for this tutorial, but you can write data loaders in any language — not just JavaScript. Here’s a forecast.json.py you could use in place of the JavaScript data loader [above](#data-loader): -### Installing into an existing project +```py run=false +import json +import requests +import sys -You can install the CLI as a dependency on an existing project if you don’t want to create a new project using our default template as described above. +longitude = -122.47 +latitude = 37.80 -```sh -npm install https://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@github.com/observablehq/cli -``` +station = requests.get(f"https://api.weather.gov/points/{latitude},{longitude}").json() +forecast = requests.get(station["properties"]["forecastHourly"]).json() -```sh -yarn add https://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@github.com/observablehq/cli +json.dump(forecast, sys.stdout) ``` -You can also install the CLI globally so that the `observable` command is available across projects, but we don’t recommend this approach. By installing the CLI into each project, everyone you work with will use the same version of the CLI. +To write the data loader in R, name it forecast.json.R. Or as shell script, forecast.json.sh. You get the idea. See [Data loaders: Routing](./loaders#routing) for more. The beauty of this approach is that you can leverage the strengths (and libraries) of multiple languages, and still get instant updates in the browser as you develop. + +### Deploying via GitHub Actions + +You can schedule builds and deploy your project automatically on commit, or on a schedule. See this documentation site’s deploy.yml for an example. + +### Ask for help, or share your feedback + +Please reach out if you have questions or thoughts! You can post on the Observable forum, start a GitHub discussion, or file a GitHub issue. And if you like Framework, please give us a star ⭐️ on GitHub — we appreciate your support. 🙏 diff --git a/docs/getting-started/hello-data.webp b/docs/getting-started/hello-data.webp new file mode 100644 index 000000000..a830d46b7 Binary files /dev/null and b/docs/getting-started/hello-data.webp differ diff --git a/docs/getting-started/hello-framework.webp b/docs/getting-started/hello-framework.webp new file mode 100644 index 000000000..a6f2ac12c Binary files /dev/null and b/docs/getting-started/hello-framework.webp differ diff --git a/docs/getting-started/hello-grid.webp b/docs/getting-started/hello-grid.webp new file mode 100644 index 000000000..93b49e528 Binary files /dev/null and b/docs/getting-started/hello-grid.webp differ diff --git a/docs/getting-started/hello-loader.webp b/docs/getting-started/hello-loader.webp new file mode 100644 index 000000000..ba4c3ada4 Binary files /dev/null and b/docs/getting-started/hello-loader.webp differ diff --git a/docs/getting-started/hello-plot.webp b/docs/getting-started/hello-plot.webp new file mode 100644 index 000000000..e63967ee4 Binary files /dev/null and b/docs/getting-started/hello-plot.webp differ diff --git a/docs/getting-started/hello-weather.webp b/docs/getting-started/hello-weather.webp new file mode 100644 index 000000000..28f232e05 Binary files /dev/null and b/docs/getting-started/hello-weather.webp differ diff --git a/docs/getting-started/hi-mom.webp b/docs/getting-started/hi-mom.webp new file mode 100644 index 000000000..ee7f72938 Binary files /dev/null and b/docs/getting-started/hi-mom.webp differ diff --git a/docs/index.md b/docs/index.md index 36041715d..8dda15ab3 100644 --- a/docs/index.md +++ b/docs/index.md @@ -2,39 +2,63 @@ toc: false --- -
- The best dashboards are built with code - Create and deploy fast, beautiful dashboards, reports, and data apps from the command line with the open-source Observable CLI. Write JavaScript, SQL, Python, R… and any language you like. -
+ -The **Observable CLI** is an [open-source](https://github.com/observablehq/cli) static site generator for business intelligence dashboards, reports, and data apps. +
+

The best dashboards are built with code

+

Create fast, beautiful data apps, dashboards, and reports from the command line. Write Markdown, JavaScript, SQL, Python, R… and any language you like. Free and open-source.

+
npm init @observablehq
+ Get started +
+ +**Observable Framework** is an [open-source](https://github.com/observablehq/cli) static site generator for data apps, dashboards, reports, and more. Framework includes a preview server for local development, and a command-line interface for automating builds & deploys. -The Observable CLI converts [Markdown](./markdown) pages — with interactive charts and inputs written in [reactive JavaScript](./javascript), and data snapshots generated by [loaders](./loaders) written in any programming language (SQL, Python, R, and more) — into a static site with instant page loads for a great user experience. +You write simple [Markdown](./markdown) pages — with interactive charts and inputs in [reactive JavaScript](./javascript), and with data snapshots generated by [loaders](./loaders) in _any_ programming language (SQL, Python, R, and more) — and Framework compiles it into a static site with instant page loads for a great user experience. Since everything is just files, you can use your preferred editor and source control, write unit tests, share code with other apps, integrate with CI/CD, and host projects anywhere. -The Observable CLI includes thoughtfully-designed themes and components to help you build beautiful apps that look great on any device. And you can quickly create custom components using our popular open-source libraries [D3.js](./lib/d3) and [Observable Plot](./lib/plot) — as well as myriad other open-source tools such as [Arquero](./lib/arquero), [DuckDB](./lib/duckdb), [Graphviz](./lib/graphviz), [Leaflet](./lib/leaflet), and [TeX](./lib/tex). +Framework includes thoughtfully-designed [themes and components](./components) to help you build displays of data that look great on any device. And you can quickly craft custom components using open-source libraries such as [Observable Plot](./lib/plot), [D3.js](./lib/d3), [Vega-Lite](./lib/vega-lite), [Graphviz](./lib/graphviz), [Mermaid](./lib/mermaid), [Leaflet](./lib/leaflet), [KaTeX](./lib/tex), and myriad more. (And for working with data, don’t forget about [Arquero](./lib/arquero), [DuckDB](./lib/duckdb), and [SQLite](./lib/sqlite), too.) -You can host static sites generated by the Observable CLI anywhere. And the Observable CLI seamlessly integrates with the [Observable platform](https://observablehq.com) for sharing your site securely with your team and collaborating on data analysis, visualization, and business intelligence. +Want the best dashboards? [Get started now.](./getting-started) diff --git a/docs/javascript.md b/docs/javascript.md index 970fa05f2..446302741 100644 --- a/docs/javascript.md +++ b/docs/javascript.md @@ -1,6 +1,6 @@ # JavaScript -The Observable CLI supports JavaScript in Markdown for charts, inputs, and other dynamic, interactive, and graphical content. This client-side JavaScript runs in the browser on load, and re-runs automatically when [reactive variables](./javascript/reactivity) change or when you edit pages during preview. +Observable Framework supports JavaScript in Markdown for charts, inputs, and other dynamic, interactive, and graphical content. This client-side JavaScript runs in the browser on load, and re-runs automatically when [reactive variables](./javascript/reactivity) change or when you edit pages during preview. JavaScript in Markdown can be expressed either as [fenced code blocks](#fenced-code-blocks) or [inline expressions](#inline-expressions). You can also write JavaScript modules alongside Markdown files and [import them](./javascript/imports) into Markdown. (And you can run JavaScript, TypeScript, Python, or any other programming language during build to generate data using [data loaders](./loaders).) diff --git a/docs/javascript/imports.md b/docs/javascript/imports.md index 416f51586..48dd8b90c 100644 --- a/docs/javascript/imports.md +++ b/docs/javascript/imports.md @@ -58,13 +58,13 @@ import {foo} from "./foo.js"; and the imported value of `foo` is: ${foo}. -The preview server automatically watches imported local modules, so any changes to these files will instantly update in the browser via hot module replacement. +Observable Framework automatically watches imported local modules during preview, so any changes to these files will instantly update in the browser via hot module replacement.
While there is reactivity across JavaScript code blocks in Markdown, there’s no reactivity within a JavaScript module. However, you can write async functions and generator functions to define reactive variables. And you can import the Observable standard library into local modules, so you can reference files and use other standard library features.
## Module preloads -During build, Observable will resolve the current exact version of the imported library from npm. Importing `npm:canvas-confetti` is thus equivalent to: +During build, Observable Framework will resolve the current exact version of the imported library from npm. Importing `npm:canvas-confetti` is thus equivalent to: ```js run=false import confetti from "https://cdn.jsdelivr.net/npm/canvas-confetti@1.9.2/+esm"; @@ -72,7 +72,7 @@ import confetti from "https://cdn.jsdelivr.net/npm/canvas-confetti@1.9.2/+esm"; Version resolution locks the version of imported libraries so you don’t have to worry about new releases breaking your built site in the future. At the same time, you’ll conveniently get the latest version of libraries during local development and the next time you build. -In addition to resolving versions of directly-imported modules, Observable recursively resolves dependencies, too! All transitively imported modules are automatically preloaded, greatly improving page load speed because the browser requests all imported modules in parallel. +In addition to resolving versions of directly-imported modules, Observable Framework recursively resolves dependencies, too! All transitively imported modules are automatically preloaded, greatly improving page load speed because the browser requests all imported modules in parallel. ```html run=false @@ -82,7 +82,7 @@ In addition to resolving versions of directly-imported modules, Observable recur ## Implicit imports -For convenience, Observable provides recommended libraries by default in Markdown. These implicit imports are only evaluated if you reference the corresponding symbol and hence don’t add overhead if you don’t use them; for example, D3 won’t be loaded unless you have an unbound reference to `d3`. +For convenience, Observable Framework provides recommended libraries by default in Markdown. These implicit imports are only evaluated if you reference the corresponding symbol and hence don’t add overhead if you don’t use them; for example, D3 won’t be loaded unless you have an unbound reference to `d3`. Click on any of the imported symbols below to learn more. @@ -111,7 +111,7 @@ Click on any of the imported symbols below to learn more. ## Require -If you’re familiar with Observable notebooks, you may be familiar with `require`. We strongly recommend that you avoid `require` as the underlying Asynchronous Module Definition (AMD) convention has been made obsolete by standard imports in JavaScript, and AMD tends to be implemented inconsistently by libraries. +If you’re familiar with Observable notebooks, you may have noticed that we don’t mention `require` above. We recommend that you avoid `require` as the underlying Asynchronous Module Definition (AMD) convention has been made obsolete by standard imports in JavaScript, and AMD tends to be implemented inconsistently by libraries. If you really need `require`, you can import it from [d3-require](https://github.com/d3/d3-require): diff --git a/docs/javascript/reactivity.md b/docs/javascript/reactivity.md index 0c4cfce81..4ed39d1ae 100644 --- a/docs/javascript/reactivity.md +++ b/docs/javascript/reactivity.md @@ -1,6 +1,6 @@ # JavaScript: Reactivity -The Observable CLI uses the open-source [Observable Runtime](https://github.com/observablehq/runtime) to run JavaScript in Markdown reactively: in topological order as determined by [top-level variable](#top-level-variables) references, as in a spreadsheet. For example, here we reference variables `x` and `y` even though they are defined in a code block below: +Observable Framework uses the open-source [Observable Runtime](https://github.com/observablehq/runtime) to run JavaScript in Markdown reactively: in topological order as determined by [top-level variable](#top-level-variables) references, as in a spreadsheet. For example, here we reference variables `x` and `y` even though they are defined in a code block below: ```js echo x + y diff --git a/docs/layout/grid.md b/docs/layout/grid.md index a612d3e4f..ca5b92106 100644 --- a/docs/layout/grid.md +++ b/docs/layout/grid.md @@ -5,7 +5,7 @@ theme: dashboard # Layout: grid -The CLI provides a set of grid CSS classes to help layout page content. These grids pair well with the [dashboard theme](../themes) and the [card](./card) class. This page uses both. +Observable Framework provides a set of grid CSS classes to help layout page content. These grids pair well with the [dashboard theme](../themes) and the [card](./card) class. This page uses both. To see these examples change dynamically, adjust the page width or collapse the sidebar on the left. diff --git a/docs/loaders.md b/docs/loaders.md index 3d83a21ca..9f4f944fd 100644 --- a/docs/loaders.md +++ b/docs/loaders.md @@ -4,7 +4,7 @@ Why generate data at build time? Conventional dashboards are often slow or even unreliable because database queries are executed for each viewer on load. By preparing static data snapshots ahead of time during build, dashboards load instantly with no external dependency on your database. You can also optimize data snapshots for what your dashboard needs, further improving performance and offering more control over what information is shared with viewers. -Data loaders can be written in any programming language. They can even invoke binary executables such as ffmpeg or DuckDB! For convenience, the Observable CLI has built-in support for common languages: JavaScript, TypeScript, Python, and R. Naturally you can use any third-party library or SDK for these languages, too. +Data loaders can be written in any programming language. They can even invoke binary executables such as ffmpeg or DuckDB! For convenience, Observable Framework has built-in support for common languages: JavaScript, TypeScript, Python, and R. Naturally you can use any third-party library or SDK for these languages, too. A data loader can be as simple as a shell script that invokes [curl](https://curl.se/) to fetch recent earthquakes from the [USGS](https://earthquake.usgs.gov/earthquakes/feed/v1.0/geojson.php): @@ -12,7 +12,7 @@ A data loader can be as simple as a shell script that invokes [curl](https://cur curl https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_day.geojson ``` -The Observable CLI uses [file-based routing](./routing), so assuming this shell script is named `quakes.json.sh`, a `quakes.json` file is then generated at build time. You can access this file from the client using [`FileAttachment`](./javascript/files): +Observable Framework uses [file-based routing](./routing), so assuming this shell script is named `quakes.json.sh`, a `quakes.json` file is then generated at build time. You can access this file from the client using [`FileAttachment`](./javascript/files): ```js echo FileAttachment("quakes.json").json() @@ -67,7 +67,7 @@ Plot.plot({ }) ``` -During preview, the CLI automatically runs the data loader the first time its output is needed and [caches](#caching) the result; if you edit the data loader, the CLI will automatically run it again and push the new result to the client. +During preview, the preview server automatically runs the data loader the first time its output is needed and [caches](#caching) the result; if you edit the data loader, the preview server will automatically run it again and push the new result to the client. Here are some more details on data loaders. @@ -126,7 +126,7 @@ Like with any other file, these files from generated archives are live in previe ## Routing -Data loaders live in the source root (typically `docs`) alongside your other source files. When a file is referenced from JavaScript via `FileAttachment`, if the file does not exist, the CLI will look for a file of the same name with a double extension to see if there is a corresponding data loader. The following second extensions are checked, in order, with the corresponding language and interpreter: +Data loaders live in the source root (typically `docs`) alongside your other source files. When a file is referenced from JavaScript via `FileAttachment`, if the file does not exist, Observable Framework will look for a file of the same name with a double extension to see if there is a corresponding data loader. The following second extensions are checked, in order, with the corresponding language and interpreter: - `.js` - JavaScript (`node`) - `.ts` - TypeScript (`tsx`) @@ -170,7 +170,7 @@ Data loaders must output to [standard output](```js
) or [inline expressions](./javascript#inline-expressions) ($\{…}), and [HTML in Markdown](#html), and [front matter](#front-matter) for page-level configuration. If you don’t already know Markdown, please see [GitHub’s guide to Markdown](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax) for an introduction. +Markdown in Observable Framework follows the [CommonMark spec](https://spec.commonmark.org/) and is powered by [markdown-it](https://github.com/markdown-it/markdown-it). We also feature [live JavaScript](./javascript) as either [fenced code blocks](./javascript#fenced-code-blocks) (```js) or [inline expressions](./javascript#inline-expressions) ($\{…}), and [HTML in Markdown](#html), and [front matter](#front-matter) for page-level configuration. If you don’t already know Markdown, please see [GitHub’s guide to Markdown](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax) for an introduction. -
The Observable CLI currently deviates from CommonMark in how blank lines are handled in HTML; see below. This is a limitation of our parser needed for incremental update during preview.
+
Observable Framework currently deviates from CommonMark in how blank lines are handled in HTML; see below. This is a limitation of our parser needed for incremental update during preview.
Here are a few examples of Markdown content to get you started. diff --git a/docs/routing.md b/docs/routing.md index ea9ed43f4..9b7fd35a1 100644 --- a/docs/routing.md +++ b/docs/routing.md @@ -1,6 +1,58 @@ # Routing -The Observable CLI uses file-based routing. This means each page in your project has a corresponding [Markdown](./markdown) file of the same name. For example, here’s a simple project that only has two pages (`hello.md` and `index.md`) in the source root (`docs`): +Observable Framework uses file-based routing: each page in your project has a corresponding [Markdown](./markdown) file (`.md`) of the same name. In addition to pages, you can have [importable](./javascript/imports) JavaScript modules (`.js`), [data loaders](./loaders) for generating data snapshots (_e.g._, `.csv.py`), and [static assets](./javascript/files) such as images and files (_e.g._, `.png`). + +A typical project looks like this: + +```ini +. +├─ docs +│ ├─ .observablehq +│ │ └─ cache +│ ├─ components +│ │ └─ dotmap.js +│ ├─ data +│ │ └─ quakes.csv.ts +│ ├─ quakes.md +│ └─ index.md +├─ .gitignore +├─ README.md +├─ observablehq.config.ts +├─ yarn.lock +└─ package.json +``` + +#### `docs` + +This is the “source root” — where your source files live. It doesn’t have to be named `docs`, but that’s the default; you can change it using the **root** [config option](./config). Pages go here. Each page is a Markdown file. Observable Framework uses [file-based routing](./routing), which means that the name of the file controls where the page is served. You can create as many pages as you like. Use folders to organize your pages. + +#### `docs/.observablehq/cache` + +This is where the [data loader](./loaders) cache lives. You don’t typically have to worry about this since it’s autogenerated when the first data loader is referenced. You can `rm -rf docs/.observablehq/cache` to clean the cache and force data loaders to re-run. + +#### `docs/.observablehq/deploy.json` + +This file is autogenerated. If you deploy your project to the Observable platform, we’ll save some information here to make it easier to redeploy next time. + +#### `docs/components` + +You can put shared [JavaScript modules](./javascript/imports) anywhere in your source root, but we recommend putting them here. This helps you pull code out of Markdown files and into JavaScript, making it easier to reuse code across pages, write tests and run linters, and even share code with vanilla web applications. + +#### `docs/data` + +You can put [data loaders](./loaders) or static files anywhere in your source root, but we recommend putting them here. + +#### `docs/index.md` + +This is the home page for your site. You can have as many additional pages as you’d like, but you should always have a home page, too. + +#### `observablehq.config.ts` + +This is the [project configuration](./config) file, such as the pages and sections in the sidebar navigation, and the project’s title. The config file can be written in either TypeScript (`.ts`) or JavaScript (`.js`). + +## Pages + +For example, here’s a simple project that only has two pages (`hello.md` and `index.md`) in the source root (`docs`): ```ini . @@ -112,9 +164,9 @@ The resulting output root is: └─ ... ``` -The import declaration is automatically rewritten during build to point to `./_import/chart.js` instead of `./chart.js`. (In the future [#260](https://github.com/observablehq/cli/issues/260), the Observable CLI will add a content hash to the imported module name for cache-breaking.) +The import declaration is automatically rewritten during build to point to `./_import/chart.js` instead of `./chart.js`. (In the future [#260](https://github.com/observablehq/cli/issues/260), Observable Framework will add a content hash to the imported module name for cache-breaking.) -Use a leading slash to denote paths relative to the source root, such as `/chart.js` instead of `./chart.js`. This allows you to use the same path to import a module from anywhere, even in nested folders. The Observable CLI always generates relative links so that the generated site can be served under a base path. +Use a leading slash to denote paths relative to the source root, such as `/chart.js` instead of `./chart.js`. This allows you to use the same path to import a module from anywhere, even in nested folders. Observable Framework always generates relative links so that the generated site can be served under a base path. ## Files @@ -141,7 +193,7 @@ Any files referenced by `FileAttachment` will automatically be copied to the `_f └─ ... ``` -`FileAttachment` references are automatically rewritten during build; for example, a reference to `quakes.csv` might be replaced with `_file/quakes.csv`. (In the future [#260](https://github.com/observablehq/cli/issues/260), the Observable CLI will add a content hash to the attached file name for cache-breaking.) Only the files you reference statically are copied to the output root (`dist`), so nothing extra or unused is included in the built site. +`FileAttachment` references are automatically rewritten during build; for example, a reference to `quakes.csv` might be replaced with `_file/quakes.csv`. (In the future [#260](https://github.com/observablehq/cli/issues/260), Observable Framework will add a content hash to the attached file name for cache-breaking.) Only the files you reference statically are copied to the output root (`dist`), so nothing extra or unused is included in the built site. [Imported modules](#imports) can use `FileAttachment`, too. In this case, the path to the file is _relative to the importing module_ in the same fashion as `import`; this is accomplished by resolving relative paths at runtime with [`import.meta.url`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import.meta). @@ -188,7 +240,7 @@ File attachments can be also be pulled from archives. The following archive exte - `.tar` - for [tarballs]() - `.tar.gz` and `.tgz` - for [compressed tarballs](https://en.wikipedia.org/wiki/Gzip) -For example, say you have a `quakes.zip` archive that includes yearly files for observed earthquakes. If you reference `FileAttachment("quakes/2021.csv")` in code, the Observable CLI will pull the `2021.csv` from `quakes.zip`. So this source root: +For example, say you have a `quakes.zip` archive that includes yearly files for observed earthquakes. If you reference `FileAttachment("quakes/2021.csv")` in code, Observable Framework will pull the `2021.csv` from `quakes.zip`. So this source root: ```ini . diff --git a/docs/telemetry.md b/docs/telemetry.md index 013516348..bfb7534bc 100644 --- a/docs/telemetry.md +++ b/docs/telemetry.md @@ -1,6 +1,6 @@ # Telemetry -The Observable CLI collects anonymous usage data to help us improve the product. This data is sent to Observable and is not shared with third parties. Telemetry data is covered by [Observable’s privacy policy](https://observablehq.com/privacy-policy). +Observable Framework collects anonymous usage data to help us improve the product. This data is sent to Observable and is not shared with third parties. Telemetry data is covered by [Observable’s privacy policy](https://observablehq.com/privacy-policy). You can [opt-out of telemetry](#disabling-telemetry) by setting the `OBSERVABLE_TELEMETRY_DISABLE` environment variable to `true`. diff --git a/examples/hello-world/README.md b/examples/hello-world/README.md index c94efda8c..4d8c9517e 100644 --- a/examples/hello-world/README.md +++ b/examples/hello-world/README.md @@ -1,3 +1,3 @@ # Hello, world! -This is a minimal example Observable CLI project. It contains a single page in `docs/index.md`, with no configuration file. +This is a minimal example Observable Framework project. It contains a single page in `docs/index.md`, with no configuration file. diff --git a/examples/pmms/README.md b/examples/pmms/README.md index 0ba3961b8..07a9351cc 100644 --- a/examples/pmms/README.md +++ b/examples/pmms/README.md @@ -1,6 +1,6 @@ # Mortage tracker -This is an example Observable CLI project. It uses a data loader to track the mortage rates published by Freddie Mac — Federal Home Loan Mortgage Corporation — every week since 1971. +This is an example Observable Framework project. It uses a data loader to track the mortage rates published by Freddie Mac — Federal Home Loan Mortgage Corporation — every week since 1971. ## Data loader diff --git a/observablehq.config.ts b/observablehq.config.ts index 4ec4b81bf..20f657120 100644 --- a/observablehq.config.ts +++ b/observablehq.config.ts @@ -1,7 +1,7 @@ import {version} from "./package.json" assert {type: "json"}; export default { - title: "Observable CLI", + title: "Observable Framework", pages: [ {name: "Getting started", path: "/getting-started"}, {name: "Routing", path: "/routing"}, @@ -13,6 +13,7 @@ export default { {name: "Configuration", path: "/config"}, { name: "JavaScript", + open: false, pages: [ {name: "Reactivity", path: "/javascript/reactivity"}, {name: "Display", path: "/javascript/display"}, @@ -103,7 +104,7 @@ export default { - Observable CLI + Observable Framework ${version} diff --git a/package.json b/package.json index cb7947bb5..9558875dd 100644 --- a/package.json +++ b/package.json @@ -72,6 +72,7 @@ "send": "^0.18.0", "tar-stream": "^3.1.6", "tsx": "~4.2.1", + "untildify": "^5.0.0", "ws": "^8.14.2" }, "devDependencies": { diff --git a/src/client/pre.js b/src/client/pre.js index 95dd7603b..cec27833f 100644 --- a/src/client/pre.js +++ b/src/client/pre.js @@ -4,7 +4,7 @@ copyButton.innerHTML = '