# DebugManager

Create a lazy debug pane and control when it appears.



<DemoPreview name="DebugManager" />

## Usage [#usage]

```ts
import { DebugManager, property, slider } from '@joycostudio/xyz/debug'
import { Pane } from 'tweakpane'

const state = { speed: 1 }
const debug = new DebugManager({ createPane: () => new Pane() })

debug.bind({
  title: 'Motion',
  schema: () => ({ speed: slider(property(state, 'speed'), { min: 0, max: 3 }) }),
})

debug.enable() // Show the pane; dragging the slider updates state.speed.
// Option+D toggles it. Opening the page with ?debug=true also enables it.

// When the owner is removed:
debug.dispose()
```

## API [#api]

### new DebugManager [#new-debugmanager]

```ts
new DebugManager(options: DebugManagerOptions)
```

Owns URL/keyboard activation and lazy pane creation. Registrations survive disable/enable cycles and mount again from their definitions.

| Argument                         | Description                                                                                                                                                          |
| -------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `options.createPane`             | Required factory returning a pane, or a promise of one. The pane must provide `addFolder()` and `dispose()`. Called when debug is enabled.                           |
| `options.parameter?`             | URL parameter controlling activation. Defaults to `'debug'`. Presence enables debug unless the value is `0`, `false`, `no`, or `off` (case-insensitive).             |
| `options.shortcuts?`             | One shortcut object, an array of them, or `false` to disable keyboard activation. Defaults to `{ code: 'KeyD', altKey: true }`. Shortcut fields are described below. |
| `options.binding?`               | Shared binding options applied whenever a definition mounts.                                                                                                         |
| `options.binding.registerFrame?` | Receives a refresh callback to attach to your frame loop and returns its teardown. Required for automatic refresh of controls with `live: true`.                     |
| `options.binding.copyText?`      | Receives serialized values and returns `void` or a promise. Defaults to `navigator.clipboard.writeText`.                                                             |
| `options.onError?`               | Receives `(error, context)` for pane, binding, action, or cleanup failures. Defaults to console logging.                                                             |
| `options.window?`                | Browser window to use. Defaults to the global window when available.                                                                                                 |

For a single shortcut, use these fields on `options.shortcuts`; for an array, use them on each entry. Modifier flags must match exactly.

| Argument    | Description                                         |
| ----------- | --------------------------------------------------- |
| `code`      | Required keyboard event code, such as `'KeyD'`.     |
| `altKey?`   | Require Alt (Option on Mac). Defaults to `false`.   |
| `ctrlKey?`  | Require Control. Defaults to `false`.               |
| `metaKey?`  | Require Meta (Command on Mac). Defaults to `false`. |
| `shiftKey?` | Require Shift. Defaults to `false`.                 |

```ts
const debug = new DebugManager({
  createPane: () => new Pane(),
  parameter: 'debug',
  shortcuts: { code: 'KeyD', altKey: true },
  onError: (error, context) => console.error(context, error),
})
```

### register [#register]

```ts
debug.register(source: Debuggable): Disposable
```

Registers an object that supplies debug folders. Returns a disposable registration; disposing it removes its controls and prevents future remounts.

`Debuggable` is exported from `@joycostudio/xyz/debug`. A class implements it by exposing `debug()`, which returns one titled folder definition or a readonly array of them. Each folder supplies a `schema()` factory; its other options are described under [bind](#bind).

Call `debug.register(instance)` to register the class instance. Implementing the interface checks its shape at compile time; the manager does not discover instances automatically. The manager calls `debug()` when the pane mounts and again after each disable/enable cycle.

| Argument | Description                                                                                                               |
| -------- | ------------------------------------------------------------------------------------------------------------------------- |
| `source` | An object with a `debug()` method returning one titled folder definition or an array of them. Called again on each mount. |

```ts
import { DebugManager, type Debuggable, property, slider } from '@joycostudio/xyz/debug'
import { Pane } from 'tweakpane'

class Motion implements Debuggable {
  speed = 1

  debug() {
    return {
      title: 'Motion',
      schema: () => ({
        speed: slider(property(this, 'speed'), { min: 0, max: 3 }),
      }),
    }
  }
}

const motion = new Motion()
const debug = new DebugManager({ createPane: () => new Pane() })
const registration = debug.register(motion)
debug.enable()

// When the Motion owner is removed:
registration.dispose() // Remove its controls and prevent future remounts.

// When the application is removed:
debug.dispose()
```

### bind [#bind]

```ts
debug.bind(definition: DebugFolder | (() => DebugFolder)): Disposable
```

Registers one folder and calls [bindDebugSchema](./schemas#binddebugschema) internally when mounting it. Returns a disposable registration. If debug is disabled, control creation waits until activation.

| Argument               | Description                                                                                       |
| ---------------------- | ------------------------------------------------------------------------------------------------- |
| `definition`           | A titled folder definition or a factory returning one. Factories run on each mount.               |
| `definition.title`     | Required folder title.                                                                            |
| `definition.expanded?` | Initial expanded state; when omitted, uses Tweakpane's default.                                   |
| `definition.schema`    | Required factory returning the control fields.                                                    |
| `definition.copy?`     | Adds a copy-values button when `true`. Off when omitted.                                          |
| `definition.dispose?`  | Cleanup function, disposable object, or array of either, run when the binding unmounts.           |
| `definition.extend?`   | Receives the Tweakpane folder and `{ fields }` to add custom controls; may return cleanup.        |
| `definition.sources?`  | Source descriptors with a `label` and `value: () => object`. Collected by the underlying binding. |

For a factory, the fields above describe its returned object.

```ts
const state = { speed: 1 }
const registration = debug.bind(() => ({
  title: 'Motion',
  expanded: true,
  schema: () => ({
    speed: slider(property(state, 'speed'), { min: 0, max: 3 }),
  }),
}))

debug.enable() // Mount controls; the factory runs again after disable/enable.
registration.dispose()
```

### enable [#enable]

```ts
debug.enable(): void
```

Enables debug, adds the configured URL parameter with value `true`, and creates the pane lazily. Takes no arguments; does nothing if already enabled.

### disable [#disable]

```ts
debug.disable(): void
```

Disables debug, removes the configured URL parameter, and disposes the pane and mounted controls. Takes no arguments; registrations remain available for the next enable cycle.

### toggle [#toggle]

```ts
debug.toggle(): void
```

Switches between enabled and disabled, updating the URL. Takes no arguments.

### sync [#sync]

```ts
debug.sync(): void
```

Re-reads activation from the current URL without rewriting it. Takes no arguments. The manager also calls it on `popstate`.

### enabled [#enabled]

```ts
debug.enabled // boolean
```

Read-only current activation state. Pane creation may still be pending when this is `true`.

### dispose [#dispose]

```ts
debug.dispose(): void
```

Unmounts the pane, clears registrations, and removes browser listeners. Takes no arguments and leaves the URL unchanged.
