diff --git a/README.md b/README.md
index b24d5e3..47e5ed3 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,129 @@
-# Armco Template for the tech stack: React, TS, Dart Sass, Redux Tookkit, react-redux, react browser routing, TS based plop generator
\ No newline at end of file
+# @armco/icon-spot
+
+> Icon Management UI for Armco Platform
+
+icon-spot is a React-based icon browser and editor that provides browsing, searching, styling, and downloading capabilities for the Armco icon library.
+
+## Features
+
+- **Icon Browser**: Paginated grid with search and tag filtering
+- **View Modes**: Comfy, Compact, and List layouts
+- **Style Editor**: Customize fill, stroke, background colors
+- **SVG Download**: Export styled icons as SVG files
+- **Favorites**: Mark icons for quick access
+- **Selection Mode**: Multi-select for batch operations
+- **Similar Icons**: ML-powered icon recommendations
+
+## Installation
+
+```bash
+pnpm add @armco/icon-spot
+```
+
+## Peer Dependencies
+
+```json
+{
+ "@armco/components": "^0.0.60",
+ "@armco/configs": "^0.0.11",
+ "@armco/icon": "^0.0.10",
+ "@armco/types": "^0.0.18",
+ "@armco/utils": "^0.0.29",
+ "@reduxjs/toolkit": "^1.8.1",
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0",
+ "react-redux": "^8.0.1",
+ "react-router-dom": "^6.13.0"
+}
+```
+
+## Usage
+
+### Basic Import
+
+```tsx
+import Icons from "@armco/icon-spot"
+import { IconInfo, IconsMerge, Clusters } from "@armco/icon-spot"
+```
+
+### With Redux Setup
+
+```tsx
+import { configureStore } from "@reduxjs/toolkit"
+import { Provider } from "react-redux"
+import { BrowserRouter, Routes, Route } from "react-router-dom"
+import Icons, { IconInfo, IconsMerge } from "@armco/icon-spot"
+
+// Import reducers from icon-spot
+import iconsReducer from "@armco/icon-spot/Icons.slice"
+import iconInfoReducer from "@armco/icon-spot/IconInfo.slice"
+
+const store = configureStore({
+ reducer: {
+ icons: iconsReducer,
+ iconInfo: iconInfoReducer,
+ },
+})
+
+function App() {
+ return (
+
+
+
+ } />
+ } />
+ } />
+
+
+
+ )
+}
+```
+
+## Exported Components
+
+| Component | Description |
+|-----------|-------------|
+| `Icons` | Main icon browser with filters and grid |
+| `IconInfo` | Icon detail page with editor |
+| `IconsMerge` | Multi-icon merge tool |
+| `Clusters` | ML-generated cluster visualization |
+
+## Development
+
+```bash
+# Install dependencies
+pnpm install
+
+# Start development server
+pnpm dev
+
+# Build library
+pnpm build
+```
+
+## Project Structure
+
+```
+src/
+├── index.tsx # Exports
+├── store.ts # Redux store
+├── Icons.tsx # Main browser
+├── IconsList.tsx # Icon grid
+├── IconTile.tsx # Icon card
+├── IconInfo.tsx # Detail view
+├── IconController.tsx # Detail controls
+├── IconEditor.tsx # Style editor
+└── helper.ts # Utilities
+```
+
+## Documentation
+
+- [SPECS.md](docs/SPECS.md) - Feature specifications
+- [DESIGN.md](docs/DESIGN.md) - Architecture diagrams
+- [DEBT.md](docs/DEBT.md) - Technical debt register
+- [READINESS.md](docs/READINESS.md) - Production checklist
+
+## License
+
+ISC
diff --git a/docs/DEBT.md b/docs/DEBT.md
new file mode 100644
index 0000000..9f4c3e7
--- /dev/null
+++ b/docs/DEBT.md
@@ -0,0 +1,242 @@
+# icon-spot - Technical Debt Register
+
+## ✅ Recently Fixed
+
+### 1. ~~Hardcoded ML Service URL~~ (FIXED)
+**Location:** `src/IconController.tsx` (Line ~52)
+**Issue:** ~~ML service URL was hardcoded~~
+**Resolution:** ✅ Changed to use `API_CONFIG.TASKER[process.env.NODE_ENV]` for environment-aware configuration.
+**Date Fixed:** Session
+
+---
+
+## 🔴 Critical (Must Fix Before Production)
+
+### 2. Console.log in Production Code
+**Locations:** Multiple files
+| File | Line | Usage |
+|------|------|-------|
+| `IconsList.tsx` | ~65 | `console.error(error)` |
+| `IconMergeContainer.tsx` | ~118 | `console.log(pathList)` |
+| `IconMergeContainer.tsx` | ~122 | `console.log(e)` (drag event) |
+
+**Risk:** Pollutes browser console, exposes internal data
+**Resolution:** Remove or replace with proper logging/error handling
+**Effort:** Low (1 hour)
+
+---
+
+### 3. Missing Error Boundaries
+**Issue:** No React error boundaries around components
+**Risk:** Single component error crashes entire app
+**Resolution:** Add error boundaries at route level
+**Effort:** Medium (4-6 hours)
+
+---
+
+### 4. No Loading States for Icon Controller
+**Location:** `src/IconController.tsx`
+**Issue:** Similar icons fetch has no loading indicator
+**Risk:** User sees empty space while loading
+**Resolution:** Add loading state for ML results
+**Effort:** Low (2 hours)
+
+---
+
+## 🟠 High Priority
+
+### 5. TypeScript `any` Types
+**Locations:** Various
+| File | Issue |
+|------|-------|
+| `Icons.slice.ts` | `state: any` in selectors |
+| `IconInfo.slice.ts` | `state: any` in selectors |
+| `Icons.tsx` | `clickHandler(e: any)` |
+| `IconTile.tsx` | `navigate` param typing |
+
+**Resolution:** Define proper RootState type and use throughout
+**Effort:** Medium (4-6 hours)
+
+---
+
+### 6. Incomplete Favorites Implementation
+**Location:** `Icons.slice.ts`
+**Issue:** `removeFavorite` has incorrect splice logic
+```typescript
+removeFavorite: (state, action: PayloadAction) => {
+ state.favorites.splice(state.favorites.indexOf(action.payload))
+ // Missing second argument to splice!
+}
+```
+**Risk:** Removes from wrong index to end
+**Resolution:** Fix splice or use filter
+**Effort:** Low (30 minutes)
+
+---
+
+### 7. Favorites Not Persisted
+**Issue:** Favorites stored only in Redux, lost on refresh
+**Resolution:** Sync to localStorage or backend
+**Effort:** Medium (4-6 hours)
+
+---
+
+### 8. Dead/Commented Code
+**Locations:**
+| File | Lines | Description |
+|------|-------|-------------|
+| `IconTile.tsx` | 41-82 | `generateTools` function |
+| `IconTile.tsx` | 100-103 | `iconTools` variable |
+| `IconController.tsx` | 37 | `getSuggestions` commented |
+
+**Resolution:** Remove or complete implementation
+**Effort:** Low (1-2 hours)
+
+---
+
+### 9. Test Data in Production Code
+**Location:** `src/IconMergeContainer.tsx`
+**Issue:** 100+ lines of hardcoded `iconTest` array
+```typescript
+const iconTest = [
+ { name: "CiAlignBottom", icon: "...", ... },
+ // 3 full icon objects
+]
+```
+**Risk:** Shipped to production, increases bundle size
+**Resolution:** Move to test file or mock data folder
+**Effort:** Low (1 hour)
+
+---
+
+## 🟡 Medium Priority
+
+### 10. Missing Accessibility
+**Issue:** No ARIA labels, keyboard navigation incomplete
+| Component | Issue |
+|-----------|-------|
+| `IconTile` | No `role` attribute |
+| `IconStyleSelector` | Color pickers need labels |
+| `FavoritesList` | No keyboard navigation |
+
+**Resolution:** Add ARIA attributes and keyboard handlers
+**Effort:** Medium (8-12 hours)
+
+---
+
+### 11. No Unit Tests
+**Issue:** 0% test coverage
+**Files needing tests:**
+- `helper.ts` - `downloadSvg()`, `complement()`
+- `Icons.slice.ts` - Reducer actions
+- `IconInfo.slice.ts` - Reducer actions
+- `IconTile.tsx` - Click handlers
+
+**Resolution:** Add Vitest tests
+**Effort:** High (16-24 hours)
+
+---
+
+### 12. Inconsistent File Naming
+**Issue:** Mixed naming conventions
+- Some: `IconsList.component.scss`
+- Some: `IconController.component.scss`
+- Index: `IconInfo.slice.ts` vs folder `IconInfoSlice/`
+
+**Root level folders unused:** `IconController/`, `IconEditor/`, `IconInfo/`, `IconInfoSlice/`, `IconStyleSelector/`, `IconTile/`, `Icons/`, `IconsMerge/`
+
+**Resolution:** Choose convention, refactor file structure
+**Effort:** Medium (4-6 hours)
+
+---
+
+### 13. Large Initial Fetch
+**Location:** `src/IconsList.tsx`
+**Issue:** Fetches 2000 icons on load
+```typescript
+fetchIconsPage(2000, 0, filters, ...)
+```
+**Risk:** Slow initial load, high memory usage
+**Resolution:** Implement true pagination or virtual scrolling
+**Effort:** Medium (6-8 hours)
+
+---
+
+### 14. Missing Loading Skeleton
+**Issue:** Only spinner shown during load
+**Resolution:** Add skeleton components matching grid layout
+**Effort:** Low (3-4 hours)
+
+---
+
+## 🟢 Low Priority
+
+### 15. Upload Button Non-functional
+**Location:** `src/IconsList.tsx`
+**Issue:** Upload button exists but has no handler
+```tsx
+
+```
+**Resolution:** Implement or remove
+**Effort:** High to implement (16+ hours)
+
+---
+
+### 16. Categories Menu Non-functional
+**Location:** `src/IconsList.tsx`
+**Issue:** Categories dropdown with hardcoded items
+```tsx
+splitOptions={[
+ { label: "All" },
+ { label: "Business" },
+ { label: "Medical" },
+]}
+```
+**Resolution:** Wire to actual category data
+**Effort:** Medium (4-6 hours)
+
+---
+
+### 17. Empty Top-Level Folders
+**Location:** Root directory
+**Issue:** Multiple empty folders from old structure:
+```
+IconController/
+IconEditor/
+IconInfo/
+IconInfoSlice/
+IconStyleSelector/
+IconTile/
+Icons/
+IconsMerge/
+Clusters/
+helper/
+```
+**Resolution:** Remove or migrate files into them
+**Effort:** Low (1 hour)
+
+---
+
+### 18. Outdated README
+**Location:** `README.md`
+**Issue:** Single line, no setup instructions
+**Resolution:** Update with proper documentation
+**Effort:** Low (2-3 hours)
+
+---
+
+## Debt Resolution Priority Matrix
+
+| Priority | Items | Total Effort |
+|----------|-------|--------------|
+| 🔴 Critical | 4 items | 8-12 hours |
+| 🟠 High | 5 items | 11-16 hours |
+| 🟡 Medium | 5 items | 37-54 hours |
+| 🟢 Low | 4 items | 23-30 hours |
+
+**Recommended Sprint 1 Focus:**
+1. Fix hardcoded ML service URL
+2. Remove console.log statements
+3. Fix removeFavorite splice bug
+4. Remove test data from IconMergeContainer
+5. Add RootState typing to Redux selectors
diff --git a/docs/DESIGN.md b/docs/DESIGN.md
new file mode 100644
index 0000000..0e8715b
--- /dev/null
+++ b/docs/DESIGN.md
@@ -0,0 +1,219 @@
+# icon-spot - Design Documentation
+
+## Component Architecture
+
+```
+┌─────────────────────────────────────────────────────────────────────────────────┐
+│ icon-spot Application │
+│ │
+│ ┌────────────────────────────────────────────────────────────────────────────┐ │
+│ │ Redux Store (store.ts) │ │
+│ │ ┌─────────────────────────┐ ┌─────────────────────────┐ │ │
+│ │ │ Icons.slice.ts │ │ IconInfo.slice.ts │ │ │
+│ │ │ - favorites │ │ - iconStyles │ │ │
+│ │ │ - selectedTag │ │ (fill, stroke, bg) │ │ │
+│ │ └─────────────────────────┘ └─────────────────────────┘ │ │
+│ └────────────────────────────────────────────────────────────────────────────┘ │
+│ │ │
+│ ┌──────────────┴──────────────┐ │
+│ ▼ ▼ │
+│ ┌──────────────────────────────┐ ┌──────────────────────────────┐ │
+│ │ Icons.tsx │ │ IconInfo.tsx │ │
+│ │ (Main Browser Container) │ │ (Detail View Container) │ │
+│ └─────────────┬────────────────┘ └─────────────┬────────────────┘ │
+│ │ │ │
+│ ┌────────┴────────┐ ┌───────┴───────┐ │
+│ ▼ ▼ ▼ ▼ │
+│ ┌──────────┐ ┌──────────────┐ ┌────────────┐ ┌──────────────┐ │
+│ │ Filters │ │ IconsList │ │IconController│ │ IconEditor │ │
+│ │ (Sidebar)│ │ (Grid) │ │ (Main) │ │ (Panel) │ │
+│ └──────────┘ └──────┬───────┘ └──────┬──────┘ └──────────────┘ │
+│ │ │ │
+│ ┌────────┼────────┐ │ │
+│ ▼ ▼ ▼ ▼ │
+│ ┌────────┐ ┌────────┐ ┌────────────────────┐ │
+│ │IconTile│ │IconTile│ │IconStyleSelector │ │
+│ └────────┘ └────────┘ └────────────────────┘ │
+│ │
+└─────────────────────────────────────────────────────────────────────────────────┘
+```
+
+---
+
+## Data Flow: Icon Browser
+
+```
+┌───────────────┐ ┌───────────────┐ ┌───────────────┐
+│ User Action │────▶│ Component │────▶│ API Call │
+│ │ │ useState │ │ getStatic │
+└───────────────┘ └───────────────┘ └───────┬───────┘
+ │
+ ▼
+ ┌───────────────┐
+ │ ArmoryStatic │
+ │ Backend │
+ └───────┬───────┘
+ │
+ ┌──────────────────────────────┘
+ ▼
+ ┌───────────────┐ ┌───────────────┐
+ │ Response Body │────▶│ setIcons │
+ │ IconResponse[]│ │ setPage │
+ └───────────────┘ └───────┬───────┘
+ │
+ ▼
+ ┌───────────────┐
+ │ IconTile x N │
+ │ (Grid) │
+ └───────────────┘
+```
+
+---
+
+## Data Flow: Icon Style Update
+
+```
+┌───────────────────────────────────────────────────────────────────────────────┐
+│ Style Update Flow │
+│ │
+│ ┌────────────────┐ ┌────────────────┐ ┌────────────────┐ │
+│ │AdvancedColor │───▶│setIconStyles │───▶│ iconInfoSlice │ │
+│ │Picker onChange │ │ (local state) │ │ (Redux action) │ │
+│ └────────────────┘ └────────────────┘ └───────┬────────┘ │
+│ │ │
+│ ┌──────────────────────────────┘ │
+│ ▼ │
+│ ┌────────────────┐ ┌────────────────┐ │
+│ │ iconInfo.state │───▶│ useSelector │ │
+│ │ { iconStyles } │ │ getIconStyles │ │
+│ └────────────────┘ └───────┬────────┘ │
+│ │ │
+│ ┌─────────────────────┼─────────────────────┐ │
+│ ▼ ▼ ▼ │
+│ ┌────────────────┐ ┌────────────────┐ ┌────────────────┐ │
+│ │ IconTile │ │ IconController │ │ Icon Preview │ │
+│ │ (fillColor) │ │ (all styles) │ │ (all styles) │ │
+│ └────────────────┘ └────────────────┘ └────────────────┘ │
+└───────────────────────────────────────────────────────────────────────────────┘
+```
+
+---
+
+## Data Flow: Icon Download
+
+```
+User clicks Download
+ │
+ ▼
+┌─────────────────┐
+│ IconController │
+│ onClick handler │
+└────────┬────────┘
+ │
+ ▼
+┌─────────────────┐ ┌─────────────────┐
+│ downloadSvg() │───▶│ applyStyles() │
+│ helper.ts │ │ @armco/icon │
+└────────┬────────┘ └─────────────────┘
+ │
+ ▼
+┌─────────────────┐ ┌─────────────────┐
+│ DOMParser │───▶│ btoa() encode │
+│ Parse SVG │ │ Base64 string │
+└─────────────────┘ └────────┬────────┘
+ │
+ ▼
+ ┌─────────────────┐
+ │ Create tag │
+ │ Trigger download│
+ └─────────────────┘
+```
+
+---
+
+## Feature Completion Matrix
+
+| Feature | Status | What's Done | What's Pending | Potential Additions |
+|---------|--------|-------------|----------------|---------------------|
+| **Icon Grid** | ✅ Complete | Pagination, search, filters | - | Infinite scroll |
+| **View Modes** | ✅ Complete | Comfy, Compact, List | - | Custom grid size |
+| **Tag Filtering** | ✅ Complete | Sidebar filter, tag click | - | Multi-tag select |
+| **Search** | ✅ Complete | Debounced text search | - | Fuzzy matching |
+| **Icon Preview** | ✅ Complete | Multi-size display | - | Custom size input |
+| **Style Editor** | ✅ Complete | Fill, stroke, bg, width | - | Gradients, shadows |
+| **SVG Download** | ✅ Complete | Styled SVG download | - | PNG/WebP export |
+| **Favorites** | ⚠️ Partial | Add to favorites | Persist to backend | Sync with account |
+| **Selection Mode** | ✅ Complete | Multi-select UI | - | Bulk operations |
+| **Icon Merge** | ❌ Incomplete | SVG parsing, UI shell | Drag/drop, save | Path editing |
+| **Similar Icons** | ⚠️ Partial | ML integration | Requires local service | Pre-computed cache |
+| **Clusters** | ⚠️ Partial | Display only | Cluster navigation | Click to filter |
+| **Animation** | ⚠️ Route only | Navigation exists | Separate module | Integrate inline |
+| **Categories** | ❌ Stub | Menu button exists | Category data | Category pages |
+| **Upload** | ❌ Stub | Button exists | Upload flow | Validation, preview |
+
+---
+
+## Directory Structure
+
+```
+icon-spot/
+├── src/
+│ ├── index.tsx # Exports
+│ ├── store.ts # Redux store config
+│ ├── types.ts # TypeScript types
+│ ├── helper.ts # Utility functions
+│ │
+│ ├── Icons.tsx # Main browser container
+│ ├── Icons.slice.ts # Icons Redux slice
+│ ├── IconsList.tsx # Icon grid component
+│ ├── IconsList.component.scss # Grid styles
+│ │
+│ ├── IconInfo.tsx # Detail view container
+│ ├── IconInfo.slice.ts # Icon info Redux slice
+│ │
+│ ├── IconTile.tsx # Icon card component
+│ ├── IconTile.component.scss # Card styles
+│ │
+│ ├── IconController.tsx # Detail view main
+│ ├── IconController.component.scss
+│ │
+│ ├── IconEditor.tsx # Style editor wrapper
+│ ├── IconStyleSelector.tsx # Color/stroke controls
+│ │
+│ ├── FavoritesList.tsx # Favorites panel
+│ ├── FavoritesList.component.scss
+│ │
+│ ├── IconsMerge.tsx # Merge container
+│ ├── IconMergeContainer.tsx # Merge implementation
+│ ├── IconMergeContainer.component.scss
+│ │
+│ └── Clusters.tsx # ML clusters view
+│
+├── build-tools/
+│ ├── build.sh # Build script
+│ ├── generate-module.js # Module generator
+│ └── post-processor.js # Build post-processing
+│
+├── vite.config.ts # Library build config
+├── vite-dev.config.ts # Dev server config
+├── vite-run.config.ts # Standalone run config
+├── tsconfig.json # TypeScript config
+└── package.json # Package definition
+```
+
+---
+
+## Component Props Summary
+
+| Component | Key Props |
+|-----------|-----------|
+| `Icons` | None (self-contained) |
+| `IconInfo` | Route params: `group`, `name` |
+| `IconsList` | `filters?`, `tags?` |
+| `IconTile` | `icon`, `onClick?`, `selectable?`, `type?`, `iconSize?` |
+| `IconController` | `group?`, `icon?`, `name?` |
+| `IconEditor` | `layout?` |
+| `IconStyleSelector` | `iconStyles?`, `setIconStyles`, `layout?` |
+| `FavoritesList` | `classes?` |
+| `IconsMerge` | Location state: `icons` |
+| `Clusters` | None |
diff --git a/docs/READINESS.md b/docs/READINESS.md
new file mode 100644
index 0000000..cf97d63
--- /dev/null
+++ b/docs/READINESS.md
@@ -0,0 +1,249 @@
+# icon-spot - Production Readiness Checklist
+
+## Overview
+
+This document tracks production readiness for icon-spot as a standalone application and as an npm package consumed by other Armco applications.
+
+---
+
+## 1. Build & Packaging
+
+| Item | Status | Notes |
+|------|--------|-------|
+| ESM Build | ✅ | `build/es/` output |
+| CJS Build | ✅ | `build/cjs/` output |
+| Type Definitions | ✅ | `build/types/` output |
+| CSS Injection | ✅ | Using `vite-plugin-lib-inject-css` |
+| Tree Shaking | ✅ | Configured in rollup |
+| External Dependencies | ✅ | React, armco libs externalized |
+| Source Maps | ❌ | Not generated in production build |
+| Bundle Size Analysis | ❌ | No bundle analyzer configured |
+
+### Actions Required:
+- [ ] Enable source maps for debugging
+- [ ] Add bundle size analysis to CI
+
+---
+
+## 2. Code Quality
+
+| Item | Status | Notes |
+|------|--------|-------|
+| TypeScript Strict Mode | ⚠️ | Some `any` types present |
+| ESLint Configured | ✅ | Via eslintConfig in package.json |
+| Prettier Configured | ✅ | Using prettier-config-nick |
+| No Console Statements | ❌ | console.log/error in code |
+| Error Boundaries | ❌ | No React error boundaries |
+| PropTypes/Type Safety | ⚠️ | Types defined but not strict |
+
+### Actions Required:
+- [ ] Remove all console.log/error statements
+- [ ] Add error boundaries at route level
+- [ ] Enforce strict TypeScript
+
+---
+
+## 3. Testing
+
+| Item | Status | Notes |
+|------|--------|-------|
+| Unit Tests | ❌ | 0% coverage |
+| Component Tests | ❌ | No tests |
+| Integration Tests | ❌ | No tests |
+| Visual Regression | ❌ | No Storybook/Chromatic |
+| Accessibility Tests | ❌ | No a11y testing |
+
+### Actions Required:
+- [ ] Add Vitest configuration
+- [ ] Write tests for helper functions
+- [ ] Write tests for Redux slices
+- [ ] Add component tests for IconTile, IconsList
+
+---
+
+## 4. Accessibility
+
+| Item | Status | Notes |
+|------|--------|-------|
+| Semantic HTML | ⚠️ | Basic semantics |
+| ARIA Labels | ❌ | Missing throughout |
+| Keyboard Navigation | ⚠️ | Partial support |
+| Focus Management | ❌ | Not implemented |
+| Screen Reader Testing | ❌ | Not tested |
+| Color Contrast | ⚠️ | Not verified |
+
+### Actions Required:
+- [ ] Add ARIA labels to interactive elements
+- [ ] Implement keyboard navigation for icon grid
+- [ ] Add focus ring styles
+- [ ] Test with screen reader
+
+---
+
+## 5. Performance
+
+| Item | Status | Notes |
+|------|--------|-------|
+| Code Splitting | ⚠️ | All exports in single chunk |
+| Lazy Loading | ❌ | All components loaded eagerly |
+| Virtual Scrolling | ❌ | Loads all 2000 icons |
+| Memoization | ⚠️ | Minimal use of useMemo/useCallback |
+| Image Optimization | ⚠️ | Base64 SVGs (reasonable) |
+| Bundle Size | ⚠️ | Not optimized |
+
+### Actions Required:
+- [ ] Implement virtual scrolling for icon grid
+- [ ] Add React.lazy for route components
+- [ ] Profile and optimize re-renders
+- [ ] Reduce initial fetch size
+
+---
+
+## 6. State Management
+
+| Item | Status | Notes |
+|------|--------|-------|
+| Redux DevTools | ✅ | Configured with name |
+| State Persistence | ❌ | Lost on refresh |
+| Error State | ❌ | No error handling in slices |
+| Loading State | ⚠️ | Local component state only |
+| Typed Selectors | ❌ | Uses `any` |
+
+### Actions Required:
+- [ ] Add RootState type to store
+- [ ] Implement redux-persist for favorites
+- [ ] Add error and loading states to slices
+
+---
+
+## 7. API Integration
+
+| Item | Status | Notes |
+|------|--------|-------|
+| Error Handling | ❌ | No user-facing error messages |
+| Retry Logic | ❌ | No retry on failure |
+| Loading States | ⚠️ | Basic spinner only |
+| Caching | ❌ | No response caching |
+| Offline Support | ❌ | Not considered |
+
+### Actions Required:
+- [ ] Add error toasts for failed requests
+- [ ] Implement retry logic for transient failures
+- [ ] Add response caching (react-query or SWR)
+
+---
+
+## 8. Environment Configuration
+
+| Item | Status | Notes |
+|------|--------|-------|
+| Environment Variables | ⚠️ | .env exists |
+| Config Validation | ❌ | No validation |
+| API URL Configuration | ✅ | Now uses `API_CONFIG.TASKER[process.env.NODE_ENV]` |
+| Feature Flags | ❌ | Not implemented |
+
+### Actions Required:
+- [x] ~~Move all URLs to environment config~~ ✅ DONE
+- [ ] Add config validation on startup
+- [ ] Implement feature flags for incomplete features
+
+---
+
+## 9. Documentation
+
+| Item | Status | Notes |
+|------|--------|-------|
+| README | ✅ | Updated with comprehensive docs |
+| API Documentation | ❌ | No docs |
+| Component Storybook | ❌ | Not set up |
+| Usage Examples | ✅ | Provided in README |
+| Changelog | ❌ | Not maintained |
+
+### Actions Required:
+- [ ] Write comprehensive README
+- [ ] Add JSDoc comments to exports
+- [ ] Consider Storybook setup
+- [ ] Maintain CHANGELOG.md
+
+---
+
+## 10. Security
+
+| Item | Status | Notes |
+|------|--------|-------|
+| XSS Prevention | ⚠️ | React escapes by default |
+| SVG Sanitization | ❌ | Direct innerHTML via dangerouslySetInnerHTML |
+| CORS Handling | ⚠️ | Backend configured |
+| Dependency Audit | ⚠️ | Needs `npm audit` |
+
+### Actions Required:
+- [ ] Sanitize SVG content before rendering
+- [ ] Run `npm audit fix`
+- [ ] Review third-party dependencies
+
+---
+
+## Production Readiness Score
+
+| Category | Score | Max |
+|----------|-------|-----|
+| Build & Packaging | 5 | 8 |
+| Code Quality | 2 | 6 |
+| Testing | 0 | 5 |
+| Accessibility | 1 | 5 |
+| Performance | 1 | 6 |
+| State Management | 1 | 5 |
+| API Integration | 0.5 | 5 |
+| Environment Config | 1 | 4 |
+| Documentation | 0 | 5 |
+| Security | 1.5 | 4 |
+| **Total** | **13** | **53** |
+
+**Readiness Level: 25% - NOT READY FOR PRODUCTION**
+
+---
+
+## Minimum Viable Production Checklist
+
+### Week 1: Critical Fixes
+- [ ] Remove hardcoded ML service URL
+- [ ] Remove console.log statements
+- [ ] Fix removeFavorite splice bug
+- [ ] Add error boundaries
+
+### Week 2: Quality & Stability
+- [ ] Add RootState typing
+- [ ] Remove test data from production code
+- [ ] Clean up unused folders
+- [ ] Update README
+
+### Week 3: UX Improvements
+- [ ] Add error toasts for failed requests
+- [ ] Implement loading skeletons
+- [ ] Reduce initial fetch size (500 max)
+
+### Week 4: Testing & Polish
+- [ ] Add unit tests for helpers
+- [ ] Add tests for Redux slices
+- [ ] Run accessibility audit
+- [ ] Run npm audit
+
+---
+
+## Package Consumption Checklist
+
+For apps consuming `@armco/icon-spot`:
+
+| Requirement | Provided |
+|-------------|----------|
+| Peer dependencies documented | ✅ |
+| Type definitions | ✅ |
+| CSS bundled | ✅ |
+| Tree-shakeable | ✅ |
+| Redux store required | ⚠️ Needs documentation |
+| Router required | ⚠️ Needs documentation |
+
+### Integration Requirements:
+1. Wrap with Redux Provider containing `icons` and `iconInfo` reducers
+2. Wrap with BrowserRouter
+3. Configure ArmoryStatic API endpoint in environment
diff --git a/docs/SPECS.md b/docs/SPECS.md
new file mode 100644
index 0000000..f2f210b
--- /dev/null
+++ b/docs/SPECS.md
@@ -0,0 +1,223 @@
+# icon-spot - Feature Specifications
+
+## Overview
+
+**icon-spot** is a React-based icon management application that provides browsing, searching, editing, and downloading capabilities for the Armco icon library. It consumes the ArmoryStatic backend API.
+
+---
+
+## Technology Stack
+
+| Component | Technology | Version |
+|-----------|------------|---------|
+| UI Framework | React | 18/19 |
+| State Management | Redux Toolkit | 2.x |
+| Routing | react-router-dom | 6.x |
+| Build Tool | Vite | 5.x |
+| Styling | SCSS (Dart Sass) | 1.x |
+| Language | TypeScript | 5.x |
+| Package Format | ESM + CJS | - |
+
+---
+
+## Exported Components
+
+### Primary Exports
+
+| Component | Description | Export Path |
+|-----------|-------------|-------------|
+| `Icons` | Main icon browser with filters, search, pagination | `@armco/icon-spot` (default) |
+| `IconInfo` | Individual icon detail page with editor | `@armco/icon-spot/IconInfo` |
+| `IconsMerge` | Multi-icon selection and merge tool | `@armco/icon-spot/IconsMerge` |
+| `Clusters` | ML-generated icon cluster visualization | `@armco/icon-spot/Clusters` |
+
+---
+
+## Features
+
+### 1. Icon Browser (`Icons.tsx` → `IconsList.tsx`)
+
+**Description:** Main grid view for browsing all available icons
+
+**Capabilities:**
+- Paginated icon grid (2000 icons per fetch, 100 per page)
+- Tag-based filtering via sidebar
+- Text search with debounce (1 second)
+- Multiple view modes: Comfy, Compact, List
+- Icon style customization (fill, stroke, background)
+- Selection mode for multi-icon operations
+
+**API Integration:**
+- `GET /icon/tag/all?variants=gt100` - Fetch tags
+- `GET /icon/page?pageSize=2000&from=0` - Fetch icons
+
+**State Management:**
+- `Icons.slice.ts` - Manages favorites and selected tag
+- `IconInfo.slice.ts` - Manages icon styles
+
+---
+
+### 2. Icon Detail View (`IconInfo.tsx` → `IconController.tsx`)
+
+**Description:** Detailed view for individual icon with download and styling
+
+**Capabilities:**
+- Icon preview at multiple sizes (3rem, 7rem)
+- Real-time style editing (fill, stroke, background, stroke-width)
+- SVG download with applied styles
+- Navigate to animation editor
+- Similar icon suggestions via ML service
+- Tag display and editing
+- Breadcrumb navigation
+
+**API Integration:**
+- `GET /static/{group}_{name}-black.png` - Get PNG for ML
+- `POST http://localhost:5002/api/similar-icon-paths` - Get similar icons
+- `POST /icon/png-to-svg` - Convert similar PNGs to SVG
+
+**Helper Functions:**
+- `downloadSvg()` - Generate styled SVG download
+- `complement()` - Calculate complementary color for contrast
+
+---
+
+### 3. Icon Style Selector (`IconStyleSelector.tsx`)
+
+**Description:** Color picker panel for customizing icon appearance
+
+**Properties:**
+- `fillColor` - Interior color
+- `strokeColor` - Border/outline color
+- `strokeWidth` - Stroke thickness (1-10)
+- `bgColor` - Background color
+
+**Components Used:**
+- `AdvancedColorPicker` - Color selection
+- `Slider` - Stroke width adjustment
+
+---
+
+### 4. Icon Tile (`IconTile.tsx`)
+
+**Description:** Individual icon card component
+
+**Display Modes:**
+- `comfy` - Large grid cells
+- `compact` - Small grid cells
+- `list` - Horizontal list layout
+
+**Interactions:**
+- Click → Navigate to icon detail
+- Hover → Change fill color
+- Footer click → Copy icon link to clipboard
+- Selectable mode → Checkbox selection
+
+---
+
+### 5. Favorites List (`FavoritesList.tsx`)
+
+**Description:** Right panel showing user's favorited icons
+
+**Features:**
+- Login state awareness
+- Sync warning for anonymous users
+- Animated add/remove transitions
+- Login prompt for guests
+
+**State Source:** `Icons.slice.ts` → `getFavorites`
+
+---
+
+### 6. Icon Merge (`IconsMerge.tsx` → `IconMergeContainer.tsx`)
+
+**Description:** Tool for combining multiple icons
+
+**Current Status:** Partial implementation
+- SVG parsing and display
+- Path extraction from SVGs
+- Drag-and-drop started (incomplete)
+
+---
+
+### 7. Clusters (`Clusters.tsx`)
+
+**Description:** Visualization of ML-generated icon clusters
+
+**API Integration:**
+- `GET /icon/clusters` - Fetch cluster data
+
+**Display:** Grid of cluster groups with PNG thumbnails
+
+---
+
+## Redux Store Structure
+
+```typescript
+{
+ icons: {
+ favorites: IconTileProps[], // Favorited icons
+ selectedTag?: string // Currently selected filter tag
+ },
+ iconInfo: {
+ iconStyles?: {
+ fillColor?: string,
+ strokeColor?: string,
+ bgColor?: string,
+ strokeWidth?: string
+ }
+ }
+}
+```
+
+---
+
+## Route Structure
+
+| Route | Component | Description |
+|-------|-----------|-------------|
+| `/` | `Icons` | Main icon browser |
+| `/icon/:group/:name` | `IconInfo` | Icon detail page |
+| `/icons/merge-icons` | `IconsMerge` | Multi-icon merge tool |
+| `/icon/animate` | External | Animation editor |
+
+---
+
+## Peer Dependencies
+
+```json
+{
+ "@armco/components": "^0.0.60",
+ "@armco/configs": "^0.0.11",
+ "@armco/icon": "^0.0.10",
+ "@armco/types": "^0.0.18",
+ "@armco/utils": "^0.0.29",
+ "@reduxjs/toolkit": "^1.8.1",
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0",
+ "react-redux": "^8.0.1",
+ "react-router-dom": "^6.13.0"
+}
+```
+
+---
+
+## Build Output
+
+```
+build/
+├── cjs/ # CommonJS modules
+├── es/ # ES modules
+└── types/ # TypeScript declarations
+```
+
+---
+
+## Configuration Files
+
+| File | Purpose |
+|------|---------|
+| `vite.config.ts` | Library build configuration |
+| `vite-dev.config.ts` | Development server |
+| `vite-run.config.ts` | Standalone run mode |
+| `tsconfig.json` | TypeScript configuration |
+| `.env` | Environment variables |
diff --git a/package-lock.json b/package-lock.json
index 29f44ac..f74c9fb 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,7 +9,8 @@
"version": "0.0.6",
"license": "ISC",
"dependencies": {
- "bootstrap": "^5.3.8"
+ "bootstrap": "^5.3.8",
+ "react-router": "^6.22.3"
},
"devDependencies": {
"@armco/components": "^0.0.60",
@@ -31,7 +32,7 @@
"react": "^19.2.3",
"react-dom": "^19.2.3",
"react-redux": "^9.2.0",
- "react-router-dom": "^7.11.0",
+ "react-router-dom": "^6.22.3",
"sass-embedded": "^1.97.1",
"typescript": "^5.9.3",
"uuid": "^13.0.0",
@@ -156,40 +157,6 @@
"react": "^18.3.1"
}
},
- "node_modules/@armco/shared-components/node_modules/react-router": {
- "version": "6.30.2",
- "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.2.tgz",
- "integrity": "sha512-H2Bm38Zu1bm8KUE5NVWRMzuIyAV8p/JrOaBJAwVmp37AXG72+CZJlEBw6pdn9i5TBgLMhNDgijS4ZlblpHyWTA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@remix-run/router": "1.23.1"
- },
- "engines": {
- "node": ">=14.0.0"
- },
- "peerDependencies": {
- "react": ">=16.8"
- }
- },
- "node_modules/@armco/shared-components/node_modules/react-router-dom": {
- "version": "6.30.2",
- "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.2.tgz",
- "integrity": "sha512-l2OwHn3UUnEVUqc6/1VMmR1cvZryZ3j3NzapC2eUXO1dB0sYp5mvwdjiXhpUbRb21eFow3qSxpP8Yv6oAU824Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@remix-run/router": "1.23.1",
- "react-router": "6.30.2"
- },
- "engines": {
- "node": ">=14.0.0"
- },
- "peerDependencies": {
- "react": ">=16.8",
- "react-dom": ">=16.8"
- }
- },
"node_modules/@armco/shared-components/node_modules/scheduler": {
"version": "0.23.2",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
@@ -1920,10 +1887,9 @@
}
},
"node_modules/@remix-run/router": {
- "version": "1.23.1",
- "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.1.tgz",
- "integrity": "sha512-vDbaOzF7yT2Qs4vO6XV1MHcJv+3dgR1sT+l3B8xxOVhUC336prMvqrvsLL/9Dnw2xr6Qhz4J0dmS0llNAbnUmQ==",
- "dev": true,
+ "version": "1.15.3",
+ "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.3.tgz",
+ "integrity": "sha512-Oy8rmScVrVxWZVOpEF57ovlnhpZ8CCPlnIIumVcV9nFdiSIrus99+Lw78ekXyGvVDlIsFJbSfmSovJUhCWYV3w==",
"license": "MIT",
"engines": {
"node": ">=14.0.0"
@@ -3450,20 +3416,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/cookie": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz",
- "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/express"
- }
- },
"node_modules/core-js": {
"version": "3.47.0",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.47.0.tgz",
@@ -6079,43 +6031,36 @@
}
},
"node_modules/react-router": {
- "version": "7.11.0",
- "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.11.0.tgz",
- "integrity": "sha512-uI4JkMmjbWCZc01WVP2cH7ZfSzH91JAZUDd7/nIprDgWxBV1TkkmLToFh7EbMTcMak8URFRa2YoBL/W8GWnCTQ==",
- "dev": true,
+ "version": "6.22.3",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.3.tgz",
+ "integrity": "sha512-dr2eb3Mj5zK2YISHK++foM9w4eBnO23eKnZEDs7c880P6oKbrjz/Svg9+nxqtHQK+oMW4OtjZca0RqPglXxguQ==",
"license": "MIT",
"dependencies": {
- "cookie": "^1.0.1",
- "set-cookie-parser": "^2.6.0"
+ "@remix-run/router": "1.15.3"
},
"engines": {
- "node": ">=20.0.0"
+ "node": ">=14.0.0"
},
"peerDependencies": {
- "react": ">=18",
- "react-dom": ">=18"
- },
- "peerDependenciesMeta": {
- "react-dom": {
- "optional": true
- }
+ "react": ">=16.8"
}
},
"node_modules/react-router-dom": {
- "version": "7.11.0",
- "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.11.0.tgz",
- "integrity": "sha512-e49Ir/kMGRzFOOrYQBdoitq3ULigw4lKbAyKusnvtDu2t4dBX4AGYPrzNvorXmVuOyeakai6FUPW5MmibvVG8g==",
+ "version": "6.22.3",
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.3.tgz",
+ "integrity": "sha512-7ZILI7HjcE+p31oQvwbokjk6OA/bnFxrhJ19n82Ex9Ph8fNAq+Hm/7KchpMGlTgWhUxRHMMCut+vEtNpWpowKw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "react-router": "7.11.0"
+ "@remix-run/router": "1.15.3",
+ "react-router": "6.22.3"
},
"engines": {
- "node": ">=20.0.0"
+ "node": ">=14.0.0"
},
"peerDependencies": {
- "react": ">=18",
- "react-dom": ">=18"
+ "react": ">=16.8",
+ "react-dom": ">=16.8"
}
},
"node_modules/react-transition-group": {
@@ -6843,13 +6788,6 @@
"node": ">=10"
}
},
- "node_modules/set-cookie-parser": {
- "version": "2.7.2",
- "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz",
- "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
diff --git a/package.json b/package.json
index f09c8cf..61aa1f3 100644
--- a/package.json
+++ b/package.json
@@ -75,7 +75,7 @@
"react": "^19.2.3",
"react-dom": "^19.2.3",
"react-redux": "^9.2.0",
- "react-router-dom": "^7.11.0",
+ "react-router-dom": "^6.22.3",
"sass-embedded": "^1.97.1",
"typescript": "^5.9.3",
"uuid": "^13.0.0",
@@ -86,6 +86,7 @@
"vitest": "^4.0.8"
},
"dependencies": {
- "bootstrap": "^5.3.8"
+ "bootstrap": "^5.3.8",
+ "react-router": "^6.22.3"
}
}
diff --git a/src/IconController.tsx b/src/IconController.tsx
index 87ffd1f..37bf8a1 100755
--- a/src/IconController.tsx
+++ b/src/IconController.tsx
@@ -49,7 +49,7 @@ const IconController = (props: IconControllerProps): JSX.Element => {
formData.append("file", file)
formData.append("count", "20")
post(
- "http://localhost:5002/api/similar-icon-paths",
+ `${API_CONFIG.TASKER[process.env.NODE_ENV]}/similar-icon-paths`,
formData,
undefined,
{ headers: { "Content-Type": "multipart/form-data" } },
diff --git a/src/IconsList.tsx b/src/IconsList.tsx
index 066018b..c106f2d 100755
--- a/src/IconsList.tsx
+++ b/src/IconsList.tsx
@@ -116,8 +116,8 @@ const IconsList = (props: IconsListProps): JSX.Element => {
}, [selectedTag, searchText])
const onIconTileClick = (iconProps: IconResponse) => {
- isSelectMode
- ? setSelectedIcons((currentIcons) => {
+ if (isSelectMode) {
+ setSelectedIcons((currentIcons) => {
currentIcons = [...(currentIcons || [])]
const existingIconIndex = currentIcons?.findIndex(
(currentIcon) => currentIcon.name === iconProps.name,
@@ -129,12 +129,11 @@ const IconsList = (props: IconsListProps): JSX.Element => {
}
return currentIcons
})
- : view?.name === ArIconTileTypes.COMPACT &&
- notify({
- show: true,
- message: `Icon link for ${iconProps.name} copied`,
- uid: uuid(),
+ } else {
+ navigate(`/icon/${iconProps.group}/${iconProps.name}`, {
+ state: iconProps,
})
+ }
}
return (
diff --git a/src/helper.ts b/src/helper.ts
index 687d5de..b2289f6 100644
--- a/src/helper.ts
+++ b/src/helper.ts
@@ -24,6 +24,8 @@ export function downloadSvg(
}
export const complement = (hex?: string) => {
+ if (hex === "white") return "#000000"
+ if (hex === "black") return "#ffffff"
if (hex) {
const hexParts = hex.substring(1).toLowerCase().split("")
const hexMap = { a: 10, b: 11, c: 12, d: 13, e: 14, f: 15 }