npm package: @neuroenglab/nel-feedback-ui
Description: Neuroengineering feedback UI: body part selector and sensation locator components
Latest version: 0.0.2

# nel-feedback-ui

Neuroengineering feedback UI: body part selector and sensation locator React components.

## Install

From your GitHub repo (replace `neuroenglab/feedback-locator` with your org/repo if different):

```bash
npm install github:neuroenglab/feedback-locator
# or, with full URL
npm install git+https://github.com/neuroenglab/feedback-locator.git
```

## Build the package

Clone this repo and install dependencies:

```bash
npm install
```

Build the library into `dist/`:

```bash
npm run build
```

This produces:

- `dist/nel-feedback-ui.es.js` (ESM)
- `dist/nel-feedback-ui.umd.js` (UMD)
- `dist/*.d.ts` (TypeScript declarations)

## Publish / use within your org

### 1. Authenticate with GitHub Packages

You cannot publish without telling your local terminal who you are.

Create a Personal Access Token (Classic) on GitHub with write:packages and read:packages scopes.

Run this command:

```bash
npm login --scope=@neuroenglab --registry=https://npm.pkg.github.com
```

### 2. Publishing

```bash
npm publish
```

- **Install directly from GitHub (recommended for internal use):**

  1. Push this repo to your org GitHub (e.g. `github.com/<org>/feedback-locator`).
  2. In consuming apps, install:

  ```bash
  npm install github:<org>/feedback-locator
```

- **Publish to a registry (optional):**

  1. Ensure `name` in `package.json` is unique on your registry (`nel-feedback-ui` or a scoped name like `@your-org/nel-feedback-ui`).
  2. Log in to the registry (`npm login` or GitHub Packages).
  3. Build, then publish:

  ```bash
  npm run build
  npm publish
  ```

  Adjust `publishConfig.registry` in `package.json` if you want to target GitHub Packages instead of the public npm registry.

## Usage

In project that uses this package, create a `.npmrc` file with following contents:

```sh
# 1. Map the scope to the registry
@neuroenglab:registry=https://npm.pkg.github.com

# 2. Tell NPM how to authenticate with that registry
//npm.pkg.github.com/:_authToken=${NODE_AUTH_TOKEN}
```

Set your NODE_AUTH_TOKEN environment variable:

```sh
echo $SHELL
echo 'export NODE_AUTH_TOKEN="<ACTUAL_TOKEN>"' >> ~/.zshrc
source ~/.zshrc

echo $NODE_AUTH_TOKEN
```

## Exports

- **Components:** `BodyPartSelector`, `FeedbackLocator`
- **Types:** `SelectionResult`, `SensationLocation`, `FeedbackLocatorProps`, `ChosenPointsDict`, `ImageSize`, `SegmentSizePx`

```tsx
import {
  BodyPartSelector,
  FeedbackLocator,
  type SelectionResult,
  type SensationLocation,
  type FeedbackLocatorProps,
  type ChosenPointsDict,
  type ImageSize,
  type SegmentSizePx,
} from '@neuroenglab/nel-feedback-ui';;

// BodyPartSelector: pass asset URLs, get selection (path, bodyPart, side)
// FeedbackLocator: pass image URL and optional SensationLocation, get updates via onUpdate
```

## Component API

### ***FeedbackLocator***

A grid-based overlay component that allows users to "paint" sensation areas over a body part image. It features a brush-based selection tool and automatic transparency masking.

**Props**

| Prop | Type | Description |
| :--- | :--- | :--- |
| `imageSrc` | `string` | **Required.** The URL or path of the image to display. |
| `onUpdate` | `(location: SensationLocation) => void` | **Required.** Callback triggered whenever the selection or coarseness changes. |
| `feedbackLocation` | `SensationLocation` | Optional. Current state to initialize or control the component. |
| `coarseness` | `number` | Optional. Default grid density (0-100). Defaults to `90`. |

**SensationLocation Object**

```typescript
{
  image_path: string;        // Path to the image used
  image_size: { w: number, h: number };      // Rendered dimensions of the image
  segment_size_px: { w: number, h: number }; // Size of a single grid cell in pixels
  coarsenessPercent: number; // The grid density used
  chosenPoints: {            // Parallel arrays of selected cell coordinates
    row: number[];
    col: number[];
  };
}
```

### ***BodyPartSelector***

A dropdown and radio button interface to navigate through available body part assets based on a specific naming convention.

#### Props

| Prop          | Type                                     | Description                                                                 |
|---------------|------------------------------------------|-----------------------------------------------------------------------------|
| assets        | `Record<string, string>`                 | Required. Map of keys to image URLs. Keys must follow the naming convention below. |
| onSelect      | `(selection: SelectionResult) => void`   | Required. Callback when a part or side is picked.                            |
| selectedPath  | `string`                                 | Optional. The URL of the currently selected asset for controlled state.      |

#### SelectionResult Object

```ts
{
  path: string;        // The URL of the specific asset
  bodyPart: string;    // The category (e.g., 'hand', 'arm')
  side: string | null; // 'left', 'right', or null for center/default
}
```

## Asset Naming Convention

The `BodyPartSelector` parses the keys of your `assets` object to build the UI. Keys should be formatted using hyphens as delimiters:

`part-side-Nice_Name.svg`

* part: The internal ID for the body area (e.g., hand, leg).
* side: The lateral identifier. Use an empty string for "Center" or a specific side (e.g., left, right).
* Nice_Name: The human-readable label. Underscores are automatically converted to spaces.

**Example Keys:**
* hand-left-Left_Hand: Becomes Part: hand, Side: left, Label: Left Hand.
* torso--Chest_Area: Becomes Part: torso, Side: , Label: Chest Area.


## Peer dependencies

- "react": "^18.0.0 || ^19.0.0",
- "react-dom": "^18.0.0 || ^19.0.0"