# Track D — Create React Hook Packages (`use-theme` + `use-keyboard-shortcuts`)

> **Repo:** `learning_ai_common_plat`
> **Can start:** Immediately (no dependencies)
> **Estimated time:** ~5 hours
> **Roadmap:** `../LOCAL_AI_CONSOLIDATION_ROADMAP.md` — sections 4.3 and 4.4.

---

## Goal

Create TWO new `@bytelyst/*` packages that eliminate ~550 lines of duplicated React hooks across the ecosystem.

---

## Package 1: `packages/use-theme/`

### Problem

Near-identical `useTheme()` hook duplicated across **6 web apps** — only the storage key and class-application strategy differ.

| Repo        | File                                 | Storage Key | Lines |
| ----------- | ------------------------------------ | ----------- | ----- |
| LocalMemGPT | `web/src/lib/use-theme.ts`           | `lmg-theme` | 49    |
| LLM Lab     | `dashboard/src/app/lib/use-theme.ts` | `llm-theme` | 48    |
| ChronoMind  | `web/src/lib/use-theme.ts`           | `cm-theme`  | ~50   |
| FlowMonk    | `web/src/lib/use-theme.ts`           | `fm-theme`  | ~50   |
| NomGap      | `web/src/lib/use-theme.ts`           | `ng-theme`  | ~50   |
| ActionTrail | `web/src/lib/use-theme.ts`           | `at-theme`  | ~50   |

### Reference Implementations

Read these two files to understand the pattern:

- `../learning_ai_local_memory_gpt/web/src/lib/use-theme.ts` (49 lines)
- `../learning_ai_local_llms/dashboard/src/app/lib/use-theme.ts` (48 lines)

### API

```ts
export function useTheme(options?: {
  storageKey?: string; // default: 'theme'
  attribute?: 'class' | 'data-theme'; // default: 'class'
}): {
  theme: 'light' | 'dark';
  setTheme: (t: 'light' | 'dark') => void;
  toggleTheme: () => void;
};
```

### Package Structure

```
packages/use-theme/
├── src/
│   ├── index.ts          # Public export
│   └── use-theme.ts      # Hook implementation
├── src/__tests__/
│   └── use-theme.test.ts # Vitest + @testing-library/react
├── package.json
├── tsconfig.json
└── vitest.config.ts
```

### Tests

- Toggles between light and dark
- Persists to localStorage with configurable key
- Applies class to `document.documentElement`
- Reads initial value from localStorage
- Falls back to `'dark'` when no stored value
- Supports `data-theme` attribute mode

### Commit

`feat(use-theme): shared React theme toggle hook`

---

## Package 2: `packages/use-keyboard-shortcuts/`

### Problem

Near-identical `useKeyboardShortcuts()` hook duplicated across **5+ web apps**, each with hardcoded if/else chains.

| Repo        | File                                                         |
| ----------- | ------------------------------------------------------------ |
| LocalMemGPT | `web/src/lib/use-keyboard-shortcuts.ts` (53 lines)           |
| LLM Lab     | `dashboard/src/app/lib/use-keyboard-shortcuts.ts` (57 lines) |
| ChronoMind  | `web/src/lib/use-keyboard-shortcuts.ts`                      |
| ActionTrail | `web/src/lib/use-keyboard-shortcuts.ts`                      |
| NoteLett    | `web/src/lib/use-keyboard-shortcuts.ts`                      |

### Reference Implementations

Read these two files to understand the pattern:

- `../learning_ai_local_memory_gpt/web/src/lib/use-keyboard-shortcuts.ts` (53 lines)
- `../learning_ai_local_llms/dashboard/src/app/lib/use-keyboard-shortcuts.ts` (57 lines)

### API

```ts
export interface ShortcutDef {
  key: string;
  meta?: boolean;
  shift?: boolean;
  alt?: boolean;
  handler: () => void;
  allowInInput?: boolean; // default: false — skip if focus is in input/textarea
  description?: string; // for help overlay / accessibility
}

export function useKeyboardShortcuts(shortcuts: ShortcutDef[]): void;
```

### Package Structure

```
packages/use-keyboard-shortcuts/
├── src/
│   ├── index.ts                    # Public export
│   └── use-keyboard-shortcuts.ts   # Hook implementation
├── src/__tests__/
│   └── use-keyboard-shortcuts.test.ts
├── package.json
├── tsconfig.json
└── vitest.config.ts
```

### Tests

- Fires handler on matching key combo
- Respects `meta`, `shift`, `alt` modifiers
- Skips handler when focus is in `<input>` or `<textarea>` (unless `allowInInput: true`)
- Handles multiple shortcuts simultaneously
- Cleans up event listener on unmount
- Handles empty shortcuts array

### Commit

`feat(use-keyboard-shortcuts): shared React keyboard shortcuts hook`

---

## Conventions (both packages)

- ESM (`"type": "module"`), TypeScript
- React 19 as `peerDependency`
- Vitest + `@testing-library/react` + `jsdom` for tests
- Extend `../../tsconfig.base.json`
- `exports` field in `package.json`
- Build: `tsc` → `dist/`
- Publish to Gitea registry

---

## Scope Boundary

**DO NOT** migrate consumer repos yet — migrations can happen as a follow-up ecosystem sweep. Only create the packages, write tests, and publish.
