Gigi 0b058440bc refactor(components): improve type safety and simplify IconButton
- Add proper type guards in ContentWithResolvedProfiles to avoid type assertions
- Remove href/link functionality from IconButton component for simplification
- Replace 'as any' with proper type narrowing using type predicates
2025-10-03 01:43:13 +02:00

Markr

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

Features

  • Nostr Authentication: Connect using your nostr account
  • Bookmark Display: View your nostr bookmarks as per NIP-51
  • Minimal UI: Clean, simple 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 markr
  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
  3. Navigate: Click on bookmark URLs to open them in a new tab

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  # Bookmark display component
├── 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

Contributing

This is a minimal MVP. Future enhancements could include:

  • Bookmark creation and editing
  • Bookmark organization and tagging
  • Search functionality
  • Export capabilities
  • Mobile-responsive design improvements

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%