@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.
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-editorTailwind 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 tailwindcssAdd 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
- Authentication: Implement proper authentication and authorization for all endpoints
- Error Handling: Return appropriate HTTP status codes and error messages
- File Size Limits: Implement file size restrictions for uploads
- CORS: Configure CORS appropriately for your environment
- Rate Limiting: Implement rate limiting for API endpoints
- 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 installScripts
# Build the package
npm run build
# Watch for changes (development)
npm run dev
# Type checking
npm run type-check
# Clean build artifacts
npm run cleanTesting
# Run type checking
npm run type-check
# Build to verify everything works
npm run buildArchitecture
@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.tsxMigration Guide
From Earlier Versions
If you're upgrading from an earlier version:
- Import changes: Update imports to use the new structure
- API endpoints: Ensure all required API endpoints are implemented
- Upload function: Update to use the new
UploadFunctionsignature - Extension system: Migrate to the new extension configuration
Troubleshooting
Common Issues
- Styles not loading: Ensure you've imported
@tnkrai/tnkr-editor/styles - Upload not working: Check that your upload endpoint returns the correct format
- Assembly mode errors: Verify all required API endpoints are implemented
- TypeScript errors: Update to the latest version and check type imports
Contributing
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
License
This project is licensed under the MIT License - see the LICENSE file for details.
Support
- 📧 Email: [email protected]
- 🐛 Issues: GitHub Issues
- 📖 Documentation: GitHub Wiki
Changelog
See CHANGELOG.md for a list of changes in each version.
Made with ❤️ by TNKR AI
