# Tulis — Skill Guide for AI Agents

> You are an automation agent driving **Tulis** (https://tulis.app), a
> browser-only markdown editor. There is **no backend and no REST API** — Tulis
> stores everything locally in the browser. This guide is everything you need to
> create and edit notes reliably. Read it once and you can operate the whole app.

## Golden rules

1. **Do not type content character-by-character.** Synthetic typing races the
   input-method engine and silently drops characters (especially CJK / long
   text). Use the URL or `window.tulis.setContent()` instead.
2. **Do not write to the `<textarea>` DOM directly.** Raw `value` assignments
   never reach the app's state, so the note is never saved. Use the bridge.
3. **Read state from `data-*` attributes**, not screenshots.

## 1. Create a note in one navigation (preferred)

Navigate to `/note/new` with query params. The note is created and the editor
opens immediately — no preview or confirm step.

```
/note/new?title=<urlencoded title>&content=<urlencoded markdown>
```

- `content` — markdown body. If omitted/over ~1 MB it is ignored.
- `title` — optional. If omitted, the title is taken from the first heading.
- Example: `/note/new?title=Weekly%20review&content=%23%20Goals%0A%0A-%20Ship%20v2`

This is the most reliable way to put content into Tulis. Prefer it whenever you
are starting a new note.

## 2. Drive the open editor: `window.tulis`

Defined **only while a note editor is open**. Feature-detect with
`window.tulis?.version`.

```js
window.tulis.version            // number — schema marker (currently 1)
window.tulis.setContent(md)     // replace the note BODY (front matter is kept)
await window.tulis.save()        // force-save now (skips the 10s auto-save debounce)
window.tulis.getState()         // { noteId, mode, saveStatus, title, wordCount, charCount }
```

Reliable edit-and-save flow:

```js
window.tulis.setContent('# Title\n\nBody written through React state.')
await window.tulis.save()
// confirm it persisted:
//   document.body.dataset.saveStatus === 'saved'
```

`setContent` routes through the same path a human edit uses, so the write is
rendered, auto-saved, and undoable.

## 3. Read state without screenshots

| Where to read | Values |
| --- | --- |
| `document.body.dataset.saveStatus` | `saved` · `saving` · `unsaved` |
| `document.body.dataset.editorMode` | `visual` · `raw` · `read` |

## 4. Stable selectors (`data-testid`)

Target these instead of CSS classes or visible label text:

| `data-testid` | Element |
| --- | --- |
| `new-note` | "New Note" button |
| `rename-note` | Title — click to edit it inline |
| `title-input` | Title input while renaming (text auto-selects on focus) |
| `mode-read` / `mode-visual` / `mode-raw` | Editor mode toggle |
| `raw-textarea` | Raw markdown textarea |
| `save-status` | Save indicator (also exposes `data-status`) |

## Editor modes

- **visual** — live WYSIWYG (TipTap). Rich text, slash commands.
- **raw** — plain markdown in a textarea.
- **read** — rendered, read-only preview.

Switch with the `mode-*` buttons. Mode changes flush any pending save.

## Quick recipe

```js
// 1. Create with content already in place:
location.assign('/note/new?title=Notes&content=' + encodeURIComponent('# Hi\n\nBody'))
// 2. ...editor loads. To revise the body later:
window.tulis.setContent('# Hi\n\nRevised body')
await window.tulis.save()
// 3. Verify:
document.body.dataset.saveStatus === 'saved'
```

That's the whole skill. Tulis keeps all data in the browser (IndexedDB), so the
note lives on the device that created it — there is nothing to upload.
