# Agents Index

This file contains the concatenated content of all cards to help LLMs understand the available documentation.



---

# Introduction to Deck (/cards/introduction.md)

# Introduction to Deck

Deck turns a directory of Markdown into a scalable, zero-config component playground and documentation site.

It is designed to handle hundreds or even thousands of documentation files without a slow initial load time, making it ideal for large projects and component libraries.

## Core Features

- **Scalable Backend:** Uses **IndexedDB** to store documentation content on the client-side, so the browser only loads the content it needs, when it needs it.
- **Precompiled Search:** A published deck ships a search index built at publish time and downloaded before any card. The first search covers the whole deck, not just the part that happens to have loaded.
- **Priority Loading:** A deep-linked card arrives first, cards matching the search on screen arrive next — including a search typed while the rest of the deck is still downloading — and everything else fills in behind them.
- **Runs Anywhere:** `deck-dev` needs no bundler and no config. Plugins are also available for Vite, `@web/dev-server`, and webpack-dev-server / Rspack.
- **Static Site Generation:** A `deck-build` command generates a fully static, production-ready site that can be hosted on any static hosting provider.
- **`<deck-demo>`:** A powerful custom element for embedding live, stateful, and hot-reloading component demos directly in your documentation.
- **Agent-Readable:** Every build writes `agents.md` and `agents.html` — the whole deck as one document, with demo sources inlined — for LLM agents that cannot run a single-page app.
- **Offline Support:** After the first visit, the entire site shell and all visited cards are cached for offline use. Live demos that have been previously viewed will also work offline.


---

# Getting Started (/cards/getting-started.md)

# Getting Started

A Deck project is typically created as a sub-project to document a larger library or application. It's common to create the deck inside a subdirectory like `/deck` or `/docs` within your main project.

This guide assumes you are setting up a new deck in a subdirectory.

## 1. Install

```bash
npm install --save-dev @3sln/deck
```

That is the only dependency. Deck brings its own dev server and its own bundler.

## 2. Configure Your Project

In your `package.json`, add scripts and a `@3sln/deck` configuration block. You must provide a `title` for your documentation site.

```json
{
  "name": "my-cool-project-docs",
  "private": true,
  "type": "module",
  "scripts": {
    "dev": "deck-dev",
    "build": "deck-build"
  },
  "devDependencies": {
    "@3sln/deck": "^0.0.14"
  },
  "@3sln/deck": {
    "title": "My Cool Project"
  }
}
```

## 3. Add Content

Create your documentation files using Markdown (`.md`). You can organize them in any directory structure you like.

```
my-project/
├── deck/  <-- Your Deck project lives here
│   ├── docs/
│   │   ├── introduction.md
│   │   └── components/
│   │       └── button.md
│   └── package.json
└── src/ < -- Your main project code
```

## 4. Run the Dev Server

Start the development server from within your deck subdirectory and you're ready to go!

```bash
cd deck
npm run dev
```

## 5. Publish

```bash
npm run build
```

`deck-build` writes a static site to `out/`, ready for any file host.

## Using a Dev Server You Already Have

`deck-dev` is the zero-config option, but a deck can also run inside a dev server your project already uses. Each of these is a drop-in replacement for step 4.

### Vite

```javascript
// vite.config.js
import {defineConfig} from 'vite';
import deck from '@3sln/deck/vite-plugin';

export default defineConfig({plugins: [deck()]});
```

Deck uses Vite's own HMR socket here rather than opening a second one.

### @web/dev-server

```javascript
// web-dev-server.config.mjs
import deck from '@3sln/deck/wds-plugin';

export default {
  nodeResolve: true,
  plugins: [deck()],
};
```

### webpack-dev-server / Rspack

```javascript
// rspack.config.js — webpack.config.js is identical
import {setupMiddlewares} from '@3sln/deck/webpack-plugin';

export default {
  mode: 'development',
  entry: {},
  devServer: {
    static: {directory: import.meta.dirname},
    setupMiddlewares: setupMiddlewares({root: import.meta.dirname}),
  },
};
```

### Anything Else

Deck's dev server is a plain connect/express middleware, so express, connect, polka and Node's own `http` all work:

```javascript
import {createDeckMiddleware} from '@3sln/deck/dev-core';

app.use(createDeckMiddleware({root: process.cwd()}));
```


---

# The <deck-demo> Element (/cards/deck-demo.md)

# The `<deck-demo>` Element

The `<deck-demo>` custom element is the heart of Deck's interactive documentation. It allows you to embed live, stateful, and hot-reloading component demos directly in your Markdown files.

To use it, you place the tag in your Markdown, give it an id,
and point its `src` attribute to the demo script.

```markdown
<deck-demo id="my-awesome-demo" src="/demos/my-awesome-demo.js"></deck-demo>
```

## The Demo Script

The script referenced in `src` must have a default export that is a function. This function receives a `driver` object as its first argument.

```javascript
// /demos/my-awesome-demo.js
export default driver => {
  // Your demo logic goes here
};
```

## The Demo Driver API

The `driver` object is an API that allows your demo to interact with the `<deck-demo>` element's UI, which includes a source code viewer, property editor, and content panels.

### `driver.panel(name, renderFn)`

Creates a tabbed panel for rendering content.

-   `name` (string): The title of the tab.
-   `renderFn` (function): A function that receives `(container, signal)`.
    -   `container`: The HTML element to render your demo into.
    -   `signal`: An `AbortSignal` that fires when the demo is about to be unmounted. Use this for cleanup.

### `driver.property(name, {type, defaultValue})`

Creates a reactive property control in the "Properties" panel. This allows users to interact with your demo.  It returns an observable
that provides the current value of the property input.

-   `name` (string): The name of the property.
-   `type` (string, optional): The input type to render for the property.
-   `defaultValue` (optional): The initial value to use if the property doesn't already exist.

### `driver.signal`

An `AbortSignal` that's aborted when the demo is being torn down.

## Full Example

Here is a simple demo script that shows a message and lets the user control its text and color.

```javascript
import {p, reconcile} from '@3sln/dodo';

export default driver => {
  const message$ = driver.property('Message', { type: 'text', value: 'Hello, Deck!' });

  driver.panel('Demo', (container, signal) => {
    const render = (message) => {
      reconcile(container, [
        p({ $styling: { color: currentProps.textColor } },
          message
        )
      ]);
    };

    const sub = message$.subscribe(message => {
      render(message);
    });

    signal.addEventListener('abort', () => {
      sub.unsubscribe();
      reconcile(container, []);
    });
  });
};
```

## A Live Example

This card embeds the demo below with a single tag. Edit `/demos/counter-demo.js`
while the dev server is running and it re-runs in place, keeping the properties
you have set.



### Demo Code (/demos/counter-demo.js)

```javascript
import * as d from '@3sln/dodo';
import {cell, watch} from '@3sln/dodo/reactive';

/**
 * The demo shown on the `<deck-demo>` card.
 *
 * A demo module's default export is called with a `driver` and re-called with
 * the same argument whenever this file changes, so anything it sets up has to
 * be torn down through `driver.signal`.
 */
export default driver => {
  const step$ = driver.property('Step', {type: 'range', min: 1, max: 10, defaultValue: 1});
  const label$ = driver.property('Label', {type: 'text', defaultValue: 'Clicks'});

  driver.panel('Counter', (container, signal) => {
    const count = cell(0);
    let step = 1;
    let label = 'Clicks';

    const rerender = () => {
      d.reconcile(container, [
        watch(count, value =>
          d.div(
            {
              $styling: {
                display: 'flex',
                'align-items': 'center',
                gap: '1em',
                'font-family': 'system-ui, sans-serif',
                color: 'var(--text-color, #222)',
              },
            },
            d.button(
              {
                $styling: {
                  padding: '0.5em 1em',
                  'border-radius': '6px',
                  border: '1px solid #8884',
                  cursor: 'pointer',
                },
              },
              `+${step}`,
            ).on({click: () => count.update(n => n + step)}),
            d.span(`${label}: ${value}`),
          ),
        ),
      ]);
    };

    const subscriptions = [
      step$.subscribe(value => {
        step = Number(value) || 1;
        rerender();
      }),
      label$.subscribe(value => {
        label = value || 'Clicks';
        rerender();
      }),
    ];

    rerender();

    signal.addEventListener('abort', () => {
      subscriptions.forEach(subscription => subscription.unsubscribe());
      d.reconcile(container, null);
    });
  });
};

```




---

# Configuration (/cards/configuration.md)

# Configuration

Deck is configured through a `@3sln/deck` field in your project's `package.json` file.

## The Override Model

Deck uses a simple override system for configuration. You can define a base configuration at the root of the `@3sln/deck` object. Then, you can create `dev` and `build` sub-objects to override any of those settings for a specific environment.

-   **Root Configuration**: The base settings used by both environments.
-   **`dev` Block**: Overrides for the development server (`deck-dev`, or whichever dev server plugin you use).
-   **`build` Block**: Overrides for the production build (`deck-build`).

When Deck loads, it merges the root configuration with the environment-specific block. For example, when running the `deck-build` command, Deck will merge the root `{...}` options with the `build: {...}` options.

## Configuration Options

Any of the following options can be placed at the root or within the `dev` and `build` blocks.

-   `title` (string): The title of your documentation site.

-   `pinned` (array of strings): A list of absolute paths to cards that should be pinned to the top of the card list.

-   `importMap` (object): An [import map](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script/type/importmap) to be included in the `index.html`. This is essential for remapping module specifiers, especially for demos.

-   `outDir` (string): The output directory for the built site, relative to the project root. This is primarily useful in the `build` block. Defaults to `out`.

-   `url` (string): Where this deck is published, e.g. `https://deck.3sln.com`. A sitemap needs absolute URLs and only you know them, so setting this is what produces `sitemap.xml`; `robots.txt` and `llms.txt` are written either way, with relative links when it is unset.

-   `description` (string): One sentence about the deck. Used for the page's `<meta name="description">` and as the summary line in `llms.txt`.

-   `esbuild` (object): Options passed to esbuild when it bundles your `<deck-demo>` modules — `alias`, `define`, `loader`, `tsconfig`, `jsxImportSource` and friends. Under the Vite plugin a demo is resolved by your own Vite config and this is not needed; every other dev server, and the build, bundle demos with esbuild and would otherwise not know what `@app/button` refers to.

    ```json
    {
      "@3sln/deck": {
        "esbuild": {
          "alias": {"@app": "./src"},
          "define": {"__DEV__": "true"}
        }
      }
    }
    ```

-   `pick` (object): A map of source paths to destination paths, allowing you to copy files or directories into the build output. This is primarily useful in the `build` block to include assets or dependencies for your static site.

## Example

Here is an example demonstrating the override system.

```json
{
  "@3sln/deck": {
    "title": "My Awesome Project",
    "importMap": {
      "imports": {
        "my-lib": "/node_modules/my-lib/index.js"
      }
    },
    "dev": {
      "title": "My Awesome Project (DEV)"
    },
    "build": {
      "outDir": "dist/docs",
      "pick": {
        "../node_modules/my-lib/dist": "lib/my-lib"
      },
      "importMap": {
        "imports": {
          "my-lib": "/lib/my-lib/index.js"
        }
      }
    }
  }
}
```

### Behavior

-   **`npm run dev`**: The title will be `My Awesome Project (DEV)` and `my-lib` will resolve to `/node_modules/my-lib/index.js`.
-   **`npm run build`**: The title will be `My Awesome Project`, the output will go to `dist/docs`, and the `importMap` will be overridden to point `my-lib` to the locally copied version at `/lib/my-lib/index.js`.

## Files for Crawlers and Agents

A deck is a single-page application: every card lives behind a `?c=` query parameter and its body arrives by fetch. Anything that does not run JavaScript sees one nearly empty page. So `deck-build` also writes the content down where it can be read directly:

-   **`/llms.txt`** — the [llmstxt.org](https://llmstxt.org) convention. The deck's title, its description, and one line per card linking that card's Markdown file. This is the best starting point for an agent.
-   **`/agents.md`** and **`/agents.html`** — every card concatenated into one document, with the source of each live demo inlined where its `<deck-demo>` tag was.
-   **`/robots.txt`** — allows everything, and names the sitemap when `url` is set.
-   **`/sitemap.xml`** — the root plus the `?c=` URL that opens each card. Written only when `url` is set.

The generated `index.html` also carries `<link rel="alternate">` tags pointing at `/agents.md` and `/llms.txt`, so an agent that starts at the page still finds them.


---

# Core Concepts (/cards/concepts.md)

# Core Concepts

Deck's design is based on a few key architectural choices.

## Client-Side Indexing

Deck stores documentation content in a client-side database (IndexedDB). The initial `index.html` file contains a manifest of all card paths along with a hash of their content.

Upon loading, the application compares this manifest with the data already stored in its IndexedDB, and fetches only the cards that are new or have changed. Nothing blocks on the whole deck arriving: the shell renders immediately, and cards fill in behind it.

Assets that are not cards, such as the JavaScript files for `<deck-demo>` elements, are not pre-fetched. They are fetched and cached by the service worker only when a user views them for the first time.

## The Precompiled Search Index

A published deck ships a search index built at publish time. The browser downloads it **before any card**, which is what lets the very first search cover the whole deck rather than the fraction of it that has finished loading. Because the index carries each card's title and summary, results render immediately, whether or not their bodies have arrived.

A dev server has no precompiled index — the deck is being edited, and an index built a moment ago already describes the previous version. There, search is answered from the IndexedDB index of whatever has been loaded, which is the right trade when the content changes every few seconds.

## Loading Priority

Which card arrives first matters more than how many arrive per second. A published deck queues every card at once and lets priority decide the order:

1.  the precompiled search index, before any card;
2.  the card named by `?c=`, so a deep link opens immediately rather than after the rest of the deck has drained;
3.  cards matching the search currently on screen;
4.  everything else.

Steps 2–4 are priorities on one queue, not phases. A search typed while the backlog is still draining moves its matches to the front of that queue in place, so clicking a result opens it instead of showing a spinner.

## The `<deck-demo>` Element

The `<deck-demo>` custom element is used to embed live demos in Markdown files. Its `src` attribute points to a JavaScript file.

The script is executed within the element and is provided with a `driver` API. This API allows the demo to render content and manage its own state, keeping it isolated from the main Deck application and other demos.

In development the dev server serves two generated modules per demo: one that re-runs the demo when its file changes, and one that carries its source text for the Source panel. In a published deck the demo is bundled ahead of time, and the Source panel still shows the original file rather than the bundle.

## Dev Servers and the Build

Deck has two modes of operation:

1.  **Development:** `deck-dev` serves a deck with no bundler and no config. Plugins for Vite (`@3sln/deck/vite-plugin`), `@web/dev-server` (`@3sln/deck/wds-plugin`) and webpack-dev-server / Rspack (`@3sln/deck/webpack-plugin`) do the same job inside a dev server you already run — and the middleware behind them is plain connect, so any Node server can host a deck. Deck's own client app is pre-bundled either way, so the host runtime is never asked to resolve Deck's own dependencies.

2.  **Production (`deck-build`):** Creates a production-ready static site. It bundles the Deck application and every demo module with esbuild, copies the project's documentation files, precompiles the search index, writes `agents.md` and `agents.html`, and generates a static `index.html` for deployment.
