Commit Graph

4447 Commits

Author SHA1 Message Date
filemon d92790f0af refactor: replace hardcoded colors with app theme tokens in BlobbiPage
- Remove purple/pink gradients and hardcoded colors
- Use theme tokens: primary, muted, accent, border, card, background
- Keep semantic stat colors (orange/yellow/green/blue/violet) for meaning
- Page chrome now adapts to light/dark theme automatically
2026-03-06 23:22:46 -03:00
filemon e18d0592d6 refactor: redesign BlobbiPage with dashboard-style layout
Visual changes:
- Add frosted glass dashboard container with purple top border
- Add decorative purple/pink gradient overlay
- Center hero layout with prominent Blobbi visual
- Add floating quick action buttons (switch, rest, visibility)
- Replace stat bars with circular stat indicators
- Improve visual hierarchy with hero section focus
- Add glow effect behind main Blobbi visual
- Use purple/pink color theme consistently

Extracted components:
- DashboardShell: Frosted glass container wrapper
- BlobbiDashboard: Main dashboard view with hero section
- QuickActionButton: Floating circular action buttons
- StatIndicator: Circular progress stat display
- DashboardLoadingState: Skeleton loading for dashboard

Preserved unchanged:
- All data hooks (useBlobbonautProfile, useBlobbisCollection, etc.)
- Selection flow with localStorage priority
- Migration flow with ensureCanonicalBlobbiBeforeAction
- Create egg / rest / visibility handlers
- EggGraphic adapter integration
- Selector page and card components (restyled)
2026-03-06 23:16:35 -03:00
filemon 11802fc38a refactor: tighten all adapter types to exact Egg module unions
- Add EggPattern, EggSpecialMark, EggThemeVariant type aliases
- All derived via NonNullable<EggVisualBlobbi[field]>
- Update PATTERN_MAP to Record<BlobbiPattern, EggPattern>
- Update SPECIAL_MARK_MAP to Record<BlobbiSpecialMark, EggSpecialMark>
- Update all fallback constants with exact Egg types
- Update themeVariant parameter to EggThemeVariant
- No runtime changes, type-only refinement
2026-03-06 23:08:04 -03:00
filemon b2ae35d597 refactor: tighten adapter typing for EggVisualBlobbi
- Derive EggLifeStage type from EggVisualBlobbi using NonNullable
- Type LIFE_STAGE_MAP with exact EggLifeStage return type
- Add DEFAULT_THEME_VARIANT constant for consistency
- Remove unnecessary 'as const' assertions on mapping objects
- Improve JSDoc for toEggGraphicVisualBlobbi return type
- Rename areEggGraphicVisualsEqual parameter types to EggVisualBlobbi
2026-03-06 23:04:25 -03:00
filemon ec37a8befe refactor: cleanup Blobbi egg integration
- Remove fake try/catch render wrapper (not a real error boundary)
- Use real EggVisualBlobbi type from @/blobbi/egg module
- Pass full allTags to EggGraphic instead of filtering
- Adjust size mapping: sm (size-14/small), md (size-24/medium), lg (size-40/large)
- Gate debug logs behind import.meta.env.DEV check
- Simplify adapter by removing duplicated type definitions
- Reduce adapter from 290 lines to ~150 lines
- Keep architecture intact: domain → adapter → BlobbiEggVisual → EggGraphic
2026-03-06 21:24:58 -03:00
filemon 804dd550a2 feat(blobbi): add egg visual module 2026-03-06 21:10:29 -03:00
filemon 736d76f457 feat: integrate EggGraphic visual module into Blobbi UI
- Create BlobbiEggVisual reusable component in src/blobbi/ui/
- Replace placeholder Egg icons with real EggGraphic rendering
- Update adapter tags format to string[][] for EggGraphic compatibility
- Main display: large animated egg visual with seed-derived colors
- Selector cards: small egg visual showing unique traits per Blobbi
- Switch dialog: uses same selector cards with real visuals
- Add memoization for adapter output to avoid re-renders
- Include fallback safety with simple placeholder on render errors
- Preserve all existing fetch/migration/selection behavior
2026-03-06 21:06:01 -03:00
Chad Curtis c2651dd36e Add lists intro image and remove rename input focus ring 2026-03-06 17:47:45 -06:00
Chad Curtis bbea03f517 Move Lists page into Settings as a sub-page
- /lists now redirects to /settings/lists
- UserListsPage redesigned to match the settings sub-page pattern (back arrow to /settings, collapsible section, IntroImage)
- Lists added as an entry in the SettingsPage menu
- Removed Lists from sidebar nav (it was requiresAuth and felt out of place)
2026-03-06 17:33:06 -06:00
Chad Curtis c7f3211b7d Add user lists, follow packs, home feed tabs, and list-based feed filtering
- Add NIP-51 Follow Sets management (useUserLists, UserListsPage at /lists)
- Add Follow Packs support (useFollowPacks) for curated people lists
- Add FeedEditModal for creating/editing home feed tabs with author scope,
  kind picker, and list/pack author population
- Add AddToListDialog accessible from the note more-menu
- Unify home feed tab resolution with profile tabs via useResolveTabFilter,
  supporting $follows variable expansion
- Wire People filter in Search and FeedEditModal with list/pack picker
- Extract shared ScopeToggle, ListPackPicker, parseSelectedKinds primitives
  into SavedFeedFiltersEditor to eliminate duplication across three call sites
- Cache savedFeeds in AppConfig for instant tab render on load
- Strip orphaned profile-destination entries from encrypted settings on read
2026-03-06 17:23:47 -06:00
filemon 84734d7304 feat: add EggGraphic adapter and improve visual trait derivation
- Create blobbi-egg-adapter.ts with toEggGraphicVisualBlobbi() function
- Add explicit mapping tables for pattern, specialMark, size, lifeStage
- Add comprehensive VISUAL TRAIT POLICY documentation
- Export LEGACY_VISUAL_TAG_NAMES constant for migration preservation
- Improve deriveNameFromLegacyD() to handle multi-word names (mr-cool → Mr Cool)
- Update buildMigrationTags() to explicitly preserve legacy visual tags
- Add capitalizeWords() helper for human-friendly name formatting
- Add areEggGraphicVisualsEqual() utility for memoization support
2026-03-06 20:09:46 -03:00
Alex Gleason f26542dc12 Remove unused imports (Users, Info) from SavedFeedFiltersEditor 2026-03-06 12:05:32 -06:00
Alex Gleason d2bfb3566f Merge branch 'main' of gitlab.com:soapbox-pub/ditto 2026-03-06 12:00:46 -06:00
Alex Gleason f73f5da752 Append file extension to Blossom URLs when missing
Blossom servers return content-addressed URLs (/<sha256>) that may omit
the file extension. After upload, extract the extension from the source
filename and append it to the URL if not already present. This helps
clients infer the media type from the URL alone.
2026-03-06 11:57:27 -06:00
filemon e19b99a2bc refactor: align visual traits with EggGraphic contract
Visual Traits Interface:
- baseColor: hex value (e.g., '#F59E0B')
- secondaryColor: hex value for accent
- eyeColor: hex value for eyes
- pattern: 'solid' | 'spotted' | 'striped' | 'gradient'
- specialMark: 'none' | 'star' | 'heart' | 'sparkle' | 'blush'
- size: 'small' | 'medium' | 'large'

Color Palettes (10 colors each):
- BLOBBI_BASE_COLORS: Amber, Teal, Sky Blue, Pink, Purple, Coral, Emerald, Yellow, Indigo, Orange
- BLOBBI_SECONDARY_COLORS: Light variants for accents
- BLOBBI_EYE_COLORS: 8 expressive options

Seed Derivation:
- deriveIndexFromSeed with documented offset layout
- Each trait derived from different seed segment
- Validation/normalization for legacy tags
- Falls back to DEFAULT_VISUAL_TRAITS when seed missing

Debug Logs:
- Concise format: d, name, isLegacy, hasSeed, traits summary
- Removed verbose visualTraits object dump

parseBlobbiEvent remains single source of truth for:
- name resolution (tag > legacy d > fallback)
- seed
- visualTraits
- isLegacy flag

Migration flow unchanged - ready for EggGraphic integration.
2026-03-06 14:45:58 -03:00
filemon 577334cbaa feat: add visual trait derivation from seed and centralized migration
PART 1 - Visual Traits from Seed:
- Add BlobbiVisualTraits interface with baseColor, pattern, specialMark, size
- Add deriveNumberFromSeed helper for deterministic derivation
- Add deriveBaseColorFromSeed, derivePatternFromSeed, deriveSpecialMarkFromSeed, deriveSizeFromSeed
- Add deriveVisualTraits to combine tag values with seed-derived fallbacks
- Visual trait priority: explicit tags > derived from seed > default values

PART 2 - Legacy Event Detection:
- Add isLegacyBlobbiEvent helper function
- Legacy criteria: non-canonical d-tag, missing seed, missing name tag, visual traits without seed
- Add companionNeedsMigration convenience wrapper
- Add isLegacy and visualTraits fields to BlobbiCompanion interface

PART 3-7 - Centralized Migration:
- Create useBlobbiMigration hook with ensureCanonicalBlobbiBeforeAction
- Migration auto-updates: localStorage selection, React Query caches, profile references
- Refactor BlobbiPage action handlers to use centralized helper
- Migration continues original action after upgrading legacy pet

No changes to event kinds, collection fetching, or selection system.
2026-03-06 14:27:18 -03:00
filemon 35b615dc9f fix: support legacy Blobbi names derived from d-tag
- Add deriveNameFromLegacyD() helper to extract name from legacy d-tags
- Update parseBlobbiEvent to use name resolution priority:
  1. Use 'name' tag if present
  2. Derive from legacy d-tag format (blobbi-{name} → Name)
  3. Fall back to 'Unnamed Blobbi'
- Add debug logs in parser showing d, name, nameTag, stage, state
- Add '[Blobbi UI]' debug log when selected companion changes
- Legacy pets like 'blobbi-puck' now display as 'Puck'
2026-03-06 14:13:40 -03:00
Mary Kate 5fe9b1f236 Merge branch 'update-themes' into 'main'
feat: add new theme presets (Silent Torii, Quiet Horizon, Where Paths Meet, Once)

See merge request soapbox-pub/ditto!43
2026-03-06 17:10:58 +00:00
filemon ef8e9a3ccf fix: load ALL Blobbis and implement proper UI selection flow
- Update useBlobbisCollection to fetch ALL pets without limit:1
- Add chunking support (20 items per chunk) for relay compatibility
- Add debug logs for dList and 31124 query filter
- Implement localStorage-based UI selection (user-scoped key)
- Selection priority: localStorage > first in profile.has > show selector
- Add BlobbiSelectorPage for when no valid selection exists
- Add BlobbiSelectorCard component for pet selection UI
- Add 'Switch Blobbi' button in header for users with multiple pets
- Separate concerns: currentCompanion (global) vs selectedBlobbi (page UI)
2026-03-06 13:45:51 -03:00
filemon 27d5544f8b Fetch all Blobbi pets (Kind 31124) by multiple d-tags
- Add useBlobbisCollection hook to query all d-tags from profile.has[] and currentCompanion
- Update BlobbiContent to use collection hook instead of single companion hook
- Keep only newest event per d-tag for deduplication
- Add debug logging to verify multi-d-tag REQs in DevTools
- UI still renders only the selected companion (currentCompanion or first in has[])
2026-03-06 13:25:49 -03:00
Alex Gleason cb64a7fb6b Emit canonical NIP-01 filters for core profile tabs in kind 16769 events
Core tabs (Posts, Media, Likes, Wall, etc.) were published with empty
filter stubs, making the event uninterpretable by other clients. Now
each core tab carries a proper NIP-01 filter with the actual pubkey
interpolated (e.g. Wall: {kinds:[1111], #A:["0:<pubkey>:"]}).
2026-03-06 01:41:26 -06:00
Alex Gleason 9ea8788b92 Replace kind 16769 SavedFeedFilters with standard NIP-01 filters and variable tags
The proprietary SavedFeedFilters JSON schema in kind 16769 tab events was
non-interoperable. Replace it with standard NIP-01 filter objects that any
Nostr client can execute, plus a variable system (var tags) for dynamic
values like follow lists.

- Tab filters are now standard NIP-01 filters with optional NIP-50 search
- Variables ($name) in filters are resolved via var tags that extract tag
  values from referenced events (e.g. follow list pubkeys from kind 3)
- $me is the only runtime variable (profile owner's pubkey)
- Variables in arrays are expanded in-place (spliced)
- Add useResolveTabFilter hook for variable resolution with caching
- Update all consumers: ProfilePage, Feed, SearchPage, ContentSettings
- Remove SavedFeedFilters type entirely in favor of TabFilter
2026-03-06 01:28:09 -06:00
Alex Gleason d95e5ef380 Fix profile to default-select the leftmost custom tab on load 2026-03-06 00:52:40 -06:00
Alex Gleason 7222ae663f Add sync theme toggle to /themes page 2026-03-06 00:30:16 -06:00
Alex Gleason ded544a521 Update search query placeholder text 2026-03-06 00:22:34 -06:00
Alex Gleason 2850405664 Redesign profile tab editor: multi-kind selection, author scope toggle, remove unused filters
Simplify the ProfileTabEditModal to only show Search Query, Author Scope
(Me/Contacts/Global), and a multi-select Kind picker with checkboxes and
chips. Remove Media, Platform, and Language fields that were not useful
for profile tabs.

Extract parseKindFilter utility to handle comma-separated kind values
across all consumers (Feed, ProfilePage, SearchPage).
2026-03-06 00:18:16 -06:00
Alex Gleason 86eae15118 Merge branch 'main' of gitlab.com:soapbox-pub/ditto 2026-03-05 23:29:45 -06:00
Alex Gleason 8901bdfde3 Show subdivision name in 'Commenting on' context 2026-03-05 23:29:25 -06:00
Alex Gleason 41eb5a4ef0 Add subdivision support with Wikipedia data, proper names, and thumbnail flags 2026-03-05 23:27:47 -06:00
Alex Gleason 112d1fa1d4 Remove thumbnail banner from country pages 2026-03-05 23:21:29 -06:00
Alex Gleason 4245249524 Add Read more/Show less truncation to Wikipedia extract on country pages 2026-03-05 23:20:25 -06:00
Chad Curtis 3fb9751dc1 useMemo quote selection in NoTabsEmptyState 2026-03-05 23:13:28 -06:00
Chad Curtis 0d2cd42059 add fancy book-style empty state with Harlan Ellison quotes when profile has no tabs 2026-03-05 23:12:50 -06:00
Alex Gleason 8d53ac9379 Add Wikipedia summary and thumbnail to country pages 2026-03-05 23:11:53 -06:00
Chad Curtis 217cbe6ec4 skip content loading and rendering when profile has no tabs 2026-03-05 23:04:27 -06:00
Chad Curtis 766ba1a698 Profile tabs UX polish: fix edit mode, stroke weight, deletion respect
- enterTabEditMode now starts from exactly viewTabs (no auto-restore of
  deleted default tabs)
- + and Check icons in the tab edit dropdown and save button use strokeWidth=4
- × remove button uses text-xl × character (no SVG blurriness)
- Fix Posts & replies active indicator by using CORE_TAB_IDS lookup for
  both onSelect and active prop
- Visitor ... menu shows missing default tabs when profile has kind 16769
  event with some defaults removed
2026-03-05 22:57:55 -06:00
Chad Curtis ca706ada4c Fix profile tabs: kind 16769 now controls full tab list including core tabs
- viewTabs derives from kind 16769 event: null=show all 5 defaults, []=show
  nothing, [...]= show exactly those in that order
- handleSaveTabEdit publishes all tabs (core + custom) as the full ordered
  list — core tabs stored as stubs with default filters
- enterTabEditMode starts from viewTabs + any missing core tabs at the end
- Deleting all tabs now correctly shows an empty tab bar (not the defaults)
- Encrypted settings fallback only triggers when data===null (no event yet)
- useProfileData no longer seeds profile-tabs cache (removes stale-seed race)
- usePublishProfileTabs invalidates both profile-tabs and profile-supplementary
- useProfileTabs returns null (no event) vs [] (event with no tabs) so
  deletions are respected across page reloads
- Visitor '...' dropdown shows missing default tabs when a profile has a
  published kind 16769 event that omits some defaults
2026-03-05 22:54:32 -06:00
Chad Curtis 134236636a Profile tabs: kind 16769 Nostr event, manager modal, + dropdown, wider save button
Kind 16769 (profile tabs):
- src/lib/profileTabsEvent.ts: encode/decode kind 16769 — tab tags are
  ['tab', label, filtersJSON], no UUIDs, label is the stable key
- useProfileTabs: queries kind 16769 with authors filter (secure)
- usePublishProfileTabs: publishes kind 16769, invalidates cache
- useProfileSupplementary: now fetches kind 16769 alongside kind 3 and
  10001 in a single relay round-trip, seeds profile-tabs query cache
- NIP.md: documents kind 16769 schema

ProfileTabEditModal:
- Save button is now full-width with a Check icon (Cancel below)
- hideFrom + hideSort props on SavedFeedFiltersEditor used here — author
  is the profile owner implicitly, sort is irrelevant for profile tabs

ProfileTabsManagerModal:
- dnd-kit drag-to-reorder sortable list of custom tabs
- Per-row edit (pencil) and remove (trash) buttons
- Add custom tab button at the bottom
- Single Save button publishes the full updated list

ProfilePage tab bar (own profile):
- Custom tabs read from kind 16769 via useProfileTabs
- + button opens a DropdownMenu: core tabs listed as already-added
  (dimmed with checkmarks), Add custom tab at the bottom
- SlidersHorizontal pencil at end of row opens the manager modal
  (only shown when custom tabs exist)
- Per-tab onEdit pencil removed — manager handles all editing
- ProfileTab type aliased to CoreProfileTab to avoid collision with
  CustomProfileTab from profileTabsEvent
2026-03-05 22:53:44 -06:00
Chad Curtis bc1d64d26f Extract SavedFeedFiltersEditor; full filter editing in settings
- New src/components/SavedFeedFiltersEditor.tsx exports:
  SavedFeedFiltersEditor (controlled), KindPicker, AuthorChip,
  AuthorFilterDropdown, buildKindOptions, useScrollCarets
- SavedFeedFiltersEditor renders all filter controls: query input, author
  scope toggle (Anyone/Follows/People with chips), sort toggle
  (Recent/Hot/Trending), media/platform/language/kind selects
- ContentSettings SavedFeedRow now uses SavedFeedFiltersEditor in its
  edit panel — clicking pencil expands the full filter set, not just a
  query text box; Save writes label + all filters atomically
- SearchPage filter popover delegates to SavedFeedFiltersEditor
  (showQuery=false); all duplicate local implementations removed
- useSavedFeeds gets updateSavedFeed(id, {label?, filters?}) for atomic
  label+filters updates
2026-03-05 22:52:24 -06:00
Chad Curtis 8aac7c2a27 Saved feed rows: inline edit panel for label + query
- Add updateSavedFeed to useSavedFeeds (updates label and/or filters atomically)
- SavedFeedRow now has a collapsible edit panel below the summary row:
  two inputs side-by-side (Tab name / Search query), Cancel + Save buttons
- Pencil icon toggles the panel open/closed; X in the panel header closes it
- Enter in the query field saves, Escape cancels
- The summary row (label + filter chips) always stays visible above the panel
- Remove button stays in the header row, hidden until hover
2026-03-05 22:52:24 -06:00
Chad Curtis 04c069dab1 Add saved feeds management to Settings > Feed
SavedFeedsSection appears inside the Feed Tabs collapsible in ContentSettings
after the Community block (only when feeds exist). Shows two groups:
'Home feed tabs' and 'Profile tabs', each listing saved feeds with:
- Feed label + compact filter summary (query, author scope, sort icon)
- Inline rename: click pencil, edit input, Enter/check to save, Escape to cancel
- Remove button (destructive hover, disabled while saving)
- Actions fade in on row hover to keep the list clean
- Icons: Home/User for group headers, Globe/Flame/TrendingUp/Clock for filter hints

Also removes pre-existing unused arrayMove import in ProfileSettings.tsx.
2026-03-05 22:52:24 -06:00
Alex Gleason a5a81e55ed Improve profile fields: move website/lightning to header, add nostr embeds and email links 2026-03-05 21:52:26 -06:00
Alex Gleason 1f10acc3d9 Add bottom padding to profile settings form 2026-03-05 21:33:26 -06:00
Alex Gleason 40ad1f0170 Fix unused arrayMove import in ProfileSettings 2026-03-05 21:31:41 -06:00
Alex Gleason 92c6b5fa63 Merge branch 'main' of gitlab.com:soapbox-pub/ditto 2026-03-05 21:24:46 -06:00
Alex Gleason 4b8831497a Add mpga and other audio extensions to MiniAudioPlayer detection 2026-03-05 21:24:07 -06:00
Alex Gleason 899865b508 Render mini audio player for profile fields with audio URLs
Add MiniAudioPlayer component with play/pause, seekable progress bar,
and time display. Profile fields whose value is a URL ending in an audio
extension (.mp3, .ogg, .wav, .flac, .aac, .m4a, .opus, .weba) now render
the player instead of a plain link, in both mobile and desktop views.
2026-03-05 21:16:57 -06:00
Chad Curtis 25c6a41c96 KindPicker: restore scroll carets using SidebarMoreMenu pattern
Replace ScrollArea with the exact useScrollCarets + ScrollCaret pattern
from SidebarMoreMenu — ResizeObserver tracks scroll state, ChevronUp/Down
carets appear above/below the list when there is more content, and
hovering them continuously scrolls at 8px per 16ms frame.
2026-03-05 21:14:30 -06:00
Chad Curtis fcac7348ac Replace Kind select with searchable icon picker popover
KindPicker component replaces the plain Select for the Kind filter:
- Mini search input at the top filters by label, description, or kind number
- Each row shows the Lucide icon from CONTENT_KIND_ICONS (same icons as
  sidebar/more menu) at size-3.5 with the kind label
- Active item highlighted with primary color + checkmark
- Trigger button mirrors Select trigger style with icon + chevron
- Custom… option always available at the bottom
- Scrollable list capped at 216px so it doesn't overflow the popover
2026-03-05 21:14:30 -06:00
Chad Curtis ba62b42ce0 Search filters: multi-person authors, sort toggle, icon polish
- Author scope 'person' renamed to 'people' supporting multiple authors;
  stored as repeated ?author= URL params; chips show avatar/name with
  individual remove buttons resolved via useAuthor
- Add sort filter: Recent / Hot / Trending — maps to NIP-50 sort:hot /
  sort:trending search terms, persisted to URL and SavedFeedFilters
- Both segment toggles (From / Sort) now show size-3.5 Lucide icons:
  Globe / Users / UserSearch for author scope; Clock / Flame / TrendingUp
  for sort
- SavedFeedFilters: authorPubkey→authorPubkeys[], add sort field; schema
  and all consumers (Feed.tsx, ProfilePage.tsx) updated accordingly
- useStreamPosts: resolves array of npub/hex pubkeys, applies sort: NIP-50
  term to initial search query
2026-03-05 21:14:30 -06:00