# @nanonomad/core

The framework-agnostic engine that powers the NanoNomad digital pet. It handles physics, behavior, animation, and rendering — with no dependencies on React, Vue, or any other UI library.

Use this package directly when working with vanilla JavaScript, or as the foundation when integrating into any framework.

For a **single install** that includes characters helpers and assets metadata, use [`@nanonomad/web`](https://www.npmjs.com/package/@nanonomad/web). For **React / Next.js**, use [`@nanonomad/react`](https://www.npmjs.com/package/@nanonomad/react) (it depends on core + characters).

---

## Installation

```bash
npm install @nanonomad/core @nanonomad/characters
```

`@nanonomad/characters` supplies the SVG assets, CSS, and `character.json` files the engine loads at runtime (or install `@nanonomad/web` / `@nanonomad/react` instead).

---

## Quick start

The engine needs three things to run: a **host** (an abstraction over `window` and `document`), a **runtime config** that tells it which character to display and where to find its assets, and a call to `start()`.

```js
import NanoNomad, { createBrowserHost } from '@nanonomad/core'

const pet = new NanoNomad({
  host: createBrowserHost(window),
  runtimeConfig: {
    character: 'default',
    characterBaseUrl: '/nanonomad-characters/default/',
    characterConfigUrl: '/nanonomad-characters/default/character.json',
  },
})

await pet.start()
```

The pet will appear immediately and begin walking, jumping, and idling across the browser window. It sits on a fixed overlay with `pointer-events: none`, so it never blocks clicks or form inputs.

---

## Loading character assets

The engine fetches `character.json` at startup to learn which poses, capabilities, and physics settings the character uses. All SVG and CSS files are loaded relative to `characterBaseUrl`.

There are two ways to serve these assets.

### Self-hosted (recommended for production)

Copy the character files into your project's static folder using the CLI tool from `@nanonomad/characters`:

```bash
npx nanonomad-characters-copy ./public/nanonomad-characters
```

Then point the engine at the copied files:

```js
runtimeConfig: {
  character: 'default',
  characterBaseUrl: '/nanonomad-characters/default/',
  characterConfigUrl: '/nanonomad-characters/default/character.json',
}
```

### CDN (zero-setup)

Use the jsDelivr CDN, which mirrors the npm package automatically. No server configuration is needed.

```js
import { getCharacterUrls } from '@nanonomad/characters'

const urls = getCharacterUrls('default', { version: '1.0.0' })

const pet = new NanoNomad({
  host: createBrowserHost(window),
  runtimeConfig: {
    ...urls,
    // urls provides: character, characterBaseUrl, characterConfigUrl
  },
})
```

The page must be served over HTTP or HTTPS — `fetch()` does not work on `file://` URLs.

---

## Constructor options

```js
const pet = new NanoNomad(options)
```

| Option | Type | Default | Description |
|---|---|---|---|
| `host` | `object` | — | A host object created by `createBrowserHost(window)`. Required before calling `start()`. |
| `runtimeConfig` | `object` | `{}` | Configuration passed to the engine. See [Runtime configuration](#runtime-configuration) below. |
| `mountTarget` | `HTMLElement` | `null` | A DOM element to mount the pet inside. When omitted the engine creates its own full-screen overlay appended to `<body>`. |
| `capabilityRegistry` | `CapabilityRegistry` | auto | Supply a custom registry if you want to add or replace capabilities before startup. |
| `registerBuiltin` | `boolean` | `true` | Set to `false` to skip loading the built-in capabilities (`idle`, `walk`, `jump`, `sit`, `fall`, `run`, `sleep`, `pee`). Useful when you want to provide your own capability set. |
| `fetch` | `function` | `globalThis.fetch` | Override the fetch function used to load `character.json`. Useful in environments where `fetch` is not global. |

---

## Runtime configuration

The `runtimeConfig` object controls every aspect of the pet's behavior and appearance.

### Asset loading

| Parameter | Type | Default | Description |
|---|---|---|---|
| `character` | `string` | `'default'` | The character identifier to load. Must match a folder name in your characters directory (e.g. `'dog'`, `'default'`). |
| `characterBaseUrl` | `string` | `''` | Base URL for the character's asset folder. All SVG and CSS files are fetched relative to this path. Must end with `/`. |
| `characterConfigUrl` | `string` | `''` | Full URL to the character's `character.json` file. |
| `characterProvider` | `object` | `null` | An alternative to `characterBaseUrl` / `characterConfigUrl`. Supply an object with a `getCharacter(id)` async method. See [Character providers](#character-providers). |

### Behavior

| Parameter | Type | Default | Description |
|---|---|---|---|
| `enabledCapabilities` | `string[]` | `[]` | List of capabilities the pet is allowed to use. An empty array means all capabilities defined in `character.json` are enabled. Valid values: `'idle'`, `'walk'`, `'run'`, `'jump'`, `'sit'`, `'sleep'`, `'pee'`, `'fall'`. Note: `idle` and `fall` are always included regardless of this list. |
| `movementSpeed` | `number` | `50` | Overall movement speed on a scale of `1` to `100`. Affects walk and run velocity. Higher values make the pet visibly faster. |
| `behaviorFrequency` | `number` | `50` | How often the behavior engine considers changing state, on a scale of `1` to `100`. Higher values produce more active, restless behavior. Lower values make the pet calmer and more likely to idle. |
| `mobileEnabled` | `boolean` | `false` | When `false`, the pet does not start on screens narrower than 768 px. Set to `true` to enable it on mobile devices. |
| `persistenceEnabled` | `boolean` | `true` | Saves the pet's position to `localStorage` every 5 seconds and on page unload. On the next page load the pet resumes from where it was. |

### Appearance

| Parameter | Type | Default | Description |
|---|---|---|---|
| `customColors` | `object` | `{}` | Override the character's default color palette. Keys must match the palette keys defined in `character.json`. Values are CSS color strings. |

---

## Lifecycle methods

### `await pet.start()`

Fetches `character.json`, mounts the pet into the DOM, initializes the physics and behavior engines, and begins the animation loop. This is the only async step. Safe to call once per instance.

### `pet.pause()`

Stops the animation loop and pauses both the behavior and movement engines. The pet remains visible in the DOM at its last position. Use this when the pet should temporarily stop (e.g. during a modal or video).

### `pet.resume()`

Resumes a paused pet. Restarts the animation loop and engines. Has no effect if the pet was not previously paused.

### `pet.destroy()`

Stops the loop, removes all event listeners, clears all timers, and removes the pet's DOM elements. After calling `destroy()`, the instance cannot be restarted — create a new one if needed.

### `pet.getConfig()`

Returns the fully merged configuration object that was resolved at `start()` time, including values loaded from `character.json`. Useful for inspecting what the engine is actually running with.

---

## Automatic behaviors

Once running, the engine manages several behaviors automatically without any configuration.

**Tab visibility** — the animation loop stops when the browser tab is hidden (via the Page Visibility API) and resumes when the tab regains focus. This prevents unnecessary CPU usage on inactive tabs.

**Idle detection** — if the user has not moved the mouse, pressed a key, or touched the screen for 60 seconds, the pet pauses. It resumes as soon as the user interacts again.

**Scroll fading** — the pet's opacity drops to 30% while the page is being scrolled and returns to full opacity 400 ms after scrolling stops.

**Platform detection** — the engine scans the page for DOM elements (`header`, `nav`, `footer`, `section`, `article`, `.card`, `.widget`, and others) and treats their top edges as platforms. The pet can land on and walk across these surfaces.

---

## Capabilities

Capabilities are the individual behaviors the pet can perform. Each capability is a state the pet transitions into and out of over time.

| Capability | Description | Characters that include it |
|---|---|---|
| `idle` | The pet stands still facing forward. Always active. | All |
| `walk` | The pet walks left or right across the screen or platform. | All |
| `fall` | The pet falls when it steps off a platform or edge. Always active. | All |
| `jump` | The pet launches upward and falls back down. | All |
| `sit` | The pet sits and rests for a random duration. | All |
| `run` | The pet moves at 2.2× walk speed. | Dog |
| `sleep` | The pet lies down and sleeps for an extended period. | Dog |
| `pee` | The pet walks toward a nearby text element and marks it. | Dog |

The `idle` and `fall` capabilities are always included even if omitted from `enabledCapabilities`. Other capabilities must be listed in the character's `character.json` AND present in `enabledCapabilities` (or `enabledCapabilities` must be empty) to activate.

---

## Color customization

Each character defines a color palette in its `character.json`. You can override any or all palette keys by passing `customColors` in your runtime config.

```js
// Default character palette keys: body, head, legs, blush, eyes, detail
runtimeConfig: {
  character: 'default',
  customColors: {
    body: '#2563eb',
    head: '#1d4ed8',
    legs: '#1e3a8a',
  },
}
```

```js
// Dog character palette keys: body, belly, ears, detail, eyes, tongue
runtimeConfig: {
  character: 'dog',
  customColors: {
    body: '#8b4513',
    belly: '#d2691e',
    tongue: '#ff4444',
  },
}
```

Color keys not provided in `customColors` retain their defaults from `character.json`. Any valid CSS color string is accepted (`#hex`, `rgb()`, `hsl()`, named colors).

---

## Character providers

For advanced setups — such as loading characters from a custom API or a private CDN — you can supply a `characterProvider` instead of `characterBaseUrl` and `characterConfigUrl`.

A character provider is any object with a `getCharacter(id)` async method that returns `{ config, baseUrl, configUrl }`.

```js
import { createUrlCharacterProvider } from '@nanonomad/core'

// Built-in provider: resolves characters from a static root URL
const provider = createUrlCharacterProvider('https://example.com/pets/')

const pet = new NanoNomad({
  host: createBrowserHost(window),
  runtimeConfig: {
    character: 'dog',
    characterProvider: provider,
  },
})
```

To write a custom provider:

```js
const myProvider = {
  async getCharacter(id) {
    const res = await fetch(`/api/characters/${id}`)
    const data = await res.json()
    return {
      config: data.characterConfig,         // the character.json contents
      baseUrl: data.assetBaseUrl,           // e.g. 'https://assets.example.com/dog/'
      configUrl: data.assetBaseUrl + 'character.json',
    }
  },
}
```

---

## Custom capabilities

You can extend the engine with your own behavior states. Register a custom capability on a `CapabilityRegistry` before passing it to `NanoNomad`.

```js
import NanoNomad, { createBrowserHost, CapabilityRegistry, registerBuiltinCapabilities } from '@nanonomad/core'

const registry = new CapabilityRegistry()
registerBuiltinCapabilities(registry)

// Add a custom 'spin' capability
registry.register('spin', {
  id: 'spin',
  state: 'spin',
  system: false,

  enter(ctx) {
    ctx.movement.stopWalk()
    ctx.stateTimer = ctx.getStateDuration('spin') || 2000
  },

  update(ctx, dt) {
    ctx.stateTimer -= dt
    if (ctx.stateTimer <= 0) ctx.transitionToNext()
  },

  exit(ctx) {},
})

const pet = new NanoNomad({
  host: createBrowserHost(window),
  capabilityRegistry: registry,
  runtimeConfig: {
    character: 'default',
    characterBaseUrl: '/nanonomad-characters/default/',
    characterConfigUrl: '/nanonomad-characters/default/character.json',
    enabledCapabilities: ['idle', 'walk', 'jump', 'sit', 'fall', 'spin'],
  },
})

await pet.start()
```

---

## Named exports

```js
import NanoNomad, {
  createBrowserHost,
  CapabilityRegistry,
  registerBuiltinCapabilities,
  createUrlCharacterProvider,
  mergeRuntimeConfig,
  Environment,
  MovementEngine,
  BehaviorEngine,
  Renderer,
} from '@nanonomad/core'
```

| Export | Description |
|---|---|
| `NanoNomad` (default) | The main orchestrator class. |
| `createBrowserHost` | Creates the host object required by the engine. Pass `window`. |
| `CapabilityRegistry` | Registry for registering and resolving behavior capabilities. |
| `registerBuiltinCapabilities` | Registers all built-in capabilities on a registry instance. |
| `createUrlCharacterProvider` | Factory for a URL-based character provider. |
| `mergeRuntimeConfig` | Merges runtime options with character JSON. Used internally; exposed for advanced use. |
| `Environment` | Scans the DOM for platforms and text targets. |
| `MovementEngine` | Velocity-based physics engine. |
| `BehaviorEngine` | State machine that drives capability transitions. |
| `Renderer` | Loads SVG assets and applies CSS transforms and color overrides. |

---

## Formats

The package ships three build formats.

| Format | File | Use case |
|---|---|---|
| ESM | `dist/nanonomad.esm.js` | Bundlers (webpack, Vite, Rollup), modern `import` |
| CJS | `dist/nanonomad.cjs` | Node.js `require()` |
| UMD | `dist/nanonomad.umd.js` | `<script>` tags, CDN, vanilla JS |

---

## Requirements

- Node.js 18 or later (for tooling)
- Modern browser with `fetch`, `requestAnimationFrame`, and `localStorage`
- Pages must be served over HTTP or HTTPS (not `file://`)

---

## License

GPL-2.0-or-later