Debug logging was added to troubleshoot rank detection logic.
Now that icons are working correctly, removing the console.log.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Set Rating column table cells to fixed height (h-12 = 48px)
- Wrap icons in flex container for vertical centering
- Reduce icon size from 14x14 to 11x11 to fit within row height
- Add max-h-11 constraint to prevent overflow
- Add inline-block and align-middle to icon for proper alignment
This ensures all table rows remain the same height regardless of
whether they show rank icons or Premier rating badges.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- sm: 6x6 → 10x10 (used in scoreboard tables)
- md: 8x8 → 12x12
- lg: 12x12 → 16x16
The icons were too small to see details at 6x6 pixels.
Now properly visible in the Rating column of match scoreboards.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The API returns CS:GO skill groups (0-18) in BOTH rank_old and rank_new
fields for legacy matches. Updated detection logic to:
- Check if rating (rank_new) is in 0-18 range (CS:GO skill groups)
- CS2 ratings are typically 1000-30000, so no overlap
- Use rating value for RankIcon display (contains current skill group)
Debug output showed:
- rating: 15-17 (CS:GO skill groups)
- oldRating: same values (15-17)
- Previous logic failed because rating was not 0
This fix properly detects and displays CS:GO rank icons for legacy matches.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Update isLegacyRank check to properly detect CS:GO skill groups (0-18)
- Handle edge case where oldRating could be 0 (unranked)
- Add temporary debug logging to trace rank data values
- Fix detection for matches where rating is outside CS2 range
This will help identify if rank data is being passed correctly from the API.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implemented rank icon display system for pre-CS2 matches:
New Components:
- RankIcon.svelte: Displays CS:GO skill group icons (0-18)
- Supports sm/md/lg sizes
- Shows appropriate rank icon based on skill group
- Includes hover tooltips with rank names
- Handles all 19 rank tiers (Silver I → Global Elite)
Updated Components:
- PremierRatingBadge: Now intelligently switches between:
- CS:GO rank icons (when rank_old exists, rank_new doesn't)
- Premier rating badge (when rank_new exists)
- "Unranked" text (when neither exists)
Assets:
- Rank icons already present in static/images/rank_icons/
- Weapon icons already present in static/images/weapons/
- All icons in SVG format for crisp display at any size
Display Logic:
- Legacy matches (pre-Sept 2023): Show CS:GO rank icons
- Modern matches (CS2): Show Premier rating with trophy icon
- Automatically detects based on rank_old/rank_new fields
The scoreboard now displays the appropriate ranking visualization
based on match era, matching the original CSGO.WTF design.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implemented comprehensive visual overhaul based on UX feedback:
Background & Framing:
- Replace single gradient with dual-layer system for depth
- Add top-to-bottom gradient (30% → transparent → 40%) for framing
- Add left-to-right gradient (70% → 40% → 70%) for content focus
- Creates natural vignette effect that draws eye to center
Hero Info Panel:
- Wrap score and metadata in translucent panel (black/40 + backdrop-blur)
- Add subtle border (white/10) for definition
- Center-align and constrain width (max-w-3xl) for focused composition
- Cleaner hierarchy with reduced text shadows (rely on panel for contrast)
Typography & Layout:
- Increase map title to text-5xl, remove badge duplication
- Score numbers to text-6xl for prominence
- Team labels to text-xs with reduced opacity (70%)
- Metadata with bullet separators (•) for cleaner inline layout
- Smaller icons (3.5) and tighter spacing
Download Demo Button:
- Ghost style with translucent background (white/15)
- Subtle border (white/25) instead of solid primary color
- Hover effect (white/25) for interaction feedback
- Hide text on mobile (icon only) for space efficiency
Navigation:
- Reduce tabs background to 35% opacity with stronger backdrop-blur-lg
- Add border (white/10) for subtle definition
- Maintains readability while showing more background
Result: Modern, cinematic interface with improved visual hierarchy
and readability on both dark and bright map backgrounds.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Back button: solid black/60 background with backdrop-blur instead of ghost
- Download Demo button: solid primary background instead of outline
- Map name: triple-layer text shadow for maximum contrast
- Score labels: full white with strong shadows, uppercase styling
- Score numbers: triple shadow with glow effect (0.95/0.8/0.6 opacity layers)
- Colon separator: full white with strong shadow
- Metadata text/icons: strong text shadows and drop-shadow filters
- Tabs container: increased to black/70 with stronger backdrop-blur
All text elements now have multiple layers of shadows for readability
on bright map backgrounds like de_dust2.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Strengthen background gradient overlay from 90/70/50% to 95/85/75% opacity
- Add stronger text shadows to score numbers (double shadow for depth)
- Increase team label opacity from 70% to 90%
- Increase metadata text from white/80 to white with drop-shadow
- Increase tabs background from black/30 to black/50
- Improve colon separator contrast
Fixes readability issues on bright maps like de_dust2.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Use parseMatchWeaponsSafe instead of parseMatchWeapons
- Use parseMatchChatSafe instead of parseMatchChat
- Throw clear error message when demo not parsed yet
- Prevents Zod validation errors from reaching page loaders
- Matches the pattern already used for rounds endpoint
This fixes Zod errors when navigating to match detail pages where
the demo hasn't been fully parsed yet by the backend.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Fix toast notification imports: change from showToast to toast.success/error
- Remove hover preloading from app.html and Tabs component
- Fix match rounds API handling with safe parsing for incomplete data
- Fix pagination timestamp calculation (API returns Unix timestamp, not ISO string)
- Refactor matches page state management to fix reactivity issues:
- Replace separate state variables with single matchesState object
- Completely replace state object on updates to trigger reactivity
- Fix infinite loop in intersection observer effect
- Add keyed each blocks for proper list rendering
- Remove client-side filtering (temporarily) to isolate reactivity issues
- Add error state handling with nextPageTime in matches loader
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Create detailed privacy policy explaining data collection and usage practices.
## New Page: /privacy
### Content Sections
**Introduction**
- Overview of CS2.WTF's commitment to privacy
**Information We Collect**
- Public Steam Data: Steam ID, match stats, chat messages, VAC status
- Usage Data: Page visits and feature interactions
- Browser Storage: Theme, favorites, recent players (localStorage only)
**How We Use Information**
- Provide match statistics and analysis
- Track performance over time
- Generate visualizations
- Improve service quality
**Cookies and Local Storage**
- Details on localStorage usage for preferences
- No tracking cookies
- User-controllable through browser settings
**Data Sharing**
- No selling/trading of personal information
- Only share when legally required or for safety
- May use service providers
**Security**
- Reasonable security measures implemented
- Acknowledgment of inherent internet risks
- Note that Steam data is already public
**Your Rights**
- Access, correction, and deletion rights
- Opt-out by not using service
- Control via Steam privacy settings
**Third-Party Services**
- Links to Steam profiles
- Google Translate integration
- Not responsible for third-party privacy practices
**Children's Privacy**
- Not directed at children under 13
- No knowingly collected children's data
**Policy Changes**
- May update with notice
- Continued use implies acceptance
**Contact**
- Direct users to GitHub for questions/issues
### UI Implementation
- Clean, readable layout with Card components
- Icons for each section (Shield, Eye, Cookie, Server, Mail)
- Responsive design optimized for all screen sizes
- Proper semantic HTML and accessibility
- SEO-optimized with meta tags
- Current date automatically displayed
The policy is comprehensive yet clear, covering all necessary legal bases while
explaining data practices in user-friendly language. Already linked from the
footer navigation.
This completes Phase 3 Feature 3 - privacy compliance established.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add comprehensive match record breakdown showing wins, losses, and ties.
## Changes
### Data Calculation (+ page.ts)
- Enhanced playerStats mapping to detect tied matches
- Calculate `isTie` by comparing team scores (score_team_a === score_team_b)
- Add `tied` boolean field alongside existing `won` field
- Ensure wins exclude tied matches for accurate statistics
### Statistics Display (+page.svelte)
- Added "Match Record" card to Career Statistics section
- Calculate wins, losses, and ties from playerStats
- Display in compact format: "XW / YL / ZT"
- Color-coded: wins (green), losses (red), ties (blue)
- Show total matches analyzed below the record
### UI Improvements
- Expanded Career Statistics grid from 4 to 5 columns
- Responsive: 2 columns on mobile, 5 on desktop
- Consistent card styling with other career stats
- Trophy icon for Match Record card
## Implementation Details
**Tie Detection Logic:**
```typescript
const isTie = match.score_team_a === match.score_team_b;
const won = !isTie &&
((playerData.team_id === 2 && match.score_team_a > match.score_team_b) ||
(playerData.team_id === 3 && match.score_team_b > match.score_team_a));
```
**Record Format:**
- Wins: Green text, "W" suffix
- Losses: Red text, "L" suffix
- Ties: Blue text, "T" suffix
- Separators: Gray "/"
The statistics are calculated from the last 15 matches with full details,
providing accurate win/loss/tie breakdown for performance tracking.
This completes Phase 3 Feature 2 - match records now fully visible.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add direct link to Steam Community profile for easy access to player's Steam page.
## Changes
### UI Addition
- Added "Steam Profile" button to player page actions section
- Positioned alongside "Track Player" and "View All Matches" buttons
- Uses ExternalLink icon from lucide-svelte
- Ghost button variant for secondary action styling
### Link Implementation
- Opens Steam Community profile in new tab
- Uses player's Steam ID (uint64) to construct profile URL
- Format: `https://steamcommunity.com/profiles/{steamid64}`
- Includes `target="_blank"` and `rel="noopener noreferrer"` for security
### UX Improvements
- Changed actions container to use `flex-wrap` for responsive layout
- Buttons wrap on smaller screens to prevent overflow
- External link icon clearly indicates opening in new tab
**Security Note:** The `rel="noopener noreferrer"` attribute prevents:
- Potential security issues with window.opener access
- Referrer information leakage to external site
This provides users quick access to full Steam profile information including
inventory, game library, friends list, and other Steam-specific data not
available in CS2.WTF.
This completes Phase 3 Feature 1 - Steam profile integration added.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Enable translation of non-English chat messages to help users understand
international communications.
## Changes
### Translation Detection
- Added `mightNeedTranslation()` function to detect non-English text
- Checks for Cyrillic, Chinese, Japanese, Korean, and Arabic characters
- Uses Unicode range pattern matching for language detection
### Translation UI
- Added Languages icon from lucide-svelte
- Display translate button next to messages that contain non-English text
- Button shows icon only on mobile, "Translate" text on desktop
- Positioned using flexbox to prevent text wrapping issues
### Translation Functionality
- `translateMessage()` opens Google Translate in new popup window
- Auto-detects source language, translates to English
- Uses Google Translate's free web interface (no API key required)
- Opens in 800x600 popup for optimal translation viewing
## Implementation Details
The feature works by:
1. Scanning each message for non-ASCII character ranges
2. Showing translate button only for messages likely in foreign languages
3. Opening Google Translate web UI in popup when clicked
4. Preserving original message while providing translation access
**Why Google Translate Web Interface:**
- No API keys or authentication required
- Free and unlimited usage
- Familiar translation interface for users
- Supports all languages Google Translate offers
- Popup window keeps context while showing translation
This approach avoids the complexity and cost of translation APIs while
providing full-featured translation capabilities to users.
This completes Phase 2 Feature 4 and ALL Phase 2 features! 🎉🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implement dynamic sitemap.xml and robots.txt for search engine optimization.
## New Files
### src/routes/sitemap.xml/+server.ts
- Generate XML sitemap with all indexable pages
- Include static pages (home, matches list)
- Dynamically fetch and include recent 100 matches
- Set appropriate priority and changefreq for each URL type
- Cache response for 1 hour to reduce server load
### src/routes/robots.txt/+server.ts
- Allow all crawlers to index the site
- Point crawlers to sitemap.xml location
- Set crawl-delay of 1 second to be polite to server
- Cache response for 1 day
## Implementation Details
**Sitemap Structure:**
- Static pages: Priority 0.9-1.0, updated daily
- Match pages: Priority 0.7, updated weekly
- Fetches up to 100 most recent matches from API
- Uses match date as lastmod timestamp for accurate indexing
**SEO Benefits:**
- Helps search engines discover all match pages efficiently
- Provides crawlers with update frequency hints
- Improves indexing of dynamic content
- Reduces unnecessary crawling with robots.txt directives
The sitemap automatically stays current by fetching recent matches on each
request. The 1-hour cache balances freshness with server performance.
Note: Player profile pages not included in sitemap due to lack of bulk listing
API endpoint. Individual player pages will still be indexed via internal links.
This completes Phase 2 Feature 3 - site now properly configured for SEO.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Display average Premier rating for each team in match statistics cards.
## Changes
### Average Rating Calculation
- Calculate average rank_new across all ranked players on each team
- Filter out unranked players (rating = 0 or null/undefined)
- Round to nearest integer for clean display
### UI Display
- Add PremierRatingBadge below team name in statistics cards
- Small size badge with trophy icon
- Automatically styled with tier colors (gray/blue/purple/pink/red/gold)
- Only show badge if team has at least one ranked player
## Implementation Details
Extended `calcTeamStats()` function to compute `avgRating` by:
1. Filtering players with valid rank_new > 0
2. Computing average if any ranked players exist
3. Returning undefined if no players are ranked
The PremierRatingBadge component handles all tier styling automatically based
on the rating value using the existing formatPremierRating utility.
Team badges provide quick visual indication of match skill level and help
identify skill disparities between teams.
This completes Phase 2 Feature 2 - team ratings now prominently displayed.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Enhance match details scoreboard with additional visual information for better
player identification and context.
## Changes
### Avatar Column
- Display player profile images in first column (40x40px)
- Rounded style with border for consistent appearance
- Non-sortable for visual continuity
### Score Column
- Show in-game score for each player
- Sortable to identify top performers
- Monospace font for numeric alignment
### Player Color Indicators
- Add colored dot next to player names
- Map CS2 player colors (green, yellow, purple, blue, orange, grey) to hex values
- Visual indicator helps identify specific players during match review
## Implementation Details
Created `playerColors` mapping object to convert CS2's player color names to
hex color codes for display. Updated Player name column render function to
include inline colored dot element.
All columns maintain existing team color styling (terrorist/CT) for consistency.
Note: MVP and K/D ratio columns already existed in scoreboard.
This completes Phase 2 Feature 1 - scoreboard now provides comprehensive player
information at a glance.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implement localStorage-based player visit tracking with display on home page.
## New Features
### Recently Visited Players Component
- **RecentPlayers.svelte**: Grid display of up to 10 recently visited players
- **Responsive Layout**: 1-4 columns based on screen size
- **Player Cards**: Avatar, name, and time since last visit
- **Remove Functionality**: Individual player removal with X button
- **Auto-show/hide**: Component only renders when players have been visited
### Player Visit Tracking
- **recentPlayers utility**: localStorage management functions
- `getRecentPlayers()`: Retrieve sorted list by most recent
- `addRecentPlayer()`: Add/update player visit with timestamp
- `removeRecentPlayer()`: Remove specific player from list
- `clearRecentPlayers()`: Clear entire history
- **Auto-tracking**: Player profile page automatically records visits on mount
- **Smart deduplication**: Visiting same player updates timestamp, no duplicates
- **Max limit**: Maintains up to 10 most recent players
### Time Display
- Relative time formatting: "Just now", "5m ago", "2h ago", "3d ago"
- Real-time updates when component mounts
- Human-readable timestamps
### UX Features
- **Hover Effects**: Border highlights and shadow on card hover
- **Team Colors**: Player names inherit team colors from profiles
- **Remove on Hover**: X button appears only on hover for clean interface
- **Click Protection**: Remove button prevents navigation when clicked
- **Footer Info**: Shows count of displayed players
## Implementation Details
- **localStorage Key**: `cs2wtf_recent_players`
- **Data Structure**: Array of `{id, name, avatar, visitedAt}` objects
- **Sort Order**: Most recently visited first
- **SSR Safe**: All localStorage operations check for browser environment
- **Error Handling**: Try-catch wraps all storage operations with console errors
## Integration
- Added to home page between hero and featured matches
- Integrates seamlessly with existing layout and styling
- No data fetching required - pure client-side functionality
- Persists across sessions via localStorage
This completes Phase 1 (6/6) - all critical features now implemented!
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implement detailed weapon performance tracking and visualization.
## New Features
### Weapons Tab
- Added "Weapons" tab to match layout navigation
- Created `/match/[id]/weapons` route with server-side data loading
- Displays weapon statistics for all players in the match
### Statistics Displayed
**Overview Cards:**
- Total kills across all weapons
- Total damage dealt
- Total hits landed
**Charts & Visualizations:**
- Bar chart: Top 10 most-used weapons by kills
- Pie chart: Hit location distribution (head, chest, stomach, arms, legs)
- Legend with exact hit counts for each body part
**Player Performance Table:**
- Player name (with team color coding)
- Top weapon for each player
- Total kills per player
- Total damage per player
- Total hits per player
- Sortable columns for easy comparison
### Technical Implementation
- **Data Loading**: Server-side fetch of weapons data via `getMatchWeapons()` API
- **Type Safety**: Full TypeScript types with WeaponStats, PlayerWeaponStats, MatchWeaponsResponse
- **Error Handling**: Graceful fallback when weapons data unavailable
- **Aggregation**: Weapon stats aggregated across all players for match-wide insights
- **Team Colors**: Player names colored by team (terrorist/CT)
### UX Improvements
- Empty state with helpful message when no weapons data exists
- Responsive grid layouts for cards and charts
- Consistent styling with existing tabs
- Interactive data table with hover states and sorting
This completes Phase 1 feature 5 of 6 - comprehensive weapon performance analysis
gives players insight into their weapon choices and accuracy.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add per-player VAC and game ban status display in match details scoreboard.
## Changes
- **Types & Schema**: Add vac and game_ban optional boolean fields to MatchPlayer
- **Transformer**: Extract vac and game_ban from legacy player.vac and player.game_ban
- **UI**: Add "Status" column to details table showing VAC/BAN badges
- **Mocks**: Update mock player data with ban status fields
## Implementation Details
The backend API provides per-player ban status in match details via the player
object (player.vac and player.game_ban). These were previously not being extracted
by the transformer.
The new Status column displays:
- Red "VAC" badge if player has VAC ban
- Red "BAN" badge if player has game ban
- Both badges if player has both bans
- "-" if player has no bans
Users can identify banned players at a glance in the scoreboard, improving
transparency and match context.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit completes the first phase of feature parity implementation and
resolves all API integration issues to match the backend API format.
## API Integration Fixes
- Remove all hardcoded default values from transformers (tick_rate, kast, player_count, steam_updated)
- Update TypeScript types to make fields optional where backend doesn't guarantee them
- Update Zod schemas to validate optional fields correctly
- Fix mock data to match real API response format (plain arrays, not wrapped objects)
- Update UI components to handle undefined values with proper fallbacks
- Add comprehensive API documentation for Match and Player endpoints
## Phase 1 Features Implemented (3/6)
### 1. Player Tracking System ✅
- Created TrackPlayerModal.svelte with auth code input
- Integrated track/untrack player API endpoints
- Added UI for providing optional share code
- Displays tracked status on player profiles
- Full validation and error handling
### 2. Share Code Parsing ✅
- Created ShareCodeInput.svelte component
- Added to matches page for easy match submission
- Real-time validation of share code format
- Parse status feedback with loading states
- Auto-redirect to match page on success
### 3. VAC/Game Ban Status ✅
- Added VAC and game ban count/date fields to Player type
- Display status badges on player profile pages
- Show ban count and date when available
- Visual indicators using DaisyUI badge components
## Component Improvements
- Modal.svelte: Added Svelte 5 Snippet types, actions slot support
- ThemeToggle.svelte: Removed deprecated svelte:component usage
- Tooltip.svelte: Fixed type safety with Snippet type
- All new components follow Svelte 5 runes pattern ($state, $derived, $bindable)
## Type Safety & Linting
- Fixed all ESLint errors (any types → proper types)
- Fixed form label accessibility issues
- Replaced error: any with error: unknown + proper type guards
- Added Snippet type imports where needed
- Updated all catch blocks to use instanceof Error checks
## Static Assets
- Migrated all files from public/ to static/ directory per SvelteKit best practices
- Moved 200+ map icons, screenshots, and other assets
- Updated all import paths to use /images/ (served from static/)
## Documentation
- Created IMPLEMENTATION_STATUS.md tracking all 15 missing features
- Updated API.md with optional field annotations
- Created MATCHES_API.md with comprehensive endpoint documentation
- Added inline comments marking optional vs required fields
## Testing
- Updated mock fixtures to remove default values
- Fixed mock handlers to return plain arrays like real API
- Ensured all components handle undefined gracefully
## Remaining Phase 1 Tasks
- [ ] Add VAC status column to match scoreboard
- [ ] Create weapons statistics tab for matches
- [ ] Implement recently visited players on home page
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add LegacyPlayerProfile transformer to handle API response format mismatch
- Transform avatar hashes to full Steam CDN URLs
- Map team IDs correctly (API 1/2 -> Schema 2/3)
- Calculate aggregate stats (avg_kills, avg_deaths, win_rate) from matches
- Reduce featured matches on homepage from 6 to 3
- Show 4 recent matches on player profile instead of 10
- Display recent matches in 4-column grid on desktop (side-by-side)
Fixes "Player not found" error for all player profiles.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Enable SSR for match pages by detecting server vs client context in API client
- Fix 500 errors on economy, chat, and details tabs by adding data loaders
- Handle unparsed matches gracefully with "Match Not Parsed" messages
- Fix dynamic team ID detection instead of hardcoding team IDs 2/3
- Fix DataTable component to properly render HTML in render functions
- Add fixed column widths to tables for visual consistency
- Add meta titles to all tab page loaders
- Fix Svelte 5 $derived syntax errors
- Fix ESLint errors (unused imports, any types, reactive state)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Adds client-side sorting and filtering capabilities:
Sorting options:
- Date (newest/oldest)
- Duration (shortest/longest)
- Score difference (close games/blowouts)
- Toggle ascending/descending order
Result filters:
- All matches
- Wins only (Team A won)
- Losses only (Team B won)
- Ties only
Features:
- Reactive $derived computed matches list
- Shows filtered count vs total matches
- Empty state when no matches match filters
- Clear filter button when results are empty
- Works seamlessly with pagination (Load More)
Completes Phase 5.2 advanced features from TODO.md.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
JavaScript's Number type cannot accurately represent uint64 values exceeding
Number.MAX_SAFE_INTEGER (2^53-1). Converting these IDs to numbers causes
precision loss and API errors.
Root cause found:
- match/[id]/+layout.ts: `Number(params.id)` corrupted match IDs
- player/[id]/+page.ts: `Number(params.id)` corrupted player IDs
Example of the bug:
- URL param: "3638078243082338615" (correct)
- After Number(): 3638078243082339000 (rounded!)
- API response: "Match 3638078243082339000 not found"
Changes:
- Remove Number() conversions in route loaders
- Keep params.id as string throughout the application
- Update API functions to only accept string (not string | number)
- Update MatchesQueryParams.player_id type to string
- Add comprehensive transformers for legacy API responses
- Transform player stats: duo→mk_2, triple→mk_3, steamid64→id
- Build full Steam avatar URLs
- Make share_code optional (not always present)
This ensures uint64 IDs maintain full precision from URL → API → response.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add pagination state management (matches, hasMore, nextPageTime)
- Create loadMore() function to fetch and append next page of results
- Replace placeholder "pagination coming soon" with functional Load More button
- Add loading spinner during pagination requests
- Show total matches count and "all loaded" message when complete
- Use $effect to reset pagination state when filters change
Completes Phase 5.2 pagination requirement from TODO.md.
Users can now browse through large match lists efficiently.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Create transformers.ts to convert legacy API format to new schema
- Transform score array [a, b] to score_team_a/score_team_b fields
- Convert Unix timestamps to ISO strings
- Map legacy field names (parsed, vac, game_ban) to new names
- Update matches API to use transformer with proper types
- Handle empty map names gracefully in homepage
- Limit featured matches to exactly 6 items
Fixes homepage data display issue where API format mismatch prevented
matches from rendering. API returns legacy CSGO:WTF format while frontend
expects new CS2.WTF schema.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implemented comprehensive performance analysis for player profiles with interactive charts
and detailed statistics visualization.
Key Features:
- Performance Trend Chart (K/D and KAST over last 15 matches)
- Map Performance Chart (win rate per map with color coding)
- Utility Effectiveness Stats (flash assists, enemies blinded, HE/flame damage)
- Responsive charts using Chart.js LineChart and BarChart components
Technical Updates:
- Enhanced page loader to fetch 15 detailed matches with player stats
- Fixed DataTable Svelte 5 compatibility and type safety
- Updated MatchCard and PlayerCard to use PlayerMeta properties
- Proper error handling and typed data structures
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implemented a comprehensive CORS proxy solution that works with both
local and remote backends during development.
## Changes
### Vite Configuration (vite.config.ts)
- Use loadEnv() to properly read VITE_API_BASE_URL from .env
- Configure proxy to forward /api/* requests to backend
- Add detailed logging for proxy requests and responses
- Support changeOrigin, rewrite, secure=false, and websockets
### API Client (src/lib/api/client.ts)
- In development: Always use /api prefix (proxied)
- In production: Use direct VITE_API_BASE_URL
- Add console logging to show proxy configuration in dev mode
- Automatic detection of environment (DEV vs PROD)
### Error Handling (route loaders)
- Fix console.error() calls that caused TypeError with circular refs
- Use error.message instead of logging full error objects
- Affects: +page.ts, matches/+page.ts
### Documentation
- docs/LOCAL_DEVELOPMENT.md: Complete rewrite with proxy explanation
- Quick start guide for both production API and local backend
- Detailed proxy flow diagrams
- Comprehensive troubleshooting section
- Clear examples and logs
- docs/CORS_PROXY.md: Technical deep-dive on proxy implementation
- How the proxy works internally
- Configuration options explained
- Testing procedures
- Common issues and solutions
- .env.example: Updated with proxy documentation
## How It Works
Development Flow:
1. Frontend makes request: /api/matches
2. Vite proxy intercepts and forwards to: ${VITE_API_BASE_URL}/matches
3. Backend responds (no CORS headers needed)
4. Proxy returns response to frontend (same-origin)
Production Flow:
1. Frontend makes request directly to: https://api.csgow.tf/matches
2. Backend responds with CORS headers
3. Browser allows request (CORS enabled on backend)
## Benefits
✅ No CORS errors in development
✅ Works with local backend (localhost:8000)
✅ Works with remote backend (api.csgow.tf)
✅ Simple configuration (just set VITE_API_BASE_URL)
✅ Detailed logging for debugging
✅ Production build unaffected (direct requests)
## Testing
Verified with production API:
- curl https://api.csgow.tf/matches ✓
- Dev server proxy logs show successful forwarding ✓
- Browser Network tab shows /api/* requests ✓
- No CORS errors in console ✓
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replace invalid {@const} usage with proper <svelte:component this={...}> syntax.
The {@const} tag must be an immediate child of specific block structures,
not directly inside regular HTML elements.
This commit implements significant portions of Phase 5 (Feature Delivery) including:
Chart Components (src/lib/components/charts/):
- LineChart.svelte: Line charts with Chart.js integration
- BarChart.svelte: Vertical/horizontal bar charts with stacking
- PieChart.svelte: Pie/Doughnut charts with legend
- All charts use Svelte 5 runes ($effect) for reactivity
- Responsive design with customizable options
Data Display Components (src/lib/components/data-display/):
- DataTable.svelte: Generic sortable, filterable table component
- TypeScript generics support for type safety
- Custom formatters and renderers
- Sort indicators and column alignment options
Match Detail Pages:
- Match layout with header, tabs, and score display
- Economy tab: Equipment value charts, buy type classification, round-by-round table
- Details tab: Multi-kill distribution charts, team performance, top performers
- Chat tab: Chronological messages with filtering, search, and round grouping
Additional Components:
- SearchBar, ThemeToggle (layout components)
- MatchCard, PlayerCard (domain components)
- Modal, Skeleton, Tabs, Tooltip (UI components)
- Player profile page with stats and recent matches
Dependencies:
- Installed chart.js for data visualization
- Created Svelte 5 compatible chart wrappers
Phase 4 marked as complete, Phase 5 at 50% completion.
Flashes and Damage tabs deferred for future implementation.
Note: Minor linting warnings to be addressed in follow-up commit.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Phase 4 establishes the core application structure with SvelteKit routing,
data loading, error handling, and state management.
## Routing & Data Loading
- Created root layout load function (+layout.ts) with app version and feature flags
- Implemented comprehensive error boundary (+error.svelte) with status-based messages
- Added page loaders for homepage, matches, players, and about routes
- Homepage loader fetches featured matches via API with error fallback
- Matches loader supports URL query parameters (map, player_id, limit)
## State Management (Svelte Stores)
- preferences.ts: User settings with localStorage persistence
* Theme selection (cs2dark, cs2light, auto)
* Favorite players tracking
* Advanced stats toggle, date format preferences
- search.ts: Search state with recent searches (localStorage)
- toast.ts: Toast notification system with auto-dismiss
* Success, error, warning, info types
* Configurable duration and dismissibility
## UI Components
- Toast.svelte: Individual notification with Lucide icons
- ToastContainer.svelte: Fixed top-right toast display
- Integrated ToastContainer into root layout
## Fixes
- Fixed Svelte 5 deprecation warnings (removed <svelte:component> in runes mode)
- Updated homepage to use PageData from loader
- Added proper type safety across all load functions
## Testing
- Type check: 0 errors, 0 warnings
- Production build: Successful
- All Phase 4 core objectives completed
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Phase 3 (Domain Modeling & Data Layer) is now 100% complete:
- ✅ TypeScript interfaces for all data models
- ✅ Zod schemas with runtime validation
- ✅ API client with error handling and cancellation
- ✅ MSW mock handlers for testing
- Real-time features deferred to Phase 5
Now starting Phase 4: Application Architecture & Routing
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implements comprehensive type system, runtime validation, API client,
and testing infrastructure for CS2.WTF.
TypeScript Interfaces (src/lib/types/):
- Match.ts: Match, MatchPlayer, MatchListItem types
- Player.ts: Player, PlayerMeta, PlayerProfile types
- RoundStats.ts: Round economy and performance data
- Weapon.ts: Weapon statistics with hit groups (HitGroup, WeaponType enums)
- Message.ts: Chat messages with filtering support
- api.ts: API responses, errors, and APIException class
- Complete type safety with strict null checks
Zod Schemas (src/lib/schemas/):
- Runtime validation for all data models
- Schema parsers with safe/unsafe variants
- Special handling for backend typo (looses → losses)
- Share code validation regex
- CS2-specific validations (rank 0-30000, MR12 rounds)
API Client (src/lib/api/):
- client.ts: Axios-based HTTP client with error handling
- Request cancellation support (AbortController)
- Automatic retry logic for transient failures
- Timeout handling (10s default)
- Typed APIException errors
- players.ts: Player endpoints (profile, meta, track/untrack, search)
- matches.ts: Match endpoints (parse, details, weapons, rounds, chat, search)
- Zod validation on all API responses
MSW Mock Handlers (src/mocks/):
- fixtures.ts: Comprehensive mock data for testing
- handlers/players.ts: Mock player API endpoints
- handlers/matches.ts: Mock match API endpoints
- browser.ts: Browser MSW worker for development
- server.ts: Node MSW server for Vitest tests
- Realistic responses with delays and pagination
- Safe integer IDs to avoid precision loss
Configuration:
- .env.example: Complete environment variable documentation
- src/vite-env.d.ts: Vite environment type definitions
- All strict TypeScript checks passing (0 errors, 0 warnings)
Features:
- Cancellable requests for search (prevent race conditions)
- Data normalization (backend typo handling)
- Comprehensive error types (NetworkError, Timeout, etc.)
- Share code parsing and validation
- Pagination support for players and matches
- Mock data for offline development and testing
Build Status: ✓ Production build successful
Type Check: ✓ 0 errors, 0 warnings
Lint: ✓ All checks passed
Phase 3 Completion: 100%
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Reflect backend audit findings in TODO.md:
- 12 REST endpoints documented for player, match, matches, and sitemap
- Data models aligned with backend schemas (Match, Player, MatchPlayer,
etc.)
- CS2 compatibility confirmed with Premier rating support (0-30000)
Add comprehensive API documentation covering:
- Endpoint specifications and response structures
- Integration guide with TypeScript examples
- Error handling and caching strategies
- CS2 migration notes for rank system and MR12 changes