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

@tnkrai/tnkr-editor

v0.10.4

Published

A customizable wrapper around Tiptap Editor with TypeScript support and modular architecture

Readme

@tnkrai/tnkr-editor

A modern, feature-rich editor package built on TipTap with advanced 3D model viewing capabilities, assembly instructions, and comprehensive documentation tools.

npm version CI/CD License: MIT

Features

  • 🎯 TipTap-based Editor: Modern rich text editor built on TipTap v2 with extensive customization
  • 🎨 Pre-styled Components: Beautiful UI components with Tailwind CSS
  • 🔧 TypeScript Support: Full type safety and excellent developer experience
  • 📦 Modular Architecture: Import only what you need
  • 🎭 3D Model Viewer: Advanced assembly and part visualization with annotation support
  • Performance Optimized: Tree-shakeable and optimized for production
  • 🎪 Assembly Mode: Step-by-step assembly instructions with 3D visualization
  • 📝 Preview System: Comprehensive documentation preview with sidebar navigation
  • 🎯 Extensible: Support for custom extensions and plugins

Installation

npm install @tnkrai/tnkr-editor
# or
yarn add @tnkrai/tnkr-editor
# or
pnpm add @tnkrai/tnkr-editor

Tailwind CSS Requirement

This package requires Tailwind CSS to be installed and configured in your application. The components use Tailwind utility classes for styling.

# Install Tailwind CSS (if not already installed)
npm install -D tailwindcss

Add the package to your tailwind.config.js:

module.exports = {
  content: [
    // ... your content paths
    "./node_modules/@tnkrai/tnkr-editor/dist/**/*.{js,ts,jsx,tsx}",
  ],
  // ... rest of config
}

See TAILWIND_SETUP.md for detailed configuration instructions.

Core Components

1. TnkrEditor

The main rich text editor component based on TipTap v2 with extensive features.

import { TnkrEditor } from '@tnkrai/tnkr-editor';
import '@tnkrai/tnkr-editor/styles';

function App() {
  const [content, setContent] = useState(null);

  const handleUploadImage = async (file, onProgress, abortSignal) => {
    // Your upload implementation
    // Must return a URL string or { src: string; signedUrl?: string }
    const formData = new FormData();
    formData.append('file', file);

    const response = await fetch('/api/upload', {
      method: 'POST',
      body: formData,
      signal: abortSignal
    });

    const data = await response.json();
    return data.url; // or return { src: data.url, signedUrl: data.signedUrl }
  };

  return (
    <TnkrEditor
      content={content}
      onChange={setContent}
      placeholder="Start writing..."
      readOnly={false}
      className="min-h-[400px]"
      minHeight={200}
      pageTitle="My Document"
      pageId="doc-1"
      onUploadMedia={handleUploadImage}

      // Extension configuration
      extensions={[]}  // Custom extensions
      extensionOptions={{}}  // Extension-specific options
      disabledExtensions={[]}  // Extensions to disable
      customExtensionSet={null}  // Use custom extension set

      // Feature flags
      enableAI={false}
      enableCollaboration={false}

      // AI configuration (if enabled)
      aiToken={null}
      aiAppId="tnkr-editor-ai"

      // Collaboration (if enabled)
      collaborationProvider={null}
      collaborationYdoc={null}
      collaborationUser={{ id: "1", name: "User", color: "#40E0D0" }}

      // UI configuration
      showUndoRedo={true}
      showDragHandle={true}

      // WhatsNext configuration
      whatsNextPages={[
        { id: 'page-1', name: 'Next Page' },
        { id: 'page-2', name: 'Another Page' }
      ]}
      whatsNextGoToPage={(pageId) => console.log('Go to:', pageId)}
      showWhatsNext={true}
    />
  );
}

Key Features:

  • Rich Text Editing: Full formatting capabilities with markdown support
  • Image Upload: Built-in image upload with progress tracking
  • Extensions: Modular extension system for custom functionality
  • AI Integration: Optional AI features for content generation
  • Collaboration: Real-time collaboration support
  • WhatsNext Navigation: Built-in page navigation component

2. AssemblyViewMode

Interactive 3D assembly instructions viewer with step-by-step guidance.

import { AssemblyViewMode } from '@tnkrai/tnkr-editor';

const phases = [
  {
    id: 'phase-1',
    title: 'Foundation Setup',
    description: 'Prepare the base structure',
    steps: [
      {
        id: 'step-1',
        title: 'Install Base Plate',
        description: 'Position and secure the foundation',
        parts: [
          {
            id: 'part-1',
            name: 'Base Plate A',
            position: { x: 0, y: 0, z: 0 }
          }
        ],
        tools: [
          { id: 'tool-1', title: 'Screwdriver' }
        ],
        annotations: [
          {
            id: 'ann-1',
            type: 'arrow',
            text: { value: 'Insert here' },
            position: { x: 10, y: 20 }
          }
        ]
      }
    ],
    subassemblies: []
  }
];

function AssemblyApp() {
  const handlePhasesUpdate = (updatedPhases) => {
    console.log('Phases updated:', updatedPhases);
  };

  const handleUploadImage = async (file) => {
    // Your upload implementation
    const url = await uploadToServer(file);
    return url;
  };

  return (
    <AssemblyViewMode
      phases={phases}
      htmlContent="<div>3D Model HTML Content</div>"
      productTitle="Product Assembly"
      parts={[
        { id: 'part-1', name: 'Base Plate A' },
        { id: 'part-2', name: 'Side Panel B' }
      ]}
      className="h-screen"

      // Assembly-specific props
      organizationId="org-123"
      productId="product-456"
      editMode={true}  // Enable editing of descriptions
      onUploadMedia={handleUploadImage}
      onPhasesUpdate={handlePhasesUpdate}
    />
  );
}

Key Features:

  • 3D Model Integration: Display and interact with 3D models
  • Step-by-Step Instructions: Organized phases and steps
  • Parts & Tools Management: Track required components
  • Annotations: 2D annotations overlaying the 3D model
  • Edit Mode: In-place editing of titles and descriptions
  • Progress Tracking: Visual progress indicators

3. TnkrPreview

Documentation preview system with sidebar navigation and content organization.

import { TnkrPreview } from '@tnkrai/tnkr-editor';

const categories = [
  {
    id: 'cat-1',
    name: 'Getting Started',
    parentId: null,
    children: [
      {
        id: 'cat-2',
        name: 'Installation',
        parentId: 'cat-1',
        children: [],
        order: 1,
        isExpanded: false,
        hasPage: true,
        pageContent: { /* TipTap content */ },
        type: 'documentation'
      }
    ],
    order: 0,
    isExpanded: true,
    hasPage: false,
    type: 'category',
    // For assembly type pages
    organizationId: 'org-123',
    productId: 'product-456'
  }
];

function PreviewApp() {
  return (
    <TnkrPreview
      categories={categories}
      className="h-screen"
      title="Product Documentation"
      organizationName="TNKR AI"
      organizationLogo="/logo.png"
      onCategorySelect={(category) => console.log('Selected:', category)}
      onContactClick={() => console.log('Contact clicked')}
      onDiscussionsClick={() => console.log('Discussions clicked')}
    />
  );
}

Key Features:

  • Hierarchical Navigation: Multi-level category tree
  • Content Types: Support for documentation, tutorials, assembly instructions
  • Breadcrumb Navigation: Clear path indication
  • Search Integration: Built-in search capabilities
  • Responsive Design: Mobile-friendly sidebar

4. CustomPage

A flexible page component that combines editor and viewer capabilities.

import { CustomPage } from '@tnkrai/tnkr-editor';

function MyPage() {
  const handleEditorChange = (content) => {
    console.log('Content changed:', content);
  };

  const handleSave = (content) => {
    console.log('Saving content:', content);
  };

  const handleUploadImage = async (file) => {
    // Your upload implementation
    return uploadedUrl;
  };

  return (
    <CustomPage
      pageParent="Documentation"
      pageTitle="Getting Started"
      pageId="page-1"
      pageType="documentation"  // or 'tutorial', 'assembly', etc.

      initialData={existingContent}
      onEditorChange={handleEditorChange}
      onSave={handleSave}
      onUploadMedia={handleUploadImage}

      className="min-h-screen"
      showBreadcrumbs={true}
      breadcrumbs={['Home', 'Documentation', 'Getting Started']}
      editMode={true}

      // WhatsNext navigation
      pages={[
        { id: 'page-2', name: 'Next Topic' }
      ]}
      goToPage={(pageId) => navigateToPage(pageId)}

      // For assembly pages
      organizationId="org-123"
      productId="product-456"

      // Utility card handler
      onUtilityCardClick={() => console.log('Utility card clicked')}

      editorProps={{
        placeholder: "Start writing your documentation...",
        minHeight: 400
      }}
    />
  );
}

Key Features:

  • Multiple Page Types: Documentation, tutorials, assembly instructions
  • Flexible Content: Support for various content types
  • Breadcrumb Navigation: Context-aware navigation
  • Edit/View Modes: Toggle between editing and viewing
  • Auto-save Support: Built-in save functionality

API Dependencies

Important: This package requires certain API endpoints to be implemented in your consuming application for full functionality.

Required API Endpoints

1. Image Upload Endpoint

// POST /api/upload
// Request: FormData with 'file' field
// Response: { url: string } or { url: string, signedUrl: string }

app.post('/api/upload', async (req, res) => {
  const file = req.files.file;
  // Upload to your storage (S3, Cloudinary, etc.)
  const url = await uploadToStorage(file);
  res.json({ url });
});

2. S3 Content Fetching (for Assembly Mode)

// POST /api/fetch-s3-content
// Request: { url: string }
// Response: { content: string }

app.post('/api/fetch-s3-content', async (req, res) => {
  const { url } = req.body;
  // Fetch content from S3
  const content = await fetchFromS3(url);
  res.json({ content });
});

3. Phases Management (for Assembly Mode)

// GET /api/phases?productId={productId}&organizationId={organizationId}
// Response: { phases: Phase[] }

app.get('/api/phases', async (req, res) => {
  const { productId, organizationId } = req.query;
  const phases = await getPhases(productId, organizationId);
  res.json({ phases });
});

// POST /api/phases?productId={productId}&organizationId={organizationId}
// Request: { phases: Phase[], productId: string, organizationId: string }
// Response: { success: boolean }

app.post('/api/phases', async (req, res) => {
  const { phases, productId, organizationId } = req.body;
  await savePhases(phases, productId, organizationId);
  res.json({ success: true });
});

4. Product Information

// GET /api/get_product_by_org_id_and_product_id?orgId={orgId}&productId={productId}
// Response: { product: Product }

app.get('/api/get_product_by_org_id_and_product_id', async (req, res) => {
  const { orgId, productId } = req.query;
  const product = await getProduct(orgId, productId);
  res.json({ product });
});

// GET /api/organizations/{organizationId}/products/{productId}
// Response: { product: Product }

app.get('/api/organizations/:organizationId/products/:productId', async (req, res) => {
  const { organizationId, productId } = req.params;
  const product = await getProduct(organizationId, productId);
  res.json({ product });
});

5. Collaboration (Optional)

// POST /api/collaboration
// Request: { action: string, document: any }
// Response: { success: boolean }

app.post('/api/collaboration', async (req, res) => {
  const { action, document } = req.body;
  // Handle collaboration actions
  res.json({ success: true });
});

6. AI Integration (Optional)

// POST /api/ai
// Request: { prompt: string, context: any }
// Response: { response: string }

app.post('/api/ai', async (req, res) => {
  const { prompt, context } = req.body;
  // Call AI service
  const response = await callAIService(prompt, context);
  res.json({ response });
});

API Implementation Notes

  1. Authentication: Implement proper authentication and authorization for all endpoints
  2. Error Handling: Return appropriate HTTP status codes and error messages
  3. File Size Limits: Implement file size restrictions for uploads
  4. CORS: Configure CORS appropriately for your environment
  5. Rate Limiting: Implement rate limiting for API endpoints
  6. Validation: Validate all input data before processing

Extension System

The editor uses a modular extension system. You can customize extensions:

import {
  TnkrEditor,
  getBasicExtensions,
  getFullExtensions,
  getHeaderOnlyExtensions
} from '@tnkrai/tnkr-editor';

// Use predefined extension sets
const basicExtensions = getBasicExtensions();
const fullExtensions = getFullExtensions();
const headerExtensions = getHeaderOnlyExtensions();

// Or create custom extensions
const customExtensions = [
  // Your custom TipTap extensions
];

<TnkrEditor
  customExtensionSet={basicExtensions}
  extensions={customExtensions}
  disabledExtensions={['emoji', 'mention']}
/>

TypeScript Support

All components come with full TypeScript definitions:

import type {
  // Core types
  TnkrEditorProps,
  AssemblyViewModeProps,
  CustomPageProps,
  TnkrPreviewProps,
  PreviewCategory,

  // Assembly types
  Phase,
  Step,
  Part,
  Tool,
  IAnnotation2D,

  // Editor types
  UploadFunction,
  WhatsNextPage,
  PageType,
  TnkrExtensionConfig,
  TnkrExtensionSet,
  ExtensionManagerOptions
} from '@tnkrai/tnkr-editor';

Styling

Import Styles

// Import all styles
import '@tnkrai/tnkr-editor/styles';

// Or import specific style modules
import '@tnkrai/tnkr-editor/core/styles';
import '@tnkrai/tnkr-editor/preview/styles';

Custom Styling

The package uses CSS variables for theming:

:root {
  --tnkr-primary: #AA423A;
  --tnkr-background: #161617;
  --tnkr-surface: #0D0D0D;
  --tnkr-border: #313133;
  --tnkr-text: #ffffff;
  --tnkr-text-secondary: rgba(255, 255, 255, 0.7);
}

Advanced Usage

Custom Upload Handler

const handleUploadImage: UploadFunction = async (file, onProgress, abortSignal) => {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();

    xhr.upload.addEventListener('progress', (e) => {
      if (e.lengthComputable && onProgress) {
        onProgress({ progress: (e.loaded / e.total) * 100 });
      }
    });

    xhr.addEventListener('load', () => {
      if (xhr.status === 200) {
        const response = JSON.parse(xhr.responseText);
        resolve(response.url);
      } else {
        reject(new Error('Upload failed'));
      }
    });

    if (abortSignal) {
      abortSignal.addEventListener('abort', () => {
        xhr.abort();
        reject(new Error('Upload aborted'));
      });
    }

    const formData = new FormData();
    formData.append('file', file);

    xhr.open('POST', '/api/upload');
    xhr.send(formData);
  });
};

Assembly Annotations

const annotation: IAnnotation2D = {
  id: 'annotation-1',
  type: 'arrow', // or 'label'
  text: {
    value: 'Important note',
    style: {
      fontSize: '14px',
      color: '#ffffff'
    }
  },
  position: { x: 100, y: 200 },
  anchor: { x: 150, y: 250 }, // For arrow type
  isVisible: true,
  cameraSettings: {
    position: [0, 0, 5],
    rotation: [0, 0, 0],
    zoom: 1
  }
};

Content Analysis

import {
  extractText,
  countWords,
  estimateReadingTime,
  generateTableOfContents,
  extractMetadata
} from '@tnkrai/tnkr-editor';

const metadata = extractMetadata(content);
console.log('Word count:', metadata.wordCount);
console.log('Reading time:', metadata.readingTime, 'minutes');

const toc = generateTableOfContents(content);
// Returns array of { id, text, level }

Development

Prerequisites

  • Node.js >= 18.0.0
  • npm >= 8.0.0

Setup

git clone https://github.com/tnkrai/tnkr-editor.git
cd tnkr-editor
npm install

Scripts

# Build the package
npm run build

# Watch for changes (development)
npm run dev

# Type checking
npm run type-check

# Clean build artifacts
npm run clean

Testing

# Run type checking
npm run type-check

# Build to verify everything works
npm run build

Architecture

@tnkrai/tnkr-editor/
├── core/                 # Core editor functionality
│   ├── TnkrEditor.tsx   # Main editor component
│   ├── components/      # TipTap components
│   ├── extensions/      # TipTap extensions
│   ├── lib/            # Utilities and helpers
│   └── styles/         # Core styles
├── src/                 # React components and UI
│   ├── components/
│   │   ├── assembly/   # Assembly mode components
│   │   ├── ui/        # UI components
│   │   └── CustomPage.tsx
│   ├── utils/         # Utility functions
│   └── styles/        # Component styles
└── preview/            # Preview system
    ├── TnkrPreview.tsx
    ├── PreviewSidebar.tsx
    └── PageNavigation.tsx

Migration Guide

From Earlier Versions

If you're upgrading from an earlier version:

  1. Import changes: Update imports to use the new structure
  2. API endpoints: Ensure all required API endpoints are implemented
  3. Upload function: Update to use the new UploadFunction signature
  4. Extension system: Migrate to the new extension configuration

Troubleshooting

Common Issues

  1. Styles not loading: Ensure you've imported @tnkrai/tnkr-editor/styles
  2. Upload not working: Check that your upload endpoint returns the correct format
  3. Assembly mode errors: Verify all required API endpoints are implemented
  4. TypeScript errors: Update to the latest version and check type imports

Contributing

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

License

This project is licensed under the MIT License - see the LICENSE file for details.

Support

Changelog

See CHANGELOG.md for a list of changes in each version.


Made with ❤️ by TNKR AI