npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@slorenzot/memento-web-ui

v0.2.5

Published

Next.js Web UI for Memento persistent memory system

Readme

@slorenzot/memento-web-ui

NPM Version License: CC BY-NC-ND 4.0 TypeScript React

Modern React components and hooks for building Memento-powered web interfaces with Tailwind CSS styling and state management integration.

🚀 Installation

# Using Bun (recommended)
bun add @slorenzot/memento-web-ui

# Using npm
npm install @slorenzot/memento-web-ui

# Using yarn
yarn add @slorenzot/memento-web-ui

💡 Basic Usage

TypeScript

import { App } from '@slorenzot/memento-web-ui';
import { useMemory } from '@slorenzot/memento-web-ui';

function MyComponent() {
  const { observations, search, create } = useMemory();

  return (
    <div>
      <button onClick={() => search('architecture')}>
        Search
      </button>
      <App />
    </div>
  );
}

Shell/Bun

# Run web application
bunx @slorenzot/memento-web-ui

# Or with custom port
PORT=5174 bunx @slorenzot/memento-web-ui

🔧 Core API

Main Components

App

Main component of the Memento Web UI application.

Props:

{
  dbPath?: string;           // Custom database path
  apiBase?: string;          // Custom API base URL
  theme?: 'light' | 'dark'; // Application theme
}

Example:

import { App } from '@slorenzot/memento-web-ui';

function RootComponent() {
  return (
    <App
      dbPath="./data/memento.db"
      apiBase="http://localhost:3000/api"
      theme="light"
    />
  );
}

Custom Hooks

useMemory()

Main hook for accessing memory functionality.

Returns:

{
  observations: Observation[];
  sessions: Session[];
  search: (query: SearchParams) => Promise<SearchResult>;
  createObservation: (data: CreateObservationData) => Promise<Observation>;
  updateObservation: (id: number, data: UpdateObservationData) => Promise<Observation>;
  deleteObservation: (id: number) => Promise<void>;
  loading: boolean;
  error: Error | null;
}

Example:

function ObservationList() {
  const { observations, search, loading } = useMemory();

  const handleSearch = async () => {
    const results = await search({
      query: 'architecture',
      type: 'decision'
    });
    console.log('Results:', results);
  };

  return (
    <div>
      <button onClick={handleSearch}>
        {loading ? 'Searching...' : 'Search'}
      </button>
      <ul>
        {observations.map(obs => (
          <li key={obs.id}>{obs.title}</li>
        ))}
      </ul>
    </div>
  );
}

useSession()

Hook for managing active sessions.

Returns:

{
  activeSession: Session | null;
  startSession: (data: CreateSessionData) => Promise<Session>;
  endSession: (id: number) => Promise<Session>;
  loading: boolean;
}

Example:

function SessionManager() {
  const { activeSession, startSession, endSession, loading } = useSession();

  const handleStart = async () => {
    const session = await startSession({
      projectId: 'my-app',
      metadata: { agent: 'web-ui' }
    });
    console.log('Session started:', session.uuid);
  };

  const handleEnd = async () => {
    if (activeSession) {
      await endSession(activeSession.id);
    }
  };

  return (
    <div>
      {!activeSession ? (
        <button onClick={handleStart}>
          {loading ? 'Starting...' : 'Start Session'}
        </button>
      ) : (
        <button onClick={handleEnd}>
          {loading ? 'Ending...' : 'End Session'}
        </button>
      )}
    </div>
  );
}

useSearch()

Specialized hook for search functionality.

Parameters:

  • debounceMs (number): Debounce time in ms (default: 300)

Returns:

{
  query: string;
  results: SearchResult | null;
  searching: boolean;
  setQuery: (query: string) => void;
  search: (params: SearchParams) => Promise<SearchResult>;
}

Example:

function SearchComponent() {
  const { query, setQuery, results, searching } = useSearch({ debounceMs: 300 });

  return (
    <div>
      <input
        type="text"
        value={query}
        onChange={(e) => setQuery(e.target.value)}
        placeholder="Search memory..."
      />
      {searching && <p>Searching...</p>}
      {results && (
        <ul>
          {results.observations.map(obs => (
            <li key={obs.id}>
              <strong>{obs.title}</strong>
              <p>{obs.content.substring(0, 100)}...</p>
            </li>
          ))}
        </ul>
      )}
    </div>
  );
}

🎨 UI Components

ObservationCard

Card component for displaying an individual observation.

Props:

{
  observation: Observation;
  onEdit?: (id: number) => void;
  onDelete?: (id: number) => void;
  compact?: boolean;
}

Example:

function ObservationsList() {
  const { observations } = useMemory();

  return (
    <div className="grid gap-4">
      {observations.map(obs => (
        <ObservationCard
          key={obs.id}
          observation={obs}
          compact={true}
          onEdit={(id) => console.log('Edit:', id)}
          onDelete={(id) => console.log('Delete:', id)}
        />
      ))}
    </div>
  );
}

SearchBox

Search component with autocomplete.

Props:

{
  placeholder?: string;
  onSearch: (query: string) => void;
  filters?: SearchFilters;
  debounce?: number;
}

Example:

function Header() {
  const handleSearch = (query: string) => {
    console.log('Searching:', query);
  };

  return (
    <header className="bg-white shadow">
      <SearchBox
        placeholder="Search observations, decisions, bugs..."
        onSearch={handleSearch}
        filters={{
          types: ['decision', 'bug', 'discovery', 'note'],
          defaultType: 'all'
        }}
        debounce={300}
      />
    </header>
  );
}

StatsPanel

Memory system statistics panel.

Props:

{
  projectId?: string;
  refreshInterval?: number;  // ms for auto-refresh
}

Example:

function Dashboard() {
  return (
    <div className="p-6">
      <StatsPanel
        projectId="my-app"
        refreshInterval={60000} // Refresh every minute
      />
    </div>
  );
}

TimelineView

Observation timeline view.

Props:

{
  observations: Observation[];
  groupBy?: 'day' | 'week' | 'month';
  onObservationClick?: (id: number) => void;
}

Example:

function Timeline() {
  const { observations } = useMemory();

  return (
    <div className="max-w-4xl mx-auto">
      <TimelineView
        observations={observations}
        groupBy="day"
        onObservationClick={(id) => console.log('Click:', id)}
      />
    </div>
  );
}

⚡ Practical Examples

Example 1: Full Next.js Integration

// app/page.tsx
'use client';

import { App, useMemory } from '@slorenzot/memento-web-ui';

export default function Home() {
  const { observations, loading } = useMemory();

  return (
    <main className="min-h-screen bg-gray-100">
      <div className="container mx-auto p-6">
        <h1 className="text-3xl font-bold mb-6">
          Memento Memory System
        </h1>

        {loading ? (
          <p className="text-center">Loading observations...</p>
        ) : (
          <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
            {observations.map(obs => (
              <ObservationCard
                key={obs.id}
                observation={obs}
              />
            ))}
          </div>
        )}
      </div>
    </main>
  );
}

Example 2: Advanced Search Component

import { useSearch, SearchBox } from '@slorenzot/memento-web-ui';

function AdvancedSearch() {
  const { query, setQuery, results, searching } = useSearch();

  const filters = {
    types: ['decision', 'bug', 'discovery', 'note'],
    defaultType: 'decision'
  };

  return (
    <div className="max-w-2xl mx-auto p-6">
      <h2 className="text-2xl font-bold mb-4">
        Advanced Search
      </h2>

      <SearchBox
        value={query}
        onChange={setQuery}
        placeholder="Search memory..."
        filters={filters}
      />

      {searching && (
        <div className="mt-4 text-center">
          <div className="inline-block animate-spin rounded-full h-8 w-8 border-b-2 border-gray-900"></div>
          <p className="ml-2">Searching...</p>
        </div>
      )}

      {results && results.observations.length > 0 && (
        <div className="mt-6">
          <h3 className="text-xl font-semibold mb-3">
            Results ({results.total})
          </h3>
          <div className="space-y-4">
            {results.observations.map(obs => (
              <ObservationCard
                key={obs.id}
                observation={obs}
              />
            ))}
          </div>
        </div>
      )}
    </div>
  );
}

Example 3: Admin Panel

import { useMemory, useSession, StatsPanel } from '@slorenzot/memento-web-ui';

function AdminPanel() {
  const { loading, error } = useMemory();
  const { activeSession, startSession, endSession } = useSession();

  return (
    <div className="max-w-6xl mx-auto p-6">
      <div className="grid gap-6 lg:grid-cols-2">
        {/* Session Panel */}
        <div className="bg-white rounded-lg shadow p-6">
          <h2 className="text-xl font-bold mb-4">
            Session Management
          </h2>

          {activeSession ? (
            <div className="space-y-3">
              <p className="text-green-600 font-medium">
                ✓ Active session: {activeSession.uuid}
              </p>
              <button
                onClick={() => endSession(activeSession.id)}
                className="w-full bg-red-500 text-white px-4 py-2 rounded hover:bg-red-600"
              >
                End Session
              </button>
            </div>
          ) : (
            <button
              onClick={() => startSession({ projectId: 'admin' })}
              className="w-full bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600"
            >
              Start New Session
            </button>
          )}
        </div>

        {/* Statistics Panel */}
        <div className="bg-white rounded-lg shadow p-6">
          <h2 className="text-xl font-bold mb-4">
            System Statistics
          </h2>
          <StatsPanel refreshInterval={30000} />
        </div>
      </div>

      {/* Status Messages */}
      {loading && (
        <div className="mt-6 bg-blue-50 text-blue-700 p-4 rounded">
          Loading system data...
        </div>
      )}

      {error && (
        <div className="mt-6 bg-red-50 text-red-700 p-4 rounded">
          Error: {error.message}
        </div>
      )}
    </div>
  );
}

Example 4: Standalone Application

// index.tsx
import { App } from '@slorenzot/memento-web-ui';

// Custom configuration
const config = {
  dbPath: process.env.MEMENTO_DB_PATH || './data/memento.db',
  apiBase: process.env.MEMENTO_API_URL || 'http://localhost:3000/api',
  theme: (localStorage.getItem('theme') as 'light' | 'dark') || 'light'
};

// Render application
document.getElementById('root').render(
  <App {...config} />
);

🔧 Configuration

App Component Props

interface AppProps {
  dbPath?: string;           // Database path (default: './data/memento.db')
  apiBase?: string;          // API base URL (default: 'http://localhost:3000/api')
  theme?: 'light' | 'dark'; // App theme (default: 'light')
  locale?: string;           // UI language (default: 'es')
}

Environment Variables

  • MEMENTO_DB_PATH: Custom database path
  • MEMENTO_API_URL: Custom API base URL
  • MEMENTO_THEME: Default theme ('light'|'dark')
  • MEMENTO_LOCALE: Default language

Example:

# Configure environment
export MEMENTO_API_URL="http://api.example.com/api"
export MEMENTO_THEME="dark"

# Run application
bunx @slorenzot/memento-web-ui

⚠️ Restrictive License

This package is under CC BY-NC-ND 4.0 License:

  • Personal and educational use permitted
  • Share with attribution to the author
  • Commercial use NOT permitted
  • Modifications or forks NOT permitted

Author: Soulberto Lorenzo ([email protected])

🔄 Dependencies

Main Dependencies

  • react - UI framework
  • react-dom - Browser rendering
  • @tanstack/react-query - State and cache management
  • zustand - Global state management
  • clsx - CSS class utility
  • date-fns - Date formatting
  • lucide-react - SVG icons
  • zod - Schema validation

Peer Dependencies

  • react v18+
  • react-dom v18+

🛠️ Development

# Clone the project
git clone https://github.com/slorenzot/memento.git
cd memento/packages/web-ui

# Install dependencies
bun install

# Development
bun run dev

# Build
bun run build

# Preview build
bun run preview

📋 Changelog

[0.1.0] - 2024-04-04

  • Added: Initial version of React components
  • Added: Custom hooks (useMemory, useSession, useSearch)
  • Added: UI components (ObservationCard, SearchBox, StatsPanel)
  • Added: Tailwind CSS integration
  • Added: Light/dark theme support

👤 Author

Soulberto Lorenzo

📄 License

This package is licensed under Creative Commons Attribution-NonCommercial-NoDerivs 4.0 International.

View Full License


⚠️ Important: This package has a restrictive license. Please respect the CC BY-NC-ND 4.0 license terms.

📖 Spanish version (Versión en español)