# Solveit reference

> Source: <https://gist.github.com/jph00/9e7b444aba5ecf6d14295ba2cee890c3>
> Published: 2026-01-18 02:45:08+00:00

# Solveit Reference

## What is Solveit?

Solveit is a "Dialog Engineering" web application for interactive development. Unlike ChatGPT (pure chat) or Jupyter (pure code), Solveit combines three message types in one workspace: code execution, markdown notes, and AI prompts. Users build solutions incrementally—writing a few lines, understanding them, then continuing—rather than generating large code blocks.

The AI sees the full dialog context (code, outputs, notes, prompts) when responding -- but only those ABOVE the current message. Users can edit any message at any time, including AI responses—the dialog is a living document, not an append-only log.

The dialog is a running ipykernel instance. A "dialog" is like a "Jupyter notebook", and uses a compatible ipynb file format, but provides a superset of functionality (in particular, "prompt messages"). A "message" is like a "Jupyter cell", with additional attributes stored as ipynb cell metadata. Most standard jupyter functionality is supported (including cell magics like `%%js`, `%%html`, `%%bash`, and custom magics via `@line_magic`/`@cell_magic` decorators), except for ipywidgets. Cell magics can be async; if an async magic returns a FastHTML FT component, it's auto-converted to HTML.

## Architecture

- **Instance**: A persistent Linux container (virtual private server) with home dir at `/app/data`. Each user can have multiple instances. Instances have private URLs (for the solveit interface) and public URLs (for hosting apps on port 8000).
- **Dialog**: An `.ipynb` file containing messages. Each open dialog runs its own Python kernel (ipykernel). Dialogs can be organized into folders.
- **Kernel**: A running Python 3.12 interpreter maintaining state. Variables persist between code messages. Each dialog has its own isolated kernel. Kernels keep running when navigating away; must explicitly stop via UI or shutdown. Use `%load_ext autoreload` and `%autoreload 2` to auto-reload modules when files change. The current dialog name is available as `__dialog_name` in the kernel namespace and as the `__DIALOG_NAME` environment variable.

### CRAFT and TEMPLATE Files

**CRAFT.ipynb** files provide reusable AI context and auto-executed code for a folder:
- **Note/prompt messages** are prepended to the AI's context for all dialogs in that folder and subfolders
- **Code messages** are automatically executed in the kernel when opening a dialog


**CRAFT.css** and **CRAFT.js** files can be placed alongside `CRAFT.ipynb`. They follow the same parent-chain hierarchy and are injected as `<style>` and `<script type="module">` tags when a dialog opens. Useful for custom theming or adding client-side behavior without touching the main app.
**TEMPLATE.ipynb** files serve as dialog templates—their cells are prepended to new dialogs created in that folder.

Both use path hierarchy: files from parent folders are included too, letting you build layered configurations (e.g., org-wide settings in root, project-specific in subfolders).

### Optional CRAFTs (CRAFTs Folders)

In addition to `CRAFT.ipynb` files (which are always loaded), you can place notebooks in a `CRAFTs/` subfolder at any level of the folder hierarchy. These are **optional crafts** — they are discovered but not automatically loaded.

The AI always sees optional crafts listed in its context on every prompt: only the first message and the file size are shown, wrapped in `<optional-craft-block>` tags. This gives the AI enough information to decide whether a particular optional craft is relevant to the current task. If so, the AI can call the `load_dialog` tool, which runs all its code cells in the kernel and returns the full contents.

Example structure:
```
project/
├── CRAFT.ipynb          # Always loaded (required)
├── CRAFTs/
│   ├── testing.ipynb    # Optional — AI loads on demand
│   └── advanced.ipynb   # Optional — AI loads on demand
└── my_dialog.ipynb
```

Optional crafts loaded via `load_dialog` are automatically re-imported when the kernel restarts, so you don't need to re-load them manually after a restart. Solveit achieves this by scanning the dialog's prompt history for prior `load_dialog` calls and replaying them during startup.


### AUTORUN Folders

Place notebooks inside an `AUTORUN/` folder at the root of your instance to have them automatically opened and run on startup. Useful for persistent background processes (Discord bots, FastHTML apps, scheduled tasks). Dialogs in `AUTORUN/` folders are exempt from idle shutdown.

## Message Types

| Type | Purpose | Input | Output |
|------|---------|-------|--------|
| `code` | Python execution | Python source | ipynb-format output list (stream, execute_result, display_data, error) |
| `note` | Documentation | Markdown with optional attachments | None |
| `prompt` | AI interaction | User question/instruction | AI response (markdown string) |
| `raw` | Frontmatter/metadata | Raw text (no processing, no variable injection) | None |

The Monaco editor dynamically switches syntax highlighting based on cell magics — `%%html` switches to HTML highlighting, `%%js` to JavaScript, etc. Raw messages also pick up highlighting from magic headers. `Markdown()` objects returned from code cells render with full formatting. Markdown is rendered using mistlefoot (an extension of mistletoe) with support for header attributes, todo lists, and other extensions.


### Message ID Hyperlinks
Use `#_msg_id` syntax in markdown to create clickable links to specific messages within a dialog. You can also link to messages in other dialogs by including the dialog path: `#path/to/dialog/_msg_id`. Clicking a cross-dialog link opens that dialog and scrolls to the target message.
### Output Editing
- **Prompt outputs**: Editable via `n` key or clicking title bar. Can undo AI responses with `Cmd+Z`.
- **Code outputs**: NOT directly editable. Clear with `Backspace`, or re-run message.

## AI Context System

When a user sends a prompt, solveit builds context for the AI:

1. **Message Collection**: Gather all messages up to and including the current prompt
2. **Filtering**: Exclude messages where `skipped=True` (hidden via `h` key or 👁️ button). Hiding propagates to children when applied to a collapsed section.
3. **Truncation**: If total tokens exceed model limit, oldest non-pinned messages are dropped
4. **History**: Messages grouped into chunks by prompt boundaries and LLM history recreated

### Pinning (`p` key)
Pinned messages serve three purposes:
- **Context preservation**: Pinned messages stay available even when long dialogs get truncated
- **Export control**: Pinned messages are excluded from exports (Python files, gists, published dialogs)
- **Bulk management**: Pinning a collapsed header pins all child messages too

Use for API docs, examples, and reference materials that should persist but not be exported.

**Context Window Implications**: The AI only sees messages ABOVE the current prompt. Working at the bottom of a dialog includes MORE context (all messages above). Working higher up includes LESS context. Adding messages earlier in the dialog doesn't increase context for prompts below them.

### Variable and Expression Injection
Users can inject Python values into AI context using **$`name`** syntax in prompts. This works with:
- **Variables**: `$`myvar`` — injects the current value
- **Expressions**: `$`len(items)`**, `$`df.shape`**, `$`random()`** — evaluated fresh each prompt

When the AI sees the prompt, values are fetched from the kernel and included in a separate section of the chat history. Expressions are re-evaluated on every prompt, so `$`random()`** gives a new value each time.

Images stored in variables (as bytes) are also supported and sent as base64, as are message image attachments if referred to in the content in a link with a `#ai` hash.

## Tools System

Tools let the AI call Python functions to accomplish tasks. This is the mechanism for AI to read files, search code, modify the dialog, etc.

### Exposing Tools
Users declare tools in any message using & followed by `` `function_name` `` or `` `[func1, func2, func3]` ``. Object methods can be exposed using dotted syntax: `` & `obj.method` `` — dots are converted to dashes for API compatibility and back when calling. Solveit then:

1. Finds the function in the kernel's namespace
2. Extracts the schema from type annotations and docstring
3. Sends the schema to the AI via standard tool calling mechanisms
4. When AI calls the tool, solveit executes `function_name(**kwargs)` in the kernel
5. Returns the result to the AI

### Tool Requirements
A function becomes a valid tool if it has:
- Type annotations for ALL parameters
- A docstring describing what it does
- Fastcore param docments are passed in the json schema if present

```python
def get_user(user_id: int) -> dict:
    "Fetch user data by ID."
    return {"id": user_id, "name": "Alice"}
```

### Built-in Tools (dialoghelper.stdtools)
When `use_tools` setting is enabled (🔧 wrench icon), tools from `dialoghelper.stdtools` are added. This includes:
- **Python execution**: `pyrun` — safe sandboxed access to the running interpreter via [safepyrun](https://github.com/AnswerDotAI/safepyrun) (see below)
- File tools: `view`, `create`, `insert`, `str_replace`, `strs_replace`, `replace_lines`. Enhanced variants with regex filtering: `file_str_replace`, `file_strs_replace`, `file_replace_lines`, `file_insert_line`, `file_del_lines`, `file_pyrun`
- Search tools: `rg`, `sed`, `ast_grep`
- Dialog tools: `add_msg`, `update_msg`, `del_msg`, `find_msgs`, `read_msg`, `read_msgid`, `view_msg`, `view_dlg`, `dialog_link`, `create_or_run_dialog`, `stop_dialog`, `toggle_comment`, `toggle_bookmark`, `toggle_header`, `copy_msg`, `paste_msg`
- Inspection tools: `symsrc`, `symval`, `symdir`, `symtype`, `symlen`, `symslice`, `symsearch`, `symnth`. `symtype` and `symval` accept comma-separated symbol lists.
- Utility tools: `doc` returns a function's full signature with parameter docments, useful for checking how to call any function before using it
- Web tools: `read_url`, `web_answer`, `search`, and `searches`. Models without native web search (Kimi, DeepSeek, Qwen) get fallback search tools so SolveitAI can search the web when needed; no setup required.

### The `pyrun` Tool
Gives the AI safe, direct access to the running Python interpreter via [safepyrun](https://github.com/AnswerDotAI/safepyrun), an allowlist-based Python sandbox. The AI can write and execute arbitrary Python — iterate over data, filter, parse, define helpers — all autonomously within a single tool loop. Access includes: all non-callable globals, registered tools, most builtins (`dict`, `list`, `Path`, comprehensions, `sorted`, etc.). Callable object attributes are gated — only methods registered via `allow()` can be called. Symbols named with a trailing `_` (e.g. `result_`) are exported back to the namespace. Async/await is supported. `print()` and warnings are captured and returned alongside the result. File writes are controlled via `ok_dests` — a list of directory prefixes where writes are permitted; writes elsewhere raise `PermissionError`.

### dialoghelper Module
The `dialoghelper` module provides async functions for programmatic dialog manipulation:
- `await add_msg(content, msg_type='note')` - Add message below current (supports `placement='add_after'`/`'add_before'` with `id` in other dialogs via `dname`)
- `await add_prompt(content, dname=None)` - Add and run a prompt, optionally in another running dialog. Returns the AI response text when complete. Useful for running sub-prompts in parallel with different context.
- `await update_msg(id, content=None, ...)` - Update existing message. `log_changed=True` inserts a diff note.
- `await del_msg(id)` - Delete message. `log_changed=True` inserts a note showing deleted content.
- `await find_msgs(re_pattern)` - Search messages by content. `headers_only=True` returns just headings. `header_section="## Name"` returns a section and its children. `before`/`after`/`context` params include surrounding messages (like `grep -B`/`-A`/`-C`), which helps the AI understand the context of matched messages.
- `await read_msg()` - Read message at index
- `await read_msgid(id)` - Read message by ID. `add_to_dlg=True` copies content into current dialog.
- `await view_msg(id)` - View message content with line numbers (returns just content string; use `read_msgid` for full metadata)
- `dialog_link(path, msg_id)` - Generate clickable HTML link to a dialog. Path is optional — with just `msg_id`, links to a message in the current dialog.
- `create_or_run_dialog(name)` - Create a new dialog, or set an existing one running
- `stop_dialog(name)` - Stop a running dialog kernel
- `toggle_comment(id)` - Toggle line comments on code message(s)
- `url2note(url)` - Fetch URL and add as markdown note
- Browser integration: `js_eval(expr)` evaluates a JS expression and returns the result, `js_run(code)` runs JS that calls `done()` when finished, `add_mod(code)` injects a script module, `event_once(evt)` provides safe one-shot browser event handling. All have `_a` async variants.
- Message editing tools (`msg_str_replace`, `msg_replace_lines`, `msg_insert_line`, `msg_del_lines`, `msg_strs_replace`, `msg_pyrun`) all support `update_output=True` to edit message output instead of content, and `log_changed=True` to record diffs. String/line tools also support `re_filter` (like `g/pattern/` in vi) and `start_line`/`end_line` for range limiting. All `msg_` editing tools have `file_` counterparts for editing regular text files.

**Note**: Nearly all dialoghelper tools are async. Use `await` when calling from code cells.

When tools are enabled, SolveitAI can call available tools directly. If tools are disabled, the AI writes code or instructions for you to run yourself.

### Tool Result Handling
Tool results are truncated if too long, to 2000 chars. The `ToolResponse` class can wrap results with metadata. Code eval and tool call results include the Python type of return values in metadata, helping the AI distinguish between different types. Tools can call `input()` to prompt the user mid-execution — the prompt appears in the message output and the tool waits for a response. This enables permission gates and interactive feedback within a single AI tool loop. Input form buttons display underlined access-key labels and respond to bare keypresses, so you can respond without reaching for the mouse. When a sandbox `PermissionError` is raised by a tool call, Solveit stops the AI loop and adds a new code message containing the blocked call. You can run it yourself, edit it first, or `allow()` it so the AI can do that kind of thing directly next time; `Cmd+Shift+.` runs the message and lets the AI immediately see the result and continue. This replaces the modal "ask for permission" flow used by tools like Claude Code and Codex CLI.

## File System

- `datapath`: `/app/data` (production) or current working directory (local/test)
- Dialogs stored at: `{datapath}/some/folders/{dialog_name}.ipynb`
- Static files served at: `/static/some/folders/` → maps to `{datapath}/some/folders/`
- Per-dialog Python modules: `{datapath}/some/folders/{dialog_name}.py` (auto-generated from exported messages)

### Dialog Persistence
Dialogs are saved as Jupyter notebooks (`.ipynb`). Solveit converts messages to notebook cells when saving:
- `note`/`prompt` → markdown cells
  - Prompt responses stored by appending to message content with separator
- `code`/`raw` → code/raw cells
- Metadata stored in cell metadata (skipped, pinned, collapsed states, etc.)

## Keyboard Shortcuts (Selection Mode)

When not editing (blue border shows selected message):
- `↑/↓` or `j/k`: Navigate messages (`Shift` extends multi-selection)
- `a/b`: Add message above/below
- `x/c/v`: Cut/copy/paste messages (`c` also copies to system clipboard as markdown). Paste also works on closed dialogs. Works with multi-select.
- `,`: Copy message input to clipboard
- `.`: Copy message output to clipboard
- `h`: Toggle hidden from AI (`skipped`). Works with multi-select.
- `p`: Toggle pinned. Works with multi-select.
- `e`: Toggle export. Works with multi-select.
- `1-6`: Set heading level (converts to `## Heading` etc.)
- `Enter`: Edit selected message
- `Cmd+Enter`: Run code or send prompt (stay on message)
- `Shift+Enter`: Run and go to next message
- `Opt+Enter`: Run and create new message
- `Shift+R`: Restart kernel
- `Shift+T`: Open terminal
- `Shift+S`: Stop AI/code execution
- `Shift+A/B`: Run all code above/below (skips prompts)
- `Cmd+Slash`: Toggle comments on code messages. Works with multi-select.
- `w`: Extract fenced code blocks to new code messages
- `Shift+W`: Extract AI tool calls to new code messages (useful for inspecting, editing, or re-running what the AI did)
- `n`: Edit AI response (output) of prompt message
- `Cmd+Shift+J/K/L/;`: Switch to code/note/prompt/raw mode
- `g`: Wrap selected messages in a collapsible section (or unwrap if on a section start header). Works with multi-select.
- `←/→`: Collapse/expand section (based on markdown headers)
- `r`: Re-run all code messages in dialog (skips prompts)
- `m`: Copy code blocks from AI response
- `d`: Duplicate dialog (opens duplicate with bomb icon 💣 to discard)
- `s`: Save dialog (commits to git if versioning enabled)
- `0`: Jump to last edited message
- `/`: Focus bottom editor/input field
- `Shift+Esc`: Focus back on message list
- `Home/End`: Jump to first/last message
- `Cmd+A`: Select all messages
- `F`: Open find/search modal (`Shift+F` to clear filters)
- `Backspace`: Clear outputs from selected message(s). Works with multi-select.
- `i/o`: Toggle collapse on message input/output. Works with multi-select.
- `q`: Duplicate message. Works with multi-select.
- `Shift+O`: Clamp output height

### Multi-Selection
Use `Shift+↑/↓` or `Shift+j/k` to extend selection. `Cmd+A` selects all. Operations marked "Works with multi-select" above apply to all selected messages at once. Shift up/down moves the entire selected block as a unit.

### Editor Shortcuts
When editing a message:
- `Shift+Tab`: Show parameter info/documentation
- `Cmd+I`: Manually trigger symbol dropdown
- `Ctrl+Shift+Minus`: Split message at cursor(s)
- `Cmd+Shift+Comma`: Screenshot last output into context
- `Cmd+Shift+Period`: Super completion (prefill in prompts) or super edit (with selection)
- `Cmd+Shift+S`: Save file (in full-page editor view)
- `Ctrl+Shift+Right/Left` (Mac) / `Alt+Shift+Right/Left` (Windows): Smart select — progressively expands or shrinks the selection by semantic scope (variable → expression → statement → block → function). Very useful for quickly selecting exactly the right amount of code.
- `ESC`: Save changes and exit edit mode
- `→` (Right Arrow): Accept ghost text suggestion
- `Tab`: Select from symbol dropdown

## AI Modes

Set per-dialog, affects AI behavior:

| Mode | Behavior | Ghost Text |
|------|----------|------------|
| `learning` | Pedagogical, asks clarifying questions, explains step-by-step | Disabled by default |
| `concise` | Minimal responses, compact code, no boilerplate | Enabled |
| `standard` | Default Claude behavior, verbose explanations | Enabled |

## Ghost Text / Completions

### Ghost Text
Inline AI suggestions as you type (faded gray text). Uses smaller/faster FIM model. Press `→` to accept.

### Super Completions (`Cmd+Shift+.`)
Uses main model for larger completions. Can prefill AI response start in prompt messages.

### Super Edits
Select text + `Cmd+Shift+.` + instruction → AI rewrites selection. Uses `edit_call()` with GPT-4.1.


### Path Autocomplete
File and dialog paths autocomplete as you type in note and prompt messages, making it easy to reference files in your prompts.
## Export System

### Message Export
Press `e` on code message to mark `is_exported=True`. Adds `#| export` marker. When dialog is saved, exported messages are combined into `{dialog_name}.py`. Splitting an exported message preserves the export flag on both halves. Merging messages exports the result if any source message was exported. Image attachments are preserved when splitting and merging—they are distributed to the correct resulting messages based on which attachments are referenced in each message's content.

### Publish
See "Publishing and Sharing" section for full details. Creates shareable view at `share.solve.it.com`. Pinned messages excluded.

### Download Options
- `.ipynb`: Full notebook
- `.py` (Full Script): All code messages concatenated  
- `.py` (Exported): Only `is_exported=True` messages
- `.md`: Markdown format
- GitHub Gist: Upload to gist

## FastHTML Integration

Solveit is built with FastHTML. Users can also build FastHTML apps inside dialogs:

```python
from fasthtml.common import *
from fasthtml.jupyter import JupyUvi

app = FastHTML()
rt = app.route

@rt
def index(): return H1("Hello")

srv = JupyUvi(app)  # Serves on port 8000 → accessible at public URL
```

`JupyUvi` runs uvicorn in a way compatible with notebook contexts. Changes to routes take effect immediately without restart.

**Note**: The root route `/` is used by Solveit itself. Use different routes like `@rt def homepage()` for your app's entry point.

### Rendering Components
- `render_ft()` - Render FastHTML components as HTML in message outputs
- `show()` - Alternative with optional iframe wrapping and HTMX processing

## Feature Flags (Secrets)

Stored in `solveit_settings.json` under `secrets` key. Accessed via `get_secret(name)`.

| Flag | Values | Effect |
|------|--------|--------|
| `USE_KATEX` | `1`/`true` | Enable LaTeX with safe delimiters (`$$`, `\[`, `\(`) |
| `USE_KATEX` | `dollar` | Also enable `$...$` inline math |
| `USE_VIM` | any truthy | Vim keybindings in editor |
| `LITELLM_USAGE` | any truthy | Append token usage and cost summary to each prompt output |

## Image Handling

### Pasted Images
Paste into note/prompt → stored as message attachment → visible to AI automatically.

If the clipboard contains both an image AND HTML (e.g., from a DOM screenshot tool), solveit automatically appends the HTML in a fenced code block after the image link—useful for sharing UI screenshots with their underlying markup.

Attachments are stored in notebook message metadata (standard Jupyter format), referenced as `![name](attachment:uuid)`. Attachments are message-specific—cannot be referenced from other messages.

### Markdown Images
- `![alt](path)` in notes → displays but NOT sent to AI
- `![alt](path#ai)` → displays AND sent to AI
- For reusable images, save to `/app/data/` and use `/static/` URLs

### Code Output Images
Images in code output (matplotlib, PIL) are captured and can be sent to AI.

### Static Images
Use `/static/` URLs to reference files in `/app/data/`. Example: `Img(src='/static/myapp/image.png')` for FastHTML, or `![alt](/static/image.png)` in markdown.

## Collapsible Sections

Markdown headings (`# H1` through `###### H6`) create collapsible sections:
- `heading_collapsed=True` on heading message → children have `hidden=True`
- Token count shows: `heading_tokens + hidden_content_tokens`
- Cut/copy on collapsed heading includes all children
- `←` jumps to section start, `→` to section end

### Section Wrapping (`g` key)

Select one or more messages and press `g` to wrap them in a collapsible section. This creates a pair of start/end section header messages around the selected messages. The section level is auto-detected from the surrounding context. Press `g` again on the start header to unwrap (remove the start/end pair). This is useful for organizing long dialogs into logical groups you can collapse and expand.

## Token Counting

Token counts estimated and used for:
- Display in UI (message headers show token counts)
- Context truncation decisions
- Image tokens calculated separately

## Error Handling

- Errors trigger `msg.uncollapse()` to show the problematic message
- Missing variables, broken tools, oversized values, and truncated context are collected as warnings and passed to the AI via `important_prompt`. The AI continues with what's available and informs the user about issues, rather than failing entirely.

## Settings

Stored in `{datapath}/solveit_settings.json`. Common settings:
- `use_tools`: Enable standard tools (🔧 wrench icon in navbar)
- `use_thinking`: Enable extended thinking mode. Click the 🧠 brain icon or press `Cmd+Shift+D` to toggle between standard and reasoning models. Hover over the brain icon to open a popover where you can choose standard and reasoning models separately, and set effort levels for each. Available models include Anthropic (Sonnet/Opus) and Kimi.
- `use_diff`: Enable diff mode (shows changed messages as unified diffs)
- `default_code`: After AI response, default to code mode (not prompt)
- `idle_timeout_hours`: Hours before idle dialogs are automatically closed (default: 3). Dialogs in `AUTORUN/` folders are exempt.

## Feature Flags (Secrets)

Stored in `solveit_settings.json` under `secrets` key. Accessed via `get_secret(name)`.

| Flag | Values | Effect |
|------|--------|--------|
| `USE_KATEX` | `1`/`true` | Enable LaTeX with safe delimiters (`$$`, `\[`, `\(`) |
| `USE_KATEX` | `dollar` | Also enable `$...$` inline math |
| `USE_VIM` | any truthy | Vim keybindings in editor |
| `LITELLM_USAGE` | any truthy | Append token usage and cost summary to each prompt output |

## UI Controls

### Header Bar

From left to right:

**Left section:**
| Element | Description |
|---------|-------------|
| 📚 **solveit** | Logo/home link - closes dialog, returns to dialog list |
| 🟢 Green dot | WebSocket connection indicator |
| Dialog name | Editable - click to rename dialog |

**Mode dropdown:**
| Element | Description |
|---------|-------------|
| `learning`/`standard`/`concise` | AI mode selector - affects response style and ghost text |

**Toggle group (blue when active):**
| Icon | Tooltip | Setting | Shortcut |
|------|---------|---------|----------|
| `<>` | "Default new message type to code?" | `default_code` | — |
| 🔧 | "Include dialoghelper standard tools in prompts?" | `use_tools` | — |
| 🧠 | "Toggle thinking / model chooser (hover)" | `use_thinking` + model selection | `Cmd+Shift+D` |
| Diff | "Toggle diff mode" | `use_diff` | — |

**Action buttons:**
| Icon | Tooltip | Action | Shortcut |
|------|---------|--------|----------|
| ? | "Keyboard Shortcuts" | Open info modal | `?` |
| Git | "Checkpoint" | Create git checkpoint | `Shift+C` |
| Share | "Share your dialog" | Publish to share.solve.it.com | — |
| Cloud ↑ | "Upload File" | Open upload modal | — |
| ⊗ | "Stop" | Stop AI/code execution | `Shift+S` |
| ▶ | "Run all" | Run all code messages | `r` |
| ↺ | "Restart" | Restart Python kernel | `Shift+R` |
| ⚙ | "Settings" | Open settings modal | — |
| Copy+ | "Duplicate Dialog" | Duplicate and open | `d` |
| 💣 | "DELETE and close" | Delete duplicate (hidden until duplicated) | — |
| Terminal | "Open Terminal" | Open terminal in new tab | `Shift+T` |
| TOC | "Contents" | Toggle sidebar | `Ctrl+Shift+V` |
| ↗ | "Dialogs" | Open dialog list in new tab | — |

### Message Bar

Each message has a header bar for input (and optionally output). Click the header text to edit. On mobile, the button bar collapses behind a ☰ hamburger menu.

**Left side:**
| Element | Description |
|---------|-------------|
| Message type | `Code`, `Note`, `Prompt`, or `Raw` |
| Token count | Estimated tokens for this message |

**Button bar (left to right):**
| Icon | Tooltip | Shortcut | Action |
|------|---------|----------|--------|
| 📋 | "Copy" | `,` | Copy message to clipboard |
| ▷ | "Run Message" | `Cmd+Enter` | Execute code or send prompt (code/prompt only) |
| 👁 | "Toggle AI visibility" | `h` | Hide/show message from AI context |
| 🔖 | "Toggle message export" | `e` | Mark for export to .py file |
| 🗑 | "Delete Message" | `Shift+D` | Delete this message |
| ↑⚡ | "Delete above" | — | Delete all messages above (confirms) |
| ↓⚡ | "Delete below" | — | Delete all messages below (confirms) |
| ↳ | "Run Above" | `Shift+A` | Run all code messages above |
| ↲ | "Run Below" | `Shift+B` | Run all code messages below |
| ↑ | "Add Above" | `a` | Insert new message above |
| ↓ | "Add Below" | `b` | Insert new message below |
| 📖↑ | "Shift up" | — | Move message up (works with multi-select) |
| 📖↓ | "Shift down" | — | Move message down (works with multi-select) |
| 💬+ | "Add copy of message" | `q` | Duplicate this message |
| 🔀 | "Merge with message below" | `Shift+M` | Combine with next message |
| ↔ | "Split message" | `Shift+Minus` | Split into multiple messages (regular notes: at headings, code blocks, and paragraph boundaries; notes with only one fenced block: using ast-grep blocks; code: using python ast) |
| 📌 | "Toggle message pin" | `p` | Pin/unpin message |
| 🔗 | "Copy link to message" | — | Copy URL with message anchor |
| ⌃ | "Collapse contents" | `i` | Collapse/expand input |
| ↗ | "Open in new tab" | `t` | View and edit message in standalone Monaco editor tab |

### Message Output Bar

Only code and prompt messages have outputs. Click the header text to edit (prompt outputs only—code outputs are not editable).

**Left side:**
| Element | Description |
|---------|-------------|
| Label | `Output` (code) or `Assistant` (prompt) |
| Token count | Estimated tokens for this output |

**Button bar (left to right):**
| Icon | Tooltip | Shortcut | Action |
|------|---------|----------|--------|
| 📋 | "Copy" | `.` | Copy output to clipboard |
| 📋 | "Copy code" | `m` | Copy fenced code blocks to clipboard |
| 💬+ | "Add fenced block messages" | `w` | Extract fenced blocks to new code messages |
| 💬+ | "Add tool call messages" | `Shift+W` | Extract AI tool calls to new code messages |
| ✨ | "Re-run AI" | — | Re-send prompt to AI (prompt outputs only) |
| ⌃ | "Collapse contents" | `o` | Collapse/expand output |
| ↗ | "Open in new tab" | `t` | View and edit output in standalone tab |


When copying AI outputs (via `.`, `m`, or `c`), tool call details, tool extraction controls, and brain emojis (🧠) are stripped for clean clipboard content.
### Variable Sidebar

Toggle with `Ctrl+Shift+V` (or the TOC icon in the header bar). Displays collapsible sections:
- **Bookmarks**: Numbered bookmarks (1–9) with click-to-navigate
- **Headings**: Markdown heading hierarchy with exported/hidden indicators
- **Variables**: Live kernel variables
- **Functions & Classes**: All defined functions and classes (including async) with signatures extracted via `codesigs`. Click to navigate to the definition message. Populates on dialog open/reset, not just after running code.

## Terminal

Press `Shift+T` to open an integrated terminal in the current dialog's folder. Useful for:
- File operations (`ls`, `mv`, `rm`, etc.)
- Git commands
- Package installation (`pip install`)
- Running shell scripts

## Find/Search

Press `F` to open find panel (selection mode, not editing). All filtering and highlighting happens client-side for instant responsiveness. Features:
- Regex search across message content and outputs using the CSS Custom Highlight API
- Filter by message type (colored buttons match UI colors)
- Filter by: changed in git diff, errors, pinned, bookmarked, exported
- Hidden messages (skipped with `h`) are excluded from results by default
- `Shift+F` clears all filters and restores heading collapse state
- `Enter` in the search box returns focus to the message list; `Escape` closes the panel
- `headers_only` and `header_section` parameters available programmatically via `find_msgs`, along with `before`/`after`/`context` for surrounding messages

## Dialog Duplication and Branching

Press `d` to duplicate current dialog. The duplicate preserves the dialog options, opens immediately, and gets a bomb icon (💣) in the navbar. This supports a "branching" workflow:
- Duplicate before experimenting with risky changes
- If the branch is useful, keep it
- If not, click the bomb icon to delete and return to the original

## Git/Versioning System

Solveit integrates git for version control at the folder level.

### Enabling Versioning
Click "Enable versioning" button in any folder to initialize a git repo there. Can create nested repos (repo-in-repo) at any directory level.

### Saving and Committing
- Press `s` to save the dialog and auto-commit with Solveit's distinctive message template
- Dialogs auto-save on most actions (submit, cancel edit, etc.) but do not commit without `s` or checkpoint

### Checkpoints
Click "checkpoint" button (appears after versioning enabled) to create a manual commit with a custom message. Useful for marking significant milestones.

### Squashing
Solveit automatically squashes commits that match its auto-save message template. This keeps history clean—many small saves become one commit, while checkpoint commits with custom messages are preserved.

### Diff Mode
Toggle the diff icon in the header bar to highlight messages that have changed since the last git commit. Changed messages display as syntax-highlighted unified diffs (additions in green, deletions in red). New messages are tagged as added. The diff auto-refreshes when HEAD changes. Requires versioning to be enabled. Combine with the "changed" filter in Find/Search (`F`) to see only modified messages with their diffs. Clicking to edit a diff-toggled message opens a side-by-side Monaco diff editor, focused at the first change. Click the `→` between sides to revert individual changes.

### Publish Button
Creates a read-only shareable view at `https://share.solve.it.com/d/[hash]`. Pinned messages are excluded from published output. Brain emoji chains (🧠) from AI thinking are stripped from published output. The scrollspy sidebar is visible on medium-width screens and above.

URL suffixes:
- `.html` - Blog-style rendered view
- `.md` - Raw markdown

## Dialog index page

The dialog index page shows dialogs, folders, and files in a grid layout with name, size, and last modified date. A clickable breadcrumb path sits at the top. File actions (duplicate, download, delete) are available via a `⋮` dropdown menu on each row.

The page has two main panels: the file grid on the left, and a **Recent Dialogs** sidebar on the right showing your last 40 opened dialogs. Press `Shift+1` through `Shift+9` to quickly jump to a recent dialog.

**Type-to-filter**: Just start typing on the index page to fuzzy-filter the file list by name, with matching text highlighted. No need to click a search box first. Fuzzy matching is case-aware: lowercase letters match after word boundaries and separators, uppercase letters match anywhere. `Backspace` deletes one character, `Escape` clears the filter entirely. If fuzzy matching yields no results, it falls back to exact substring matching.

Other features of this page:
- "Show all files" toggle to see non-ipynb files (editable in Monaco)
- Secrets management (API keys stored as environment variables)
- Running dialogs (can shut down here)
- Upload files/folders (zip and unzip for folders)

### Keyboard Shortcuts

| Key | Action |
|-----|--------|
| `↑/↓` | Navigate rows |
| `Enter` | Open dialog/folder |
| `N` | Focus "new dialog" input |
| `T` | Open terminal |
| `D` | Delete selected |
| `C` | Duplicate selected |
| `?` | Show keyboard shortcuts |
| `/` | Focus search/filter input |
| `Shift+1-9` | Open recent dialog by number |
| `Shift+Backspace` | Navigate up one directory |
| `Shift+S` | Toggle "Show all files" |
| `Backspace` | Delete one filter character |
| `Escape` | Clear filter |
| Any letter/digit | Type-to-filter dialogs and files (with highlighting) |


### Secrets
Add name-value pairs in Secrets section. Common secrets:
- `ANTHROPIC_API_KEY`, `OPENAI_API_KEY` - LLM API access
- `AOC_SESSION` - Advent of Code session cookie
- `USE_KATEX`, `USE_VIM`, `LITELLM_USAGE` - Feature flags (see Feature Flags section)

Secrets load when opening a dialog or terminal; add secret then open new dialog to use it.

### Show All Files
Toggle in dialog list view to see all files, not just `.ipynb` dialogs. Enables:
- Click text files to open in Monaco editor (VS Code-based)
- Download files via three-dot menu
- Append `#L<n>` to a file editor URL (e.g. `#L50`) to scroll directly to that line number

## Folder Operations

- Create folders: `mkdir` in terminal, or create dialog with path like `newfolder/dialogname`
- Move dialogs to folders: Rename dialog to include folder path (e.g., `myfolder/mydialog`)

## Dashboard

The dashboard at `solve.it.com/dashboard` provides:
- Instance management (start/stop/restart; rename public instance and get URL; share; allow guests)
- Live session times for courses
- Version updates (stop → wait → start to upgrade)

### Instance Sharing (Dashboard)
- **Copy Public URL**: Get URL for apps running on port 8000 (one URL per instance, all dialogs share it)
- **Share button**: Add collaborators by email (requires Solveit account)
- **Lock icon (Allow Guests)**: Enable public access to entire instance without authentication

## Raw Message Type

Raw messages (`Cmd+Shift+;`) are for content that shouldn't be processed:
- Large documents as AI context (no sidebar headings, no variable injection)
- Quarto/YAML frontmatter
- Content with special characters that might interfere with markdown

Access any message content programmatically in dialoghelper: `read_msg()['msg']['content']` from code message below it.
