Commit Graph

4447 Commits

Author SHA1 Message Date
Mary Kate Fain 6948938768 Fix AI chat widget layout with fillHeight option for fixed-height widgets
Add fillHeight property to WidgetDefinition. When true, WidgetCard uses
a fixed height instead of max-height on the ScrollArea, allowing the
widget's internal flex layout to properly fill the container. The AI chat
widget's messages area now scrolls correctly at a fixed height instead
of awkwardly growing with content.
2026-04-12 16:46:17 -05:00
Mary Kate Fain db9cdd04c5 Fix AI chat widget 400 error by fetching available models dynamically
The widget was hardcoding 'shakespeare' as the model name, which is
not a valid model ID. Now fetches available models from the API and
uses the cheapest one as default, matching how AIChatPage works.
2026-04-12 16:39:43 -05:00
Mary Kate Fain 528cf905fb Show 'out exploring' state in Blobbi widget when companion is floating
Uses useBlobbiCompanionData() to detect if this Blobbi is the active
floating companion (same check as BlobbiPage). When active, hides the
visual and stat wheels, showing a Footprints icon + 'Out exploring
with you' message + gradient 'Bring home' button instead.
2026-04-12 16:29:09 -05:00
Mary Kate Fain 2c08bcd94a Add Take Along companion toggle button to Blobbi widget 2026-04-12 16:21:14 -05:00
Mary Kate Fain 9de3fa7112 Unify stat rings and action buttons into clickable StatIndicator wheels
Extract StatIndicator into a shared component (src/blobbi/ui/StatIndicator.tsx)
with size ('sm'/'md') and onClick/disabled props. Reuse it in both
BlobbiPage (display-only, size='md') and BlobbiWidget (clickable, size='sm').

The widget now shows a single row of stat wheels that double as action
buttons: clicking the hunger wheel feeds, hygiene cleans, health heals,
happiness plays, and energy toggles sleep/wake. Removes the separate
action button row entirely.
2026-04-12 16:11:53 -05:00
Mary Kate Fain 28027cd7b2 Add Heal (medicine) quick action button to Blobbi widget 2026-04-12 16:00:48 -05:00
Mary Kate Fain e54fad61ae Replace stat bars with compact circular ring indicators in Blobbi widget
Uses the same SVG progress ring + lucide icon pattern as BlobbiPage,
scaled down to 36px circles. Shows warning/critical alert triangles
on low stats. Much more compact vertically than the horizontal bars.
2026-04-12 15:58:00 -05:00
Mary Kate Fain 31189801f8 Fix Blobbi widget: show status-reactive visuals and increase height for action buttons
- Pass useStatusReaction recipe to BlobbiStageVisual so the widget
  reflects the actual health state (dizzy eyes, stink clouds, etc.)
- Increase default widget height from 280px to 350px so quick action
  buttons aren't clipped by the scroll container
2026-04-12 15:53:50 -05:00
Mary Kate Fain d579e91bbd Enhance Blobbi widget with live stats and quick action buttons
- Syncs companion selection with BlobbiPage (localStorage + profile.has)
- Shows projected decay stats that update every 60s
- Adds Feed, Play, Clean, and Sleep/Wake quick action buttons
- Hides actions irrelevant to the current stage (eggs can't eat/play/sleep)
- Uses the same ensureCanonical + decay + publish flow as BlobbiPage
- Buttons disable while an action is in progress
2026-04-12 15:41:28 -05:00
Mary Kate Fain 27133d69f2 Use curator follow list for Articles and Events widgets when logged out 2026-04-12 15:29:40 -05:00
Mary Kate Fain 5e895e59ae Replace Photos and Music widgets with rich single-item formats
Photos widget shows the latest photo with image, author, and caption.
Music widget shows the latest track with artwork and playable controls
via the global audio player. Both scope to the user's follow list when
logged in, or the curator's follow list when logged out.
2026-04-12 15:23:34 -05:00
Mary Kate Fain c5f9f8be6c Remove Books sidebar widget 2026-04-12 15:14:51 -05:00
Mary Kate Fain 1a58875418 Make widget header labels clickable links to their full pages 2026-04-12 15:09:40 -05:00
Mary Kate Fain 8ee6388ab8 Fix extra empty space in widgets by using max-height instead of fixed height 2026-04-12 15:02:43 -05:00
Mary Kate Fain 5878b8ad5f Set default sidebar widgets to Trending, Hot Posts, and Wikipedia 2026-04-12 14:59:43 -05:00
Mary Kate Fain ec4359f1aa Add Hot Posts sidebar widget showing top posts from the hot feed 2026-04-12 14:57:19 -05:00
Mary Kate Fain f217394012 Merge remote-tracking branch 'origin/main' into new-sidebar 2026-04-12 14:47:09 -05:00
Alex Gleason bd333b9584 Fix Android WebView resize bugs caused by @capacitor/keyboard
Remove resizeOnFullScreen config which caused possiblyResizeChildOfContent()
to corrupt CoordinatorLayout height on Android 16 (API 36). Upgrade plugin
from 8.0.2 to 8.0.3 which adds a SystemBars guard as additional safety.
Platform-gate setAccessoryBarVisible to iOS only (unimplemented on Android).
2026-04-12 14:07:52 -05:00
Alex Gleason 0fca39a1bd Remove androidResume utility and its foreground-resume retry logic
The visibility-change-based Android resume detection was causing more
problems than it solved. Remove the module and simplify LoginDialog and
signerWithNudge to operate without retry-on-resume behavior.
2026-04-12 11:37:10 -05:00
Chad Curtis 3152f7f0ec Merge branch 'fix/emoji-shortcode-autocomplete' into 'main'
Fix emoji shortcode autocomplete clipped by compose box and emojis rendering as text

Closes #216

See merge request soapbox-pub/ditto!160
2026-04-12 14:13:32 +00:00
filemon 4764202a44 Reset attention cooldown on clearAttention to fix rapid route changes
clearAttention() now resets lastAttentionTimeRef to 0 so that a
triggerAttention call immediately after a forced clear is not silently
rejected by the 1500ms cooldown guard.
2026-04-11 20:43:55 -03:00
filemon 3d951cdaea Simplify route reaction to single center-look with random duration
Remove right-sidebar detection and multi-target chaining. The generic
reaction now fires a single attention target at the top-center of the
main content area for a random 2-6 seconds.

- Remove findRightSidebarPosition() and all sidebar scan logic
- Replace fixed 1200ms duration with random 2000-6000ms
- Compute target position at reaction start via live DOM query
  and window.innerWidth/Height fallback (no stale closure)
- Cancel both pending timeouts and active attention on drag
- Remove viewport prop from hook options (no longer needed)
- Update docstrings to describe the simplified behavior
2026-04-11 20:23:05 -03:00
filemon aadd2908e2 Add generic route-transition reaction for Blobbi companion
On page navigation the companion now briefly pauses and scans the
layout areas that changed. Center content is always scanned first,
followed by the right sidebar if a non-placeholder sidebar is
detected in the DOM.

Implementation:
- useRouteReaction.ts: thin orchestration hook that watches pathname,
  determines changed areas, and chains triggerAttention calls via
  setTimeout. Cancels on new route change, drag, or unmount.
- useBlobbiCompanion.ts: wires the new hook with existing
  triggerAttention/clearAttention from useBlobbiAttention.

No changes to the attention system, state machine, gaze hook, motion
hook, or entry animation. The existing attending state and attend-ui
gaze mode handle all the visual behavior.

Includes an empty ROUTE_REACTIONS map for future per-route overrides.
2026-04-11 19:52:21 -03:00
Alex Gleason dc81585f9a Pre-fetch all nsite blobs on Android before WebView navigates
Android's shouldInterceptRequest blocks a pool of ~6 IO threads, each
waiting for JS to respond via the Capacitor bridge. With 200+ files
each requiring a network round-trip to Blossom, loading is painfully
slow. iOS doesn't have this problem — WKURLSchemeHandler is async.

Split the native plugin lifecycle into create() and navigate():
- create() adds the WebView container with spinner overlay (visible)
- navigate() loads the entry URL (triggers fetch interception)

On Android, onReady downloads all manifest blobs in parallel (12
concurrent fetches) into an in-memory cache while the native
ProgressBar spinner animates. Once navigate() fires, every resolveFile
call is an instant cache hit.

On iOS/web, onReady is a no-op and navigate() fires immediately.
2026-04-11 17:20:21 -05:00
Alex Gleason 54e6c964db Add Blossom server affinity to speed up nsite loading
The fetchFromBlossom function previously tried servers sequentially for
every file request. For nsites without server tags (falling back to 3
app default servers), each of the 200+ files paid a full round-trip
penalty when the first server returned 404 before falling through.

Now tracks a module-level preferred server. Once any server successfully
serves a blob it becomes preferred and is tried first for all subsequent
requests. This means only the first file pays the discovery cost; the
rest go directly to the server that has the content.
2026-04-11 17:20:06 -05:00
Alex Gleason 0b73d4aac5 Remove dedicated Share button from profile pages
The 'Copy profile link' option is already available in the more menu,
making the standalone Share button redundant.
2026-04-11 15:40:08 -05:00
Alex Gleason 6f53f7ad99 Fix avatar fallback showing '?' instead of name initial
ComposeBox and LeftSidebar avatar fallbacks only checked metadata.name,
ignoring display_name and genUserName. Now uses the same fallback chain
as ProfileCard: display_name -> name -> genUserName(pubkey). Also fixed
the getDisplayName helper in LeftSidebar to check display_name.
2026-04-11 15:36:47 -05:00
Alex Gleason 399df4da4d Improve empty feed state with icon and discover CTA
Redesign FeedEmptyState with a centered icon, cleaner layout, and
two actionable buttons for the follows tab: 'Discover people to
follow' linking to /packs, and 'Browse the Global feed' to switch
tabs. Other call sites are unaffected (new props are optional).
2026-04-11 15:29:10 -05:00
Alex Gleason c06a66ade4 Ensure sticky desktop FAB anchors to bottom on empty feeds
Add min-h-dvh to the Feed <main> element so it always fills at least
the viewport height. Without this, the sticky FAB (a sibling after
<main>) sits in normal flow right after the short content instead of
at the bottom of the center column.
2026-04-11 15:25:37 -05:00
Alex Gleason 1fca26ae2e Clean up signup profile step: hide pencil badges, remove extra fields
- Hide the small pencil icon on avatar and banner until an image is
  actually set (the hover overlay still shows so users can discover
  the action)
- Remove the Profile Fields collapsible from the signup flow to keep
  the onboarding lightweight
2026-04-11 15:12:28 -05:00
Alex Gleason ccd8f213f6 Replace Skip/Continue with single Continue button in profile step
handlePublishProfile already skips publishing when no data is entered,
so the Skip button was redundant. A single full-width Continue button
simplifies the UI.
2026-04-11 15:09:38 -05:00
Alex Gleason 1c25702453 Fix signup dialog not clearing background when switching to light/dark theme
ThemeStep was reading customTheme?.background?.url unconditionally,
so the background persisted even after selecting a built-in theme.
Now resolves the active theme config the same way AppProvider does,
only showing the background when the active theme actually has one.
2026-04-11 14:58:52 -05:00
Alex Gleason 357ba7d8c8 fix: migrate to SystemBars API for Android 16+ safe area inset support
Android 16 (API 36) enforces edge-to-edge rendering unconditionally,
breaking @capacitor/status-bar's setOverlaysWebView and setBackgroundColor.
Additionally, a Chromium bug (<140) causes env(safe-area-inset-*) to report
0 in some Android WebViews.

- Replace @capacitor/status-bar with SystemBars from @capacitor/core 8+
- Enable insetsHandling: 'css' in capacitor.config.ts so the SystemBars
  plugin injects --safe-area-inset-* CSS variables on Android
- Update all safe area CSS utilities and inline styles to use
  var(--safe-area-inset-*, env(safe-area-inset-*, 0px)) fallback pattern
- Remove @capacitor/status-bar dependency (no longer needed)
2026-04-11 14:47:15 -05:00
Alex Gleason 207ca6893a Add iCloud Keychain credential saving/restoring on iOS via @capgo/capacitor-autofill-save-password
- Use SecAddSharedWebCredential to prompt 'Save Password?' on signup
- Use ASAuthorizationPasswordProvider to restore credentials on login
- Add webcredentials:ditto.pub Associated Domains entitlement
- Deploy apple-app-site-association for domain validation
- Keep existing Chromium PasswordCredential flow as web fallback
- Add saveNsec() helper: native credential manager on iOS/Android,
  file download + bonus PasswordCredential on web
- Single 'Continue' button triggers the appropriate save method per platform
2026-04-11 14:01:34 -05:00
Chad Curtis 6dc7fb7ade Replace localStorage with in-memory Map for daily missions
Daily mission session state is now a pubkey-scoped Map instead of
localStorage. Hydrates from kind 11125 content on mount/account switch.
Completed missions are already persisted by useAwardDailyXp; intermediate
progress resets on refresh (low-impact).
2026-04-11 10:28:33 -05:00
Alex Gleason 19906cf918 Merge branch 'fix/badge-image-aspect-ratio-hint' into 'main'
Show recommended 1:1 aspect ratio hint on badge image upload

Closes #212

See merge request soapbox-pub/ditto!178
2026-04-11 03:49:14 +00:00
Alex Gleason 874010c4fe Store nsec in browser password manager via Credential Management API
Progressive enhancement using PasswordCredential (Chromium-only).
On sign-up, the nsec is offered to the browser's password manager
alongside the existing file download. The prompt appears while the
user is looking at their key on the download step. On login, stored
credentials are retrieved for one-tap login on supported browsers.

Safari/Firefox/iOS silently skip — existing flows are unchanged.
2026-04-10 21:49:14 -05:00
Chad Curtis 98e0273bdb Fix sleeping blobbi not showing bedroom room 2026-04-10 17:21:51 -05:00
Mary Kate Fain e26407d740 Change default sidebar widgets to Trends, Bluesky, and Wikipedia 2026-04-10 17:18:08 -05:00
Chad Curtis b42f12ce77 Fridge as blur overlay, room UX refinements
- Fridge opens as full-page blur overlay with flex-wrap food grid
  and 2x2 stat icons per item (lucide icons, no boxes/borders)
- X dismiss button with strokeWidth 4, click negative space to close
- Overlay renders above navigation arrows (z-50)
- Sleeping Blobbi cannot leave bedroom (toast + gate on room change)
- Upgrade lucide-react, add arrow nudge keyframe animations
- Replace all emoji button/room icons with lucide equivalents
- Room indicator moved below Blobbi name in hero
- Touch swipe support on room shell
- Larger nav arrows (size-7/8, strokeWidth 4)
2026-04-10 17:16:17 -05:00
Chad Curtis 7a10e4a406 Room UX polish: swipe navigation, lucide icons, indicator below name
- Add touch swipe support to BlobbiRoomShell (50px threshold)
- Larger navigation arrows (size-7/8) with strokeWidth 4
- Move room indicator (icon + label + dots) below Blobbi name in hero
- Remove room header overlay from shell top
- Replace all emoji button icons with lucide: Refrigerator, ShowerHead,
  TowelRack, Candy, Shovel
- Replace room config emoji icons with lucide: Home, Refrigerator, Cross,
  Moon, Shirt
- Upgrade lucide-react 0.462.0 -> 1.8.0
- Add room-arrow-nudge keyframe animations to index.css
2026-04-10 17:03:52 -05:00
Chad Curtis eda18d8b93 Integrate room system into BlobbiPage
- Replace Care + Items tabs with room-based navigation
- Drawer shrinks to Quests + Blobbis only
- Room shell renders hero + nav arrows + dots + sleep overlay + poop
- Per-room bottom bars: HomeBar (toys/music/sing + photo + companion),
  KitchenBar (food carousel + shovel + fridge), CareBar (hygiene/medicine
  with context-sensitive side buttons), RestBar (sleep/wake), ClosetBar
- Remove CareTabContent, CareActionButton, ItemsTabContent, ItemTypeIndicator,
  StatIndicator and related constants (now in BlobbiRoomHero)
- Net reduction: -72 lines from BlobbiPage
2026-04-10 16:49:29 -05:00
Alex Gleason 126dce1dfc Surface account deletion as 'Delete Account' for App Store compliance
Add a 'Delete Account' pill button to the bottom of the Settings
page (Guideline 5.1.1v). Rename the Danger Zone heading in Advanced
Settings to match. Simplify the deletion dialog to a single screen:
plain-language warning, list of what gets deleted, type DELETE to
confirm, and Cancel/Delete buttons. Always broadcasts to all relays.

The underlying NIP-62 mechanism and components that render vanish
events to other users are unchanged.
2026-04-10 16:44:35 -05:00
Chad Curtis 70809a8c7c Add BlobbiRoomHero and BlobbiRoomShell components
- BlobbiRoomHero: focused props interface (15 props, not 80+), stats crown
  with arc layout, responsive sizing, sleep animation suppression,
  floating companion placeholder
- BlobbiRoomShell: children-based layout, room navigation arrows with
  destination labels, pagination dots, sleep overlay, ephemeral poop
  state management, hero/middle/children slot architecture
2026-04-10 16:24:17 -05:00
Chad Curtis 5b15300f23 Add room system foundation: config, layout, poop system, carousel, action button
- room-config.ts: room IDs, metadata, navigation helpers, default order (no hatchery)
- room-layout.ts: shared bottom bar class constant
- poop-system.ts: ephemeral poop generation/cleanup with XP reward
- ItemCarousel.tsx: single-focus item carousel with prev/next previews
- RoomActionButton.tsx: unified circular action button for room bottom bars
- Add 'room' tag to kind 11125 schema for cross-session persistence
- Barrel exports from rooms/index.ts
2026-04-10 16:17:04 -05:00
Chad Curtis 8585dd4833 Add item cooldown module and poop cleanup XP constant
- item-cooldown.ts: shared singleton with per-item cooldown tracking,
  subscriber system for React integration
- useItemCooldown.ts: useSyncExternalStore hook for reactive cooldown state
- Add POOP_CLEANUP_XP (5) to blobbi-xp.ts
- Export all new APIs from actions barrel
2026-04-10 16:05:02 -05:00
Chad Curtis 12bda76526 Rewrite progression and missions system: tags-first, minimal, DRY
- Add progression.ts: xpToLevel, levelToXp, xpProgress, getUnlocks (pure functions, ~110 lines)
- Add missions.ts: tally/event mission types, ProfileContent parse/serialize for kind 11125 content
- Add xp/level tags to kind 11125 BlobbonautProfile schema
- Rewrite daily-missions.ts: drop completed/claimed/currentCount, use target+count/events model
- Unify hatch/evolve into single 'evolution' key in missions content
- Replace coin rewards with XP rewards throughout
- Remove explicit claim flow (completion is implicit from progress >= target)
- Rewrite tracker, hooks, and UI consumers to new data shape
- Guard against old localStorage format during migration
2026-04-10 15:53:36 -05:00
Alex Gleason a542dd3b36 Sanitize all event-sourced URLs and prevent CSS injection
Nostr events are untrusted user input. Any URL extracted from event tags
or metadata must be validated before use in any context — not just
navigable hrefs, but also img src, CSS url(), and style attributes.

Changes:
- Theme events (kind 16767/36767): validate background and font URLs
  through sanitizeUrl() at parse time in themeEvent.ts
- Badge definitions (kind 30009): validate image and thumb URLs through
  sanitizeUrl() at parse time in parseBadgeDefinition.ts
- Font family names: sanitize with an allowlist regex before
  interpolation into CSS declarations in fontLoader.ts
- Profile fields: replace weak startsWith('http://') checks with
  sanitizeUrl() in ProfileRightSidebar and ProfilePage
- Community descriptions: validate extracted URLs through sanitizeUrl()
  in CommunityContent.tsx
- AGENTS.md: mandate unconditional URL sanitization for all
  event-sourced URLs regardless of rendering context, document CSS
  injection prevention guidelines
2026-04-10 15:48:38 -05:00
Mary Kate Fain 8f5b8264c9 Show recommended 1:1 aspect ratio hint on badge image upload 2026-04-10 15:13:18 -05:00
Alex Gleason 9d899cfe87 Sanitize all user-supplied URLs from Nostr events to prevent javascript: XSS
Add a shared sanitizeUrl() utility that validates URLs are well-formed
https: before they reach href attributes, window.open(), or openUrl().

Apply sanitization across all components that render untrusted URLs:
- CalendarEventDetailPage: r-tag links
- ZapstoreAppContent: url and repository tags
- ZapstoreReleaseContent: asset url tags passed to openUrl()
- AppHandlerContent: web handler tags and metadata.website
- NsiteCard: source tag
- GitRepoCard: web tag URLs passed to openUrl()
- FileMetadataContent: url tag used in download href
- ProfilePage: metadata.website (tighten weak startsWith check)
- useUserStatus: r-tag URL

Document sanitizeUrl usage in AGENTS.md for future agent use.
2026-04-10 14:22:42 -05:00