diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 0000000000000000000000000000000000000000..6411d62089f6c209508e98ed8a0817bbd27ec9b0 --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,35 @@ +{ + "permissions": { + "allow": [ + "Bash(claude mcp add:*)", + "mcp__figma__get_screenshot", + "mcp__figma__get_metadata", + "mcp__figma__get_design_context", + "mcp__figma__get_variable_defs", + "Bash(npm create:*)", + "Bash(dir:*)", + "Bash(npm install)", + "Bash(npm run dev:*)", + "Bash(timeout /t 3)", + "Bash(ping:*)", + "Bash(nul)", + "mcp__figma-desktop__get_screenshot", + "mcp__figma-desktop__get_metadata", + "mcp__figma-desktop__get_design_context", + "Bash(npm run build:*)", + "Bash(curl:*)", + "Bash(if not exist \"src\\assets\" mkdir \"src\\assets\")", + "WebFetch(domain:localhost)", + "Bash(del \"C:\\HuggingFace\\Huggy thumbnail crafter\\src\\data\\layouts.ts\")", + "Bash(unzip:*)", + "WebSearch", + "Bash(if not exist \"src\\assets\\layouts\" mkdir \"src\\assets\\layouts\")", + "Bash(if not exist \"src\\assets\\layouts\\thumbnails\" mkdir \"src\\assets\\layouts\\thumbnails\")", + "Bash(if not exist \"public\\assets\\layouts\\huggys\" mkdir \"public\\assets\\layouts\\huggys\")", + "Bash(git init:*)", + "Bash(git add:*)" + ], + "deny": [], + "ask": [] + } +} diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000000000000000000000000000000000..f2c1ca32a67c712fbd7381c0f2211adde560fb18 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +dist/**/*.png filter=lfs diff=lfs merge=lfs -text +public/**/*.png filter=lfs diff=lfs merge=lfs -text diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..67462ac45444f26f7715561844bb2033c34d153c --- /dev/null +++ b/.gitignore @@ -0,0 +1,27 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +# Windows reserved names +nul diff --git a/PROJECT_PLAN.md b/PROJECT_PLAN.md new file mode 100644 index 0000000000000000000000000000000000000000..ceb77a09c2b284930e385cfbe1a42d855b528e58 --- /dev/null +++ b/PROJECT_PLAN.md @@ -0,0 +1,955 @@ +# HF Thumbnail Crafter - Project Plan + +## Project Overview +A web-based thumbnail creator for HuggingFace users to craft quick thumbnails with pre-made layouts and visual assets (Huggys). The project will be hosted on HuggingFace Spaces as a static site. + +## Design Resources +- **Figma File:** https://www.figma.com/design/b7rA38IJxS0sK8ppXE8Gjb/HF-Thumbnail-Crafter?node-id=91-4463&t=oTLFdnJ8jDG93HCw-1 +- **Figma MCP Connected:** Yes (http://127.0.0.1:3845/mcp) + +## Tech Stack Decisions + +### Core Framework +- **Build Tool:** Vite +- **Framework:** React 18 +- **Language:** TypeScript +- **Canvas Library:** react-konva + Konva.js +- **Styling:** Tailwind CSS (Figma exports already in Tailwind format) +- **Icons:** Lucide React (placeholder until custom icons ready) +- **Fonts:** Inter (default), IBM Plex Mono +- **Deployment:** HuggingFace Spaces (Static Site) + +### Why These Choices? +- **React + Konva:** Excellent integration via react-konva library +- **Vite:** Fast, modern, lightweight - perfect for HF Spaces static deployment +- **TypeScript:** Type safety for complex canvas state management +- **Tailwind CSS:** Figma MCP exports Tailwind classes - zero conversion work needed, fast development + +## Key Requirements + +### 1. Floating Sidebar (Left) +Four buttons with icons and labels: +1. **Layout** - Load pre-designed layout templates +2. **Huggy** - Add visual assets from HuggingFace dataset +3. **Image** - Upload local images or drag-and-drop +4. **Text** - Add text objects to canvas + +**Design specs:** +- Width: 87px +- Background: #f8f9fa +- Border radius: 10px +- Spacing: 15px between buttons +- Position: Fixed left, floating + +### 2. Canvas Area (Center) + +#### Canvas Header +**Left side - Background Color Selector:** +- Label: "Background color:" +- Two options: Light (white) and Dark (purple/dark) +- Toggle between backgrounds + +**Right side - Canvas Size Selector:** +- Label: "Size:" +- Three options: + 1. **1200×675** (default, X icon) + 2. **LinkedIn size** (LinkedIn icon) + 3. **HF custom size** (HF icon) + +#### Canvas +- Default size: 1200×675px +- Background: Switchable (light/dark) +- Supports multiple object types: rectangles, images, text, Huggys + +**Canvas Object Interactions:** +- ✅ Drag to reposition +- ✅ Proportional scaling (maintain aspect ratio) +- ✅ Rotate objects +- ✅ Layer ordering (bring to front/send to back) +- ✅ Delete objects (keyboard + button) +- Selection with bounding box and transformer handles + +### 3. Export Button (Top Right) +- Position: Fixed top-right +- Shows: Download icon + editable filename + ".png" +- Default filename: "thumbnail_name" +- Format: PNG only +- Functionality: Export canvas as PNG using Konva's toDataURL + +### 4. Screen Background +- Dotted pattern wallpaper +- Base color: #f8f9fa with dot pattern overlay + +## Feature Details + +### Layout Feature +**How it works:** +1. Click "Layout" button → Shows layout selector menu +2. Menu displays thumbnails of 6 pre-designed layouts: + - Serious Collab + - Fun Collab + - Sandwich + - Docs + - 1:2 (two variations) +3. Click layout → Loads objects (shapes, text, images) onto canvas +4. User edits from there + +**Layout Implementation Workflow:** +1. Designer creates layouts in Figma (for all 3 canvas sizes) +2. Developer extracts layout specs using Figma MCP +3. Convert to JSON format: +```json +{ + "id": "serious-collab", + "name": "Serious Collab", + "canvasSize": "1200x675", + "objects": [ + { + "type": "rect", + "x": 100, + "y": 50, + "width": 200, + "height": 150, + "fill": "#000000", + "rotation": 0, + "zIndex": 1 + }, + { + "type": "text", + "x": 150, + "y": 300, + "text": "Title Here", + "fontSize": 100, + "fontFamily": "Inter", + "fill": "#000000", + "bold": false, + "italic": false, + "rotation": 0, + "zIndex": 2 + } + ] +} +``` +4. Store layouts in `/src/data/layouts.ts` +5. Future layouts follow same process + +### Huggy Feature +**Current status:** Visual assets will be stored in HuggingFace dataset (future) + +**Functionality:** +1. Click "Huggy" button → Opens Huggy selector menu +2. Menu includes: + - Search bar at top ("Search Huggy") + - Grid of Huggy thumbnails (3+ rows visible) + - Scroll for more Huggys +3. Example Huggys from Figma: + - IDEFICS Huggy + - Robot Huggy + - Computer Vision Huggy + - SF Meetup Huggy + - Transformer Agent Huggy +4. Click Huggy → Adds to canvas center as draggable image +5. Huggy behaves like any canvas object (drag, scale, rotate, delete) + +**Implementation notes:** +- Phase 1: Use exported Huggy images from Figma as placeholders +- Phase 2: Connect to HuggingFace dataset API +- Search filters Huggys by name + +### Image Upload Feature +**Functionality:** +1. Click "Image" button OR drag-and-drop image onto canvas +2. Drag hint shown: "Drag n drop your image anywhere to upload" +3. Supported formats: PNG, JPG, WebP +4. Image appears in canvas center +5. User can drag, scale, rotate, layer-order, delete + +**Implementation:** +- Hidden file input triggered by button click +- Drag-over visual feedback on canvas area +- Load image as Konva Image node + +### Text Feature +**Basic functionality:** +1. Click "Text" button → Adds default text object to canvas center +2. Default text: "Pretty Short Title" +3. Default style: Inter, 100px, black, normal weight + +**Editing modes:** +1. **Click once** → Select text object (shows transformer) +2. **Double-click** → Enter inline edit mode (temporary textarea overlay) +3. **Select text** → Shows floating text toolbar below canvas + +**Text toolbar appears when:** +- Text object is selected on canvas +- "Add Text" button is clicked + +### Text Toolbar (Floating) +**Position:** Below canvas, horizontally centered + +**Controls (left to right):** +1. **Font Family Dropdown** + - Options: Inter (default), IBM Plex Mono + - Shows current font name + - Dropdown arrow icon + +2. **Divider line** + +3. **Color Picker** + - Button shows current color swatch + - Click → Opens color picker popover (matches Figma design) + - Picker includes: + - Gradient palette area + - Hue slider + - Saturation slider + - Hex input field + - Format dropdown (Hex) + +4. **Bold Button** + - Icon: Bold "B" + - Toggle on/off + - Active state styling + +5. **Italic Button** + - Icon: Italic "I" + - Toggle on/off + - Active state styling + +**Design specs:** +- Background: Dark (#2b2d31 or similar) +- Height: 44px +- Padding: 2px border, 2px inner padding +- Border radius: 5px +- Toolbar width: ~288px + +## Development Phases + +### Phase 1: Project Setup ✅ +- [x] Initialize Vite + React + TypeScript +- [x] Install dependencies: react-konva, konva, lucide-react +- [x] Create folder structure: + ``` + /src + /components + /Sidebar + /Canvas + /CanvasHeader + /ExportButton + /LayoutSelector + /HuggyMenu + /TextToolbar + /types + canvas.types.ts + layout.types.ts + /utils + export.utils.ts + canvas.utils.ts + /data + layouts.ts + huggys.ts + /assets + /icons (placeholder until custom icons ready) + /huggy-images + ``` +- [x] Set up base configuration (tsconfig, vite.config) + +### Phase 2: Core UI Structure ✅ (Fully Complete) +**Components:** +- ✅ `App.tsx` - Main layout with dotted background +- ✅ `Sidebar.tsx` - Floating sidebar with 4 buttons and custom Figma icons +- ✅ `CanvasContainer.tsx` - Wrapper for canvas area with smooth transitions +- ✅ `ExportButton.tsx` - Top-right export button + +**Icons:** +- ✅ `IconLayout.tsx` - Custom layout icon with default and selected states +- ✅ `IconText.tsx` - Custom text icon with default and selected states +- ✅ `IconImage.tsx` - Custom image icon with default and selected states +- ✅ `IconHuggy.tsx` - Custom Huggy mascot icon + +**Design implementation:** +- ✅ Extract styles from Figma +- ✅ Create dotted background pattern +- ✅ Position all major UI elements +- ✅ Responsive container sizing +- ✅ Replace Lucide icons with custom Figma-exported sidebar icons + +### Phase 3: Canvas Foundation ✅ +**Setup:** +- ✅ Create `Canvas.tsx` with Konva Stage and Layer +- ✅ Implement canvas state management: + ```typescript + interface CanvasObject { + id: string; + type: 'rect' | 'image' | 'text' | 'huggy'; + x: number; + y: number; + width: number; + height: number; + rotation: number; + zIndex: number; + // type-specific properties + } + ``` +- ✅ Set up three canvas sizes with switching +- ✅ Create base object renderers + +### Phase 4: Canvas Header ✅ (Fully Complete) +**Components:** +- ✅ `BgColorSelector.tsx` - Light/dark toggle with hover states +- ✅ `CanvasSizeSelector.tsx` - Three size options with animated expansion +- ✅ `CanvasHeader.tsx` - Main container component with smooth transitions + +**Functionality:** +- ✅ Background color state management (light/dark toggle) +- ✅ Canvas size switching (1200×675, LinkedIn, HF) +- ✅ Active state styling for selected options +- ✅ Hover states with subtle `#f0f2f4` background on all buttons +- ✅ Dimension text at 80% opacity for better visual hierarchy +- ✅ Smooth 150ms ease-in-out transitions for all state changes +- ✅ Canvas dimensions animate smoothly (150ms), background color instant +- ✅ Header position adjusts smoothly when canvas size changes +- ✅ Animated button expansion with text slide-in and fade-in effects +- ✅ Icon-only display for unselected options, icon + label for selected +- ✅ Integrated into App.tsx above Canvas component + +**Animations:** +- ✅ Button width expansion/contraction when selecting canvas size +- ✅ Text fade-in/fade-out (0 → 0.8 opacity) +- ✅ Text slide-in effect (translateX animation) +- ✅ All animations synchronized at 150ms ease-in-out + +### Phase 5: Layout Feature +**Components:** +- `LayoutSelector.tsx` - Floating menu component (matching Figma LayoutOptionsList design) +- Layout thumbnail components (4 layouts: Serious Collab, Fun Collab, Sandwich, Docs) +- Note: Only 4 unique layouts, NOT 6. Each layout has 3 size variations (1200×675, LinkedIn, HF) + +**Data structure:** +- Define TypeScript interfaces for layouts +- Create layout data for all 4 layouts × 3 canvas sizes (12 total variants) +- Logo placeholder object type with Konva blur filter support (blur radius: 11.415px) +- Export all placeholder assets as PNG (Huggys, logo placeholders, decorative elements) +- Text placeholders remain as editable text objects + +**Font additions:** +- Add Bison Bold font for Sandwich layout title text +- Update TextObject fontFamily type to include 'Bison' + +**Implementation details:** +- Extract layout specs from Figma using MCP +- Convert to JSON format with object positions, sizes, rotations +- Store in `/src/data/layouts.ts` +- Implement layout loading logic to populate canvas + +**Future enhancement (Post-Phase 5):** +- **Asset Swap Feature:** When hovering over logo placeholder or Huggy objects, show a small swap button +- Clicking swap button allows users to replace with their own image from local storage +- This will be implemented in a future phase after core layout functionality is complete + +**Collaboration checkpoint:** +- Designer provides Figma layout specs +- Developer extracts and converts to JSON +- Test layout loading on all canvas sizes + +### Phase 6: Canvas Interactions ✅ (Partial - Core Complete) +**Features implemented:** +- ✅ Object selection with Transformer +- ✅ Drag functionality (Konva draggable) +- ✅ Proportional scaling (keepRatio enabled) +- ✅ Rotation handles +- ✅ Delete functionality (keyboard Delete/Backspace) +- ✅ Transformer styling (blue border, white-filled corner handles) + +**Features to implement:** +- ⚠️ Layer ordering controls (floating LayerContainer) +- ⚠️ Delete button UI +- ⚠️ Multi-select support (optional) + +**LayerContainer Component:** +- **When:** Only appears when an object is selected +- **What:** Dynamically visualizes ALL canvas objects as stacked layer icons +- **Selected state:** Selected object's layer shown in blue +- **Position:** Floats near the right side of the selected object +- **Interactions:** + - Drag layers up/down to reorder in stack + - Click a layer to bring that object to top of clicked layer +- **Visual:** Vertical stack showing layer hierarchy + +**UI elements:** +- LayerContainer (floating mini layers panel) +- Delete button +- Transformer styling + +### Phase 7: Huggy Feature +**Components:** +- `HuggyMenu.tsx` - Floating menu with search +- `HuggyGrid.tsx` - Grid display of Huggys +- `HuggyCard.tsx` - Individual Huggy thumbnail + +**Implementation:** +- Phase 1: Use exported Huggy images from Figma +- Create Huggy data structure: + ```typescript + interface Huggy { + id: string; + name: string; + thumbnail: string; + fullImage: string; + tags?: string[]; + } + ``` +- Search/filter functionality +- Add Huggy to canvas as Konva Image + +**Future:** Connect to HuggingFace dataset API + +### Phase 8: Image Upload +**Features:** +- File input button (hidden, triggered by sidebar) +- Drag-and-drop zone (entire canvas area) +- Visual drag-over feedback +- Image loading and processing +- Add to canvas as Konva Image node + +**Error handling:** +- File type validation +- File size limits +- Loading states + +### Phase 9: Text Feature ✅ (Fully Complete) +**Components:** +- Text object renderer in Canvas +- Inline text editor (textarea overlay) +- Text hint box in Sidebar + +**Functionality:** +- ✅ Add default text on button click (68px default font) +- ✅ Keyboard shortcut: Press 'T' to activate text creation mode +- ✅ Single-click selection with transformer/bounding box +- ✅ Double-click inline editing with cursor at exact click position +- ✅ Text object transformation (drag, scale, rotate) +- ✅ Font size scales proportionally during transformation +- ✅ Font loading (Inter, IBM Plex Mono) +- ✅ Two-stage text editing system: + - **Stage 1:** Auto-growing text box on initial creation + - **Stage 2:** Fixed-size box with dynamic font scaling to fit +- ✅ Real-time font size adaptation while typing +- ✅ Live bounding box resize while editing (shrinks/grows with content) +- ✅ Delete key context awareness (deletes characters while editing, deletes object when selected) +- ✅ Text hint box positioned next to Text button + +**Bug Fixes Completed (2025-11-20):** +1. ✅ **Critical bounding box visibility issue** + - Refs were passed as objects instead of callback functions + - Fixed with callback ref pattern to properly store Konva nodes +2. ✅ **Duplicate/transparent text during editing** + - Fixed by setting text opacity to 0 during editing +3. ✅ **Delete key context awareness** + - Delete key only removes object when NOT editing +4. ✅ **Text scaling on transform** + - Font size now scales proportionally with text box +5. ✅ **Live bounding box resize** + - Box shrinks/grows in real-time while typing +6. ✅ **Cursor position on double-click** + - Cursor appears at exact click position instead of selecting all +7. ✅ **Text hint box positioning** + - Hint box aligned with Text button at same vertical level + +**Phase 9 Complete:** All core functionality and UX refinements implemented. + +**Next:** Phase 10 (Text Toolbar) - Will be implemented after other core features + +### Phase 10: Text Toolbar +**Components:** +- `TextToolbar.tsx` - Main toolbar container +- `FontFamilyDropdown.tsx` +- `ColorPicker.tsx` - Full color picker UI +- `BoldButton.tsx` +- `ItalicButton.tsx` + +**Functionality:** +- Show/hide based on text selection +- Apply styles to selected text object +- Color picker popover positioning +- Real-time preview of changes + +**Design fidelity:** +- Match Figma design exactly +- Smooth animations +- Proper z-index layering + +### Phase 11: Export Feature +**Functionality:** +- Editable filename input +- PNG generation using Konva's `stage.toDataURL()` +- Download trigger +- Loading state during export + +**Technical details:** +- Set proper canvas dimensions +- Handle high-DPI displays (pixelRatio) +- File naming conventions +- Browser download API + +### Phase 12: Polish & Deploy +**Tasks:** +- Keyboard shortcuts (Delete, Undo) +- Loading states for all async operations +- Error boundaries +- Performance optimization: + - Lazy load Huggy images + - Debounce canvas updates + - Optimize re-renders +- Accessibility improvements +- Create README.md for HF Spaces +- Test on multiple browsers +- Deploy to HuggingFace Spaces + +**HF Spaces setup:** +- Create `README.md` with metadata: + ```yaml + --- + title: HF Thumbnail Crafter + emoji: 🎨 + colorFrom: blue + colorTo: purple + sdk: static + pinned: false + --- + ``` +- Configure build output +- Test deployment + +## Important Notes + +### State Management +**No persistence:** Canvas state is lost on page refresh (as per requirements) +- Use React state (useState/useReducer) for canvas objects +- No localStorage/database needed + +### Icon System +**Current:** Use Lucide React as placeholder +**Future:** Replace with custom icons from Figma +- Export SVGs from Figma +- Create React icon component wrapper +- Place in `/src/assets/icons/` + +### Huggy Assets +**Current:** Export from Figma and place in `/src/assets/huggy-images/` +**Future:** Fetch from HuggingFace dataset +- API integration in Phase 7 +- Caching strategy for performance + +### Canvas Sizes Reference +1. **Default (X):** 1200×675px +2. **LinkedIn:** TBD (standard LinkedIn post size) +3. **HF Custom:** TBD (to be defined) + +## Context for Future Sessions + +### What's been decided: +✅ Tech stack: Vite + React + TypeScript + Konva +✅ Canvas library: react-konva +✅ No state persistence (refresh loses work) +✅ PNG export only +✅ Three canvas sizes with switcher +✅ Text fonts: Inter and IBM Plex Mono +✅ Deployment: HuggingFace Spaces + +### What needs clarification: +⚠️ LinkedIn and HF canvas sizes (exact dimensions) +⚠️ Complete icon set from Figma (using Lucide temporarily) +⚠️ HuggingFace dataset API endpoint for Huggys +⚠️ Layout specs for all 6 layouts (will extract in Phase 5) + +### Session resumption checklist: +1. Review this PROJECT_PLAN.md +2. Check current phase status in todo list +3. Verify Figma MCP connection: `claude mcp add --transport http figma-desktop http://127.0.0.1:3845/mcp` +4. Continue from last completed phase + +## Contact & Resources +- **Figma:** https://www.figma.com/design/b7rA38IJxS0sK8ppXE8Gjb/HF-Thumbnail-Crafter +- **HuggingFace Spaces Docs:** https://huggingface.co/docs/hub/spaces +- **Konva Docs:** https://konvajs.org/ +- **react-konva Docs:** https://konvajs.org/docs/react/ + +--- + +## Implementation Status + +### Completed Phases: +- ✅ **Phase 1:** Project Setup +- ✅ **Phase 2:** Core UI Structure (Fully Complete - Including custom Figma sidebar icons) +- ✅ **Phase 3:** Canvas Foundation +- ✅ **Phase 4:** Canvas Header (Fully Complete - Background color & size selectors with hover states, smooth transitions, and animated button expansion) +- ✅ **Phase 6:** Canvas Interactions (Partial - Core Complete) +- ✅ **Phase 9:** Text Feature (Fully Complete) + +### Current Phase: +**Phase 7:** Huggy Feature (Completed) ✅ +**Next:** Phase 8 (Image Upload) + +**Progress (2025-11-22):** +- ✅ Fixed sidebar icons with correct selected states from Figma icons page + - Text, Layout, and Image icons now have proper default (gray) and selected (white) states + - All icons saved to `src/assets/icons/` and using local files +- ✅ Integrated Bison Bold font + - Downloaded and extracted to `public/fonts/` + - Added @font-face declaration in `src/index.css` + - Updated TypeScript types to include 'Bison' in fontFamily union type +- ✅ Updated PROJECT_PLAN.md with asset swap feature documentation (Post-Phase 5) +- ✅ Confirmed 4 layouts × 3 sizes = 12 total layout variants to implement +- ✅ Created LogoPlaceholderObject TypeScript interface with blur support +- ✅ Extended CanvasObject union type to include LogoPlaceholderObject +- ✅ Implemented logo placeholder renderer with Konva blur filter (11.415px radius) +- ✅ Extracted layout specifications from Figma for all 4 layouts + - Sandwich layout: gradient background, logo placeholder, singing Huggy, divider + - Docs layout: gradient background, logo placeholder, partial Huggy, divider + - Serious Collab: solid gray background (#d9d9d9) + - Fun Collab: solid white background (#ffffff) +- ✅ Created `/src/data/layouts.ts` with layout definitions and helper functions +- ✅ Built LayoutSelector component matching Figma design + - Floating menu positioned next to sidebar + - 4 layout options with thumbnails + - Clean hover states +- ✅ Integrated LayoutSelector into Sidebar component +- ✅ Implemented layout loading logic in App.tsx + - Applies layout background (solid or gradient) + - Loads all layout objects onto canvas + - Resets selection after loading + +**Critical Fixes (2025-11-22 - Afternoon):** +- ✅ Fixed LayoutSelector component to match Figma 2×2 grid design + - Was using vertical list, now uses proper 2×2 grid layout + - Width: 233px, proper padding and spacing + - Each layout button: 96.5px × 47.291px thumbnail area +- ✅ Extracted correct layout specifications from Figma Layouts page (node 219:1155) + - **Serious Collab**: HF logo image, logo placeholder with blur, "Logo" text, divider + - **Fun Collab**: "Pretty Short Title" text, 2 Huggy images, rotated logo placeholder with "Logo" text + - **Sandwich**: "Pretty short title" (Bison font), "supportive description", Singing Huggy image + - **Docs**: Masked Huggy image, "Transformers" title, "Documentation" subtitle +- ✅ Removed background property from Layout interface + - Layouts now only contain objects, as intended + - Background color is controlled separately by canvas settings +- ✅ Fixed App.tsx handleSelectLayout to not apply background colors + - Layouts only load objects onto canvas + - Users control background independently + +**Final Implementation (2025-11-22 - Evening):** +- ✅ User manually exported all layout assets to `public/assets/layouts/`: + - Layout thumbnails: sCollab_thumbnail.png, fCollab_thumbnail.png, sandwitch_thumbnail.png, docs_thumbnail.png + - Logo placeholders: logo_placehoder.png (single PNG with blur + text combined) + - HF logos: HF logo.png, docsHFLogo.png + - Huggy assets: fCollab_huggy_asset.png, fCollab_huggy_hand_asset.png, snadwithc_huggy_asset.png +- ✅ Updated all layouts.ts with correct asset paths and positions from Figma +- ✅ Fixed text rendering issue by setting `isFixedSize: true` on all layout text objects +- ✅ Logo placeholder now uses single image approach (no separate blur filter) +- ✅ Cleaned up empty folders (huggys/, logos/) +- ✅ All 4 layouts ready for testing in browser + +**Polish and Fixes (2025-11-22 - Late Evening):** +- ✅ Fixed Docs layout text centering issue + - Both "Transformers" and "Documentation" texts were positioned by left edge + - Updated to use center-point positioning with `offsetX` property + - "Transformers" text: x=600 (canvas center) with offsetX=321.5 (width/2) + - "Documentation" text: x=600 (canvas center) with offsetX=210.5 (width/2) +- ✅ Added SVG support for canvas objects + - Konva.Image component natively supports SVG files + - Updated Serious Collab layout to use SVG x-icon (collabX.svg) + - Changed divider rect to image object with proper positioning +- ✅ Implemented undo/redo system + - Added history management to `useCanvasState` hook + - Tracks up to 50 previous canvas states + - Keyboard shortcuts: Ctrl+Z (undo), Ctrl+Shift+Z (redo) + - Also supports Cmd+Z/Cmd+Shift+Z on Mac + - Automatically deselects objects when undoing/redoing + - Does not interfere with text editing (shortcuts disabled while editing) + - Covers: adding/deleting objects, moving/transforming, editing text, loading layouts + - Excludes: background color changes, canvas size changes (view settings) + +**Phase 5 Status:** ✅ COMPLETE - All Features Implemented & Tested + +**Additional Refinements (2025-11-23):** +- ✅ Fixed layout positioning issues across all 4 layouts + - Huggy assets repositioned to properly overlap titles (Sandwich y: 160→130, Fun Collab y: 81→60) + - Text bounding boxes tightened to reduce extra space + - Added verticalAlign="top" to text objects to minimize bottom padding + - Fixed "supportive description" horizontal centering (x=600, offsetX=375, align='center') +- ✅ Improved font loading reliability + - Explicitly preload Bison, Inter, and IBM Plex Mono fonts before first render + - Added 50ms delay after document.fonts.ready for browser sync + - Changed Bison font-display from 'swap' to 'block' to prevent fallback flash + - Fixes "Documentation" and "Pretty short title" positioning on first load +- ✅ Layout switch prompt for user-added objects + - Added `isFromLayout` flag to track object origins + - Prompts user to keep or discard custom objects when switching layouts + - Only triggers when NEW user-added objects exist (not for edited layout objects) +- ✅ Z-index and rendering order fixes + - Reordered layout objects arrays to ensure correct visual layering + - Huggy assets now properly overlap text placeholders as designed +- ✅ Text object dimensions optimized + - Sandwich title: 194px→170px height + - Sandwich description: 107px→95px height, 1015px→750px width + - Fun Collab title: 140px→120px height + - Docs title: 111px→110px height + - Docs subtitle: 56px→55px height + +**Known Limitations:** +- Text bounding boxes have inherent font metrics spacing (ascent/descent) +- With `isFixedSize: true`, bounding boxes can't dynamically shrink to exact text dimensions +- `verticalAlign="top"` minimizes visible bottom padding but doesn't eliminate font metrics spacing + +**Advanced Interaction Features (2025-11-23 - Latest):** +- ✅ **Arrow key movement with Shift modifier** + - Press arrow keys to move selected object(s) 1px in any direction + - Hold Shift while pressing arrow keys to move 10px (faster positioning) + - Works with both single and multiple selections + - Disabled during text editing to prevent interference + - Records in undo/redo history +- ✅ **Multi-select with drag-to-select box** + - Click and drag on empty canvas to create selection box (>5px threshold) + - Blue semi-transparent box with dashed border (rgba(63, 174, 230, 0.1)) + - Automatically selects all objects that intersect with the selection box + - Visual feedback during drag operation + - **Deselection on click**: Single click on empty canvas (no drag) deselects all objects + - Distinguishes between click and drag to prevent accidental deselection +- ✅ **Shift+Click to add/remove from selection** + - Click object normally: selects only that object (clears previous selection) + - Shift+Click object: adds object to current selection + - Shift+Click selected object: removes it from selection + - Works seamlessly with drag-to-select functionality + - All selected objects show transformer handles +- ✅ **Responsive layout repositioning on canvas size change** + - When canvas size changes (1200×675 ↔ 1200×627 ↔ 1280×720), all objects scale proportionally + - **Maintains aspect ratios**: Uses uniform scale factor (min of scaleX/scaleY) for width/height to prevent distortion + - Positions scale based on their respective canvas dimension ratios (scaleX for x, scaleY for y) + - Font sizes scale uniformly to maintain readability + - Prevents layout breakage and asset distortion when switching between size presets + - Recorded in undo/redo history + +**Multi-Select Implementation Details:** +- Changed `selectedId: string | null` to `selectedIds: string[]` in state management +- Updated all selection-related functions to handle arrays +- Transformer component now supports multiple nodes simultaneously +- Arrow key movement and delete operations work with multiple selected objects +- Undo/redo system handles multi-select operations correctly + +**Refinements (2025-11-23 - Final):** +- ✅ **Fixed responsive scaling to maintain aspect ratios** (App.tsx:155-167) + - Changed from independent scaleX/scaleY to uniform scale factor + - Prevents asset distortion when switching canvas sizes + - Width and height now scale with `Math.min(scaleX, scaleY)` + - Positions still scale independently (x with scaleX, y with scaleY) for proper placement +- ✅ **Fixed deselection on empty canvas click** (Canvas.tsx:400-449) + - Added 5px drag threshold to distinguish between click and drag + - Single click on empty canvas now properly deselects all objects + - Drag-to-select still works as expected with visual feedback + - Empty selection box (no intersecting objects) also deselects all +- ✅ **Added outside canvas click deselection** (App.tsx:178-192, CanvasContainer.tsx:18) + - Clicking anywhere outside the canvas container deselects all objects + - Uses global mousedown event listener on document + - Targets `.canvas-container` className for boundary detection + - Only triggers deselection when objects are currently selected (performance optimization) + +### Next Steps: +1. ✅ Complete Phase 5: Layout Feature (DONE) +2. ✅ Complete Phase 7: Huggy Feature (DONE) +3. Complete Phase 8: Image Upload +4. Complete Phase 10: Text Toolbar +5. Complete Phase 11: Export Feature +6. Complete Phase 12: Polish & Deploy + +--- + +**Last Updated:** 2025-11-24 + +**Recent Improvements (2025-11-24):** +- ✅ **Sidebar Deselection Enhancement** + - Clicking the same sidebar button now toggles it off (deselects) + - Clicking anywhere outside sidebar components deselects active button + - Improves UX by allowing users to close menus without navigating away + - **Fixed:** Canvas clicks in text creation mode now properly preserved +- ✅ **Huggy Menu UX Improvements & Fixes** + - **Infinite scroll fixed:** + - Initial display increased to 12 Huggys (from 6) to ensure scrollbar appears + - Added overflow check that auto-loads more content if container isn't full + - Scroll event triggers at 80% to load 6 more items at a time + - **Height constraint:** Maximum 4 rows visible (430px max-height) to prevent excessive menu length + - **Default scrollbar:** Removed custom scrollbar styling, using native OS scrollbars + - **Browser tooltip only:** Removed custom hover overlay, using native browser tooltip (title attribute) + - Transparent background on Huggy thumbnails (removed white bg) + - Auto-selection: Newly added Huggys are immediately selected on canvas + - Unified grid spacing: 5px gap and padding for balanced layout +- ✅ **Text Creation Bug Fix** + - **Fixed:** Text creation mode (button click or 'T' key) now works correctly + - Issue was click-outside-sidebar handler deselecting activeButton before canvas click event fired + - Solution: Added exception for canvas clicks when in text creation mode + - Text creation mode preserved when clicking on canvas to add text +- ✅ **Undo/Redo System Fixed & Re-enabled** 🎉 + - **Fixed:** Complete rewrite of history management system + - **Root causes identified and resolved:** + - Race condition: State updates and history recording competing + - Missing state change detection: Now only records when state actually changes + - Flag management: Changed from `isUndoRedo` to `isApplyingHistory` with proper async handling + - **Improvements:** + - Added `setTimeout` to ensure state updates complete before recording + - Duplicate state prevention: Compares JSON strings before adding to history + - Proper flag reset after undo/redo operations + - 50 steps of history (configurable) + - **Keyboard shortcuts re-enabled:** + - Ctrl+Z / Cmd+Z: Undo + - Ctrl+Shift+Z / Cmd+Shift+Z: Redo + - Disabled during text editing to prevent interference + - **Memory trade-off:** 50 steps ≈ 250KB (negligible for modern browsers) + - Can undo all the way back to empty canvas (expected behavior) +- ✅ **Huggy Menu Scrollbar Refinement** + - Reduced scrollbar width from default to 6px (thinner, less intrusive) + - Transparent track background for cleaner look + - Gray thumb (#b8b8b8) with darker hover state (#999999) + - Firefox support with `scrollbar-width: thin` +- ✅ **Feature Backlog Updated** + - Hover to show object bounding box (future enhancement) + - Dynamic canvas corner radius & drop shadow on hover (future enhancement) + +**Current Status:** +- Phase 5 (Layout Feature): ✅ COMPLETE WITH KNOWN ISSUES + - All 4 layouts implemented with correct specifications + - Layout positioning refined for proper Huggy overlap + - Text centering and alignment issues resolved + - Font loading reliability improved for consistent first-load rendering + - SVG support added and tested + - Layout switch prompts implemented for user-added objects + - Text bounding boxes optimized (known limitation: font metrics spacing) + - Arrow key movement with Shift modifier (1px/10px) + - Multi-select with drag-to-select box + - Shift+Click to add/remove objects from selection + - All assets exported and integrated +- Phase 6 (Canvas Interactions): ✅ ENHANCED + - Multi-select functionality fully implemented + - Arrow key positioning for precise control +- Phase 7 (Huggy Feature): ✅ FULLY COMPLETE WITH HUGGINGFACE DATASET + - **HuggingFace Dataset Integration:** All 44 Huggy assets loaded from `https://huggingface.co/datasets/Chunte/Huggy` + - **26 Modern Huggies** + **18 Outlined Huggies** (PNG only, GIFs excluded) + - **Infinite Scroll Loading:** Display 12 Huggys initially, auto-load more when scrolling to 80% + - Automatic overflow detection: loads more if content doesn't fill container + - **Native Scrollbar:** Uses default OS scrollbar styling + - **Search Functionality:** Filter by name, category (Modern/Outlined), and tags + - Built HuggyMenu component with search bar and loading states + - **Browser Tooltip:** Native title attribute shows Huggy name on hover + - Summary footer showing "X of Y Huggys" + - Added click handler to insert Huggys onto canvas as draggable images (200×200px default) + - **Auto-Selection:** Newly added Huggys are automatically selected on canvas for immediate editing + - Integrated HuggyMenu into Sidebar with toggle functionality + - Users can drag, scale, rotate, and delete Huggys like any canvas object + - **Transparent Background:** Huggy thumbnails display without white background for cleaner look + - **Unified Grid Spacing:** 5px gap between Huggy thumbnails with 5px padding for balanced layout + - **Future Assets:** Automatically loads new Huggys when added to the dataset +- All previous phases (1-4, 9) fully functional +- Dev server running cleanly at http://localhost:3000 + +## Known Issues (To Be Fixed Later) + +### 1. Responsive Layout Scaling Issue ⚠️ +**Problem:** When switching between different canvas sizes, layout assets progressively reduce in size with each change. This cumulative scaling is unwanted behavior. + +**Impact:** Layout objects become unusably small after multiple canvas size switches. + +**Temporary Solution:** Remove the responsive layout repositioning feature (App.tsx:155-167) until a proper fix is implemented. + +**Root Cause:** The scaling logic applies transformation to already-transformed objects, causing cumulative scale reduction. + +**Future Fix:** Implement absolute positioning system or store original dimensions to calculate scale from baseline rather than current state. + +--- + +**Note:** This issue is documented for future fixes and does not block progression to other phases. + +--- + +## Feature Backlog + +### Huggy Menu "Expand" Feature ⏸️ +**Status:** Backlogged for future implementation + +**Original Requirement:** +- Add an "Expand" button in the Huggy menu +- Allow users to see more Huggys at once (e.g., 12 or 18 instead of 6) +- Toggle between compressed and expanded views + +**Current Workaround:** +- Users can click "Load More" button to progressively load additional Huggys +- Scroll to browse all 44 Huggys + +**Why Backlogged:** +- Current "Load More" functionality provides similar UX +- Prioritizing core features (Image Upload, Export) first +- Can be added as a quality-of-life enhancement later + +**Future Implementation Notes:** +- Add "Expand View" toggle button in menu header +- Options: Show 6 (default) | 12 (expanded) | 18 (full expanded) +- Maintain scroll position when toggling +- Consider performance with many images loaded simultaneously + +--- + +### Hover to Show Object Bounding Box ⏸️ +**Status:** Backlogged for future implementation + +**Original Requirement:** +- When hovering over any canvas object, display its bounding box +- Visual feedback to show interactive areas before clicking +- Helps users identify object boundaries + +**Implementation Details:** +- Add hover state detection to CanvasObject component +- Show subtle bounding box outline on hover (different from selection transformer) +- Use lighter color or dashed border to distinguish from selection state +- Should not interfere with drag operations or transformer handles + +**Why Backlogged:** +- Nice-to-have UX enhancement, not critical for core functionality +- Current selection system (click to select) works adequately +- Prioritizing core features (Image Upload, Export) first + +**Future Implementation Notes:** +- Consider performance impact with many objects on canvas +- Ensure hover state doesn't conflict with selection/transformer states +- May need throttling for smooth hover detection + +--- + +### Dynamic Canvas Corner Radius & Drop Shadow ⏸️ +**Status:** Backlogged for future implementation + +**Original Requirement:** +- **Cursor IN canvas area:** + - Corner radius: 0px (sharp corners) + - Drop shadow: Increased intensity (more obvious) +- **Cursor OUTSIDE canvas area:** + - Corner radius: 10px (rounded corners) + - Drop shadow: Original subtle value + +**Visual Effect:** +- Creates a "focus mode" when user is working on the canvas +- Sharp corners give more workspace feel when actively editing +- Rounded corners provide softer appearance when not in use + +**Implementation Details:** +- Track cursor position with mouseEnter/mouseLeave events on canvas container +- CSS transition for smooth corner radius and shadow changes +- Canvas component style updates based on hover state +- Transition duration: ~200ms for smooth effect + +**Why Backlogged:** +- Polish/aesthetic feature, not functional requirement +- May cause visual distraction during active editing +- Prioritizing core features (Image Upload, Export) first + +**Future Implementation Notes:** +- Test with various canvas sizes to ensure effect works well +- Consider if drop shadow change affects perceived canvas position +- May need to adjust for different screen sizes/resolutions +- Original drop shadow value: TBD (measure current implementation) +- Increased drop shadow value: TBD (design decision needed) diff --git a/SESSION_LOGS.md b/SESSION_LOGS.md new file mode 100644 index 0000000000000000000000000000000000000000..0270d54bd7ffb8daac0295b0899219df289331a7 --- /dev/null +++ b/SESSION_LOGS.md @@ -0,0 +1,331 @@ +# HF Thumbnail Crafter - Session Logs + +## Session 2025-11-21: Canvas Header Polish & Sidebar Icon Replacement + +### Session Summary +Completed Phase 4 (Canvas Header) with polished hover states, smooth transitions, and animated button expansions. Replaced all Lucide sidebar icons with custom Figma-exported icons featuring proper default and selected states. + +### Changes Made + +#### 1. Canvas Header Hover States ✅ +**Components Modified:** `BgColorSelector.tsx`, `CanvasSizeSelector.tsx` + +**Removed hover animations:** +- Removed text expansion animation on hover from CanvasSizeSelector +- Text labels now only appear when option is selected +- Added subtle `#f0f2f4` hover background to both components + +**Updated transitions:** +- Changed from `0.2s` to `150ms ease-in-out` for consistency +- Applied to both background color and canvas size selectors + +#### 2. Dimension Text Opacity ✅ +**Component Modified:** `CanvasSizeSelector.tsx` +- Set dimension text (e.g., "1200x675") to **80% opacity** for better visual hierarchy +- Improves readability while maintaining design aesthetic + +#### 3. Canvas Area Transitions ✅ +**Components Modified:** `Canvas.tsx`, `CanvasContainer.tsx`, `CanvasHeader.tsx` + +**Canvas transitions:** +- Width and height animate smoothly with `150ms ease-in-out` +- Background color changes instantly (no transition) as requested +- Overflow hidden prevents layout shift during animation + +**Container transitions:** +- Added `all 0.15s ease-in-out` transition to canvas container +- Header position adjusts smoothly when canvas size changes + +#### 4. Animated Button Expansion ✅ +**Component Modified:** `CanvasSizeSelector.tsx` + +**Three-part animation system:** +1. **Width expansion/contraction:** + - Unselected: `minWidth: '38px'` (icon only) + - Selected: Expands to fit icon + text + - Transition: `150ms ease-in-out` + +2. **Text fade-in/fade-out:** + - Unselected: `opacity: 0` + - Selected: `opacity: 0.8` + - Transition: `150ms ease-in-out` + +3. **Text slide-in effect:** + - Unselected: `translateX(-10px)` + - Selected: `translateX(0)` + - Transition: `150ms ease-in-out` + +**Implementation details:** +- Text span always rendered (not conditional) to enable smooth transitions +- Button `justifyContent` switches between `center` (unselected) and `flex-start` (selected) +- Padding adjusts dynamically: `9px` unselected, `10px` selected +- All animations synchronized for smooth, professional effect + +#### 5. Sidebar Icon Replacement ✅ +**New Icon Components Created:** +- `IconLayout.tsx` - Layout/grid icon with default (#545865) and selected (white) states +- `IconText.tsx` - Text "T" icon with default and selected states +- `IconImage.tsx` - Image/photo icon with default and selected states +- `IconHuggy.tsx` - Huggy mascot icon (single state as requested) + +**Component Modified:** `Sidebar.tsx` +- Removed Lucide React imports (`Layers`, `Smile`, `Image`, `Type`) +- Replaced with custom Figma-exported icon components +- Icons loaded from localhost Figma MCP server URLs + +**Icon Implementation Pattern:** +```typescript +interface IconProps { + selected?: boolean; +} + +export default function Icon({ selected = false }: IconProps) { + const imgDefault = "http://localhost:3845/assets/[hash].svg"; + const imgSelected = "http://localhost:3845/assets/[hash].svg"; + + return ( +
+ +
+ ); +} +``` + +**Huggy Icon Fix:** +- Initially missed `display: 'contents'` CSS property on Face container +- This property makes container transparent in layout, allowing children to position correctly +- Fixed by adding `display: 'contents'` to Face container div + +### Files Modified +- `src/components/CanvasHeader/BgColorSelector.tsx` +- `src/components/CanvasHeader/CanvasSizeSelector.tsx` +- `src/components/CanvasHeader/CanvasHeader.tsx` +- `src/components/Canvas/Canvas.tsx` +- `src/components/Canvas/CanvasContainer.tsx` +- `src/components/Sidebar/Sidebar.tsx` +- `src/components/Icons/IconLayout.tsx` (created) +- `src/components/Icons/IconText.tsx` (created) +- `src/components/Icons/IconImage.tsx` (created) +- `src/components/Icons/IconHuggy.tsx` (created) +- `PROJECT_PLAN.md` (updated) + +### Test Results +✅ Build successful without errors +✅ Dev server running at http://localhost:3001/ +✅ All hover states working smoothly +✅ Canvas size changes animate beautifully +✅ Background color changes instantly +✅ Button expansion animations synchronized perfectly +✅ All sidebar icons display correctly with proper states +✅ Huggy mascot icon renders correctly + +### Phase Status +**Phase 2: Core UI Structure** - ✅ Fully Complete (with custom Figma icons) +**Phase 4: Canvas Header** - ✅ Fully Complete (with polished animations and transitions) + +### Next Session +Ready to proceed with **Phase 5: Layout Feature** - Pre-designed layout templates with floating menu component. + +--- + +## Session 2025-11-20: Text Editing Bug Fixes + +### Session Summary +Fixed critical text editing issues and improved the text feature implementation. The main focus was resolving the missing bounding box problem and improving text editing behavior. + +### Issues Addressed + +#### 1. Delete Key Behavior ✅ +**Problem:** When editing text, pressing Delete/Backspace would delete the entire text object instead of individual characters. + +**Solution:** Added context-aware delete key handling in `App.tsx`: +```typescript +// Check if user is editing text +const isEditingText = objects.some(obj => obj.type === 'text' && obj.isEditing); + +// Delete selected object (only when NOT editing text) +if ((e.key === 'Delete' || e.key === 'Backspace') && selectedId && !isEditingText) { + e.preventDefault(); + deleteSelected(); +} +``` + +#### 2. Missing Bounding Box (Critical) ✅ +**Problem:** Transformer/bounding box not appearing when text objects were selected. Console showed `Node: undefined` repeatedly. + +**Root Cause:** In `Canvas.tsx` line ~286, refs were passed as objects instead of callback functions: +```typescript +// BROKEN: +shapeRef={{ current: shapeRefs.current.get(obj.id) || null }} +``` +This created new objects on each render but never actually stored the node references. + +**Solution:** Implemented callback ref pattern: +```typescript +// FIXED: +shapeRef={(node: Konva.Node | null) => { + if (node) { + shapeRefs.current.set(obj.id, node); + } else { + shapeRefs.current.delete(obj.id); + } +}} +``` + +**Additional Changes:** +- Updated type definitions in all components to accept both callback and RefObject refs: + ```typescript + shapeRef?: ((node: Konva.Node | null) => void) | React.RefObject; + ``` +- Updated `TextEditable.tsx`, `CanvasObject.tsx`, and `ImageRenderer` to handle both ref patterns + +#### 3. Duplicate/Transparent Text During Editing ✅ +**Problem:** Half-transparent text showing behind textarea during editing, creating visual duplicates. + +**Solution:** Changed text opacity to 0 during editing in `TextEditable.tsx`: +```typescript + +``` + +#### 4. Textarea Rendering ✅ +**Problem:** Initial attempt to render textarea inside Konva tree caused errors ("Konva has no node with the type textarea"). + +**Solution:** Moved textarea rendering completely outside Konva tree in `Canvas.tsx`: +```typescript +{editingText && ( +