Changelog
What's new in Stashd.
v0.18.0 — Landing Rebrand & A/B Test (Apr 12, 2026)
Landing Page Redesign — Midnight + Electric Mint
- Full brand rebrand rolled out: Midnight (
#080E1F) + Electric Mint (#3EEBA8) palette, Syne 800 display + Inter 400–800 body, dark-first everywhere - Retired the old light-mode landing; every marketing / legal page (
/blog,/about,/help,/contact,/changelog,/privacy,/terms,/fair-usage-policy,/pocket-alternative) now renders through a sharedwith the new theme - Canonical shared footer applied across both variants AND every marketing / legal page — STASHD wordmark, new tagline "Save anything from anywhere\*", three nav groups, and "Extend Stashd" distribution chips linking to the Chrome extension and
@stashdbotTelegram bot - Waitlist-aware copy: when the admin toggle is on, the hero / CTAs / pricing card / FAQ flip to invite-only messaging (2 teaser saves, +3 per invite up to 30 bonus, 15/month free once admitted)
- Supported-platform honesty: all 6 live platforms surfaced (Instagram, TikTok, YouTube, X, Reddit, Threads) with new
RedditIcon,ThreadsIcon, andTelegramIconadded to the v2 icon set - Real
totalSaves/totalUserswired into the Social Proof bar;/api/ogedge route serves a dynamic 1200×630 branded OG image replacing the legacy PNG
A/B Test Infrastructure
- Variant A (control) — ported from the original landing page, dark-reskinned: navbar, hero with URL-extraction demo, social proof, how-it-works, use cases, comparison table, pricing (monthly/yearly toggle), 9-question FAQ with
FAQPageJSON-LD, final CTA, footer - Variant B (animated) — Product-Hunt-ready showstopper with all 8 Emil-Kowalski-grade animations (wordmark stagger, canvas particle field, typewriter search demo, scroll reveals, magnetic CTA, card parallax, number counter, cursor glow),
prefers-reduced-motionbypass on every single one - Vercel Edge Middleware assigns variant server-side (
src/middleware.ts+src/lib/experiments/middleware-integration.ts) with a 30-day sticky cookie — zero layout shift, no client-side flicker ?variant=control/?variant=animatedQA bypass +?ref=producthuntforces the animated variant for Product Hunt hunters- PostHog experiment wired with typed event wrappers (
page_viewed,cta_clicked,scroll_depth_reached,search_demo_interacted,feature_section_viewed,social_proof_viewed,time_on_page_milestone,animation_visible) +$experiment_startedidentifier +ab_variantsuper-property - Vercel Analytics + Speed Insights mounted in
layout.tsxfor per-variant Core Web Vitals tracking
Admin — Landing A/B Signup Tracker
- New migration:
profiles.ab_variant_at_signup(text, CHECK constraint for'control' | 'animated') stamped once by the auth callback when a brand-new user completes signup src/app/api/cron/analytics/route.tsaggregates signups per variant (all-time + last 7d, share %) and upserts aslanding_ab_variant_metricsinadmin_analytics_cache- New admin dashboard card: "Landing A/B — Signups by Variant" with per-variant progress bars, 🏆 leader indicator, delta footnote, winner pill (requires ≥40 tracked signups), and deep links to preview each variant in a new tab
Product Hunt Launch Prep
- Complete marketing kit shipped under
docs/marketing/: Instagram carousels + reels + stories, TikTok hook scripts + trending formats + 5-part series plan, YouTube Shorts + long-form outlines + community templates, X threads + standalone tweets + launch-day playbook - Fresh brand assets in
public/:og-image-v2.svg,twitter-card-v2.svg,app-icon-v2.svg,favicon-v2.svg
Chrome Extension v0.4.1
- Fixed the Google SSO "Opening Google sign-in…" hang via new
/api/auth/extension-callbackbridge route that handles the PKCE code exchange server-side and bounces back to the extension'schromiumapp.orgURL with tokens in the hash fragment - Extension now correctly propagates bridge-route errors to the popup instead of hanging
v0.17.0 — Mobile Polish & Chat Fixes (Apr 11, 2026)
Chat
- Fixed mobile layout breaking when conversation starts — messages area now scrolls correctly instead of pushing the input bar off-screen
- Conversation list now refetches from the server after deleting a conversation, restoring correct chronological order without a hard refresh
- Smooth fade-in transitions when switching between the welcome screen and message history
Feed
- Category chips now show a press/tap effect (
active:scale-90) for immediate tactile feedback on mobile - Active category chip has a subtle ring glow for clearer selection state
- Loading spinner appears on the selected category chip while items are being fetched
Settings
- Added "Admin Dashboard" link in Settings, visible only on mobile (desktop already has it in the sidebar)
AI
- Markdown rendering in AI answer cards (bold, lists, citations)
- Search results shortlisted alongside AI answer with disclaimer
v0.16.0 — Conversion Monitor & Feed Polish (Apr 11, 2026)
Share Link Conversion Monitor
- Attribution tracking: public collection pages set a cookie and CTA links carry
ref=collection&slug=params through signup - Auth callback records which shared collection drove each signup in new
collection_signupstable - Analytics cron computes overall and per-collection conversion rates (views vs signups)
- New admin card: "Share Link Conversions" with headline rate, 7-day trend, per-collection breakdown table
- LLM-powered growth tips: GPT-4o-mini analyzes conversion data and generates 2-3 actionable tips on each admin page load
Feed UX
- Skeleton loading cards shown while feed loads (5 pulsing placeholder cards)
- Smooth fade-in transition when items load, instead of abrupt state change
- Empty state only appears after loading completes, not during
Collection UX
- Quick "Copy link" button on public collections — one click instead of opening the share dialog
- Fixed trailing space in share URLs caused by
NEXT_PUBLIC_APP_URLenv var - Toggles (collaboration requests, public comments, add items) aligned with header text
v0.15.0 — Mobile Fixes & Smart Nudges (Apr 11, 2026)
Mobile Fixes
- Delete/restore buttons now visible on mobile feed items (were hidden behind hover-only state)
- Chat send button no longer overflows outside input box on mobile — model selector wraps to second row on narrow screens
- Chat conversation title no longer cut off on mobile — fixed container height with
dvhunits
Smart Nudges (Save Reminders)
- Nudge banner now cycles through all undismissed reminders, rotating every ~2 minutes with fade transition
- Replaced dismiss X with context-aware response buttons:
- Restaurants/places: "Not yet, but soon" / "Yes!" / "Decided not to"
- Recipes: "Haven't tried it" / "Made it!" / "Not for me"
- General: "Not yet" / "Done!" / "Not interested"
- User responses tracked in database (
response+response_atcolumns) - Negative responses ("Not for me" variants) suppress future nudges for that item
v0.14.0 — Rebrand: Gist → Stashd (Apr 11, 2026)
Rebrand
- Full rebrand from Gist to Stashd across 95 files
- New domain: trystashd.com (custom domain on Vercel, DNS on Namecheap)
- New color system: Midnight + Electric Mint palette (OKLCH)
- Wordmark: STASHD with accent-colored D in Electric Mint (#34D399)
- New logo/favicon/PWA icons: mint S on midnight background
- Added Syne 800 font for headings
- All UI copy, metadata, SEO, emails, legal pages, and i18n (EN/ES) updated
- Extension v0.4.0: rebranded and submitted to Chrome Web Store
- Cookie keys renamed: stashd-theme, stashd-locale
- Telegram bot renamed: @StashdBot
- Supabase auth URLs + Google OAuth redirects updated for new domain
- Resend sending domain validated (trystashd.com)
Fixes
- Railway yt-dlp service redeployed with correct root directory + start command —
/download-audioendpoint now live (was 404 due to stale deploy)
v0.13.0 — Onboarding Flow & Sidebar Setup Prompts (Apr 10, 2026)
Onboarding Overlay
- Dark overlay with white-glow spotlight on the PasteBar — guides first-time users to save their first Gist
- Triggered on first visit to
/feedwhenonboarding_completed = false - Auto-dismisses when user saves their first item, or via "Skip for now" link
- Celebratory scale-up + fade animation (400ms) on save dismiss; 300ms fade on skip
- Dynamic tooltip positioning: left of PasteBar on desktop (falls back to below on narrow viewports), below on mobile
- Self-measuring via
ResizeObserver— tooltip repositions live as PasteBar expands (error, loading, manual input) - Full accessibility:
role="dialog",aria-modal, focus trap (Tab/Shift+Tab cycling), Escape key dismiss, focus restoration on unmount - iOS PWA install bottom sheet modal with 3 numbered steps (Share → Add to Home Screen → Add), i18n in EN/ES, Escape key + focus management
- Existing users with
saves_count > 0are backfilled asonboarding_completed = true(never see the overlay)
Fixes
- Chrome Web Store URL in setup prompts now points to the real published listing (was placeholder)
Sidebar Setup Prompts
- Sequenced prompt cards appear in the desktop sidebar after onboarding completes
- Priority order: Chrome Extension (#1) → Telegram Bot (#2) — one card at a time
- Each prompt has an icon, one-liner copy, CTA button, and permanent dismiss (X)
- Telegram prompt auto-dismisses when user links their Telegram account
- PWA install prompt shown on mobile only (as a horizontal banner at top of feed)
- Dismissed state persisted in
dismissed_promptstext array on profiles table
Analytics
- 6 new PostHog events:
onboarding_shown,onboarding_completed(with method: saved/skipped),time_to_first_save(seconds),setup_prompt_shown,setup_prompt_clicked,setup_prompt_dismissed
Technical
- New DB columns:
onboarding_completed(boolean),dismissed_prompts(text[]) on profiles - New API endpoints:
POST /api/onboarding/complete,POST /api/onboarding/dismiss-prompt - 4 new components:
onboarding-overlay,setup-prompt-card,setup-prompts,mobile-setup-banner - Full i18n support (English + Spanish)
- PWA
beforeinstallpromptevent captured for install prompt trigger
v0.12.0 — Content Scripts, Cross-User Dedup & Knowledge Category (Apr 10, 2026)
Chrome Extension v0.4.0 — Platform Content Scripts
- New content scripts for TikTok, Twitter/X, and YouTube extract rich metadata directly from the page DOM
- TikTok: parses
__UNIVERSAL_DATA_FOR_REHYDRATION__,SIGI_STATE, JSON-LD, and DOM fallback for caption, author, thumbnail, video URL, likes/comments - Twitter/X: parses
data-testidDOM selectors, JSON-LD, and meta tags for tweet text, author handle, media URLs, engagement counts - YouTube: parses
ytInitialPlayerResponse,ytInitialData, and DOM fallback for title, channel, description, thumbnail, likes/comments - All content scripts follow the content-instagram.js pattern: 10-min TTL cache,
{ action: "extract" }message listener, normalized output shape background.jsnow attempts content script extraction for all 4 platforms (was Instagram-only)- Manifest.json updated with content script entries for TikTok, Twitter/X (both domains), and YouTube
- Published to Chrome Web Store (submitted for review Apr 10, 2026)
Cross-User Content Deduplication
- New
content_cachetable stores extraction results (transcript, structured data, embedding) keyed by normalized URL - When multiple users save the same URL, the second+ users skip the expensive Apify/Whisper/OpenAI pipeline and reuse cached results
- Cache integrated into all 3 extraction paths: YouTube, Instagram, and generic (TikTok/Twitter/other)
- Each user still gets their own
saved_itemsrow with independent collections, comments, and entity grouping - Thumbnails still stored per-user (CDN URLs expire, Supabase Storage URLs don't)
hit_counttracks cache reuse for cost savings analytics- PostHog events include
cache_hit: truefor tracking
Knowledge Category
- New "knowledge" category for educational videos, documentaries, explainers, opinion pieces, news, and interviews
- Structured extraction:
topic,key_takeaways[],mentions[](person/company/product/place with context) - Dedicated renderer: numbered takeaway points + mention chips with hover tooltips
- Feed preview shows first 2 key takeaways
- Blue color scheme, 📚 filter chip
- GPT prompt updated: prefers "knowledge" over "other" for educational content, never includes channel metadata (subscribers, upload frequency)
Bug Fixes
- Fixed YouTube video ID extraction failing when
normalizeUrlsorts query params (e.g.,?t=8s&v=xxx— regex expected?v=first, now uses proper URL parsing) - Fixed stale YouTube data on SPA navigation — content script now prioritizes DOM extraction over
tags (which go stale when navigating between videos without full reload), with videoId match validation on JSON fallback - Fixed archived items blocking re-saves — duplicate check now skips
archived_at IS NOT NULLitems - Fixed re-saving archived URL returning 409 — now unarchives the item, bumps
created_atto now (appears at top of feed), and returns success with "Restored to your feed!" message - Fixed extension ignoring extracted data — API route now reads
dataandplatformfrom extension request body and merges into server fetch results - Fixed
fetchYouTubedescription always null — noembed response was parsed butdescriptionwas never assigned - Extension duplicate error now shows "You've already saved this!" instead of raw "duplicate" string
v0.11.1 — Collection Detail Redesign & Analytics Fixes (Apr 9, 2026)
Collection Detail Page — Compact Layout
- Consolidated vertically stacked header into a compact horizontal layout: emoji + title + meta row (item count, date, views, unique visitors, member avatars, invite, share) all in one line
- Members shown as colored initial avatars with hover tooltips (name + role) instead of a bordered list box
- Member management moved into a collapsible
element ("Manage members") — only visible to owners when there are non-owner members - Settings toggles (collaboration requests, public comments) and "Add Items" button merged into a single horizontal toolbar
- Shortened toggle labels ("Collaboration requests" / "Public comments" instead of "Allow collaboration requests" / "Allow public comments")
CollectionStatscomponent renders as a fragment (<>) instead of a wrapperso stats flow inline in the parent meta rowUnique Visitors Fix
- Fixed
unique_visitorsalways showing 0 — was hardcoded to0on insert and never updated - Added
visitor_idstext array column tocollection_statstable to track distinct visitors per day - Each view now appends the visitor ID only if not already present, and derives
unique_visitorsfrom the array length - New migration:
20260409_collection_stats_visitor_ids.sql
Public Collection Page
- Creation date now displayed in the meta row (e.g., "Apr 8, 2026")
- View count shown when > 0 (e.g., "41 views")
- Stats fetched server-side from
collection_statstable
Admin Dashboard
- "Top Collections by Views" list now shows an external link icon next to public collections
- Clicking the icon opens the public collection page (
/c/[slug]) in a new tab - Only appears for collections that are public and have a share slug
- Cron analytics job now includes
is_publicandshare_slugin cached data
v0.11.0 — Collection Analytics & Collaboration (Apr 9, 2026)
Public Collection Comments
- Visitors can comment on individual items in public collections (requires Gist login)
- "Allow public comments" toggle for collection owners (appears when collection is public)
- Privacy-aware display names: public profiles show username, private profiles show "Gist user"
- Collection owners always see commenter usernames
- Reusable
CollectionItemCommentscomponent works in both public and private collection views - New
collection_commentstable with RLS policies - Comment icon with count on each item card in public collections
Chrome Extension v0.3.0 — Redesign
- Full popup redesign with Gist teal branding (was dark with indigo accents)
- User stats: total saves, collection count, saves remaining (free) or "Unlimited" (Plus)
- Plan badge showing Plus member or Free plan
- "Go to Feed" link in header opens the web app
- Removed settings page (API URL config was developer-only)
- New
/api/extension/statsendpoint for fetching user data - URL validation with platform-specific guidance (e.g. "You're on a profile page. Open a specific reel...")
- Fire-and-forget saves with background notifications — save and keep browsing
- Automatic JWT token refresh (tokens expire after 1 hour)
PWA Share Target
- Shared URLs from Android now auto-save immediately with a teal processing banner ("Processing 1 link... You can close the app and come back")
- Handles Android's
textshare param (extracts URL from "Title https://..." format) - Success toast and feed refresh when processing completes
UI Polish
- Replaced all native browser tooltips with Tailwind CSS tooltips across the app (item cards, chat, collections, access requests)
Bug Fixes
- Chrome extension Google SSO: fixed token storage key mismatch — OAuth saved token as
gist-access-tokenbut the extension read fromauthToken, so login appeared to succeed but auth state was never detected - PWA Android share target: shared URLs now populate the paste bar — the manifest routed shares to
/feed?url=...but the feed page never read theurlquery parameter - Instagram
/reels/(plural) URLs now recognized — the URL classifier only matched/reel/(singular) - Extension API host permission added — saves were failing with "Failed to fetch" because Manifest V3 requires explicit host_permissions for cross-origin requests
Public Collection Page — Auth-Aware UI
- Header shows "Go to Feed" for logged-in users, "Sign up free" for anonymous visitors (was always showing "Sign up free")
- Bottom CTA ("Sign up for Gist — it's free") hidden for logged-in users
- "Request to Collaborate" button hidden for collection owners and members
- Added
force-dynamicto ensure server-side auth check runs per-request (not cached at build time)
Collection Analytics
- View tracking on public collection pages (SSR + API) with daily-bucketed stats in
collection_statstable - Per-item click tracking via
collection_item_clickedPostHog event +collection_item_statsDB counters - Owner stats bar on collection detail page: created date, total views, unique visitors
- Admin analytics dashboard: top collections by views, public vs private ratio, collections created per week, avg items per collection
- Cron job refreshes admin analytics cache every 4 hours + manual refresh button
Collection Sharing & Collaboration
- Shareable invite links: multi-use, optional expiration (24h/7d/30d/never), owner can revoke
- Join-via-link page with auth check and confirmation flow
- Invite link management UI in share collection dialog
- "Request to Collaborate" button on public collections (owner enables per collection)
- Access request approval/denial with in-app UI + email notifications (Resend)
- Collection invite attribution tracking in admin dashboard (📨 icon)
Infrastructure
- Standardized all email routes to use
EMAIL_FROMandNEXT_PUBLIC_APP_URLenv vars - Replaced all hardcoded
gist-roan.vercel.appfallbacks with env var references - Added
CRON_SECRETto Vercel for cron job authentication - 5 new DB tables:
collection_stats,collection_item_stats,collection_invite_links,collection_access_requests,admin_analytics_cache - 2 new email templates: access request notification, access request approved
v0.10.1 — Collections, i18n, Extension Polish (Apr 9, 2026)
Chrome Extension
- Confirmed: "Save to Gist" button and right-click context menu already work on TikTok, Twitter/X, and YouTube — sends URL to API for backend extraction. Richer content scripts for other platforms moved to Phase 3.
- Fixed CSP inline script errors on Instagram — replaced inline
injection with external file (drain-buffer.js) loaded viachrome.runtime.getURL - Extension icon now uses the Gist teal bookmark logo (16/48/128px PNGs generated from logo.svg)
- Added
identitypermission for Google OAuth flow - Added Google OAuth "Sign in with Google" button with magic link fallback
- Debug logging added to OAuth flow for troubleshooting
Multi-Language (i18n)
- Full Spanish translation wired into all app pages via
useTranslations()from next-intl - Translated: navigation sidebar, feed page, paste bar, smart input, collections page, chat page, settings page
- Language selector reads from user profile, persists via cookie for SSR
- Profile locale syncs to cookie on login for cross-device consistency
- Description added explaining toggle changes UI language only — content transcription is language-agnostic
Blog
- Breadcrumb spacing improved (more top margin, less bottom)
Seeded Public Collections (9 total)
- 3 original collections: Dinner in 20 Minutes or Less, Actually Good Coffee Shops (NYC/LA/London), The $200 Wardrobe Refresh
- 3 World Cup 2026 collections: Mexico (GDL/CDMX/MTY), USA (11 host cities), Canada (Toronto/Vancouver)
- 3 Guadalajara-specific collections: Workout Guide, Coffee Shops, Best Places to Eat
- All 111 collection items now link to real social media content (TikTok, Instagram, YouTube, X, articles) — zero placeholder URLs remaining
- All collections owned by mmoscosa@mmoscosa.com, public with share slugs
Roadmap Additions
- Collection analytics: views, unique visitors, per-item clicks (PostHog events)
- Collection invite links (share a link to join as editor/viewer without knowing email)
- "Request to collaborate" button on public collections (owner approval required)
- Cross-user content deduplication to avoid re-scraping the same URL
v0.10.0 — Ship Everything Epic (Apr 8, 2026)
Chrome Extension
- Google OAuth popup login (replaces email-only magic link flow)
- Magic link kept as fallback option
SEO & Content
- Dedicated
/pocket-alternativelanding page targeting "pocket alternative 2026" keyword - Blog system with
/blogindex and 3 SEO-optimized posts:
- "How to Save Instagram Reels in 2026"
- "The Best TikTok Bookmark Manager in 2026"
- "Best Pocket Alternative 2026 — What to Use Now"
- FAQ schema, Article JSON-LD, internal cross-linking between all content pages
Platform Expansion
- Reddit post extraction — uses Reddit JSON API, extracts title, body, top 5 comments, images/media
- Threads post extraction — via meta scrape, extracts text, author, images
Engagement
- Weekly digest email (Vercel Cron, Mondays 9am UTC) — personalized item count, category breakdown, top 3 saves
- Auto-resurfacing reminders — daily cron creates contextual reminders for items saved 7/14/30 days ago in actionable categories (restaurant, recipe, travel, shopping, event)
- Dismissible reminder banner on feed page
Data Export
- CSV export format (flat file with all item fields)
- Markdown export format (YAML frontmatter + body, Obsidian-compatible)
- Export Data section in Settings with 3 download buttons (CSV, JSON, Markdown)
PWA
- Web app manifest with Android share target — "Share to Gist" appears in Android share sheet
- Minimal service worker (share target only, no offline caching in v1)
Multi-Language
- next-intl scaffolding with English and Spanish translations
- Language selector in Settings
localecolumn on profiles table
v0.9.2 — Stripe Pricing Fix & Admin Polish (Apr 8, 2026)
Stripe Pricing
- Archived $1/month test price, created correct $6/month price on Gist Plus product
- Annual price unchanged at $59.88/year ($4.99/mo)
Admin Dashboard
- Renamed "MRR (Stripe)" to "Current MRR (Stripe)" for clarity — distinguishes active recurring revenue from per-user lifetime charges shown in Rev column
Authentication
- Google SSO implemented (Apple SSO dropped — not needed)
Whisper Transcription
- Reviewed transcription pipeline edge case handling — already covers: empty files, oversized files (24MB pre-check), download timeout (20s), MIME type detection, graceful fallback when transcription fails
- Fixed unused
WHISPER_TIMEOUT_MSconstant — Whisper API calls now enforce the 30s timeout - Tested across TikTok and Instagram videos with good results across speech, music-heavy, and mixed content
v0.9.1 — Stripe Billing & Subscription Management (Apr 8, 2026)
Payments: Stripe SDK Removal
- Replaced Stripe Node SDK with REST API (
fetch) in webhook and admin routes — SDK was incompatible with Vercel serverless runtime - Webhook signature verification now uses Node
cryptomodule (HMAC SHA-256 + timing-safe compare) - Removed
src/lib/stripe.ts— no longer needed
Payments: Webhook Fix
- Critical: Webhook
checkout.session.completedwas silently failing —waitlist_statuscolumn missing from Supabase schema cache caused the entire profile update to fail, soplanwas never set to"plus" - Fix: split
planupdate andwaitlist_statusupdate into separate queries so one failure doesn't block the other
Subscription Management
- "Manage" link next to Plus badge in Settings — redirects to Stripe billing portal (cancel, update payment, view invoices)
- New
/api/stripe/portalendpoint creates Stripe billing portal sessions via REST API - New
/api/stripe/statusendpoint fetches subscription cancellation state from Stripe - Cancellation notice on settings page: amber banner showing "Your subscription has been cancelled. You'll have Plus access until [date]"
- Handles both Stripe cancellation mechanisms:
cancel_at_period_end(boolean) andcancel_at(specific timestamp)
Chat UI
- Fixed sources section overflow — long source links now properly truncate within container instead of overflowing
v0.9.0 — UX Polish, Admin Insights & Privacy Controls (Apr 8, 2026)
AI Chat Improvements
- Auto-switch model dropdown after fallback — when a model fails and falls back (e.g., Anthropic → GPT-4o mini), the dropdown updates so subsequent messages go directly to the working model
- Styled fallback banner in chat (separate from response content)
Referral Program
- Removed misleading "Get 1 month of Plus free" from referral landing page
- Email notification to referrer when their invitee creates an account (branded template with "Your invite worked!" heading)
- Referral page redesigned: actual SVG logo + wordmark, "Visit our homepage" link, legal footer
Branding & UX Consistency
- Logo component updated: real bookmark SVG icon replaces green "G" square (affects sidebar, navbar, login, legal pages)
- Shared
LegalFootercomponent: copyright + Terms, Privacy, Fair Usage links - Applied Logo + LegalFooter consistently to Terms, Privacy, Fair Usage, referral, and public collection pages
- Legal links added to app sidebar (below nav, above plan badge)
- Public collection page: full-width footer with border-top (matches legal pages)
Privacy Controls
- New
is_public_profilecolumn on profiles (default: true) - "Public profile" toggle in Settings > Account section
- Public collection "Curated by" respects privacy: shows names for public profiles, "another user" / "N other users" for private profiles
- Smart contributor formatting: handles all combinations (all public, mixed, all private)
Public Collections
- Items sorted newest-first on public collection pages
- Simple page-based pagination (15 items/page) with Previous/Next + page number buttons
- Logo and LegalFooter added to public collection pages
Collection Detail (Private)
- Search box to filter collection items by summary, tags, or category
Feed
- Infinite scroll: loads 25 items initially, loads more on scroll (IntersectionObserver with 200px rootMargin)
- "Loading more..." indicator while fetching
Payments
- Stripe switched to live mode (live secret key, webhook secret, monthly + annual price IDs deployed to Vercel production)
Previously Completed (moved from roadmap)
- Search upgrade: Elasticsearch vs Typesense evaluation completed — chose to fix current Postgres setup (see v0.8.2)
- Railway
/download-audioTikTok transcription pipeline verified end-to-end (see v0.3.0) - Annual plan implemented: $4.99/mo billed yearly ($59.88/yr), monthly at $6/mo, with toggle on pricing page (17% savings badge)
- Referral abuse safeguards already in place: 12/year cap on free months, self-referral prevention, duplicate referral guard, DB-managed rewards (no Stripe coupon needed)
- Referral email notifications: referrer gets notified when invitee creates an account (see above)
Admin Dashboard
- Stats time range filter: Today | This Week | This Month | All Time (default: Today)
- Time-filtered views show: New Users, Free, Plus, Referred, Direct, Saves
- Signup source indicators: ➡ arrow (direct signup) or 🔗 link (referred) with tooltips in Joined column
- User history: "Account created" now shows subtitle "Direct signup" or "Referred by [email]"
- Referral tracking:
referred_byandreferrer_emailfields in user data - Activity tracking: green dot (active, <7d), blue dot (passive, <30d), orange dot (inactive, >30d) based on Supabase Auth
last_sign_in_at - Updated legend with Active/Passive/Inactive indicators
v0.8.2 — Search Upgrade (Apr 8, 2026)
Search: Three-Signal Hybrid Scoring
- Evaluated Elasticsearch, Typesense, and pgvector hybrid — chose to fix current Postgres setup ($0 cost)
- Three-signal scoring: vector similarity (50%) + full-text rank (30%) + trigram fuzzy matching (20%)
- Expanded full-text index: now covers summary, tags, and extracted_data fields (restaurant name, recipe title, etc.) — previously only indexed summary, transcript, and category
- Added
pg_trgmextension for typo tolerance ("resturant" → "restaurant") - Weighted tsvector: summary/tags at weight A, extracted data at B, category at C
- Trigger-maintained
ftscolumn (replaces generated column —to_tsvectoris STABLE, not IMMUTABLE) search_textgenerated column for trigram matching- Tunable weights via RPC params (no migration needed to adjust)
- Graceful embedding fallback: if OpenAI embedding call fails, search degrades to FTS + trigram instead of returning 500
- New filter params: platform, tags, date range (WHERE clause filters before scoring)
- Input validation: limit clamping (1-100), JSON parse guard, array type checks
- Error messages no longer leak Postgres schema details
- HNSW index deferred — Supabase free tier's 32MB
maintenance_work_memtoo small for 1536-dim vectors; sequential scan is fine at current scale
v0.8.1 — UI/UX Consistency & Chat Rate Limit Retry (Apr 8, 2026)
UI/UX Consistency Pass
- Audited "save" vs "bookmark" vs "collection" language — "bookmark" fully removed, no conflicts
- Unified naming: "Add to collection" used everywhere (was "Save to collection" on item card button, "Add to Collection" in dialog)
- Fixed empty-state copy on collections page: "organize your saved items" (was "organize your saves")
FolderOpenicon used consistently for collection-related UI across item cards, nav, and dialogs
BYOK (Bring Your Own Key) — Complete
- API key storage migrated from localStorage to Supabase
profilestable - One-time migration shim auto-moves old localStorage keys to DB on first load, then clears them
- User-provided OpenAI key →
gpt-4o-minidirect (bypasses OpenRouter) - User-provided Anthropic key →
claude-sonnet-4-5direct - BYOK users skip the daily 10-message free tier limit
Chat Conversation List UI — Complete
- Relative timestamps on conversation rows (5m, 2h, 3d)
- Live search filter in history dropdown
- Two-step inline delete confirmation with 3-second auto-dismiss timer
Chat Rate Limit Handling
- Server: OpenRouter free models now retry once on 429 with 2s backoff before falling to next model
- Server: returns 503 with
retryableflag instead of 502 when all models are busy - Client: auto-retries up to 2 times on 503 with 3s delay between attempts
- Client: shows "Models busy, retrying..." indicator instead of immediate error
- Daily limit 429 still shows upgrade banner (not retryable — that's a hard limit)
v0.8.0 — Admin Gifting, Data Isolation & Collection Permissions (Apr 8, 2026)
Admin Gift Free Months
- Admin can gift 1-12 months of Plus to any user from the admin panel
- Gift button per user row with inline form (months + reason)
- Additive: if user is already Plus, months stack on top of existing expiry
- "Gifted Plus" stat card in admin dashboard
- Gift cost tracked as infra line item in Revenue & Infrastructure section
- Per-user gift history (expandable from "gifted" badge on user row)
- Triple notification: email (branded teal template), in-app banner on feed, Telegram message
giftstable with RLS, PostHog tracking (gift_receivedevent)
Security: Data Isolation Fix
- Critical: Feed page showed all users' saved items to any logged-in user
- Root cause: client-side queries missing
user_idfilter, relying solely on RLS - Fixed feed page, PostHog provider, and collection "Add Items" modal
- All three now explicitly filter by authenticated user's ID (defense-in-depth)
Collection Viewer Permissions
- Viewers can no longer see Share, Edit, Delete, or Add Items buttons
- API returns
userRole(owner/editor/viewer) so frontend conditionally renders actions - Editors can add items; only owners can share, edit, or delete
Collaborative Collections
- Tested invite flow end-to-end with a second user account
- Verified viewer permissions enforce correctly after fix
Authentication
- Social SSO live: Google, Instagram, TikTok (magic link kept as fallback)
Free Tier
- Free tier save limit confirmed at 15 saves (increased from 5)
Polish
- Cleaned up old garbage items from database
v0.7.0 — Chat Overhaul & BYOK (Apr 7, 2026)
Chat Fixes
- Fixed chat creation crash (empty body in conversation POST)
- Fixed SSE chunk boundary splitting — no more dropped text during streaming
- Fixed
streamingstate never resetting on API errors (permanently locked input) - Fixed
last_messagetype mismatch ([object Object]in conversation previews) - Fixed messages disappearing mid-conversation (history reload guard)
- Added conversation ownership validation (security fix)
- Added error logging across all chat catch blocks
- Added
nullbody guard — shows error message instead of blank bubble
Free Chat via OpenRouter
- Chat completions now use OpenRouter free models instead of OpenAI API key
- 7-model fallback chain: gpt-oss-20b, qwen3.6-plus, nemotron-3-nano, step-3.5-flash, minimax-m2.5, gemma-3n, gemma-3-4b
- OpenAI API key still used only for embeddings (vector search)
- Category-aware search: detects intent (cook → recipe, eat → restaurant, travel → travel)
- Context truncation: extracted_data limited to 500 chars per item to avoid model context overflow
- System prompt updated with formatting rules (numbered lists, bullets, bold)
Auto-Named Conversations
- Conversations automatically titled by LLM from the first message
- Uses OpenRouter free models with 3-model fallback chain
- Naming runs before streaming starts (server-side, not fire-and-forget)
- Title sent to client via conversation refetch
- Markdown stripped from titles and preview text
Chat UI Redesign
- Professional empty state with suggested questions grid
- Bouncing teal dots loading indicator while AI is thinking
- Message bubbles: user (teal, right-aligned), assistant (muted, left-aligned with bot avatar)
- Input bar: rounded-xl with shadow, 44px minimum height, prominent send button
- Conversation dropdown shows actual title, not "New chat"
- Removed duplicate "New chat" button from header
- Bullet list rendering in FormattedContent (- item)
BYOK (Bring Your Own Key)
- AI Settings section in Settings page
- OpenAI and Anthropic API key inputs (masked, stored in localStorage)
- Note: keys stored client-side for now, DB migration planned
App-Wide
- Logo: bold "G" lettermark on teal rounded-lg background (used in sidebar + landing)
- Fixed amber hover states leaking across app (accent token → teal tint)
- Sidebar now uses shared Logo component
Database
- Created
chat_conversationstable with RLS policies (SELECT, INSERT, UPDATE, DELETE) - Added
conversation_idcolumn tochat_messages - Added DELETE RLS policy for
chat_messages
v0.6.0 — Landing Page Refactor & SEO (Apr 6, 2026)
Visual Overhaul
- Replaced purple/violet theme with light teal (#0D9488) identity across entire landing page
- New logo: teal bookmark icon + "Gist" wordmark (SVG, works 16x16 to 512x512)
- Landing page is light-only (dark mode disabled, restored when navigating to app)
- Removed ThemeToggle from landing navbar
- All buttons changed from rounded-full to rounded-lg
New Sections (10 total)
- Social proof bar with animated counters (2,400+ links saved, 127 users)
- Comparison table: Gist vs Pocket (dead) vs Raindrop vs Instagram Saves
- FAQ section with 8 SEO-optimized questions + shadcn Accordion
- Final CTA section with teal gradient background
- Platform icons (IG, TikTok, X, YouTube) with styled tooltips
- Shopping & Products added as 5th use case category
Hero Rewrite
- Animated extraction demo cycling through 3 scenarios (restaurant, recipe, travel)
- Typing animation + field reveal with 150ms stagger, respects prefers-reduced-motion
- New H1: "Every reel, post, and tweet you've saved — finally searchable."
- CTA changed from "Try it free" to "Save your first reel free"
- "Pocket alternative" badge in navbar linking to comparison section
SEO Package
- Optimized meta title/description targeting "pocket alternative 2026", "save instagram reels", "tiktok bookmark manager"
- 3 JSON-LD schemas: SoftwareApplication (with pricing), Organization, FAQPage
- robots.ts: disallow /api/, /(app)/, /(auth)/
- Twitter card + OpenGraph tags with summary_large_image
- Keyword-rich H2s and H3s throughout all sections
- Login page set to noindex
- SITE_URL constant for canonical URLs and sitemap
Fixes
- Fixed amber hover states leaking across app (accent token changed to teal tint)
v0.5.0 — Shareable Collections & Referral Program (Apr 6, 2026)
Shareable Collections
- Public collection pages at
/c/[slug]— SSR with SEO metadata, OG images, JSON-LD - Anyone can view shared collections without logging in (growth loop)
- Share dialog with Link/Members tabs, public/private toggle, copy link
- One-tap share to WhatsApp, Twitter/X, Email
- Dynamic OG image generation (1200x630 with thumbnails, curator info)
- Dynamic sitemap including indexable public collections (5+ items)
- Slug generation utility for URL-friendly collection links
- Upgrade prompt for free users trying to share (Plus-only feature)
- Middleware updated for public
/c/*and/r/*routes
Referral Program
- "Give a month, get a month" — both referrer and invitee get 1 month Plus free
GIST-XXXXreferral codes (auto-generated per user)- Personalized referral landing pages at
/r/[code]with cookie attribution - Auth callback picks up referral cookie and creates attribution record
- Reward triggers automatically when invitee saves their first item
- Settings > Invite Friends section with referral link, share buttons, stats dashboard
- Progress bar showing months earned (capped at 12/year)
- Referral CTA in paywall modal: "Or invite a friend for a free month"
- Subtle referral nudge in feed after 3rd save (dismissible)
- Fraud prevention: 12-month cap, first-save trigger, self-referral guard
Shared Components
- CopyLinkInput: reusable copy-to-clipboard input
- ShareButtons: WhatsApp/Twitter/Email share buttons
- UpgradeSharePrompt: plan gate dialog for sharing
- ReferralNudge: inline feed banner
- Analytics events reference doc (24 existing + 7 new planned events)
v0.4.1 — Multi-Conversation Chat & UX Fixes (Apr 6, 2026)
Multi-Conversation Chat
- Multiple chat conversations (threads) — no more single global chat
chat_conversationstable with conversation_id on messages- Auto-create conversation from first message (title = first 50 chars)
- Conversation history dropdown: browse, rename, delete past chats
- CRUD API: GET/POST/PATCH/DELETE /api/chat/conversations
- Clear scoped to current conversation
- Daily limit checks across all conversations
Telegram Bot Chat
- Ask questions about saved content directly via Telegram (RAG with GPT-4o-mini)
/ask— explicit ask command/recent— shows last 5 saved items with emoji, date, summary, source link/search— semantic search, returns top 3 results/help— lists all available commands- Daily chat limit: 10 messages for free tier, unlimited for Plus
- Citations with source URLs in responses
- Updated
/startmessage with new capabilities
Chrome Extension
- Fixed race condition: content script now runs at
document_start(wasdocument_idle) - Added
__gistBuffersafety net in injected script to preserve data between script loads - Content script drains buffer on init via inline MAIN world script
- Added magic-link API endpoint for extension login flow
UX Fixes
- Card action bar redesigned: horizontal row below content (social media style) instead of floating overlay
- Comments show inline below card (toggle with chat bubble icon) instead of modal dialog
- Bookmark icon for collections (replaced confusing FolderPlus)
- Native tooltips on all action buttons
- Collections API fixed:
owner_idcolumn mismatch that caused creates to silently fail collection_items.insertnow includes requiredadded_byfield- POST /api/collections accepts description and emoji fields
- Chat redesigned: dropdown history replaces 3-column layout (app nav + conversations + chat was too cluttered)
v0.4.0 — Analytics, Collections, Notes & Entity Linking (Apr 5-6, 2026)
Analytics
- PostHog live key — replaced placeholder, analytics now tracking in production
- Conversion funnel: 8 events across signup → first save → search/chat → paywall → upgrade
- Session replay enabled at 50% sampling with console log capture
- User identification: posthog.identify on login, person properties (plan, saves_count, platform breakdown)
- Route-level pageview tracking via usePathname
- Server-side tracking utility (posthog-node) for API routes
- Dashboard setup guide + automation script for Growth, Engagement, Monetization, Retention dashboards
Collections
- Collections / folders for organizing saved items ("Japan Trip 2026", "Meal Prep Ideas")
- DB schema: 3 tables (collections, collection_items, collection_members) with RLS policies
- Full CRUD API: create, rename, delete collections; add/remove items; invite/remove members
- Collections page with grid view, create dialog, emoji picker
- Single collection detail page with item grid
- Add-to-collection dialog from item cards (Bookmark icon)
- Collaborative collections: share with friends as viewer/editor/admin
- Share dialog with member management + role badges
- Collection header component with member avatars
Notes / Comments
- User comments on saved items — social media style, inline below card content
- DB migration for item_notes table with RLS policies
- CRUD API routes for notes (create, update, delete)
- Inline notes panel with edit/delete, relative timestamps, "(edited)" indicator
- Comment count shown on chat bubble icon, syncs in real-time
Duplicate Detection
- URL normalization: strips tracking params (utm_*, fbclid, igshid, etc.), normalizes Instagram URLs
- DB unique constraint on (user_id, normalized_source_url)
- Returns HTTP 409 with existing item details when duplicate detected
- Toast notification: "You already saved this!" with summary, time ago, and "View" scroll-to-item action
- Saves API calls to Apify/OpenAI by checking before extraction
Entity Linking
- Vector similarity search (cosine > 0.85) finds related items after each save
- Automatic entity_group assignment links items about the same real-world thing
- Similar items API endpoint
- "N related" badge on item cards, expandable to show related saves
- After-save toast: "This looks related to [summary] you saved on [date]"
Chrome Extension
- 14-URL test plan with full code review (identified race condition bug + carousel limitation)
- Chrome Web Store listing guide with screenshots specs, category, permissions justification
- Privacy policy for extension submission
- Popup version fix (0.1.0 → 0.2.0)
Apify Cost Monitoring
- Budget check before Instagram extraction ($5/mo free tier)
- 90% warning log, 100% blocks with friendly error message
- Admin API endpoint for usage stats
UX Improvements
- Card action bar redesigned: horizontal row below content (social media style)
- Comments toggle via chat bubble icon (inline, not modal)
- Bookmark icon for collections (replaced confusing FolderPlus)
- Native tooltips on all action buttons
- Delete button pushed to far-right, hover-only visibility
- No more overlapping action buttons
v0.3.1 — Content Display & UX (Apr 5, 2026)
Content Display
- Category-specific rendered views replace raw JSON dump in expanded cards
- 11 category renderers: restaurant, recipe, travel, tech, movie, fitness, shopping, music, tips, event, other
- Restaurant items show name, cuisine, location with map pin, notes as callout
- Recipes show ingredients as checkbox list, steps as numbered circles
- Tech tools show name with pricing pill, use case, clickable URLs
- Travel shows destination with globe icon, spots list, best time
- Fallback renderer handles arbitrary data: arrays as bullet lists, nested objects as labeled fields, URLs as clickable links
- "Show raw data" collapsible toggle for power users
- Never shows empty fields — sections render only when data exists
UX Improvements
- Text selection works in expanded cards (card changed from button to div)
- Click-to-expand only triggers on header area, not entire card
- Text selection detected — expanding doesn't trigger if user is highlighting text
- Default cursor on cards (not pointer hand)
v0.3.0 — Instagram & Reliability (Apr 5, 2026)
Instagram Extraction
- Apify integration as primary extraction method (
apify~instagram-scraper) - Garbage content detection — rejects Instagram i18n/framework data instead of saving it
- Permanent thumbnail storage via Supabase Storage bucket (CDN URLs expire, ours don't)
- Instagram routed through dedicated
fetchInstagrampipeline in both web app and Telegram bot - Instagram Graph API webhook endpoint for
@gistsavebotmention model (ready for app review) - SOAX residential proxy integration with 4GB bandwidth cap and usage tracking
TikTok Improvements
- Short link resolution —
vt.tiktok.comandvm.tiktok.comnow follow redirects before extraction - Railway
/download-audioproxy — fixes 403 errors when downloading TikTok audio (CDN URLs are IP-bound) - Extraction strategy reversal — Railway yt-dlp runs first (returns audio URLs), oEmbed as fallback
_pick_best_audio_url()— prefers audio-only streams under 24MB for Whisper
Chrome Extension v0.2
- Manifest V3 with fetch/XHR interception in MAIN world for Instagram
- Content script cache with 10-min TTL and DOM extraction fallback
- Right-click "Save to Gist" context menu
- Popup with login, save button, and status indicator
Telegram Bot
- 6-digit linking code flow — replaces broken magic link (in-app browser issue)
/linkcommand generates code, user enters on Settings page- Unlink support via Settings page and API
- Webhook secret authentication (verifies
X-Telegram-Bot-Api-Secret-Token) - HTML escaping in bot replies (prevents injection)
sendTelegramMessageerror handling with 10s timeout
Security & Reliability
- Atomic
increment_saves_countRPC — fixes race condition that allowed paywall bypass resolveUrlnow async with short link expansion- YouTube wired into Telegram pipeline via
fetchContent cleanUrlpreserves YouTube?v=parameter"youtube"added tosource_platformTypeScript union- Audio transcription hardening: 24MB size limit, MIME type detection, 20s download timeout, User-Agent header
- Broken thumbnails hide gracefully via
onErrorhandler
Railway Service
/download-audioendpoint — proxies audio downloads from same IP that extracted themvt.tiktok.comadded to ALLOWED_DOMAINS_pick_best_audio_url()— audio-only preference, size filtering, bitrate sorting
Documentation
- Project roadmap with version history
- Instagram extraction research (API, legal, Chrome extension approaches)
v0.2.0 — Monetization & Telegram (Apr 3-4, 2026)
Payments
- Stripe Checkout integration ($4.99/mo Plus plan, test mode)
- Paywall modal at 5 free saves
- Webhook handlers for
checkout.session.completedandcustomer.subscription.deleted - Plan stored in profiles table (free/plus)
Telegram Bot
@GistSaveBotcreated with webhook- Forward any link → bot extracts → replies with category, summary, tags
/startcommand with welcome message- Account linking via Settings page URL
RAG Chat
- Streaming responses via GPT-4o-mini
- Hybrid search retrieval (70% vector + 30% full-text)
- Citation links
[1][2]linking to source items - Chat history persistence in
chat_messagestable - Clear history button
- Daily limit: 10 messages/day free, unlimited Plus
- Markdown rendering (bold, lists, styled citations)
Feed & UX
- Inline tag and category editing
- Delete items with confirmation toast
- PostHog analytics (13 events tracked)
- Dark/light mode toggle
- Responsive layout: sidebar nav on desktop, bottom tabs on mobile
- Search highlighting (matched terms in yellow)
- Skeleton loaders during extraction
- Empty states with example URLs
- Success/error toasts (sonner)
v0.1.0 — Core MVP (Apr 3, 2026)
Core Product
- Landing page with hero, how-it-works, use cases, pricing sections
- Magic link authentication via Supabase Auth
- Paste URL → AI extraction → auto-categorization → save
- 11 categories: restaurant, recipe, travel, tech, movie, fitness, shopping, music, tips, event, other
- Category-specific structured extraction (restaurant names, recipe ingredients, travel destinations, etc.)
Platform Support
- TikTok extraction via oEmbed API
- YouTube extraction via
youtube-transcript+ oEmbed metadata - Twitter/X extraction via oEmbed API
- Instagram embed page parser (works locally, blocked from cloud IPs)
- Fallback chain: oEmbed → Railway yt-dlp → meta scrape
Search
- Hybrid search combining full-text (PostgreSQL tsvector) and semantic (pgvector)
hybrid_searchRPC function (70% vector similarity + 30% FTS rank)
Feed
- Saved items feed with thumbnails, summaries, tags
- Category filter chips with per-category colors
- Expandable cards showing full extracted data
- "View original post" source links
Infrastructure
- Railway FastAPI service wrapping yt-dlp (Docker)
- Supabase database with RLS, auto-profile creation trigger
- Vercel deployment with serverless functions
- Fixed