Content template: video back on top, title below (as before).
Both templates: br before "uploaded to" / "sold by" so the
subtitle text wraps to its own line under the title.
Rewrite watch mode from basic autoplay into a full SPA media controller:
- Fix get_related_products() with 3-tier fallback (stem overlap, same
media type, recency) so orphan products still show recommendations
- Add /watch/{id}/json endpoint for SPA navigation without page refresh
- Rewrite watch.js: SPA fetch+pushState, 14-second countdown timer,
DJ crossfade with dual media elements, queue system with sessionStorage
- Add audio support in watch mode (album art + audio player)
- Add queue "+" buttons to related content sidebar
- Add noscript/js-only progressive enhancement pattern
- Preload next media for instant transitions
width: 100% forced the video element wider than its rendered
content when max-height constrained the height. width: auto lets
the element shrink to match the actual video aspect ratio.
- Subscribe link in nav bar on shop domains
- None option disables notifications for the subscriber
- Styled frequency as pill buttons, full-width submit, cleaner layout
The base .product-main rule was being overridden by more specific
selectors that set height: auto. Add max-height to each specific
rule so the constraint is respected. Also add production URL to
CLAUDE.md.
Production installs from git clone without .git, so the
setup.py approach can't resolve the hash. Committing the
file ensures it's always present in the package.
Implement "Stay in the Loop" feature set:
- ShopSubscription model for daily/weekly email digest subscriptions
- Subscribe/verify/unsubscribe views with token-based email verification
- Digest sender console script for cron-based email delivery
- @mention parsing in comments with immediate email notifications
- RSS/Atom autodiscovery link tags in page head
- Shop settings toggle for enabling/disabling subscriptions
Change section.content overflow-x from auto to clip so position:sticky
works through it. Add min-width:0 on watch-left to prevent grid blowout.
Add overflow:hidden on sticky product-images to contain media.
position: sticky only works within the containing block. In the grid
layout, each named area (images, description, comments) was its own
containing block, so the video had nowhere to stick. Wrapping all
left-column content in a single div gives the video a tall parent
to stick within while scrolling.
Shop owners can enable watch mode in settings to get: direct video autoplay
with muted fallback, sticky video player while scrolling, and a stemming-powered
"Up Next" related content sidebar. Degrades gracefully per capability.
Comments now submit via fetch() when JS is available, returning JSON
instead of triggering a full page reload that kills video/audio playback.
Falls back to the existing POST+redirect when JS is disabled.
Remove border-top from date metadata styling. Add show_dates boolean
to Shop model (default on) with radio buttons in Branding Settings
to show or hide created/updated dates on product and content pages.
Show subtle date metadata at the bottom of the description section
with muted text and a light border separator. Only shows "Updated"
when it differs from the created date.
- Fix broken image when content has no thumbnail1
- Remove play button overlay from images/GIFs (only videos get it)
- Non-video content now opens in new window on click
Validates all 51 Jinja2 templates can be parsed without syntax errors.
Includes specific tests for key templates: base, product, content, cart,
checkout, shop, invoice, billing, crypto_checkout, home, product_edit,
user_settings, and user_purchases.
Catches issues like mismatched {% if %}/{% endif %} blocks before deployment.
- Pass video URL directly to playInline() function
- No more fetching from JSON endpoint
- No-JS fallback opens video URL directly in new tab
- Play Preview button links directly to video file
- Removed all /player/ route dependencies from templates
Much simpler: click thumbnail → swap to video element with that URL
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Simple approach:
- Click thumbnail → swap it for an inline video element
- Video has native browser controls (including PiP button)
- No JavaScript fallback → opens /player/{id} in new tab
- Removed all pop-up window code
The native browser video controls already have a PiP button,
so no need for custom PiP implementation.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Native PiP API (requestPictureInPicture) not available in user's browser.
Instead, open a small pop-out window (480x320) that:
- Is movable (drag the window)
- Is resizable (resize=yes)
- Stays open when browsing main site
- Has prev/next controls (from player.j2)
- Auto-advances when video ends (from player.js)
- Positioned in bottom-right corner of screen
This is more reliable than trying to use browser APIs that may not
be available or work differently across browsers.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Use native browser PiP instead of Document PiP:
- Native PiP has right-click "always on top" option that actually works
- Media Session API adds prev/next track buttons to PiP controls
- Video stays in PiP when navigating between tracks
- Auto-advances to next video when current one ends
Key features:
- Creates hidden video element for PiP playback
- requestPictureInPicture() for native browser PiP
- navigator.mediaSession for prev/next track handlers
- Updates MediaMetadata with current title
- Stays in PiP mode during navigation
User can right-click PiP window and select "Always on top" for
true OS-level always-on-top behavior.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Major improvements to Picture-in-Picture experience:
1. New JSON endpoint /player/{product_id}/json returns:
- videoUrl: presigned S3 URL
- title: product title
- prevProductId/nextProductId: navigation data
- mediaType: video/audio/image
2. Direct PiP from product page:
- Click 📺 PiP button on product page
- Opens Document PiP directly (no pop-up window first)
- Navigation stays within PiP (no page reloads needed)
3. In-PiP navigation:
- Prev/Next buttons load new video in same PiP window
- Video auto-advances when finished (stays in PiP)
- No page navigation - pure PiP experience
4. Better error handling:
- JSON endpoint returns proper error messages
- Fallback to pop-up window if Document PiP fails
This creates a true floating video player experience where users
can browse videos entirely within the PiP window.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add 📺 PiP button overlay on video thumbnails that opens Document PiP
directly from the product page without opening pop-up window first.
Features:
- PiP button positioned at top-right of video thumbnail
- Fetches video URL from /player/{product_id} endpoint
- Opens Document PiP window with video + prev/next controls
- Navigates to /product/{id} pages (not /player/{id})
- Auto-advances to next product when video ends
- Falls back to pop-up window if Document PiP unsupported
- Comprehensive console logging for debugging
User flow now:
1. Product page with video thumbnail
2. Click ▶ play overlay -> pop-out window (existing behavior)
3. Click 📺 PiP button -> directly to Picture-in-Picture mode
This eliminates the need to open pop-up then click PiP button.
Requires Chrome 111+ or Edge 111+ for Document PiP support.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Debug logging added to diagnose why Document PiP isn't working:
- Log Document PiP API availability on page load
- Log browser user agent for compatibility checking
- Log when togglePip is called and which path is taken
- Log video dimensions and calculated aspect ratio
- Alert user if Document PiP fails with error details
- Alert user if browser doesn't support Document PiP
- Log all fallback PiP operations
This will help identify:
- Browser compatibility issues (needs Chrome/Edge 111+)
- Video metadata loading issues
- API permission errors
- Which PiP mode is actually being used
User should check browser console when clicking PiP button to see
which path is taken and any error messages.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Replace basic PiP with Document Picture-in-Picture API that provides:
- Custom navigation controls (Prev/Next buttons) in PiP window
- Automatic aspect ratio calculation from video dimensions
- True OS-level always-on-top (no window.focus() polling needed)
- Window is movable by default (browser feature)
- Syncs playback between main window and PiP
- Auto-navigates to next video when current ends
- Product title displayed in PiP controls
- Fallback to regular PiP for unsupported browsers
Technical details:
- Window sized to video aspect ratio (640px wide, height calculated)
- Controls bar at bottom with prev/next navigation
- Pauses main window video when PiP is active
- Syncs seek position and play/pause state bidirectionally
- Closes PiP window on toggle or window close
Document PiP is supported in Chrome/Edge 111+ and provides the best
experience with custom UI controls AND true always-on-top behavior.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Picture-in-Picture (PiP) improvements:
- Add native browser PiP button for videos (P key or 📺 button)
- PiP has true OS-level always-on-top when you right-click and select "Keep on top"
- PiP works on mobile and desktop with browser's native implementation
- Button shows on mobile (PiP is useful on mobile devices)
- Active state toggles button text between "📺 PiP" and "📺 Exit PiP"
Debug 'ended' event not firing:
- Add explicit ended event listener with console logging
- Log when media starts playing
- Log playback progress every 5 seconds
- This will help identify why auto-advance isn't working
The native PiP API provides better always-on-top than our window.focus()
approach because it's implemented at the OS level by the browser.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Mobile improvements:
- Controls auto-hide after 3 seconds on page load
- Tap player to show/hide controls (instant hide or show with 3s auto-hide)
- Tapping outside controls grid hides them immediately
- Mobile autoplay fix: start muted, unmute after 100ms
Button changes:
- Remove fullscreen button (not needed for pop-out windows)
- Page button now always opens in new tab (simplified logic)
- Remove F key shortcut for fullscreen
Desktop behavior unchanged (hover to show controls).
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Documents the 16.7x test speedup achieved through pytest-xdist:
- Explains per-worker database isolation strategy
- Details SQLite WAL mode configuration for concurrency
- Describes automatic worker distribution and load balancing
- Covers implementation challenges and solutions
- Provides performance metrics and hardware requirements
- Includes best practices for parallel-safe tests
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
The parallel test execution failed on 4 tests because pytest-xdist's
execnet cannot serialize Decimal objects when sending test results
between workers.
Solution: Convert Decimal objects to strings in subTest() parameters.
The Decimal values are still used for actual test logic, but the
subTest labels now use string representations.
Fixed tests:
- test_sweep_restocking_fee_fee_amount_calculation
- test_doge_expected_amount_precision_always_rounds_up
- test_xmr_expected_amount_precision_always_rounds_up
- test_fee_calculations_always_round_up
This is a common pytest-xdist limitation with non-serializable types.