Commit graph

713 commits

Author SHA1 Message Date
04a4df4ccd Reduce subtitle text size in product/content headings 2026-02-08 08:49:43 -05:00
bc9b2fd954 Revert title above media in content, break subtitle to own line
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.
2026-02-08 08:25:03 -05:00
12f738d480 Watch mode v2: continuous playback with crossfade and queue
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
2026-02-08 08:19:07 -05:00
fd33376cb7 Document CSS media sizing rule: never width 100% with max-height 2026-02-08 08:04:43 -05:00
7cd749cf5f Use width auto for watch-mode video to eliminate whitespace
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.
2026-02-08 08:01:40 -05:00
9ed4dd50e9 Move title above media in content template
Title should always appear before the image/video so the user
knows what they're looking at. product.j2 already had this order.
2026-02-08 07:48:00 -05:00
007a53f3df Left-align product images to match title text
With max-height constraining square media, images no longer fill
the container width and were centering via inherited text-align.
2026-02-08 07:07:01 -05:00
449542d3d8 Move None frequency option to leftmost position 2026-02-08 06:59:23 -05:00
1995b88615 Flash verification prompt for unauthenticated subscribe visitors 2026-02-08 06:49:06 -05:00
2235ea24a7 Move Subscribe link to left of username in nav 2026-02-08 06:48:32 -05:00
efc7ee9284 Use 33vh instead of 480px for product-main max-height
Scales with viewport so square media takes up at most 1/3 of the
screen on any device.
2026-02-08 06:39:22 -05:00
b76c0e0115 Add Subscribe nav link, None frequency option, improve form styling
- 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
2026-02-08 06:37:09 -05:00
0adc57c874 Add max-height 480px to all product-main selectors
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.
2026-02-08 06:26:03 -05:00
35819d0638 Cap product-main height at 480px to tame square media 2026-02-08 06:15:52 -05:00
d86167c00b Include GIT_HASH in package_data so it ships with pip install 2026-02-08 04:42:17 -05:00
ab8d07c04c Track GIT_HASH in repo so deploy always has it
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.
2026-02-08 04:33:04 -05:00
1b704f545f Bake git hash at install time so /version works without .git
setup.py writes GIT_HASH file during install. The version view
reads it at runtime instead of shelling out to git.
2026-02-07 19:54:57 -05:00
1ebfb5a8aa Add /version endpoint with git hash for deploy verification 2026-02-07 19:38:01 -05:00
e9bef9982d Make subscription migration idempotent
Check for existing table/column before creating, so the migration
doesn't error when init-db has already created the schema.
2026-02-07 19:24:10 -05:00
8171960847 Add email digest subscriptions, @mentions, and RSS autodiscovery
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
2026-02-07 19:04:13 -05:00
fe237ba350 Document capability-driven presentation practice in CLAUDE.md. 2026-02-07 16:40:04 -05:00
4b8d2aa4d3 Fix watch mode sticky video and media overflow
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.
2026-02-07 16:36:26 -05:00
819c0d630a Fix sticky video by wrapping left column in watch-left container
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.
2026-02-07 15:24:06 -05:00
bc4858c58a Add YouTube-style watch mode with sticky video, autoplay, and related content
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.
2026-02-07 14:57:37 -05:00
68089dff5b Add AJAX comment submission to preserve media playback
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.
2026-02-07 11:19:58 -05:00
timehexon
2ae2863a4c Replace "AI" with "machine learning" in CLAUDE.md
Machine learning is what we grow. "AI" is forbidden in all
permacomputer discourse, marketing, & documentation.
2026-02-02 19:55:59 +00:00
726cf6eebb Add shop setting to toggle product/content date visibility
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.
2026-02-01 13:47:05 -05:00
875183d201 Add created/updated dates to 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.
2026-01-31 14:01:27 -05:00
1eca7a3145 Fix content.j2 thumbnail handling - remove play overlay from non-videos
- 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
2026-01-25 10:25:55 -05:00
bbc03b0c6d Add template syntax validation tests
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.
2026-01-22 12:48:50 -05:00
4f43aa7104 Fix content.j2 template - remove extra endif causing syntax error 2026-01-22 12:24:12 -05:00
ded0941839 Fix video thumbnail container CSS - support div element not just a 2026-01-22 12:04:43 -05:00
d254147362 Simplify inline video - use direct URLs, no /player/ endpoint
- 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>
2026-01-22 12:02:55 -05:00
57a5f83b73 Replace pop-up player with inline video on thumbnail click
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>
2026-01-22 11:32:17 -05:00
34ee5a9eeb Replace broken PiP API with small floating pop-out window
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>
2026-01-22 11:24:28 -05:00
6ec2a97f34 Add PiP support to content.j2 template (was only in product.j2) 2026-01-22 10:09:12 -05:00
453334f3e4 Change thumbnail click to open PiP directly instead of pop-up 2026-01-22 09:35:56 -05:00
d7e70e9d24 Move PiP button to separate text area to avoid click conflicts 2026-01-22 09:35:13 -05:00
53a21e721b Fix PiP button click propagation to parent link 2026-01-22 09:34:42 -05:00
5d5398921a Remove URL expiration warning - data is already downloaded 2026-01-22 09:01:43 -05:00
428edc3b21 Update help text for PiP button 2026-01-22 08:58:01 -05:00
7a2aabcd6e Switch to native PiP with Media Session API for prev/next controls
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>
2026-01-22 08:57:43 -05:00
3153a98dff Add JSON API endpoint for direct Document PiP mode
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>
2026-01-22 08:47:44 -05:00
54deee7fe8 Add direct PiP button on product page bypassing pop-up window
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>
2026-01-22 08:06:19 -05:00
9fc9c71410 Add comprehensive debugging for Document PiP implementation
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>
2026-01-21 13:55:06 -05:00
2258385cf4 Implement Document Picture-in-Picture with prev/next navigation
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>
2026-01-21 13:05:37 -05:00
d244617554 Add native Picture-in-Picture support and debug ended event
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>
2026-01-21 12:49:44 -05:00
741004b2d2 Improve mobile player controls and remove fullscreen button
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>
2026-01-21 12:40:42 -05:00
c6ea5f7f4b Add comprehensive documentation for parallel test execution system
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>
2026-01-21 12:25:41 -05:00
6d40232fe4 Fix Decimal serialization in subTest for pytest-xdist
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.
2026-01-21 12:13:36 -05:00