npm package: @nonsoanetoh/gui
Description: A Route-aware lil-gui manager for Next.js with a central config and useSetting hook
Latest version: 0.3.3

# gui

Route-aware [lil-gui](https://lil-gui.georgealways.com/) for Next.js App Router. One config, one wrapper, and a `useSetting` hook so your components can read values without wiring everything by hand.

---

## Install

```bash
pnpm add @nonsoanetoh/gui
```

Or with npm:

```bash
npm i @nonsoanetoh/gui
```

---

## Usage

### 1. Define your config

Create a config file (e.g. `lib/gui-config.ts`) with your settings and which routes they apply to:

```ts
import type { SettingDefinitionItem } from "@nonsoanetoh/gui";

export const GUI_DEFINITIONS: SettingDefinitionItem[] = [
  {
    id: "home.backgroundColor",
    type: "color",
    name: "Background Color",
    default: "#fefefe",
    routes: ["/"],
  },
  {
    type: "folder",
    name: "Position",
    routes: ["/work/*"],
    children: [
      { id: "position.x", type: "number", name: "X", default: 0 },
      { id: "position.y", type: "number", name: "Y", default: 0 },
    ],
  },
];
```

**Routes** — Each setting can target single or multiple paths. Use the `routes` array on a definition:


| Pattern     | Meaning                                                         |
| ----------- | --------------------------------------------------------------- |
| `"*"`       | Show on every route.                                            |
| `"/work"`   | Show only when the path is exactly `/work`.                     |
| `"/work/*"` | Show on `/work/anything` and deeper, but not on `/work` itself. |


---

### 2. Wrap your app

In your root layout, wrap the tree with a single `GUI` component. Pass your definitions and when to show the panel (e.g. only in development):

```tsx
import { GUI } from "@nonsoanetoh/gui";
import { GUI_DEFINITIONS } from "@/lib/gui-config";
// optional: import styles from "./gui-panel.module.css";

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <GUI
          definitions={GUI_DEFINITIONS}
          visible={process.env.NODE_ENV === "development"}
          // className={styles.panel}
          // style={{ top: 8, right: 8 }}
        >
          {children}
        </GUI>
      </body>
    </html>
  );
}
```

The panel is fixed top-right. You can pass `className` and/or `style` to position or style the panel wrapper; `className` works with CSS modules (e.g. `className={styles.panel}`). When `visible` is `false`, the panel is hidden and `useSetting` returns `undefined` so your components fall back to their defaults.

---

### 3. Read values in your components

```tsx
"use client";

import { useSetting } from "@nonsoanetoh/gui";

export default function Home() {
  const bg = useSetting("home.backgroundColor", "#fefefe");

  return (
    <div style={{ minHeight: "100vh", backgroundColor: bg ?? "#fefefe" }}>
      …
    </div>
  );
}
```

When the GUI is visible, `useSetting` returns the current value (or the default you pass). When it’s hidden, it returns `undefined`, so `value ?? default` uses your fallback.

You can also pass an options object with `default` and `onChange`:

```tsx
useSetting("some.path", { default: 0, onChange: (v) => console.log(v) });
```

---

## API


| Export                                    | What it does                                                                                                                                  |
| ----------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `GUI`                                     | Single wrapper: provides config + visibility + panel. Accepts `className`, `style`, and `title` for the panel. Use this for the simple setup. |
| `useSetting(path, default?)`              | Returns the current value when the GUI is visible, or `undefined` when hidden.                                                                |
| `useSetting(path, { default, onChange })` | Same, with optional `onChange` when the value changes.                                                                                        |


---

## Advanced: custom layout and multiple panels

For custom placement or more than one panel, use the exposed building blocks instead of `GUI`: `SettingsProvider`, `GuiConfigProvider`, `GuiProvider`, and `GuiManager`. All four share the same settings state.

### Custom layout

Use the providers once, then place `GuiManager` where you want. Pass `className` and/or `style` to position or style the panel wrapper. Both work with CSS modules (e.g. `className={styles.sidebarPanel}`). The default is fixed top-right; you can override with the `style` prop.

```tsx
import {
  SettingsProvider,
  GuiConfigProvider,
  GuiProvider,
  GuiManager,
} from "@nonsoanetoh/gui";
import { GUI_DEFINITIONS } from "@/lib/gui-config";

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <SettingsProvider>
          <GuiConfigProvider definitions={GUI_DEFINITIONS}>
            <GuiProvider visible={process.env.NODE_ENV === "development"}>
              <div style={{ display: "flex" }}>
                <aside style={{ width: 280, padding: 16 }}>
                  <GuiManager
                    title="Settings"
                    style={{ position: "relative", top: 0, right: 0 }}
                  />
                </aside>
                <main style={{ flex: 1 }}>{children}</main>
              </div>
            </GuiProvider>
          </GuiConfigProvider>
        </SettingsProvider>
      </body>
    </html>
  );
}
```

### Multiple panels

**Different definitions per panel**: Use two (or more) `<GUI>` components with different `definitions`. Each panel has its own controls and its own settings state. Handy for e.g. a "Scene" panel and a "Camera" panel.

```tsx
<GUI definitions={SCENE_DEFINITIONS} visible={isDev} title="Scene" style={{ top: 16, right: 16 }}>
  <GUI definitions={CAMERA_DEFINITIONS} visible={isDev} title="Camera" style={{ top: 16, left: 16, right: "auto" }}>
    {children}
  </GUI>
</GUI>
```

**Same definitions, shared state** : Use one provider tree and multiple `GuiManager` components if you want the same controls in more than one place (e.g. top-right and in a sidebar). Both panels show the same definitions and control the same values.

```tsx
<SettingsProvider>
  <GuiConfigProvider definitions={GUI_DEFINITIONS}>
    <GuiProvider visible={process.env.NODE_ENV === "development"}>
      {children}
      <GuiManager title="Settings" style={{ top: 16, right: 16 }} />
      <GuiManager title="Settings" style={{ top: 16, left: 16, right: "auto" }} />
    </GuiProvider>
  </GuiConfigProvider>
</SettingsProvider>
```

---