# JS Modularization Design

**Date:** 2026-03-07
**Goal:** Split the 6900-line `JS/bundle.js` into feature-based ES modules, bundled with esbuild. Zero functional changes — same output, cleaner source.

## Build System

- **Bundler:** esbuild (fast, zero-config, IIFE output)
- **Entry:** `src/main.js` → `JS/bundle.js`
- **Target:** ES2015 (CEP Chromium compatibility)
- **Output format:** IIFE (same as current)
- **Sourcemaps:** Yes (inline, for localhost:8092 debugging)
- **Built file:** Committed to git (no build step needed to install extension)
- **Commands:** `npm run build`, `npm run watch`

## Reactive State Store

Replaces closure globals with a lightweight observable store (~20 lines):

```js
// src/store.js
const listeners = new Map();
const state = { /* initial values */ };

export function get(key) { return state[key]; }
export function set(key, value) {
  if (state[key] === value) return;
  state[key] = value;
  (listeners.get(key) || []).forEach(fn => fn(value));
}
export function on(key, fn) {
  if (!listeners.has(key)) listeners.set(key, []);
  listeners.get(key).push(fn);
  return () => { /* unsubscribe */ };
}
```

- Shared UI state (pan, zoom, volume, theme, etc.) goes through the store
- AudioBus stays for audio-specific events (playback lifecycle, waveform updates)
- Modules declare dependencies explicitly through imports

## Module Structure

```
src/
  main.js              entry point, init orchestration
  store.js             reactive state store
  storage-keys.js      localStorage key constants
  logger.js            logging, diagnostics, export
  utils.js             debounce, throttle, formatters, toast
  storage.js           localStorage helpers, IndexedDB init & CRUD
  audio/
    bus.js             AudioBus pub/sub emitter
    state.js           AudioState (volume, mute, speed, loop)
    waveform.js        WaveformRenderer (canvas)
    unified-bar.js     timeline scrubber + waveform
    dock-control.js    mute button + inline slider
  media.js             loadImage, loadVideo, FFmpeg conversion
  history.js           history CRUD, thumbnails, favorites
  themes.js            theme definitions, apply, selector grid
  controls.js          pan/zoom, keyboard, drag-drop, fit modes
  settings.js          settings modal, all tabs
  license.js           license API, hardware ID, verification
  cep.js               CEP integration, state persistence
```

## Dependency Graph

```
store.js, storage-keys.js          ← foundation, no dependencies
logger.js, utils.js                ← standalone utilities
audio/bus.js                       ← standalone emitter
    ↓
storage.js                         ← store + keys + logger
audio/state.js                     ← bus + store + keys
audio/waveform.js                  ← standalone
    ↓
audio/unified-bar.js               ← bus + state + waveform + store
audio/dock-control.js              ← bus + state
themes.js                          ← store + keys + logger
    ↓
media.js                           ← store + storage + logger + audio/state
history.js                         ← store + storage + utils + logger
license.js                         ← store + keys + logger
    ↓
controls.js                        ← store + media + utils + logger
settings.js                        ← store + keys + audio/state + themes
cep.js                             ← store + keys
    ↓
main.js                            ← imports everything, orchestrates init
```

No circular dependencies. One-directional flow.

## What Changes

- `JS/bundle.js` is now generated from `src/` via esbuild
- New files: `package.json`, `build.js`, `src/` directory (~17 files)
- ChatGPT cleanup during migration:
  - Remove redundant null checks and unreachable defensive code
  - Trim excessive logging to essential events
  - Remove verbose intermediate objects
  - Simplify over-described variable names
  - Remove dead code and comments-as-code

## What Does NOT Change

- `index.html` — same `<script src="JS/bundle.js">` tag
- `style.css` — untouched
- `CSXS/manifest.xml` — untouched
- All user-facing behavior — zero functional changes
- localStorage keys and IndexedDB schema — identical
- `JS/Bundle-starter.js` — stays as-is
