another-novel-svelte
v1.2.0
Published
Heavily biased Notion-style WYSIWYG editor without the bells and whistles.
Downloads
9
Maintainers
Readme
Novella Svelte
Heavily biased Notion-style WYSIWYG editor without the bells and whistles. I'm maintaining this for my blogging website called AcademiaScribes.
Based on Novel
Forked from Novel-Svelte
Made for DennisOluoch
Features
- 📝 Rich Text Editor with Notion-style formatting
- 🖼️ Image uploads with drag & drop, paste, and file selection
- 💾 Auto-saving to local storage
- ⚡ Lightning fast and lightweight
- 🎨 Fully customizable with Tailwind CSS
- 📱 Mobile-friendly and responsive
Techs and Works
- Svelte - Frontend framework
- Tiptap - Headless editor framework
- Tailwind CSS - Styling
Installation
npm i another-novel-svelteBasic Usage
<script>
import { Editor } from 'another-novel-svelte';
let saveStatus = 'Saved';
let editor;
// Image upload configuration
const imageProviderConfig = {
provider: 'vercel',
bucketName: 'your-bucket-name',
accessToken: 'your-access-token'
};
</script>
<main>
<Editor
bind:editor
{imageProviderConfig}
onUpdate={() => {
saveStatus = 'Unsaved';
}}
onDebouncedUpdate={() => {
saveStatus = 'Saving...';
// Saving code goes here
saveStatus = 'Saved';
}}
>
<div>
{saveStatus}
</div>
</Editor>
</main>Image Upload Configuration
The editor supports image uploads to multiple providers: Vercel Blob, Supabase Storage, and Cloudinary. Instead of using environment variables, we now pass the configuration directly to the Editor component.
Configuration Setup
Create a configuration object in your Svelte component:
const imageProviderConfig = {
provider: 'vercel', // Options: 'vercel', 'supabase', 'cloudinary'
bucketName: 'your-bucket-name',
accessToken: 'your-access-token',
// Additional properties based on the provider:
// For Supabase:
// supabaseUrl: 'your-project-url'
// For Cloudinary:
// cloudinaryCloudName: 'your-cloud-name'
};Then pass this configuration to the Editor component:
<Editor {imageProviderConfig} ...otherProps>
<!-- Editor content -->
</Editor>Provider Setup Instructions
Vercel Blob
- Set up a Vercel project
- Get your Blob access token from the Vercel dashboard
- Configure the
imageProviderConfigas shown above
Supabase Storage
- Create a Supabase project
- Create a storage bucket
- Set the storage bucket to public if you want the images to be publicly accessible
- Get your service role key from the project settings
- Configure the
imageProviderConfigas shown above, including thesupabaseUrl
Cloudinary
- Create a Cloudinary account
- Create an upload preset (can be done in the Settings > Upload section)
- Get your cloud name and API credentials
- Configure the
imageProviderConfigas shown above, including thecloudinaryCloudName
Image Upload Features
- 📤 Drag and drop image uploads
- 📋 Paste images directly from clipboard
- 🖼️ Support for all standard image formats (jpg, png, gif, etc.)
- ⚖️ Maximum file size: 20MB
- 🔄 Real-time upload status with toast notifications
- 📏 Image resizing after upload
- 💾 Automatic image optimization
Security Considerations
⚠️ Important: The configuration passed to the Editor component will be visible in the client-side code. For production use, we strongly recommend implementing a server-side API endpoint to handle the actual upload operations.
Recommended Production Setup
- Create a server-side API endpoint (using SvelteKit or your preferred backend):
// routes/api/upload/+server.ts
import { json } from '@sveltejs/kit';
export async function POST({ request }) {
const file = await request.blob();
// Handle upload using server-side tokens
// Return the URL of the uploaded file
return json({ url: uploadedUrl });
}- Use the server endpoint in production:
const uploadFile = async (file: File) => {
if (import.meta.env.DEV) {
// Use direct provider upload in development
return handleDirectUpload(file);
} else {
// Use server endpoint in production
const formData = new FormData();
formData.append('file', file);
const response = await fetch('/api/upload', {
method: 'POST',
body: formData
});
const { url } = await response.json();
return url;
}
};Development
Install Node.js and npm (download here)
Clone the repository:
git clone https://github.com/DennisOluoch/novel-svelte.git
cd novel-svelte- Install dependencies:
npm install- Start the development server:
npm run devVisit http://localhost:5173/ to see the preview.
Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| class | string | 'relative min-h-[500px] w-full max-w-screen-lg border-stone-200 bg-white p-12 px-8 sm:mb-[calc(20vh)] sm:rounded-lg sm:border sm:px-12 sm:shadow-lg' | Additional classes to add to the editor container |
| defaultValue | JSONContent \| string | defaultEditorContent | The default value to use for the editor |
| extensions | Extension[] | [] | Additional extensions to use for the editor |
| editorProps | EditorProps | {} | Additional props to pass to the Tiptap editor |
| onUpdate | (editor?: Editor) => void \| Promise<void> | noop | Callback function called on every editor update |
| onDebouncedUpdate | (editor?: Editor) => void \| Promise<void> | noop | Callback function called after debounce duration |
| debounceDuration | number | 750 | Duration to debounce the onDebouncedUpdate callback |
| storageKey | string | 'novel__content' | Key to use for storing editor content in localStorage |
| disableLocalStorage | boolean | false | Disable local storage read/save |
| imageProviderConfig | UploadConfig \| undefined | undefined | Configuration for image upload provider |
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
MIT License
