Migrate remaining Svelte 4 reactive statements to Svelte 5 $derived:
- media/audit/+page.svelte: Convert allSelected, hasSelection, selectedSize to $derived
- universe/compose/+page.svelte: Convert postType, initialContent to $derived
- Also migrated all let declarations to $state() in audit page for consistency
All reactive statements now use Svelte 5 runes mode.
Make page headers sticky with subtle shadow on scroll. Fix min-height to 90px to prevent jumping when switching tabs. Modernize layout to use full viewport height.
Convert admin components from Svelte 4 to Svelte 5 syntax using $props, $state, $derived, and $bindable runes. Simplifies AdminNavBar logic and improves type safety.
Extract BrandingToggle and BrandingSection components. Consolidate $effect blocks, add $derived state, and apply BEM naming. Reduces component size by 47% while improving maintainability.
Add live preview to branding form showing featured image, background color, and logo. Add database fields and toggles to control visibility of each element in project headers.
Add return false to drop handler so ProseMirror's Dropcursor extension can handle the actual node movement. Previously the handler would intercept drops but not perform any movement.
Extract reusable form patterns following Svelte 5 best practices:
**New Store Factory** (`project-form.svelte.ts`)
- Centralizes form state management with `$state` and `$derived` runes
- Provides validation, payload building, and field mutation methods
- Type-safe with ProjectFormData interface
- Reusable across different contexts
**New Helpers**
- `useDraftRecovery.svelte.ts`: Generic draft restoration with auto-detection
- `useFormGuards.svelte.ts`: Navigation guards, beforeunload warning, Cmd+S shortcut
- `DraftPrompt.svelte`: Extracted UI component for draft recovery prompts
**Refactored ProjectForm.svelte**
- Reduced from 720 lines to 417 lines (42% reduction)
- Uses new composable helpers instead of inline logic
- Cleaner separation between UI orchestration and business logic
- All form state now managed through formStore
- Draft recovery, navigation guards fully extracted
**Benefits**
- Reusable patterns for PostForm, EssayForm, etc.
- Easier to test helpers in isolation
- Consistent UX across all admin forms
- Better maintainability and code organization
Closes Task 3 of admin modernization plan (Phase 2)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fix a race condition where the clickOutside action's update() method
would remove and re-add the event listener every time it was called,
even when the enabled state hadn't changed. This caused clicks to be
missed during the setTimeout delay.
Changes:
- Track previous enabled state before updating
- Only use setTimeout when transitioning from disabled to enabled
- Immediately re-add listener when enabled state stays true
- No listener changes when enabled state stays false
This ensures click-outside events are consistently detected without
gaps in event listener registration.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replace manual click event listener with clickOutside action for the
pane backdrop click handling. This simplifies the code and ensures
consistent click-outside behavior.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replace manual click event listener with clickOutside action for the
publish menu dropdown. This simplifies the code and ensures consistent
click-outside behavior across all admin dropdowns.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Updated components to use the new clickOutside action instead of manual
event listener management:
- ProjectListItem: Add clickOutside action and dropdown coordination
- AdminSegmentedController: Replace $effect with clickOutside action
- BubbleTextStyleMenu: Simplify click-outside handling
- BubbleColorPicker: Simplify click-outside handling
- Posts/Projects pages: Remove redundant page-level click handlers
The clickOutside action provides a cleaner, more maintainable way to
handle click-outside behavior with proper lifecycle management.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Created a reusable clickOutside Svelte action that dispatches a custom
event when users click outside an element. This replaces manual
document.addEventListener patterns.
Features:
- TypeScript support with generic event types
- Configurable enabled/disabled state
- Optional callback parameter
- Proper cleanup on destroy
- setTimeout to avoid immediate triggering
Updated components to use the new action:
- BaseDropdown.svelte: Removed $effect with manual listeners
- PostDropdown.svelte: Replaced manual click handling
Part of Task 5 - Click-Outside Primitives
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed infinite loop caused by calling goto() inside beforeNavigate,
which would trigger the same navigation guard again.
The correct approach is to NOT cancel navigation, but simply await
the autosave flush. SvelteKit's beforeNavigate accepts async callbacks,
so navigation will naturally wait for the flush to complete before
proceeding.
Changes:
- Removed navigation.cancel() calls
- Removed goto() calls that created the loop
- Simply await autoSave.flush() and let navigation proceed
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed navigation guards in all forms that were calling the non-existent
navigation.retry() method. After canceling navigation and flushing autosave,
now properly uses goto() to navigate to the intended destination.
Files fixed:
- ProjectForm.svelte
- EssayForm.svelte
- PhotoPostForm.svelte
- SimplePostForm.svelte
- posts/[id]/edit/+page.svelte
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Refactored media list to follow SvelteKit data loading patterns:
- Removed client-side fetch() calls and manual state management
- Filter/sort/search state now driven by URL search params
- Page navigation triggers server-side reloads via goto()
- Mutations use invalidate('admin:media') to reload data
- Replaced error state with toast notifications for better UX
- Removed redundant loading state (handled by SvelteKit)
This completes Task 2 - all admin lists now use server-side data loading with proper session authentication.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added runes-based autosave functionality to SimplePostForm:
- Added autosave store with updatedAt conflict detection
- Smart navigation guards and beforeunload warnings
- Draft recovery banner instead of inline prompt
- Only saves to localStorage on autosave failure
- Added AutoSaveStatus component
- Fixed $derived syntax to use $derived.by()
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added runes-based autosave functionality to PhotoPostForm following the same pattern as EssayForm:
- Added autosave store with updatedAt conflict detection
- Smart navigation guards and beforeunload warnings
- Draft recovery banner instead of inline prompt
- Only saves to localStorage on autosave failure
- Added AutoSaveStatus component
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add createAutoSaveStore for edit mode
- Add updatedAt tracking for conflict detection
- Add hasLoaded flag to prevent autosave on initial load
- Prime autosave after initial data loads
- Add AutoSaveStatus indicator in header
- Move draft recovery from inline to prominent banner
- Only save draft on autosave failure (not every change)
- Smart navigation guard (only blocks if unsaved)
- Add beforeunload warning (only if unsaved changes)
- Add keyboard shortcut (Cmd/Ctrl+S)
- Add proper cleanup on unmount
- Update clearDraft calls in restore/dismiss functions
- Fix $derived syntax (use $derived.by for draftTimeText)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Update to use createAutoSaveStore with Svelte 5 runes
- Fix $derived syntax (use $derived.by for draftTimeText)
- Add hasLoaded flag to prevent autosave on initial load
- Add prime() call after loading post data
- Move draft recovery from inline header to prominent banner
- Implement missing restoreDraft() and dismissDraft() functions
- Only save draft on autosave failure (not every change)
- Smart navigation guard (only blocks if unsaved)
- Add beforeunload warning (only if unsaved changes)
- Update AutoSaveStatus to use reactive props
- Add keyboard shortcut and proper cleanup
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Update beforeNavigate guard to check autosave status before blocking
- Allow instant navigation when status is 'saved' (no interruption)
- Add beforeunload warning for browser close/reload
- Only show warnings when status is NOT 'saved' (saving/error/idle)
- Improves UX by not interrupting users when everything is already saved
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Remove redundant localStorage saves on every form change
- Only save draft when autosave status is 'error' or 'offline'
- Reduces draft recovery banner noise for successful autosaves
- Navigation guard still prevents data loss when navigating away
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Update ProjectForm to use new createAutoSaveStore with Svelte 5 runes
- Fix $derived syntax in AutoSaveStatus (use $derived.by for multi-statement)
- Add hasLoaded flag to prevent infinite loop on autosave completion
- Move draft recovery from inline header to prominent banner below header
- Style draft banner with blue info colors and slide-down animation
- Fix draft persistence by clearing localStorage on restore/dismiss
- Call beforeNavigate at top level for proper Svelte 5 lifecycle
- Add keyboard shortcut (Cmd/Ctrl+S) and navigation guard effects
- Update AutoSaveStatus to support both old stores and new reactive props
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Introduces createAutoSaveStore() using $state() for automatic reactivity.
New store provides cleaner API while maintaining backward compatibility.
Changes:
- New: src/lib/admin/autoSave.svelte.ts with createAutoSaveStore()
- Uses $state() for status and lastError (reactive getters)
- Export AutoSaveStore and AutoSaveStoreOptions types
- Add JSDoc with usage examples
- Update autoSaveLifecycle.ts to accept both old and new types
- Export AutoSaveController type from old file for compatibility
Old createAutoSaveController() remains unchanged for gradual migration.
Type checking passes with no new errors.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Enhances autosave controller with missing features:
- prime(payload): Sets initial hash baseline to prevent autosaves on page load
- idleResetMs option: Auto-transitions from 'saved' → 'idle' status (default 2s)
- onSaved callback: Now receives { prime } helper for re-priming after server response
- Cleanup: destroy() now properly clears idle reset timer
All existing tests pass. Backward compatible - forms not using new features yet.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Introduces createListFilters() with type-safe, reactive filtering and sorting
for admin list pages. Eliminates ~120 lines of duplicate code across projects
and posts pages.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Replace slot="icon" with snippet syntax for dropdown chevron button
- Fixes missing chevron icon in Media tab actions dropdown
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add video player with controls in MediaDetailsModal
- Display video metadata (duration, codecs, bitrate) in metadata panel
- Show video thumbnails with play icon overlay in MediaGrid
- Support video preview in upload components
- Replace emoji icons with SVG play icon
- Maintain natural video aspect ratio in all views
- Add proper styling for video thumbnails and placeholders
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add video MIME types support (WebM, MP4, OGG, MOV, AVI)
- Increase upload size limit to 100MB for videos
- Add ffmpeg integration for local video processing
- Generate video thumbnails at 50% duration
- Extract video metadata (duration, codecs, bitrate)
- Add database fields for video metadata
- Support video uploads in both local and Cloudinary storage
- Maintain aspect ratio for video thumbnails (1920px max width)
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
When editing a draft post, the primary action is now "Save draft" with "Publish" in the dropdown menu. When editing a published post, the primary action is "Save post" with "Save as Draft" in the dropdown.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Support video nodes with YouTube URL detection
- Support urlEmbed nodes for rich media previews
- Convert YouTube URLs to embedded iframes in RSS
- Add Twitter/X embed preview support
- Support generic iframe embeds
- Provide fallback links for better RSS reader compatibility
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Clean up RSS content parsing to only support TipTap/Edra format
since that's what the application uses. Removed unnecessary
EditorJS format handling.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Include full HTML content in RSS posts using content:encoded
- Support both TipTap and EditorJS content formats
- Use date as title for posts without titles
- Add featured images as enclosures for posts
- Implement RSS best practices:
- Add CORS headers for wider compatibility
- Support conditional requests (ETag/Last-Modified)
- Set appropriate cache headers (5 min client, 10 min CDN)
- Improve content parsing for various editor formats
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Remove album queries from RSS feed to fix production error.
Albums will be re-enabled once database schema is updated.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Update album queries to use media relation instead of direct photos relation
- Filter photography albums by checking media.isPhotography instead of album.isPhotography
- Remove reference to non-existent post.excerpt field
- Fix cover photo access and counts to use album.media
- Fix URL concatenation for Cloudinary images to handle absolute URLs
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Fix renderEdraContent to accept albumSlug option parameter
- Fix album listing links from /photos/{slug} to /albums/{slug}
- Remove album header section for cleaner photo story presentation
- Remove broken photo page conditional that was preventing render
- Pass album slug to content renderer for proper photo linking
Creates more immersive experience where content speaks for itself.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add route check to only show debug panel on /about page
- Keeps debug features accessible but hidden from main experience
- Debug panel remains available in development mode on /about
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add URL field to Apple Music data type
- Include Apple Music URL in album transformation
- Prioritize Apple Music links over Last.fm in Album component
- Falls back to Last.fm URL when Apple Music data unavailable
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Create CacheManager class to unify all Redis cache operations
- Define cache types with prefixes and default TTLs
- Provide type-safe cache operations
- Add bulk clear operations for related caches
- Include cache statistics and monitoring capabilities
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Create DebugPanel component with Now Playing, Albums, and Cache tabs
- Show real-time connection status and update intervals
- Display detailed Apple Music data for each album
- Add inline cache clearing for individual albums
- Implement Apple Music search modal for testing queries
- Add admin endpoints for cache management and API testing
- Only visible in development mode
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add fallback to search for songs when album search fails
- Handle artist name variations (with/without spaces in Japanese names)
- Create synthetic album entries for singles not found as albums
- Add search metadata to track failed searches and debug info
- Fix Hachikō preview detection by normalizing artist names
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Adjust update frequency based on remaining track time
- Poll every 5s when <20s remaining, 10s for 20-60s, 15s for >60s
- Add heartbeat timestamps to track update timing
- Implement time-based fallback for tracks without Apple Music data
- Assume tracks scrobbled within 5 minutes are still playing
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Merge albumStream and nowPlayingStream into unified musicStream store
- Simplify confidence scoring to binary detection (playing/not playing)
- Create single source of truth for music state across components
- Fix synchronization issues between header and album indicators
- Make Album spring animation more subtle (stiffness: 150, damping: 25)
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Always fetch fresh data from Last.fm for now playing detection
- Add confidence-based detection with progress tracking
- Implement dynamic polling intervals (10s when playing, 30s idle)
- Add marquee animation for now playing text in header
- Fix release date formatting to show only year
- Add gradient fade effects to marquee edges
- Enhanced logging for debugging detection issues
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Show currently playing track info when hovering over avatar
- Display album artwork, artist name, and track/album name
- Add music note icons with pulse animation
- Maintain exact same container size as navigation
- Only shows when music is actively playing (avatar has headphones)
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add Japanese character detection to prioritize JP storefront
- Improve album/artist matching logic with helper functions
- Support flexible matching for albums with extra text (e.g., "- Single")
- Handle comma-separated artists by matching primary artist
- Clean up code by removing duplicate logic and verbose logging
- Fix issue where Japanese albums weren't found due to strict matching
This ensures albums like "ランデヴー" properly fetch artwork from Apple Music
even when Last.fm has no images available.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Enhance albumEnricher with better error handling and type safety
- Refactor lastfmStreamManager for cleaner event management
- Update lastfmTransformers with improved data validation
- Add better type guards in mediaHelpers
- Improve nowPlayingDetector logic and state management
- Enhance SSE error handling in Last.fm stream endpoint
Key improvements:
- Better error boundaries and fallback values
- More robust type checking and validation
- Cleaner async/await patterns
- Improved logging for debugging
- Consistent error response formats
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Migrate all Edra components to Svelte 5 with runes syntax
- Implement unified ContentInsertionPane for consistent content insertion
- Standardize placeholder components with improved layouts
- Enhance bubble menu with better color picker and text styling
- Improve drag handle interactions and visual feedback
- Update slash command system for better extensibility
Key improvements:
- ContentInsertionPane: New unified interface for all content types
- Placeholder components: Consistent sizing and spacing
- GeolocationPlaceholder: Simplified map integration
- UrlEmbedPlaceholder: Better preview handling
- ComposerMediaHandler: Converted to Svelte 5 class syntax
- BubbleMenu components: Enhanced UI/UX with modern patterns
All components now use:
- Interface Props pattern for better type safety
- $state and $derived for reactive state
- Modern event handling syntax
- Consistent styling with CSS variables
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add BasePane and Pane components for consistent panel UI
- Create pane-manager store for centralized pane state management
- Support for different pane positions and animations
- Establish foundation for unified pane behavior across the app
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Create ContentInsertionPane component with segmented controls
- Update image placeholder to single-line format with icon + text
- Update gallery placeholder to use unified pane
- Support upload, embed link, and gallery selection for each type
- Add location search placeholder for future geocoding integration
- Add placeholder node styles to editor.css matching other media nodes
- Remove incorrect margins from individual placeholder components
- Placeholders now properly aligned with 2.25rem horizontal spacing
- Consistent with h1, h2, p text elements and YouTube embeds
- Fix slash command keyboard navigation by uncommenting handler
- Update all media placeholders to respect content block margins (2.25rem)
- Refactor placeholder styles to use SCSS variables consistently
- Fix Audio, Gallery, IFrame, Video, Location, and URL embed placeholders
- Fix Image placeholder width to match other placeholders
- Ensure responsive margins on mobile (2rem)
- Replace old slash command list styles with new design
- Use consistent SCSS variables from design system
- Match dropdown styling patterns used elsewhere
- Improve visual hierarchy with better typography
- Add smooth transitions and hover states
- Clean up old CSS styles
- Add left/right margins of 2.25rem to match text block padding
- Ensure YouTube videos, link cards, and other embeds align with text
- Add responsive margins (2rem) for mobile viewports
- Fixes issue where embeds were too wide compared to text content
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add 1rem margin-bottom to all embed types (url, iframe, video, image, gallery, audio, geolocation)
- Ensure YouTube embeds and URL cards have consistent spacing
- Prevent double margins when embeds are adjacent
- Matches the margin-bottom used for text blocks (paragraphs)
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Always initialize editor with content to create paragraph node
- Remove logic that prevented setting empty paragraph content
- Remove unused initialized variable
- This ensures the placeholder is visible when editor first loads
The issue was that the editor needs at least one paragraph node
for the placeholder to display, but the previous logic prevented
this from happening on initial load.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add padding-left and negative margin-left to placeholder pseudo-elements
- Separate styles for paragraphs and headings to target specific elements
- Include responsive styles for mobile viewports
- Fixes issue where placeholder text wasn't aligned with actual content
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Update padding for paragraphs, headings, blockquotes, lists, tables, and code blocks
- Change from 48px (3rem) to 36px (2.25rem) for better content density
- Maintains responsive behavior with existing media queries
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replace old slot syntax with new snippet syntax to fix compilation error.
BaseDropdown expects snippets as props, not slots.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Reduce from 625 lines to 115 lines (82% reduction)
- Use LastfmStreamManager to handle complex logic
- Cleaner separation of SSE streaming from business logic
- Improved error handling and state management
This completes the LastFM stream server simplification task.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Create lastfmTransformers.ts for data transformations
- Create nowPlayingDetector.ts for cleaner detection algorithms
- Create albumEnricher.ts for album data enrichment
- Create lastfmStreamManager.ts to orchestrate streaming logic
This separates concerns and makes the code more testable and maintainable.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Replace inline grid with MediaGrid component
- Replace upload zone with FileUploadZone component
- Replace file list with FilePreviewList component
- Replace metadata sections with dedicated components
- Simplify media selection to idiomatic Svelte patterns
- Remove non-idiomatic composable pattern
This completes the media modal consolidation, reducing duplication
and improving maintainability.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>