Commit Graph

4447 Commits

Author SHA1 Message Date
Dirk Rost 8ba67d03a3 Merge branch 'faq-zapstore-link' into 'main'
Update FAQ with Zapstore link for Ditto

Closes #115

See merge request soapbox-pub/ditto!72
2026-03-16 21:50:04 +00:00
Mary Kate 905317ccf8 Update FAQ with Zapstore link for Ditto 2026-03-16 21:50:04 +00:00
filemon 6e5a6b5d91 feat: reusable music/dance reaction system for all Blobbi stages
BREAKING: Egg no longer sways by default when animated=true.
Sway animation now requires explicit reaction state.

Changes:
- Remove default egg sway from animated prop in EggGraphic
- Add reaction prop to EggGraphic, BlobbiEggVisual, BlobbiBabyVisual, BlobbiStageVisual
- Add EggReactionState, BabyReactionState, BlobbiReaction types
- Wire blobbiReaction state from BlobbiPage to BlobbiStageVisual
- Add animate-blobbi-sway and animate-blobbi-bounce CSS animations
- Add animate-egg-bounce for singing reaction on eggs

Reaction states:
- idle: no animation (default)
- listening: gentle sway (music playing)
- swaying: gentle sway
- singing: bouncy animation (sing action active)
- happy: gentle sway

Trigger behavior:
- Play Music: listening reaction when audio plays
- Sing: singing reaction when card opens
- Close activity: returns to idle
2026-03-16 18:48:57 -03:00
filemon 270cb51acc fix: volume control popup now renders via portal to avoid clipping
The volume slider was being clipped by the card's overflow:hidden.
Now using Popover component (Radix UI) which renders via portal,
ensuring the volume control appears above all UI elements correctly.
2026-03-16 18:31:02 -03:00
filemon 579c78b2ad feat: add volume control to InlineMusicPlayer
- Add volume state and setVolume function to useAudioPlayback hook
- Default volume is 0.8, persists across track changes
- Volume button shows Volume2 icon (or VolumeX when muted)
- Clicking volume button shows horizontal slider popup
- Slider controls real audio volume (0-1 range)
- Click outside to dismiss volume slider
2026-03-16 18:23:39 -03:00
filemon 470cdd1c76 refactor: change stop button to restart button in InlineMusicPlayer
- Add restart() function to useAudioPlayback hook (sets currentTime=0 and plays)
- Replace Square icon with RotateCcw for restart semantics
- Restart button resets track to beginning and continues playing
- Stop function still exists for cleanup on close
2026-03-16 18:04:31 -03:00
filemon 2abbae38d9 fix: stop button now truly stops playback instead of auto-restarting
The auto-start effect was incorrectly triggering on 'stopped' state,
causing immediate restart. Now 'stopped' is a terminal state that
requires explicit play button click to restart.
2026-03-16 18:00:43 -03:00
filemon 46dc8d9e12 fix: inline music player stop/track-switch behavior and UI cleanup
- Fix stop button: add 'stopped' state so stop truly stops playback instead of pausing at 0
- Fix track switching: detect source.url changes and reload, distinguish change vs initial selection
- Disable Upload tab in PlayMusicModal (marked 'Soon' but was still clickable)
- Remove misplaced chevron arrows from lyrics toggle button in InlineSingCard
2026-03-16 17:56:28 -03:00
filemon 59cbb9d740 fix: inline activity layout and move audio assets to public/
- Move inline activity cards inside padded container to prevent overlap with fixed bottom bar
- Move audio files from src/blobbi/audio/ to public/blobbi/audio/ for correct Vite asset loading
- Update track metadata with accurate durations from ffprobe
- Update documentation comments to reflect correct asset location
- Remove unused MicOff import from InlineSingCard
2026-03-16 17:38:21 -03:00
Derek Ross adc370a598 Fix live streams showing as active when ended, and fix muted icon state
Implement NIP-53 staleness heuristic (24h threshold) to mark abandoned
status=live streams as ended. Fetch all streams globally and filter
follows client-side (including p-tag participants for streaming services).
Add Live/Planned/Past pill tabs to the streams strip on /videos.

Also fix pre-existing bug where the volume icon showed unmuted while the
video was actually muted (autoplay requires muted), by syncing the
initial muted state from the media element in usePlayerControls.

Closes #88
2026-03-16 16:36:54 -04:00
Mary Kate Fain 5bff1a2533 fix: move add field pill buttons back below the field inputs 2026-03-16 15:12:22 -05:00
Mary Kate Fain 7ad8d0e601 fix: hide Media section and Shakespeare footer when no mediaEvents provided
Conditionally render the Media section and footer only when mediaEvents
is explicitly passed, so the fields-only mobile preview shows just the
profile fields without an empty Media box or the Shakespeare link.
2026-03-16 15:11:11 -05:00
Mary Kate Fain 2306d6c25b fix: exclude media collage from mobile profile fields preview
Only pass fields to the mobile ProfileRightSidebar preview, omitting
mediaEvents and mediaLoading so the media collage section doesn't
render in the inline mobile preview.
2026-03-16 15:09:18 -05:00
Mary Kate Fain 90d62557fe fix: swap Wallet/Link order, use real sidebar component for mobile preview
Swap Wallet and Link pill button order. Replace the barebones mobile
preview with the real ProfileRightSidebar component (with media collage,
audio players, etc.) by adding a className prop to override the default
hidden xl:flex classes. Rename 'Sidebar Preview' to 'Profile Fields
Preview' since on mobile it's not in a sidebar.
2026-03-16 15:08:23 -05:00
Mary Kate Fain e07dd212d5 fix: add plus icon to field preset pill buttons
Each pill now shows a small + icon before the preset icon and label,
making it visually clear they are add-actions rather than just labels.
2026-03-16 15:02:09 -05:00
Mary Kate Fain 42c6faa748 feat: load user's media events in profile settings sidebar preview
Use the same useProfileMedia query as the profile page to fetch the
current user's media events and pass them to ProfileRightSidebar in
the settings page. The sidebar preview now shows the media collage
section just like it appears on the actual profile.
2026-03-16 15:00:34 -05:00
Mary Kate Fain 300d0f94b9 fix: move field preset pills between header and field rows
Place the pill buttons directly after the 'Profile Fields' heading so
users see the available content types immediately, before the existing
Website and Lightning fields.
2026-03-16 14:58:21 -05:00
Mary Kate Fain 4fd3859fb3 feat: replace Add Field dropdown with visible pill buttons
Replace the hidden dropdown menu with a wrapping row of small pill-shaped
buttons, each showing the preset icon and label (Music, Photo, Video,
Email, Link, Wallet, Custom). Users can now see all available field types
at a glance and add one with a single tap. Descriptions are preserved as
hover tooltips on each pill.
2026-03-16 14:56:29 -05:00
Mary Kate Fain a8a09a2c66 feat: show per-field upload spinner on media field rows
Track which field index is currently uploading and replace the upload
button with a spinning Loader2 icon on that specific field row during
upload, giving immediate local feedback instead of relying on the
distant save button spinner.
2026-03-16 14:49:50 -05:00
Mary Kate Fain 410cd2d079 feat: render images and videos inline in profile sidebar fields
Add isImageUrl and isVideoUrl helpers alongside the existing isAudioUrl.
Profile field values that point to image files now render as inline images
(clickable to open full size), and video files render using the VideoPlayer
component. Both the desktop sidebar (ProfileFieldRow) and mobile inline
view (ProfileFieldInline) support the new rendering.
2026-03-16 14:44:37 -05:00
Mary Kate Fain 5384da54e2 feat: warn when pasted URL doesn't match expected media file type
Show an inline amber warning below media field inputs when the pasted URL
doesn't have the right file extension for the field type. For example,
pasting a YouTube link in a Music field warns that it won't work as an
audio player and suggests uploading a file instead. Handles three cases:
wrong type (e.g. image URL in audio field), no recognizable extension
(e.g. youtube.com), and Blossom URLs (always allowed, type can't be
determined from URL).
2026-03-16 14:38:01 -05:00
Mary Kate Fain 4e18d91feb fix: improve media field placeholder language to emphasize uploading
Replace misleading 'URL' placeholder on media fields with action-oriented
text like 'Upload audio or paste direct file link' per preset type. Update
upload button tooltips to say 'Choose file to upload'. Update Music preset
description to say 'Upload' consistently. Each preset now carries its own
valuePlaceholder through the form schema to the row component.
2026-03-16 14:35:39 -05:00
Mary Kate Fain f55c9ec8b4 fix: use app sidebar slot for profile fields preview instead of inline two-column layout
Use useLayoutOptions({ rightSidebar }) to render the live ProfileRightSidebar
preview in the actual app sidebar, replacing the previous approach that put
a fake two-column layout inside the center content area. The form returns
to its original max-w-xl centered layout. Mobile collapsible preview is
retained for screens below xl where the sidebar is hidden.
2026-03-16 14:27:20 -05:00
filemon c50c9bec7e refactor: inline activity cards for Play Music and Sing
- Add InlineMusicPlayer component for persistent music playback UI
- Add InlineSingCard component for inline recording/lyrics experience
- Add useAudioPlayback hook for reusable audio playback logic
- Add blobbi-activity-state types for activity and reaction state management

Play Music flow:
- PlayMusicModal now serves as track picker only
- After track selection, inline player appears with play/pause/stop controls
- Action published first, playback starts only after success

Sing flow:
- Recording happens inline (no modal)
- Lyrics panel expands upward with random lyrics
- Action published only when user confirms with 'Sing for Blobbi'

Blobbi reaction state prepared for future visual animations
2026-03-16 16:21:00 -03:00
Mary Kate Fain e5f34659b2 feat: add field preset templates and live sidebar preview to profile settings
Replace the generic 3-item Add Field dropdown with 7 discoverable presets
(Music, Photo, Video, Email, Link, Wallet, Custom) that pre-configure
field type, default emoji labels, and scoped file-accept filters.

- Add tooltips on upload buttons showing accepted file formats per type
- Add HelpTip to Profile Fields section header with new FAQ entries
- Add live sidebar preview panel (right column on xl+, collapsible on mobile)
- Scope file picker accept attribute dynamically per field preset
- No changes to the underlying data model — presets are purely client-side UX
2026-03-16 14:20:36 -05:00
filemon 14ebcb1165 Merge branch 'main' into feat-blobbi 2026-03-16 15:53:56 -03:00
filemon 3728ad02e6 fix: CSP and audio recording/playback for SingModal
- Add blob: to media-src CSP directive to allow recorded audio playback
- Add robust MIME type selection helper for MediaRecorder
- Try MIME types in order: webm;opus, webm, mp4, ogg;opus, ogg
- Track actual recorder MIME type and use it for blob creation
- Add user-friendly playback error messages (non-fatal amber warnings)
- Verify PlayMusicModal blob URL handling works correctly
- Add 'Soon' badge to Upload tab in PlayMusicModal
2026-03-16 15:50:22 -03:00
filemon ba9e1f5375 fix: cleanup pass for Blobbi play_music and sing actions
- Relax egg-stage blocking in canUseAction() (UI visibility vs domain logic)
- Fix egg inventory filtering to only show items with egg-compatible effects
- Remove 'shell' wording from medicine UI text
- Fix canonical data usage in mutations (use canonical.companion for decay)
- Fix browser timer typing (NodeJS.Timeout -> ReturnType<typeof setInterval>)
- Fix PlayMusicModal audio source switching (recreate Audio on source change)
- Fix SingModal recording playback (track current playback URL)
- Add random lyrics helper for Sing action with collapsible UI
2026-03-16 15:32:09 -03:00
filemon 598c5f90ea Add play_music and sing actions, fix egg clean/medicine consistency
- Fix egg stage actions: clean and medicine now work for eggs
- Add play_music action with built-in tracks and file upload
- Add sing action with in-browser audio recording
- Hide feed/play/sleep actions for eggs in UI (not hard-blocked)
- Both new actions increase happiness only (+15/+20)
- Placeholder built-in tracks in blobbi-builtin-tracks.ts
2026-03-16 15:08:30 -03:00
filemon 6e1a195615 Deprecate egg_temperature - eggs now use warmth prop fallback 2026-03-16 11:11:34 -03:00
Chad Curtis dea3b7d7b8 fix: profile settings sticky header respects mobile top bar offset 2026-03-16 05:56:48 -05:00
Chad Curtis 93456dcd3b fix: remove border from profile settings sticky header 2026-03-16 05:55:07 -05:00
Chad Curtis 7e9decb66e fix: move profile save button to sticky header 2026-03-16 05:54:04 -05:00
Chad Curtis 04a7381ac1 fix: use emojiAvatarBorderStyle for emoji-shaped avatars in combined notifications 2026-03-16 04:02:58 -05:00
Chad Curtis 258b643ac5 Fix avatar shape not respected in notifications, audio visualizer, and follow pack cards 2026-03-16 03:55:21 -05:00
Chad Curtis 6de3eea3e9 Fix slide-in menu on custom themes: replace boxes with semi-transparent overlay 2026-03-16 03:44:14 -05:00
Chad Curtis e6f5bb926c Fix safe area insets in mobile drawer
When a theme background image is active, the rounded panel containers
were visually extending into the status bar (top) and home indicator
(bottom). Safe area insets were applied inside child elements but the
outer wrapper painted behind them. Move the insets to the outer wrapper
so the panels sit entirely within the safe area.
2026-03-16 02:40:36 -05:00
Lemon 8e8aed69c0 Add Web Push notifications via nostr-push
Replace the browser Notification constructor (tab-must-be-open) with nostr-push
Web Push notifications that work even when the browser is closed.

- Add service worker (public/sw.js) for receiving and displaying push events
- Add nostr-push RPC client (src/lib/nostrPush.ts) using an ephemeral device
  keypair for signing — the user's Nostr signer is never prompted
- Add usePushNotifications hook managing the full lifecycle: SW registration,
  VAPID key fetch, pushManager.subscribe, and nostr-push registration
- Replace the web path in useNativeNotifications with push registration that
  auto-enables/disables based on the notificationsEnabled encrypted setting
- Android native Capacitor path is unchanged
- Requires VITE_NOSTR_PUSH_PUBKEY env var (hex pubkey of the nostr-push server)
2026-03-16 00:17:05 -07:00
Chad Curtis 947bbfccb0 Add deep linking for ditto.pub Android App Links
- Add Android intent-filter with autoVerify for https://ditto.pub/*
- Create DeepLinkHandler component to handle appUrlOpen events at runtime
- Mount DeepLinkHandler in AppRouter inside BrowserRouter context
- Add public/.well-known/assetlinks.json with pub.ditto.app package and signing cert fingerprint
2026-03-16 01:39:56 -05:00
Alex Gleason 0305a53116 Reduce bottom overscroll padding from 50vh to 10vh 2026-03-16 00:03:36 -05:00
Alex Gleason d7461e0488 Add bottom overscroll padding to center column for visual comfort and bottom bar clearance 2026-03-15 23:17:15 -05:00
filemon 452848f14f Remove shell_integrity - eggs now use standard health stat
BREAKING: shell_integrity is fully removed from the egg model.
Eggs now use the standard 3-stat model: health, hygiene, happiness.

Changes:
- Remove EggStats, EggMedicineResult types from blobbi-action-utils.ts
- Remove applyMedicineToEgg function (medicine now uses applyStat directly)
- Update useBlobbiUseInventoryItem to apply medicine health effect to egg health
- Update BlobbiActionInventoryModal to preview health changes for egg medicine
- Remove shell_integrity from ItemEffect in shop types
- Remove shellIntegrity from BlobbiEggData in types/blobbi.ts
- Remove EggStats, EggMedicineResult, applyMedicineToEgg from exports
- Add DEPRECATED_BLOBBI_TAG_NAMES set with 'shell_integrity'
- Update mergeBlobbiStateTagsForRepublish to filter out deprecated tags

Migration: Existing events with shell_integrity tags will have them
automatically removed on the next republish (any user interaction).

Egg stat model is now fully consistent:
- health, hygiene, happiness: active (decay + medicine)
- hunger, energy: fixed at 100
2026-03-16 00:20:33 -03:00
filemon 4ccc123209 Apply decay before stage transitions (hatch/evolve)
- Create useBlobbiHatch hook for egg -> baby transition
- Create useBlobbiEvolve hook for baby -> adult transition
- Both hooks apply accumulated decay before publishing new state
- Wire up floating action button to trigger hatch/evolve based on stage
- Hide evolve button for adults (already fully evolved)
- Show loading state during transitions
- Export new hooks and types from blobbi/actions module

Stage transitions now consistently apply decay first, ensuring
no transition can happen from stale stats.
2026-03-15 21:10:44 -03:00
filemon a987449789 Apply decay before sleep/wake mutations in handleRest
- Import applyBlobbiDecay in BlobbiPage
- Calculate accumulated decay before state change
- Persist decayed stats along with new sleep/wake state
- Reset last_decay_at timestamp after applying decay

This ensures stats accurately reflect elapsed time when toggling
between active and sleeping states.
2026-03-15 21:04:36 -03:00
filemon ba9ff0964b feat(blobbi): implement decay system with projected UI state
Core decay system (src/lib/blobbi-decay.ts):
- Pure applyBlobbiDecay() function for deterministic decay calculation
- Stage-specific decay rates: egg (2-3hr), baby (3-5hr), adult (5-7hr)
- Health modifiers based on other stats
- Health regeneration when all stats >= 80
- Floor all deltas, clamp stats to 0-100
- Warning/critical threshold helpers

UI projection hook (src/hooks/useProjectedBlobbiState.ts):
- Calculates projected stats without publishing
- Recalculates every 60 seconds
- Returns visible stats with status indicators

BlobbiPage updates:
- Uses projected state for display
- Egg shows 3 stats (health, hygiene, happiness)
- Baby/adult shows all 5 stats
- StatIndicator supports warning/critical status styling

Mutation updates (useBlobbiUseInventoryItem):
- Applies accumulated decay before interactions
- Uses decayed stats as base for item effects
- Updates last_decay_at on every interaction

Documentation (docs/blobbi/decay-system.md):
- Comprehensive explanation of the system
- All decay rates and thresholds
- Mutation flow diagram
- Edge cases and assumptions
2026-03-15 21:01:34 -03:00
filemon 3cd64fb0af refactor(blobbi): make evolve button icon stage-aware
- egg stage: Shows Egg icon with 'Hatch' tooltip
- baby/adult stages: Shows Sparkles icon with 'Evolve' tooltip

Implementation:
- Added 'stage' prop to BlobbiDashboardFloatingControlsProps
- Created getEvolveIcon() helper - returns Egg or Sparkles based on stage
- Created getEvolveTooltip() helper - returns 'Hatch' or 'Evolve'
- Removed unused Zap import

Icon choice rationale:
- Sparkles was chosen for non-egg stages because it communicates magical
  transformation, which fits the Blobbi fantasy/pet theme better than
  technical icons like TrendingUp or ArrowUpCircle
2026-03-15 19:48:43 -03:00
filemon 6e8e6fe243 feat(blobbi): add floating action buttons to dashboard (placeholders)
New components:
- FloatingActionDef: Typed interface for button definitions
- BlobbiDashboardFloatingControls: Component that renders left and right
  floating button clusters

Buttons added (all visual-only placeholders):
- Right side (top cluster):
  - Settings (Settings icon)
  - Set as Companion (Heart icon)
  - Take a Photo (Camera icon)
  - Open PiP (PictureInPicture2 icon)
  - Blobbi Info (Info icon) - wired to existing info modal
  - Evolve (Zap icon) - styled with accent/primary colors
- Left side:
  - Back button (ArrowLeft icon) - optional, not rendered by default

Implementation:
- Uses existing QuickActionButton for consistent styling
- Evolve button has distinct accent styling (primary colors)
- Button definitions centralized in typed arrays
- Placeholder handlers use console.log('TODO: ...')
- Existing info modal functionality preserved
2026-03-15 19:37:35 -03:00
filemon 476f1bade2 feat(blobbi): apply base color to dashboard and info modal names
- BlobbiDashboard: The h2 element displaying companion.name now uses
  style={{ color: companion.visualTraits.baseColor }}
- BlobbiInfoModal: The DialogTitle displaying companion.name now uses
  style={{ color: companion.visualTraits.baseColor }}
- Both update correctly when selecting a different Blobbi
- Selector card names left unchanged (not prominent display)
2026-03-15 19:29:39 -03:00
filemon cf1d9ad53f fix(blobbi): add separate colored name display above egg
- Removed baseColor styling from Input (was not working reliably)
- Added a separate <p> element between the input and egg visual
- The <p> displays trimmedName with style={{ color: preview.visualTraits.baseColor }}
- This element updates live as the user types (uses trimmedName from preview.name)
- Input reverted to normal theme styling (text-center font-medium)
- No duplicate name under the egg (title still not passed to EggGraphic)
2026-03-15 19:22:10 -03:00
filemon 680ff86202 fix(blobbi): apply egg base color to name input field
- Removed the extra styled name display div (was creating a second name)
- Applied base color directly to the Input element via style prop
- Changed Input className to 'text-center font-semibold text-lg' for better visibility
- Now only ONE visible name exists: the input field itself, styled with the egg's baseColor
2026-03-15 19:19:06 -03:00