Gigi 6265af74f2 docs: clarify why we extract image tag directly in BookmarkItem
Add comment explaining that we extract the image tag directly from
bookmark.tags since we don't have the full NostrEvent here. When we
do have full events (like in articleService), we use getArticleImage()
helper from applesauce-core as intended.
2025-10-05 08:24:05 +01:00

Boris

A minimal nostr client for bookmark management, built with applesauce.

Features

  • Nostr Authentication: Connect using your nostr account via browser extension
  • Bookmark Display: View your nostr bookmarks as per NIP-51
  • Content Classification: Automatically detect and classify URLs (articles, videos, YouTube, images)
  • Reader Mode: View article content inline with readable formatting
  • Collapsible Sidebar: Expand/collapse bookmark list for focused reading
  • Profile Integration: Display user profile images using applesauce ProfileModel
  • Relative Timestamps: Human-friendly time display (e.g., "2 hours ago")
  • Event Links: Quick access to view bookmarks on search.dergigi.com
  • Private Bookmarks: Support for Amethyst-style hidden/encrypted bookmarks
  • Highlights Panel: View and manage your NIP-84 highlights in a dedicated collapsible panel
  • Three-Pane Layout: Bookmarks sidebar, content viewer, and highlights panel working together
  • Minimal UI: Clean, modern interface focused on bookmark management

Getting Started

Prerequisites

  • Node.js 18+
  • npm, pnpm, or yarn

Installation

  1. Clone the repository:
git clone <your-repo-url>
cd boris
  1. Install dependencies:
npm install
# or
pnpm install
# or
yarn install
  1. Start the development server:
npm run dev
# or
pnpm dev
# or
yarn dev
  1. Open your browser and navigate to http://localhost:3000

Usage

  1. Connect: Click "Connect with Nostr" to authenticate using your nostr account
  2. View Bookmarks: Once connected, you'll see all your nostr bookmarks in the left sidebar
  3. View Highlights: Your NIP-84 highlights appear in the right panel
  4. Navigate: Click on bookmark URLs to view content in the center panel
  5. Collapse Panels: Use the collapse buttons to hide/show sidebars for focused viewing

Technical Details

  • Built with React and TypeScript
  • Uses applesauce-core for nostr functionality
  • Implements NIP-51 for bookmark management
  • Supports both individual bookmarks and bookmark lists

Development

Project Structure

src/
├── components/
│   ├── Login.tsx                        # Authentication component
│   ├── Bookmarks.tsx                    # Main bookmarks view with layout
│   ├── BookmarkList.tsx                 # Bookmark list sidebar
│   ├── BookmarkItem.tsx                 # Individual bookmark card
│   ├── SidebarHeader.tsx                # Header bar with collapse, profile, logout
│   ├── ContentPanel.tsx                 # Content viewer panel
│   ├── HighlightsPanel.tsx              # Highlights sidebar panel (NIP-84)
│   ├── HighlightItem.tsx                # Individual highlight display
│   ├── IconButton.tsx                   # Reusable icon button component
│   ├── ContentWithResolvedProfiles.tsx  # Profile mention resolver
│   ├── ResolvedMention.tsx              # Nostr mention component
│   └── kindIcon.ts                      # Kind-specific icon mapping
├── services/
│   ├── bookmarkService.ts               # Main bookmark fetching orchestration
│   ├── bookmarkProcessing.ts            # Decryption and processing pipeline
│   ├── bookmarkHelpers.ts               # Shared types, guards, and utilities
│   ├── bookmarkEvents.ts                # Event type handling and deduplication
│   ├── highlightService.ts              # Highlight fetching (NIP-84)
│   └── readerService.ts                 # Content extraction via reader API
├── types/
│   ├── bookmarks.ts                     # Bookmark type definitions
│   ├── highlights.ts                    # Highlight type definitions (NIP-84)
│   ├── nostr.d.ts                       # Nostr type augmentations
│   └── relative-time.d.ts               # relative-time package types
├── utils/
│   ├── bookmarkUtils.tsx                # Bookmark rendering utilities
│   └── helpers.ts                       # General helper functions
├── App.tsx                              # Main application component
├── main.tsx                             # Application entry point
└── index.css                            # Global styles

Private (hidden) bookmarks (Amethyst-style)

We support Amethyst-style private (hidden) bookmark lists alongside public ones (NIP51):

  • Detection and unlock

    • Use Helpers.hasHiddenTags(evt) and Helpers.isHiddenTagsLocked(evt) to detect hidden tags.
    • First try Helpers.unlockHiddenTags(evt, signer); if that fails, try with 'nip44'.
    • For events with encrypted content that arent recognized as supporting hidden tags (e.g. kind 30001), manually decrypt:
      • Prefer signer.nip44.decrypt(evt.pubkey, evt.content), fallback to signer.nip04.decrypt(evt.pubkey, evt.content).
  • Parsing and rendering

    • Decrypted content is JSON string[][] (tags). Convert with Helpers.parseBookmarkTags(hiddenTags).
    • Map to IndividualBookmark[] via our processApplesauceBookmarks(..., isPrivate=true) and append to the private list so they render immediately alongside public items.
  • Caching for downstream helpers

    • Cache manual results on the event with BookmarkHiddenSymbol and also store the decrypted blob under EncryptedContentSymbol to aid debugging and hydration.
  • Structure

    • src/services/bookmarkService.ts: orchestrates fetching, hydration, and assembling the final bookmark payload.
    • src/services/bookmarkProcessing.ts: decryption/collection pipeline (unlock, manual decrypt, parse, merge).
    • src/services/bookmarkHelpers.ts: shared types, guards, mapping, hydration, and symbols.
    • src/services/bookmarkEvents.ts: event type and deduplication for NIP51 lists/sets.
  • Notes

    • We avoid any via narrow type guards for nip44/nip04 decrypt functions.
    • Files are kept small and DRY per project rules.
    • Built on applesauce helpers (Helpers.getPublicBookmarks, Helpers.getHiddenBookmarks, etc.). See applesauce docs: https://hzrd149.github.io/applesauce/typedoc/modules.html

Building for Production

npm run build
# or
pnpm build
# or
yarn build

TODO

High Priority

  • Mobile Responsive Design: Optimize sidebar and content panel for mobile devices
  • Keyboard Shortcuts: Add keyboard navigation (collapse sidebar, navigate bookmarks)
  • Search & Filter: Add ability to search bookmarks by title, URL, or content
  • Error Handling: Improve error states and retry logic for failed fetches
  • Loading States: Better skeleton screens and loading indicators

Medium Priority

  • Bookmark Creation: Add ability to create new bookmarks
  • Bookmark Editing: Edit existing bookmark metadata and tags
  • Bookmark Deletion: Remove bookmarks from lists
  • Sorting Options: Sort by date, title, kind, or custom order
  • Bulk Actions: Select and perform actions on multiple bookmarks
  • Video Embeds: Inline YouTube and video playback for video bookmarks

Nice to Have

  • Dark/Light Mode Toggle: User preference for color scheme
  • Export Functionality: Export bookmarks as JSON, CSV, or HTML
  • Import Bookmarks: Import from browser bookmarks or other formats
  • Tags & Categories: Better organization with custom tags
  • Bookmark Collections: Create and manage custom bookmark collections
  • Offline Support: Cache bookmarks for offline viewing
  • Share Bookmarks: Generate shareable links to bookmark lists
  • Performance Optimization: Virtual scrolling for large bookmark lists
  • Browser Extension: Quick bookmark saving from any page

Contributing

Contributions are welcome! Please feel free to submit a Pull Request. Make sure to:

  • Follow the existing code style
  • Keep files under 210 lines
  • Use conventional commits
  • Run linter and type checks before submitting

License

MIT

Description
No description provided
Readme MIT 29 MiB
Languages
TypeScript 89.6%
CSS 9.4%
Shell 0.6%
JavaScript 0.2%
HTML 0.2%