commits
Stop native input event from bubbling out of shadow DOM in grain-textarea.
The native input event (with composed:true) was firing alongside our custom
event, causing handleAltChange to receive undefined and overwrite the value.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Move overlay rendering from grain-alt-badge to grain-image-carousel.
The overlay now uses position:absolute within the slide instead of
position:fixed with JS positioning, so it naturally scrolls with content.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add report dialog for users to report galleries they don't own
- Implement app-level dialog registry system for proper overlay positioning
- Create grain-close-button atom for consistent close buttons
- Add action menu to gallery cards and detail page
- Improve dialog accessibility (Escape key, focus management)
- Use CSS variables consistently across dialog components
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Redirect users to /onboarding after OAuth if they don't have a Grain profile.
The onboarding page prefills with their Bluesky profile data (displayName,
description, avatar). Users can save their profile or skip to continue.
- Add hasGrainProfile and getBlueskyProfile queries to grain-api.js
- Add updateProfile and createEmptyProfile mutations to mutations.js
- Create grain-onboarding.js component with avatar crop support
- Modify OAuth callback to check profile and redirect accordingly
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Update dialog to use elevated surface background with larger border radius
- Add close button with grain-icon and escape key support
- Add links to Terms and Privacy Policy
- Adjust input background to match dialog surface
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Two-step gallery creation flow (title/description → image descriptions)
- New image descriptions page with grain-textarea for alt text entry
- ALT badge component displayed on images with alt text
- Clicking badge shows overlay with alt text over the image
- Overlay dismisses on click or carousel scroll
- Keyboard accessible (button element with aria-label)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add left/right navigation arrows to the image carousel for desktop users:
- Arrows appear on multi-image galleries only
- Left arrow hidden on first image, right hidden on last
- White semi-transparent background with muted brown chevron icons
- Uses grain-icon component for consistent iconography
- Click events don't propagate to parent (prevents gallery navigation)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Protected routes now redirect to timeline when not authenticated:
settings, edit-profile, create-gallery, notifications
- Moved terms, privacy, and copyright pages from /settings/* to /legal/*
so they remain publicly accessible
- Fixed logout to navigate before calling auth.logout()
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
On iOS PWA, tapping the close button inside the fixed sheet would cause
iOS to lose track of the scroll context, freezing page scroll. Fixed by:
- Adding touch-action: manipulation to prevent gesture tracking issues
- Blurring active element before close to release iOS focus
- Using requestAnimationFrame to let iOS finish touch processing
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Browser back/forward gestures provide their own visual transition,
so wrapping popstate navigation in View Transitions caused a brief
flash of the previous page on iOS Safari and Chrome.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix scroll container detection for shadow DOM
- Position button above bottom nav on desktop
- Style updates: accent background, white icon, border
- Responsive positioning (sticky mobile, fixed desktop)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Rename package to "grain", mark as private, and change license to Apache-2.0
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Fixes mobile scroll freezing on notifications page when navigating
away and back. Adds page caching for notifications and explore routes,
and wraps notifications content in pull-to-refresh for consistent
touch handling.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add richtext.js library for Bluesky-compatible facet parsing
- Add grain-rich-text component for rendering faceted text
- Parse facets on comment and gallery creation with handle resolution
- Render facets in comments, gallery descriptions, and profile bios
- Style facets as bold white with router navigation for mentions
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Update all related bottom offsets in:
- grain-app.js
- grain-comment-sheet.js
- grain-avatar-crop.js
- grain-toast.js
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add width: 100% to grain-feed-layout to prevent shrinking to content
- Update skeleton to use display: block matching profile header
- Use percentage-based widths for skeleton placeholders
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Full width
- Handle row with menu icon placeholder
- Proper sizing for handle, name, stats, bio
- Remove button (conditional in real component)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
searchGalleries was calling transformTimelineResponse which caches results
under the 'timeline' key. This caused explore search results to overwrite
the actual timeline data, showing only search results when navigating to
timeline.
Created separate transformSearchResponse that caches individual gallery
records but does not update the timeline query cache.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Change notifications header sticky position from top: 48px to top: 0
- Change explore search container sticky position from top: 48px to top: 0
- Constrain explore page border between search and tabs to feed max-width
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fixed header/bottom nav with scrollable outlet between them
- Flex layout through outlet → pages → feed-layout → pull-to-refresh
- Router saves/restores scroll position on outlet instead of window
- Pull-to-refresh fills available space for empty area triggering
- Update all pages for flex compatibility (align-self: center)
- Remove redundant 100vh/100dvh in favor of 100%
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Position comment sheet above bottom nav
- Find scroll container lazily on first touch instead of connectedCallback
- Simplify scroll container detection to just check overflow-y
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Find scrollable parent instead of using window.scrollY, which is
always 0 now that outlet is the scroll container. Prevents accidental
refresh triggers when scrolling up mid-content.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Change header from sticky to fixed positioning
- Make outlet the scroll container between header and bottom nav
- Remove redundant padding from feed-layout
- Scrollbar now starts below header for PWA feel
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Subscribe grain-profile to cache updates for reactive UI
- Only update cache if it exists (preserves galleries)
- Handle avatar URL correctly based on change type
- Add defensive check for missing galleries
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Integrate grain-avatar-crop component into edit profile page for
consistent avatar selection experience across the app.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add ability to change avatar directly from profile page when viewing
own profile. Includes custom crop component with drag-to-position,
zoom slider, pinch-to-zoom, and keyboard accessibility.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Stop native input event from bubbling out of shadow DOM in grain-textarea.
The native input event (with composed:true) was firing alongside our custom
event, causing handleAltChange to receive undefined and overwrite the value.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Move overlay rendering from grain-alt-badge to grain-image-carousel.
The overlay now uses position:absolute within the slide instead of
position:fixed with JS positioning, so it naturally scrolls with content.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add report dialog for users to report galleries they don't own
- Implement app-level dialog registry system for proper overlay positioning
- Create grain-close-button atom for consistent close buttons
- Add action menu to gallery cards and detail page
- Improve dialog accessibility (Escape key, focus management)
- Use CSS variables consistently across dialog components
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Redirect users to /onboarding after OAuth if they don't have a Grain profile.
The onboarding page prefills with their Bluesky profile data (displayName,
description, avatar). Users can save their profile or skip to continue.
- Add hasGrainProfile and getBlueskyProfile queries to grain-api.js
- Add updateProfile and createEmptyProfile mutations to mutations.js
- Create grain-onboarding.js component with avatar crop support
- Modify OAuth callback to check profile and redirect accordingly
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Update dialog to use elevated surface background with larger border radius
- Add close button with grain-icon and escape key support
- Add links to Terms and Privacy Policy
- Adjust input background to match dialog surface
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Two-step gallery creation flow (title/description → image descriptions)
- New image descriptions page with grain-textarea for alt text entry
- ALT badge component displayed on images with alt text
- Clicking badge shows overlay with alt text over the image
- Overlay dismisses on click or carousel scroll
- Keyboard accessible (button element with aria-label)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add left/right navigation arrows to the image carousel for desktop users:
- Arrows appear on multi-image galleries only
- Left arrow hidden on first image, right hidden on last
- White semi-transparent background with muted brown chevron icons
- Uses grain-icon component for consistent iconography
- Click events don't propagate to parent (prevents gallery navigation)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Protected routes now redirect to timeline when not authenticated:
settings, edit-profile, create-gallery, notifications
- Moved terms, privacy, and copyright pages from /settings/* to /legal/*
so they remain publicly accessible
- Fixed logout to navigate before calling auth.logout()
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
On iOS PWA, tapping the close button inside the fixed sheet would cause
iOS to lose track of the scroll context, freezing page scroll. Fixed by:
- Adding touch-action: manipulation to prevent gesture tracking issues
- Blurring active element before close to release iOS focus
- Using requestAnimationFrame to let iOS finish touch processing
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Browser back/forward gestures provide their own visual transition,
so wrapping popstate navigation in View Transitions caused a brief
flash of the previous page on iOS Safari and Chrome.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix scroll container detection for shadow DOM
- Position button above bottom nav on desktop
- Style updates: accent background, white icon, border
- Responsive positioning (sticky mobile, fixed desktop)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Fixes mobile scroll freezing on notifications page when navigating
away and back. Adds page caching for notifications and explore routes,
and wraps notifications content in pull-to-refresh for consistent
touch handling.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add richtext.js library for Bluesky-compatible facet parsing
- Add grain-rich-text component for rendering faceted text
- Parse facets on comment and gallery creation with handle resolution
- Render facets in comments, gallery descriptions, and profile bios
- Style facets as bold white with router navigation for mentions
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add width: 100% to grain-feed-layout to prevent shrinking to content
- Update skeleton to use display: block matching profile header
- Use percentage-based widths for skeleton placeholders
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
searchGalleries was calling transformTimelineResponse which caches results
under the 'timeline' key. This caused explore search results to overwrite
the actual timeline data, showing only search results when navigating to
timeline.
Created separate transformSearchResponse that caches individual gallery
records but does not update the timeline query cache.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Change notifications header sticky position from top: 48px to top: 0
- Change explore search container sticky position from top: 48px to top: 0
- Constrain explore page border between search and tabs to feed max-width
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fixed header/bottom nav with scrollable outlet between them
- Flex layout through outlet → pages → feed-layout → pull-to-refresh
- Router saves/restores scroll position on outlet instead of window
- Pull-to-refresh fills available space for empty area triggering
- Update all pages for flex compatibility (align-self: center)
- Remove redundant 100vh/100dvh in favor of 100%
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Change header from sticky to fixed positioning
- Make outlet the scroll container between header and bottom nav
- Remove redundant padding from feed-layout
- Scrollbar now starts below header for PWA feel
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Subscribe grain-profile to cache updates for reactive UI
- Only update cache if it exists (preserves galleries)
- Handle avatar URL correctly based on change type
- Add defensive check for missing galleries
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add ability to change avatar directly from profile page when viewing
own profile. Includes custom crop component with drag-to-position,
zoom slider, pinch-to-zoom, and keyboard accessibility.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>