- CollectionArtifactCard for grid view
- CollectionArtifactRow for list view
- CollectionArtifactPane for sidebar details
- artifacts collection page with filters and infinite scroll
- getArtifactImage util
- update collection layout for artifacts tab
- DisclosureRow: iOS-style disclosure row for navigation
- ArtifactSkillRow: shows skill with modifiers and level/value controls
- ArtifactModifierList: selectable list of skills by polarity
- ArtifactGradeDisplay: shows letter grade, breakdown, recommendation
- ArtifactEditPane: main edit pane combining base props, skills, grade
sidebar now uses PaneStackStore internally - openWithComponent
creates root pane, and child components can push/pop via context.
simplified Sidebar.svelte to render PaneStack when stack has items.
The addWeapons/addSummons methods were using Promise.all with Array.fill()
which created arrays where all elements referenced the same object. This
caused the request deduplication logic in BaseAdapter to cancel previous
requests since they all had the same body/requestId.
Fix:
- Use Array.from() with spread to create unique object instances
- Execute requests sequentially to avoid deduplication conflicts
- Improve error handling in AddToCollectionModal to filter CancelledErrors
- Create weapons route with page, server load, and grid/list views
- Create summons route with page, server load, and grid/list views
- Enable weapons/summons tabs in collection layout (remove disabled)
- Add dynamic "Add" button text based on active entity type
- Pass entityType to AddToCollectionModal based on current route
- WeaponEditPane: edit component with uncap, transcendence, element,
weapon keys, AX skills, and awakening support
- SummonEditPane: simple edit component with uncap and transcendence
- CollectionWeaponPane: full detail pane with Info/My Collection tabs
- CollectionSummonPane: full detail pane with Info/My Collection tabs
- CollectionWeaponCard: grid view with uncap indicator and transcendence
- CollectionWeaponRow: list view with element, uncap, and awakening/keys info
- CollectionSummonCard: grid view with uncap indicator
- CollectionSummonRow: list view with element and uncap info
Character grid images are 280x160 (7:4 ratio, wider than tall).
The previous ratio was inverted. Now using 100px width with correct
aspect-ratio: 280/160.
- Use proper 16:33 aspect ratio for character cards instead of square
- Change object-fit from cover to contain to prevent cropping
- Deduplicate search results across pages to prevent duplicate key errors
The Collection nav link now points to /{username}/collection/characters
instead of /collection. Also updates isNavSelected to properly detect
when on any collection page.
Reduces ~240 lines to ~85 lines by using the shared CharacterEditPane
component for edit controls. Both party grid and collection character
editing now use the same underlying UI component.
- Add collection route structure at [username]/collection/characters
- Create CharacterEditPane as shared component for character customizations
- Create CollectionCharacterPane with Info and My Collection tabs
- Add character grid with filters and infinite scroll
- Fix CollectionFilters a11y warnings
Large modal for batch-selecting characters to add to collection.
Features server-side search, filtering, multi-select with visual
feedback, and "X selected" link to filter to selection only.
Types, adapter, queries, and mutations for managing user collections
(characters, weapons, summons, job accessories). Supports both private
collection management and public collection viewing with privacy.
- SuggestionBadge: sparkle icon with tooltip for accept/dismiss actions
- SuggestionDetailItem: detail item wrapper with suggestion badge support
- TabbedEntitySelector: entity image grid for batch selection
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Import fonts.css in app.html
- Update --font-family CSS variable to use AGrot
- Adjust $bold weight from 600 to 700 to match AGrot's available weights
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Prevents onValueChange from firing during initialization, which
caused pushState errors before router was ready.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replace simulated save with actual API call to updateCharacter().
Invalidates TanStack Query cache on successful update.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add updateCharacter(), updateSummon(), and updateWeapon() methods to
entity adapter. Each method uses PATCH and clears the entity cache
after successful update.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add CharacterTypeahead component for async character search using Svelecte.
The component debounces input and queries the search API for matching
characters. Added recruits field to weapon creation page.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Backfill nicknames support to character creation page:
- Add nicknamesEn and nicknamesJp fields to editData
- Import and use TagInput component for nickname entry
- Add nicknames section to form UI
- Update CreateCharacterPayload interface with nickname fields
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- /database/summons/new route with full form
- UncapSection with FLB/ULB/Transcendence cascade
- TaxonomySection with element and series
- StatsSection with HP/ATK at all uncap levels
- Nicknames via TagInput component
- getSummonMaxUncapLevel() utility function
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Chip/tag style input for string arrays
- Add/remove tags with Enter or comma
- Backspace removes last tag when input empty
- Supports label, error, maxTags, disabled
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Show "New character/weapon/summon" pill button on database pages
- Keep existing circular + button for team creation elsewhere
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- new page with granblue_id validation
- role check (>= 7) on server
- API methods: validate, create, download images
- permanent edit mode with create button
- character grid starts at position 1 (skip protagonist)
- add isEmptySelected state for glow on empty slots
- empty protagonist shows relief.png background only
The WeaponRep (and other rep components) weren't visually updating when
new items were added to the party. The issue was that Svelte's reactivity
wasn't properly propagating through the {#each} blocks.
Changes:
- PartySegmentedControl: Add derived values for party sub-properties to
ensure reactivity propagates through snippet boundaries
- WeaponRep: Pre-compute rows as explicit $derived value and use keyed
{#each} blocks for proper change detection
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## Overview
Complete migration from service layer to TanStack Query v6 with
mutations, queries, and automatic cache management.
## What Was Completed
### Phase 1: Entity Queries & Database Pages ✅
- Created `entity.queries.ts` with query options for weapons,
characters, and summons
- Migrated all database detail pages to use TanStack Query with
`withInitialData()` pattern
- SSR with client-side hydration working correctly
### Phase 2: Server Load Cleanup ✅
- Removed PartyService dependency from teams detail server load
- Server loads now use adapters directly instead of service layer
- Cleaner separation of concerns
### Phase 3: Party.svelte Refactoring ✅
- Removed all PartyService, GridService, and ConflictService
dependencies
- Migrated to TanStack Query mutations for all operations:
- Grid operations: create, update, delete, swap, move
- Party operations: update, delete, remix, favorite, unfavorite
- Added swap/move mutations for drag-and-drop operations
- Automatic cache invalidation and query refetching
### Phase 4: Service Layer Removal ✅
- Deleted all service layer files (~1,345 lines removed):
- `party.service.ts` (620 lines)
- `grid.service.ts` (450 lines)
- `conflict.service.ts` (120 lines)
- `gridOperations.ts` (unused utility)
- Deleted empty `services/` directory
- Created utility functions for cross-cutting concerns:
- `localId.ts`: Anonymous user local ID management
- `editKeys.ts`: Edit key management for anonymous editing
- `party-context.ts`: Extracted PartyContext type
### Phase 5: /teams/new Migration ✅
- Migrated party creation wizard to use TanStack Query mutations
- Replaced all direct adapter calls with mutations (7 locations)
- Maintains existing behavior and flow
- Automatic cache invalidation for newly created parties
## Benefits
### Performance
- Automatic request deduplication
- Better cache utilization across pages
- Background refetching for fresh data
- Optimistic updates for instant UI feedback
### Developer Experience
- Single source of truth for data fetching
- Consistent patterns across entire app
- Query devtools for debugging
- Less boilerplate code
### Code Quality
- ~1,088 net lines removed
- Simpler mental model (no service layer)
- Better TypeScript inference
- Easier to test
### Architecture
- **100% TanStack Query coverage** - no service layer, no direct adapter
calls
- Clear separation: UI ← Queries/Mutations ← Adapters ← API
- Automatic cache management
- Consistent mutation patterns everywhere
## Testing
All features verified:
- Party creation (anonymous & authenticated)
- Grid operations (add, remove, update, swap, move)
- Party operations (update, delete, remix, favorite)
- Cache invalidation across tabs
- Error handling and rollback
- SSR with hydration
## Files Modified
### Created (3)
- `src/lib/types/party-context.ts`
- `src/lib/utils/editKeys.ts`
- `src/lib/utils/localId.ts`
### Deleted (4)
- `src/lib/services/party.service.ts`
- `src/lib/services/grid.service.ts`
- `src/lib/services/conflict.service.ts`
- `src/lib/utils/gridOperations.ts`
### Modified (13)
- `src/lib/api/mutations/grid.mutations.ts`
- `src/lib/components/grids/CharacterGrid.svelte`
- `src/lib/components/grids/SummonGrid.svelte`
- `src/lib/components/grids/WeaponGrid.svelte`
- `src/lib/components/party/Party.svelte`
- `src/routes/teams/new/+page.svelte`
- Database entity pages (characters, weapons, summons)
- Other supporting files
## Migration Complete
This PR completes the TanStack Query migration. The entire application
now uses TanStack Query v6 for all data fetching and mutations, with
zero remaining service layer code.
---------
Co-authored-by: Claude <noreply@anthropic.com>
added @tanstack/svelte-query-devtools (dev only)
- shows in dev mode only, tree-shaken from production
- provides visual debugging of queries, cache, and mutations
- accessible via floating icon in bottom-right corner
# feat: migrate components to TanStack Query v6 (Phase 2)
## Summary
This PR migrates 4 components from the custom
`createInfiniteScrollResource` pattern to TanStack Query v6's
`createInfiniteQuery`:
- **JobSkillSelectionSidebar** - Job skill search with infinite scroll
and category filtering
- **SearchContent** - Search modal for weapons/characters/summons with
element/rarity/proficiency filters
- **User Profile Page** (`[username]/+page.svelte`) - User's teams and
favorites with tab switching
- **Teams Explore Page** (`teams/explore/+page.svelte`) - Public teams
listing
All components now use:
- TanStack Query v6 infinite query pattern with thunk for reactivity
- `IsInViewport` from runed for intersection-based infinite scroll
- Debounced search (debounce the value, not the query)
- Proper loading, error, and empty states
- SSR integration with `initialData` pattern
## Updates since last revision
- **Fixed duplicate key error in SearchContent.svelte** - The API can
return the same item across multiple pages during infinite scroll (e.g.,
due to items being added/removed between page fetches). This caused
Svelte's keyed each block to throw `each_key_duplicate` errors. Fixed by
deduplicating results by `id` using a Map before rendering.
## Review & Testing Checklist for Human
This is a medium-risk change affecting core user-facing pages. Please
verify:
- [ ] **Infinite scroll works on all 4 pages** - Scroll to bottom and
verify more items load automatically
- [ ] **No duplicate items appear in search results** - After the fix,
scrolling through many pages of search results should not show
duplicates
- [ ] **SSR hydration** - Verify no flash of loading state on initial
page load (data should be pre-rendered)
- [ ] **User profile tab switching** - Test switching between "Teams"
and "Favorites" tabs; verify correct data loads
- [ ] **Search debouncing** - Type quickly in JobSkillSelectionSidebar
and SearchContent; verify queries aren't fired on every keystroke
- [ ] **Error states** - Simulate network failure and verify retry
button works
**Recommended Test Plan:**
1. Navigate to `/teams/explore` - verify teams load and infinite scroll
works
2. Navigate to a user profile page - verify teams load, switch to
favorites tab, verify favorites load
3. Open the search sidebar - search for weapons/characters/summons,
scroll through many pages, verify no duplicate key errors and no
duplicate items
4. Open job skill selection - search and filter skills, verify results
### Notes
- The Party component mutations migration (Follow-Up Prompt 5) was
deferred to a follow-up PR due to complexity
- Deprecated resource classes remain in codebase for now; removal
planned for separate PR
- Pre-existing paraglide module errors in build are unrelated to this PR
- Type assertions (`as unknown as`, `as any`) are used to handle
different query result structures between favorites and parties queries
**Link to Devin run:**
https://app.devin.ai/sessions/5aa7ea29edf34f569f95f13acee9e0d9
**Requested by:** Justin Edmund (justin@jedmund.com) / @jedmund
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Justin Edmund <justin@jedmund.com>