jedmund-svelte/src/lib/components/edra/editor-extensions.ts
Justin Edmund 93795577cd fix: complete frontend component any type cleanup
- replace any with Prisma types (Post, Project, Album, Media)
- use Component type for Svelte component parameters
- use Snippet type for Svelte 5 render slots
- use Record<string, unknown> for dynamic objects
- add proper type guards for error handling
- fix editor extension types with proper generics
- all frontend components now have zero any type errors
2025-11-23 05:50:22 -08:00

129 lines
5.3 KiB
TypeScript

import { CodeBlockLowlight } from '@tiptap/extension-code-block-lowlight'
import { all, createLowlight } from 'lowlight'
import { SvelteNodeViewRenderer } from 'svelte-tiptap'
import type { Extensions } from '@tiptap/core'
// Extension classes
import { AudioPlaceholder } from './extensions/audio/AudioPlaceholder.js'
import { ImagePlaceholder } from './extensions/image/ImagePlaceholder.js'
import { VideoPlaceholder } from './extensions/video/VideoPlaceholder.js'
import { AudioExtended } from './extensions/audio/AudiExtended.js'
import { ImageExtended } from './extensions/image/ImageExtended.js'
import { VideoExtended } from './extensions/video/VideoExtended.js'
import { GalleryPlaceholder } from './extensions/gallery/GalleryPlaceholder.js'
import { GalleryExtended } from './extensions/gallery/GalleryExtended.js'
import { IFramePlaceholder } from './extensions/iframe/IFramePlaceholder.js'
import { IFrameExtended } from './extensions/iframe/IFrameExtended.js'
import { UrlEmbed } from './extensions/url-embed/UrlEmbed.js'
import { UrlEmbedPlaceholder } from './extensions/url-embed/UrlEmbedPlaceholder.js'
import { UrlEmbedExtended } from './extensions/url-embed/UrlEmbedExtended.js'
import { LinkContextMenu } from './extensions/link-context-menu/LinkContextMenu.js'
import { GeolocationPlaceholder } from './extensions/geolocation/GeolocationPlaceholder.js'
import { GeolocationExtended } from './extensions/geolocation/GeolocationExtended.js'
import slashcommand from './extensions/slash-command/slashcommand.js'
// Component imports
import CodeExtended from './headless/components/CodeExtended.svelte'
import AudioPlaceholderComponent from './headless/components/AudioPlaceholder.svelte'
import AudioExtendedComponent from './headless/components/AudioExtended.svelte'
import ImagePlaceholderComponent from './headless/components/ImagePlaceholder.svelte'
import ImageExtendedComponent from './headless/components/ImageExtended.svelte'
import VideoPlaceholderComponent from './headless/components/VideoPlaceholder.svelte'
import VideoExtendedComponent from './headless/components/VideoExtended.svelte'
import GalleryPlaceholderComponent from './headless/components/GalleryPlaceholder.svelte'
import GalleryExtendedComponent from './headless/components/GalleryExtended.svelte'
import IFramePlaceholderComponent from './headless/components/IFramePlaceholder.svelte'
import IFrameExtendedComponent from './headless/components/IFrameExtended.svelte'
import UrlEmbedPlaceholderComponent from './headless/components/UrlEmbedPlaceholder.svelte'
import UrlEmbedExtendedComponent from './headless/components/UrlEmbedExtended.svelte'
import GeolocationPlaceholderComponent from './headless/components/GeolocationPlaceholder.svelte'
import GeolocationExtendedComponent from './headless/components/GeolocationExtended.svelte'
import SlashCommandList from './headless/components/SlashCommandList.svelte'
// Create lowlight instance
const lowlight = createLowlight(all)
import type { Component } from 'svelte'
export interface EditorExtensionOptions {
showSlashCommands?: boolean
onShowUrlConvertDropdown?: (pos: number, url: string) => void
onShowLinkContextMenu?: (pos: number, url: string, coords: { x: number; y: number }) => void
imagePlaceholderComponent?: Component // Allow custom image placeholder component
}
export function getEditorExtensions(options: EditorExtensionOptions = {}): Extensions {
const {
showSlashCommands = true,
onShowUrlConvertDropdown,
onShowLinkContextMenu,
imagePlaceholderComponent = ImagePlaceholderComponent
} = options
const extensions: Extensions = [
CodeBlockLowlight.configure({
lowlight
}).extend({
addNodeView() {
return SvelteNodeViewRenderer(CodeExtended)
}
}),
AudioPlaceholder(AudioPlaceholderComponent),
ImagePlaceholder(imagePlaceholderComponent),
VideoPlaceholder(VideoPlaceholderComponent),
AudioExtended(AudioExtendedComponent),
ImageExtended(ImageExtendedComponent),
VideoExtended(VideoExtendedComponent),
GalleryPlaceholder(GalleryPlaceholderComponent),
GalleryExtended(GalleryExtendedComponent),
IFramePlaceholder(IFramePlaceholderComponent),
IFrameExtended(IFrameExtendedComponent),
GeolocationPlaceholder(GeolocationPlaceholderComponent),
GeolocationExtended(GeolocationExtendedComponent)
]
// Add URL embed extensions with callbacks if provided
if (onShowUrlConvertDropdown) {
extensions.push(
UrlEmbed.configure({ onShowDropdown: onShowUrlConvertDropdown }),
UrlEmbedPlaceholder(UrlEmbedPlaceholderComponent),
UrlEmbedExtended(UrlEmbedExtendedComponent)
)
} else {
extensions.push(
UrlEmbed,
UrlEmbedPlaceholder(UrlEmbedPlaceholderComponent),
UrlEmbedExtended(UrlEmbedExtendedComponent)
)
}
// Add link context menu if callback provided
if (onShowLinkContextMenu) {
extensions.push(LinkContextMenu.configure({ onShowContextMenu: onShowLinkContextMenu }))
} else {
extensions.push(LinkContextMenu)
}
// Add slash commands if enabled
if (showSlashCommands) {
extensions.push(slashcommand(SlashCommandList))
}
return extensions
}
// Extension presets for different editor variants
export const EDITOR_PRESETS = {
full: {
showSlashCommands: true,
includeAllExtensions: true
},
inline: {
showSlashCommands: true,
includeAllExtensions: true
},
minimal: {
showSlashCommands: false,
includeAllExtensions: false
}
}