> ## Documentation Index
> Fetch the complete documentation index at: https://docs.inklyai.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# The editor

> The local editor that dev serves: what you can change, how saving works, and how edits land in your files.

`interactive-demo dev` serves each demo at `/<slug>/` and a visual editor for them at `/__demo/editor/`. The editor runs on your machine. Everything you change is written straight back into the demo folder in your working tree. There is no draft, no database, and no account. The files are the only copy.

<Frame>
  <img src="https://mintcdn.com/inkly/wYiUnCFudpQCRMuh/images/open-source/editor.webp?fit=max&auto=format&n=wYiUnCFudpQCRMuh&q=85&s=0dababa3f72cf8ff8b5a54c9f4737cdb" alt="The editor: the step strip on the right, the preview in the middle, the annotation toolbar below, and a hotspot opened for editing" width="1000" height="598" data-path="images/open-source/editor.webp" />
</Frame>

## Open it

```sh theme={"dark"}
interactive-demo dev
```

The startup banner prints the editor URL alongside the demo URLs. The index page at `/` lists every demo with an **Edit** link. The direct URL for one demo is:

```text theme={"dark"}
http://localhost:3000/__demo/editor/#/onboarding
```

The slug after `#/` is the demo's folder name under `demos/`.

## The layout

**Header.** The Back arrow, the Inkly logo, the demo's title, the save badge, **Open demo** (the demo's page, in a new tab), and **Share**. Back returns to the page you opened the editor from when that page is on the same host. Otherwise it goes to `/`, the demo index.

**Left: the stage.** The real player, running the demo you are editing, with drag handles on top. Click an annotation to select it. Drag it to move it. Drag the corner handles of an area or blur box to resize it. The player keeps working, so you can click through the demo as a viewer would, and the strip follows along.

**Right: the sidebar.** By default it shows the step strip: every step as a numbered thumbnail, with a dot in the corner when it carries messages.

* Drag a thumbnail by its footer handle to reorder.
* The `+` between thumbnails inserts a step there.
* The `…` menu on a thumbnail opens step settings, duplicates the step, or deletes it. A demo has to keep at least one step.

Selecting an annotation or a cover, or opening demo settings, step settings, or the voiceover panel, swaps the strip for that inspector. The **Steps** breadcrumb at the top goes back.

**The toolbar** under the stage works on the current step:

| Button | What it does |
| - | - |
| **Message** | Adds one of the four variants: cursor, pointer, callout, or area. |
| **Annotate** | Adds a blur box, a text overlay, or a zoom. |
| **Narration** | Opens the voiceover panel. |
| **Settings** | Demo settings, step settings, and **Edit Step Asset** (crop for an image step, trim for a video step). |

**Message** and **Annotate** carry a small count of what the step already has. On a cover step they are disabled, because covers take no annotations. On a video step the toolbar also holds a scrubber for the clip.

## What you can change

### Steps

Add a content step from an image or video, or a cover step (headline, form, or embed). When the demo does not start or end on a cover, the strip also offers **Add intro step** (a headline cover) and **Add outro step** (a closing cover with a Replay button).

Reorder, duplicate, delete, and name each step. Step settings also swap the step's media. A step added in the editor gets `advance.trigger: "auto"` for a content step and `"click"` for a cover.

### Video trim

**Edit Step Asset** on a video step opens a trim dialog. Pick the start and end, and the browser re-records that range into a new video file, with a poster frame, under `assets/`. The step switches to it. The original file stays where it was.

### Messages

Text is written in a small rich-text box and stored as markdown. The inspector holds the variant, the anchor side (or `auto`), text alignment, corner radius, colours, and whether clicking the message advances the step.

### Blur, text, and zoom

Text overlays and blur boxes cover sensitive parts of a screenshot. Position and resize both on the stage.

Adding a zoom puts a rectangle on the stage. Move and resize it over the region you want, and the step gets the matching `transform`. The pill beside it previews the zoomed step, or removes the zoom.

### Voiceover

One panel lists every step's narration. Write the script for a step, then either:

* record audio from your microphone, which is saved into the demo's `assets/` and attached to the step, or
* open the audio picker to choose a file that is already there or upload a new one.

The script box seeds itself from the step's first message when it is empty. **Remove** detaches the audio and clears the step's captions with it.

<Note>
  Generating audio from the script is a host feature, offered only when the host lists voices. `dev` does not, so locally the panel has **Record** and **Asset** only. See [Host capabilities](#host-capabilities).
</Note>

### Cover widgets

A cover holds one widget. The inspector switches between headline, form, and embed, and edits their fields: title, description, image, buttons, and where each button goes. A form's **Submissions** section sets where its values are sent. A `custom` widget is left alone. Edit it in the JSON.

### Demo settings

Demo settings cover the title and subtitle, the player header, the "Built with Inkly" badge, the player controls (full, minimal, or hidden), the primary colour, the canvas background behind the player (theme default, a solid colour, a gradient, or an image, with a blur), and the header logo and its link.

That logo is the demo's own `theme.brand`, drawn in the player's header. The project `brand`, drawn in the page bar around the player, is edited in `interactive-demo.json`. See [Brand and the page header](/open-source/authoring#brand-and-the-page-header).

### Assets

Any media picker in the editor opens the same dialog: the files already under the demo's `assets/`, searchable, plus a drop zone to add a new one.

* A file you drop is written into `demos/<slug>/assets/` and the step references it by that path.
* Uploads are capped at 100 MB.
* If the name is already taken by different bytes, the new file lands as `hero-2.png` and keeps its own path. Steps pointing at the old file are unaffected.
* Removing a file is a file-system job. Delete it from `assets/` yourself, then run `interactive-demo validate` to check that nothing still references it.

### What stays in the file

The editor leaves these to `demo.config.json`:

* chapters
* captions, apart from the ones **Generate** writes
* a step's `duration` and `advance.trigger`
* `chrome.autoplay` and `aspectRatio`
* the `secondary`, `font`, and `radius` tokens
* `custom` widgets

There is one theme and no theme picker. A leftover `theme.preset` is kept as written when you save, and `validate` asks you to delete it.

Deleting a step still keeps chapters honest. The step id is removed from every chapter, a chapter left empty is dropped, and any button that pointed at the deleted step or chapter is retargeted.

## Edits go straight to disk

The editor holds your demo's files in memory and writes the changed ones back into `demos/<slug>/`. Nothing else is touched.

* **Autosave** fires five seconds after your last edit. The badge in the header shows "Unsaved changes", then "Saving", then "Saved".
* **⌘S or Ctrl+S** saves immediately.
* Switching away from the tab or closing it flushes whatever is dirty. A hard reload with unflushed edits asks you to confirm first.
* A failed save says so and retries. Saves are serialised, so an older write can never land on top of a newer one.

Because the file on disk is the real thing, `dev` notices the write like any other. The demo page at `/<slug>/` reloads with your change. Your text editor, `git diff`, and the CLI all see exactly what the editor wrote.

## Your file keeps its shape

This matters if you or your agent hand-wrote `demo.config.json`. The editor writes edits into the shape of the file you wrote:

* Keys keep the order you put them in, and `$schema` stays first.
* A field you never wrote is not added when it still equals the schema's default.
* Array elements are matched by their `id`, so inserting or reordering a step does not shift later elements onto the wrong counterpart.
* A key the schema does not know, such as a note you left next to a step, stays in the file.
* Before writing, the editor parses what it is about to write and compares it against the config it meant to save. Anything that did not survive the round trip is put back explicitly.

The practical result: edit one hotspot's text, and `git diff` shows one changed line.

## The Share dialog

**Share** in the header opens a dialog grouped by how the demo will be used. It shows you commands and snippets. It never uploads or deploys anything.

* **Send the link.** Three steps: `interactive-demo login`, then `interactive-demo publish <slug>`, then paste the link it printed. A link to a `dist/` you deployed yourself works just as well.
* **Frame the page.** **Inline** is the iframe snippet, sized to this demo's own aspect ratio and player header. **Pop-up** is the loader script plus a trigger for HTML, React, Next.js, Vue, or Svelte, with the button label editable. Both stay locked until you paste a link, because a snippet pointing at a placeholder host is worse than no snippet.
* **In your React app.** **Component** is the snippet for rendering the player, or a `DemoModal` pop-up, in your own React tree.

[Sharing and embedding](/open-source/embedding) has the longer version of all of these.

## When the config does not parse

If `demo.config.json` is invalid JSON or fails the schema, the editor replaces the stage with "Editor unavailable" and lists the offending paths and messages. Fix the file in your text editor. The editor polls the file while it is broken and comes back on its own as soon as it parses. It will not overwrite a file it cannot read.

A config with a missing or malformed `id` is not an error. The editor mints one in memory and saves it on the next save.

## Host capabilities

This section is for anyone serving the editor from something other than `dev`.

The editor is a static app. Whatever serves it is its host. On load the editor asks the host what optional features it offers:

```text theme={"dark"}
GET /__demo/editor/capabilities
→ 200 application/json  {}
```

`dev` answers `{}`, meaning none. The editor treats any answer that is not a JSON object the same way: a 404, a network error, or an older CLI that returns the editor page itself.

### Voiceover generation

A host that can synthesize speech advertises it:

```json theme={"dark"}
{
  "voiceover": {
    "voices": [
      { "id": "ashley", "name": "Ashley", "descriptor": "Warm female",
        "country": "United States", "previewUrl": "https://…/ashley.mp3",
        "isDefault": true }
    ],
    "maxChars": 1000
  }
}
```

With that, the voiceover panel gains a **Voice** picker, grouped by country with a sample for each voice, and a **Generate** button per step, which calls:

```text theme={"dark"}
POST /__demo/editor/demos/:slug/voiceover
body  { "stepId": "s1", "text": "…", "voiceId": "ashley" }
→ 200 { "asset": EditorAssetMeta, "sentences": ["…", "…"] }
```

The host synthesizes the speech, stores the audio as an asset of the demo, and returns its entry plus the text split into the sentences it read. The entry has the same shape `POST …/assets` returns, so `asset.path` is what the step references.

The editor sets the step's `voiceover` to that path with the clip's duration in milliseconds, and writes one caption per sentence, timed in proportion to its length. An error answer's `{ "error" }` message is shown under the step. `maxChars` caps the text sent in one request.

### Header links

A host whose pages are not the CLI's can point the header's links:

```json theme={"dark"}
{
  "links": {
    "back": { "href": "/demos/{slug}", "label": "Back to demo" },
    "demo": { "href": "/demos/{slug}" }
  }
}
```

`{slug}` becomes the open demo's slug.

* **Back** returns to the page the editor was opened from when that page is on the same host. Otherwise it follows `back`, or goes to `/` when the host names none.
* **Open demo** follows `demo`, or the demo's page under `dev`.

An `href` must be a path on the host or an `http(s)` URL. Anything else is ignored.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.