|
| 1 | +# RadixVerse - AI Coding Agent Instructions |
| 2 | + |
| 3 | +## Project Overview |
| 4 | +**RadixVerse** is an interactive React + TypeScript web application for exploring alternative number systems and bases. It allows users to visualize and convert numbers across 6 distinct mathematical systems (standard, bijective, balanced, clock, sum, balsum) with support for radixes 2-35. |
| 5 | + |
| 6 | +## Architecture |
| 7 | + |
| 8 | +### Core System: Number Systems (src/utils.ts) |
| 9 | +The heart of the app is the `Radix` type - a configuration object for each number system that encodes: |
| 10 | +- Character set mapping (0-9, A-Z, or Unicode symbols like ➒, 🅩) |
| 11 | +- Low/high value ranges for each system |
| 12 | +- Conversion functions: `num2str()` (number→string), `str2num()` (string→number) |
| 13 | + |
| 14 | +**Key insight:** Each radix system has different arithmetic rules: |
| 15 | +- **Standard**: 0 to radix-1 (normal base conversion) |
| 16 | +- **Bijective**: 1 to radix (no zero; uses only 1 character) |
| 17 | +- **Balanced**: Negative to positive around zero (e.g., base-3 balanced uses -1,0,1) |
| 18 | +- **Clock**: Symmetric around zero with negative representation (used for hour-like notation) |
| 19 | +- **Sum**: Positional system using addition instead of multiplication (powers of r as multipliers) |
| 20 | +- **Balsum**: Hybrid balanced+sum system |
| 21 | + |
| 22 | +### UI Component Hierarchy |
| 23 | +``` |
| 24 | +App (src/app.tsx) |
| 25 | +├── Router + useStore (manages radixes, enabled radixes, value from URL params) |
| 26 | +├── Header (radix editor) |
| 27 | +├── Tabs navigation |
| 28 | +└── Route components (Show, Add, Multiply, Convert) |
| 29 | + └── Table/Tables (src/components/Table.tsx) |
| 30 | + └── renderValue (color coding by HSL hue based on value range) |
| 31 | +``` |
| 32 | + |
| 33 | +**Key pattern:** All route components receive `enabledRadixes` and generate table data locally via `computeProps()` - the app doesn't use global state management. |
| 34 | + |
| 35 | +### Data Persistence |
| 36 | +- **localStorage** keys: `theme`, `chars`, `radixes` |
| 37 | +- **URL parameters**: `?r=base2&r=base10&value=42&radix=base10` preserves state across sessions |
| 38 | +- Error handling: JSON parse failures in localStorage fallback silently and trigger error toast |
| 39 | + |
| 40 | +## Development Workflows |
| 41 | + |
| 42 | +### Build & Serve |
| 43 | +```bash |
| 44 | +npm start # Vite dev server on port 10000 with HMR |
| 45 | +npm run build # Production build with sourcemaps (vite build) |
| 46 | +npm run preview # Serve production build on port 10000 |
| 47 | +npm run check # TypeScript type checking (strict mode) |
| 48 | +npm run lint # ESLint on src/ with styled eslint-plugin |
| 49 | +npm test # Node native test runner (src/**/*.test.ts) |
| 50 | +npm run pre-commit # Check + Lint + Test + Build (CI equivalent) |
| 51 | +``` |
| 52 | + |
| 53 | +**Pre-commit hook equivalent:** Use `npm run pre-commit` before pushing. |
| 54 | + |
| 55 | +### Quality Standards |
| 56 | +- **TypeScript**: Strict mode enabled; ESNext target; JSX via React 19 compiler |
| 57 | +- **Linting**: ESLint 9 with @stylistic/eslint-plugin; Biome parser for CSS with Tailwind directives |
| 58 | +- **Testing**: Native Node.js test runner (src/**/*.test.ts); example in src/utils.test.ts |
| 59 | +- **Styling**: TailwindCSS v4 with DaisyUI components; source in src/app.css |
| 60 | + |
| 61 | +## Key Conventions |
| 62 | + |
| 63 | +### Number Conversion Pattern |
| 64 | +**Always use BigInt for arithmetic** - the Radix type stores values as `bigint` because JavaScript's `Number` overflows above 2^53. |
| 65 | + |
| 66 | +```typescript |
| 67 | +// ✓ Correct: Handle conversion via Radix methods |
| 68 | +const num: bigint = 42n; |
| 69 | +const str = num2str(num, radix); |
| 70 | +const back: bigint = str2num(str, radix); |
| 71 | + |
| 72 | +// ✗ Avoid: Direct number operations with large values |
| 73 | +const val = 999999999999999999; // Loses precision |
| 74 | +``` |
| 75 | + |
| 76 | +### Component Props Pattern |
| 77 | +Route components (Add, Multiply, Convert) receive `radixes: Radix[]` and compute derived state locally: |
| 78 | + |
| 79 | +```typescript |
| 80 | +const [ props, setProps ] = useState(computeProps(radix)); |
| 81 | +useEffect(() => setProps(computeProps(radix, columns, rows)), [ radix, columns, rows ]); |
| 82 | +``` |
| 83 | + |
| 84 | +This avoids prop drilling and keeps table layout computation close to rendering. |
| 85 | + |
| 86 | +### Error Handling |
| 87 | +- **Parser errors**: Catch, log, and call `updateError()` to show toast (10s auto-dismiss) |
| 88 | +- **Invalid input**: Sanitize via `sanitizeInput()` and throw with `allowedCharaters(radix)` message |
| 89 | +- **Missing radix in URL**: Throw error with helpful context (currently checked in app.tsx) |
| 90 | + |
| 91 | +### localStorage Serialization |
| 92 | +Only serialize safe fields (name, radix, system, chars, enabled) - omit Maps and computed fields: |
| 93 | + |
| 94 | +```typescript |
| 95 | +const rs = radixes.map(r => ({ |
| 96 | + name: r.name, |
| 97 | + radix: Number(r.radix), // BigInt → Number for JSON |
| 98 | + system: r.system, |
| 99 | + chars: r.chars, |
| 100 | + enabled: r.enabled |
| 101 | +})); |
| 102 | +``` |
| 103 | + |
| 104 | +Deserialization reconstructs Maps via `createRadix()`. |
| 105 | + |
| 106 | +## Integration Points |
| 107 | + |
| 108 | +### Radix Creation |
| 109 | +- `createRadixes(chars)` - generates all 35 radixes (radix 2-36) with default enabled set |
| 110 | +- `createRadix(num, system, chars, enabled, name)` - single radix with validation |
| 111 | +- **Validation**: System+radix combos have constraints (balanced/sum must be odd, clock even) |
| 112 | + |
| 113 | +### Character Sets |
| 114 | +- Default: 71-char balanced set (`baseBal71`) = -26 to 26 (Unicode symbols) + 0 + 1-9 + A-Z |
| 115 | +- Custom: Set via `setCharsLS()`, retrieved for Radix creation |
| 116 | +- **Constraint**: Custom char sets must have odd length and ≥ 71 chars for default radixes |
| 117 | + |
| 118 | +### URL State Recovery |
| 119 | +App.tsx `useEffect` runs once to: |
| 120 | +1. Detect `?clear-settings` → wipe localStorage |
| 121 | +2. Parse `?r=...` params → set enabled radixes |
| 122 | +3. Parse `?radix=...` → set working radix |
| 123 | +4. Parse `?value=...` → set initial number for conversion |
| 124 | + |
| 125 | +**Order matters:** Radix must be set before parsing value to avoid "Unknown radix" errors. |
| 126 | + |
| 127 | +## Testing & Debugging |
| 128 | + |
| 129 | +### Test Utils |
| 130 | +- `src/utils.test.ts` - examples of num2str, str2num, edge cases |
| 131 | +- **NaN handling**: `renderValue()` returns empty span for NaN values |
| 132 | + |
| 133 | +### Common Issues |
| 134 | +- **Color encoding**: HSL hue = `(val - low) / space * 300` (maps range to 300° color wheel) |
| 135 | +- **Table dimensions**: `columns = high - low + 1`; `rows = columns + (bijective ? 1 : 0)` |
| 136 | +- **Overflow**: Use `Number.isNaN()` to detect overflow before rendering |
| 137 | + |
| 138 | +## File Reference |
| 139 | +- [src/utils.ts](../src/utils.ts) - All number system logic, serialization |
| 140 | +- [src/app.tsx](../src/app.tsx) - Router, URL params, useStore hook |
| 141 | +- [src/components/Table.tsx](../src/components/Table.tsx) - Rendering logic, color mapping |
| 142 | +- [src/components/Show.tsx](../src/components/Show.tsx) - Example of `computeProps()` pattern |
| 143 | +- [vite.config.ts](../vite.config.ts) - PWA plugin, React compiler, Tailwind |
| 144 | +- [biome.json](../biome.json) - Linter rules (ESNext, no exhaustive deps, allow non-null assertions) |
0 commit comments