mirror of
https://github.com/dergigi/boris.git
synced 2025-12-26 19:14:52 +01:00
6265af74f2f4bee4ba61e874dadf8ce6be7a18f3
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.
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
- Clone the repository:
git clone <your-repo-url>
cd boris
- Install dependencies:
npm install
# or
pnpm install
# or
yarn install
- Start the development server:
npm run dev
# or
pnpm dev
# or
yarn dev
- Open your browser and navigate to
http://localhost:3000
Usage
- Connect: Click "Connect with Nostr" to authenticate using your nostr account
- View Bookmarks: Once connected, you'll see all your nostr bookmarks in the left sidebar
- View Highlights: Your NIP-84 highlights appear in the right panel
- Navigate: Click on bookmark URLs to view content in the center panel
- 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 (NIP‑51):
-
Detection and unlock
- Use
Helpers.hasHiddenTags(evt)andHelpers.isHiddenTagsLocked(evt)to detect hidden tags. - First try
Helpers.unlockHiddenTags(evt, signer); if that fails, try with'nip44'. - For events with encrypted
contentthat aren’t recognized as supporting hidden tags (e.g. kind 30001), manually decrypt:- Prefer
signer.nip44.decrypt(evt.pubkey, evt.content), fallback tosigner.nip04.decrypt(evt.pubkey, evt.content).
- Prefer
- Use
-
Parsing and rendering
- Decrypted
contentis JSONstring[][](tags). Convert withHelpers.parseBookmarkTags(hiddenTags). - Map to
IndividualBookmark[]via ourprocessApplesauceBookmarks(..., isPrivate=true)and append to the private list so they render immediately alongside public items.
- Decrypted
-
Caching for downstream helpers
- Cache manual results on the event with
BookmarkHiddenSymboland also store the decrypted blob underEncryptedContentSymbolto aid debugging and hydration.
- Cache manual results on the event with
-
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 de‑duplication for NIP‑51 lists/sets.
-
Notes
- We avoid
anyvia narrow type guards fornip44/nip04decrypt 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
- We avoid
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
Languages
TypeScript
89.6%
CSS
9.4%
Shell
0.6%
JavaScript
0.2%
HTML
0.2%