From 7bf0984698e43b9aa2dd879f65951318ed410390 Mon Sep 17 00:00:00 2001 From: Maze <167211895+mazeincoding@users.noreply.github.com> Date: Sat, 31 Jan 2026 00:20:04 +0100 Subject: codebase overhaul (#697) --- .cursor/rules/codebase-index.mdc | 1837 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 1837 insertions(+) create mode 100644 .cursor/rules/codebase-index.mdc (limited to '.cursor/rules/codebase-index.mdc') diff --git a/.cursor/rules/codebase-index.mdc b/.cursor/rules/codebase-index.mdc new file mode 100644 index 00000000..be1509dc --- /dev/null +++ b/.cursor/rules/codebase-index.mdc @@ -0,0 +1,1837 @@ +--- +alwaysApply: true +--- + +# video-editor-oss Codebase Index + +**This file provides an index of exported functions, types, interfaces, classes, and constants in your codebase.** + +Updated in real-time by Twiggy. Use this to discover existing utilities and avoid duplicating code. + +## How to Use + +When implementing new features: +1. Check if similar functionality already exists +2. Reuse existing types and utilities +3. Understand the API surface of your codebase + +```typescript +## apps/web/src/constants + +editor-constants.ts + export const PLATFORM_LAYOUTS: Record + export const PANEL_CONFIG + +export-constants.ts + export const DEFAULT_EXPORT_OPTIONS + export const EXPORT_MIME_TYPES + +font-constants.ts + export interface FontOption { + value: string + label: string + category: "system" | "google" | "custom" + weights?: number[] + hasClassName?: boolean + } + export const FONT_OPTIONS: FontOption[] + export const DEFAULT_FONT + export type FontFamily = (typeof FONT_OPTIONS)[number]["value"] + export const getFontByValue = (value: string): FontOption | undefined => ... + export const getGoogleFonts = (): FontOption[] => ... + export const getSystemFonts = (): FontOption[] => ... + +language-constants.ts + export const LANGUAGES + +project-constants.ts + export const DEFAULT_CANVAS_PRESETS: TCanvasSize[] + export const FPS_PRESETS + export const BLUR_INTENSITY_PRESETS: { label: string; value: number }[] + export const DEFAULT_CANVAS_SIZE: TCanvasSize + export const DEFAULT_FPS + export const DEFAULT_BLUR_INTENSITY + export const DEFAULT_COLOR + +site-constants.ts + export const SITE_URL + export const SITE_INFO + export type ExternalTool = { + name: string; + description: string; + url: string; + icon: React.ElementType; + } + export const EXTERNAL_TOOLS: ExternalTool[] + export const DEFAULT_LOGO_URL + export const SOCIAL_LINKS + export type Sponsor = { + name: string; + url: string; + logo: string; + description: string; + } + export const SPONSORS: Sponsor[] + +stickers-constants.ts + export const STICKER_CATEGORIES + export const STICKER_CATEGORY_CONFIG: Record< + (typeof STICKER_CATEGORIES)[number], + string | undefined + > + +text-constants.ts + export const DEFAULT_TEXT_ELEMENT: Omit + +timeline-constants.tsx + export const TRACK_COLORS: Record + export const TRACK_HEIGHTS: Record + export const TRACK_GAP + export const TIMELINE_CONSTANTS + export const DEFAULT_TIMELINE_VIEW_STATE: TTimelineViewState + export const TRACK_ICONS: Record + +transcription-constants.ts + export const TRANSCRIPTION_LANGUAGES + export const TRANSCRIPTION_MODELS: TranscriptionModel[] + export const DEFAULT_TRANSCRIPTION_MODEL: TranscriptionModelId + export const DEFAULT_CHUNK_LENGTH_SECONDS + export const DEFAULT_STRIDE_SECONDS + export const DEFAULT_WORDS_PER_CAPTION + export const MIN_CAPTION_DURATION_SECONDS + +## apps/web/src/core + +index.ts + export class EditorCore { + instance: EditorCore | null + command: CommandManager + playback: PlaybackManager + timeline: TimelineManager + scenes: ScenesManager + project: ProjectManager + media: MediaManager + renderer: RendererManager + save: SaveManager + audio: AudioManager + selection: SelectionManager + static getInstance(): EditorCore + static reset(): void + } + +## apps/web/src/hooks + +use-editor.ts + export function useEditor(): EditorCore + +use-file-upload.ts + export function useFileUpload({ + accept, + multiple, + onFilesSelected, + }: UseFileUploadOptions = {}) + +use-infinite-scroll.ts + export function useInfiniteScroll({ + onLoadMore, + hasMore, + isLoading, + threshold = 200, + enabled = true, + }: UseInfiniteScrollOptions) + +use-keybindings.ts + export function useKeybindingsListener() + export function useKeybindingDisabler() + +use-keyboard-shortcuts-help.ts + export interface KeyboardShortcut { + id: string + keys: string[] + description: string + category: string + action: TAction + icon?: React.ReactNode + } + export function useKeyboardShortcutsHelp() + +use-mobile.ts + export function useIsMobile() + +use-raf-loop.ts + export function useRafLoop(callback: ({ time }: { time: number }) => void) + +use-reveal-item.ts + export function useRevealItem( + highlightId: string | null, + onClearHighlight: () => void, + highlightDuration = 1000, + ) + +use-sound-search.ts + export function useSoundSearch({ + query, + commercialOnly, + }: { + query: string; + commercialOnly: boolean; + }) + +## apps/web/src/hooks/actions + +use-action-handler.ts + export function useActionHandler( + action: A, + handler: TActionFunc, + isActive: TActionHandlerOptions, + ) + +use-editor-actions.ts + export function useEditorActions() + +## apps/web/src/hooks/storage + +use-local-storage.ts + export function useLocalStorage({ + key, + defaultValue, + }: { + key: string; + defaultValue: T; + }): [ + T, + ({ value }: { value: T | ((previousValue: T) => T) }) => void, + boolean, + ] + +## apps/web/src/hooks/timeline + +use-edge-auto-scroll.ts + export function useEdgeAutoScroll({ + isActive, + getMouseClientX, + rulerScrollRef, + tracksScrollRef, + contentWidth, + edgeThreshold = 100, + maxScrollSpeed = 15, + }: UseEdgeAutoScrollParams): void + +use-scroll-sync.ts + export function useScrollSync({ + tracksScrollRef, + rulerScrollRef, + trackLabelsScrollRef, + bookmarksScrollRef, + }: UseScrollSyncProps) + +use-selection-box.ts + export function useSelectionBox({ + containerRef, + onSelectionComplete, + isEnabled = true, + tracksScrollRef, + zoomLevel, + }: UseSelectionBoxProps) + +use-snap-indicator-position.ts + export function useSnapIndicatorPosition({ + snapPoint, + zoomLevel, + tracks, + timelineRef, + trackLabelsRef, + tracksScrollRef, + }: UseSnapIndicatorPositionParams): SnapIndicatorPosition + +use-timeline-drag-drop.ts + export function useTimelineDragDrop({ + containerRef, + headerRef, + zoomLevel, + }: UseTimelineDragDropProps) + +use-timeline-playhead.ts + export function useTimelinePlayhead({ + zoomLevel, + rulerRef, + rulerScrollRef, + tracksScrollRef, + playheadRef, + }: UseTimelinePlayheadProps) + +use-timeline-seek.ts + export function useTimelineSeek({ + playheadRef, + trackLabelsRef, + rulerScrollRef, + tracksScrollRef, + zoomLevel, + duration, + isSelecting, + clearSelectedElements, + seek, + }: UseTimelineSeekProps) + +use-timeline-snapping.ts + export interface SnapPoint { + time: number + type: "element-start" | "element-end" | "playhead" + elementId?: string + trackId?: string + } + export interface SnapResult { + snappedTime: number + snapPoint: SnapPoint | null + snapDistance: number + } + export interface UseTimelineSnappingOptions { + snapThreshold?: number + enableElementSnapping?: boolean + enablePlayheadSnapping?: boolean + } + export function useTimelineSnapping({ + snapThreshold = 10, + enableElementSnapping = true, + enablePlayheadSnapping = true, + }: UseTimelineSnappingOptions = {}) + +use-timeline-zoom.ts + export function useTimelineZoom({ + containerRef, + minZoom = TIMELINE_CONSTANTS.ZOOM_MIN, + initialZoom, + initialScrollLeft, + initialPlayheadTime, + tracksScrollRef, + rulerScrollRef, + }: UseTimelineZoomProps): UseTimelineZoomReturn + +## apps/web/src/hooks/timeline/element + +use-element-interaction.ts + export function useElementInteraction({ + zoomLevel, + timelineRef, + tracksContainerRef, + tracksScrollRef, + headerRef, + snappingEnabled, + onSnapPointChange, + }: UseElementInteractionProps) + +use-element-resize.ts + export interface ResizeState { + elementId: string + side: "left" | "right" + startX: number + initialTrimStart: number + initialTrimEnd: number + initialStartTime: number + initialDuration: number + } + export function useTimelineElementResize({ + element, + track, + zoomLevel, + onSnapPointChange, + onResizeStateChange, + }: UseTimelineElementResizeProps) + +use-element-selection.ts + export function useElementSelection() + +## apps/web/src/lib + +drag-data.ts + export function setDragData({ + dataTransfer, + dragData, + }: { + dataTransfer: DataTransfer; + dragData: TimelineDragData; + }): void + export function getDragData({ + dataTransfer, + }: { + dataTransfer: DataTransfer; + }): TimelineDragData | null + export function hasDragData({ + dataTransfer, + }: { + dataTransfer: DataTransfer; + }): boolean + export function clearDragData(): void + +export.ts + export function getExportMimeType({ + format, + }: { + format: ExportFormat; + }): string + export function getExportFileExtension({ + format, + }: { + format: ExportFormat; + }): string + +iconify-api.ts + export const ICONIFY_HOSTS + export interface IconSet { + prefix: string + name: string + total: number + author?: { + name: string; + url?: string; + } + license?: { + title: string; + spdx?: string; + url?: string; + } + samples?: string[] + category?: string + palette?: boolean + } + export interface IconSearchResult { + icons: string[] + total: number + limit: number + start: number + collections: Record + } + export interface CollectionInfo { + prefix: string + total: number + title?: string + uncategorized?: string[] + categories?: Record + hidden?: string[] + aliases?: Record + } + export function getCollections( + category?: string, + ): Promise> + export function getCollection( + prefix: string, + ): Promise + export function searchIcons( + query: string, + limit: number = 64, + prefixes?: string[], + category?: string, + ): Promise + export function buildIconSvgUrl( + host: string, + iconName: string, + params?: { + color?: string; + width?: number; + height?: number; + flip?: "horizontal" | "vertical" | "horizontal,vertical"; + rotate?: number | string; + }, + ): string + export function getIconSvgUrl( + iconName: string, + params?: Parameters[2], + ): string + export function downloadSvgAsText( + iconName: string, + params?: Parameters[1], + ): Promise + export function svgToFile(svgText: string, fileName: string): File + export const POPULAR_COLLECTIONS + export function getCategoriesFromCollections( + collections: Record, + ): string[] + +rate-limit.ts + export const baseRateLimit + export function checkRateLimit({ request }: { request: Request }) + +scenes.ts + export function getMainScene({ scenes }: { scenes: TScene[] }): TScene | null + export function ensureMainScene({ scenes }: { scenes: TScene[] }): TScene[] + export function buildDefaultScene({ + name, + isMain, + }: { + name: string; + isMain: boolean; + }): TScene + export function canDeleteScene({ scene }: { scene: TScene }): { + canDelete: boolean; + reason?: string; + } + export function getFallbackSceneAfterDelete({ + scenes, + deletedSceneId, + currentSceneId, + }: { + scenes: TScene[]; + deletedSceneId: string; + currentSceneId: string | null; + }): TScene | null + export function findCurrentScene({ + scenes, + currentSceneId, + }: { + scenes: TScene[]; + currentSceneId: string; + }): TScene | null + export function getProjectDurationFromScenes({ + scenes, + }: { + scenes: TScene[]; + }): number + export function updateSceneInArray({ + scenes, + sceneId, + updates, + }: { + scenes: TScene[]; + sceneId: string; + updates: Partial; + }): TScene[] + +time.ts + export function roundToFrame({ + time, + fps, + }: { + time: number; + fps: number; + }): number + export function formatTimeCode({ + timeInSeconds, + format = "HH:MM:SS:CS", + fps, + }: { + timeInSeconds: number; + format?: TTimeCode; + fps?: number; + }): string + export function parseTimeCode({ + timeCode, + format = "HH:MM:SS:CS", + fps, + }: { + timeCode: string; + format?: TTimeCode; + fps: number; + }): number | null + export function guessTimeCodeFormat({ + timeCode, + }: { + timeCode: string; + }): TTimeCode | null + export function timeToFrame({ + time, + fps, + }: { + time: number; + fps: number; + }): number + export function frameToTime({ + frame, + fps, + }: { + frame: number; + fps: number; + }): number + export function snapTimeToFrame({ + time, + fps, + }: { + time: number; + fps: number; + }): number + export function getSnappedSeekTime({ + rawTime, + duration, + fps, + }: { + rawTime: number; + duration: number; + fps: number; + }): number + export function getLastFrameTime({ + duration, + fps, + }: { + duration: number; + fps: number; + }): number + +## apps/web/src/lib/actions + +definitions.ts + export type TActionCategory = | "playback" + | "navigation" + | "editing" + | "selection" + | "history" + | "timeline" + | "controls" + export interface TActionDefinition { + description: string + category: TActionCategory + defaultShortcuts?: ShortcutKey[] + args?: Record + } + export const ACTIONS + export type TAction = keyof typeof ACTIONS + export function getActionDefinition(action: TAction): TActionDefinition + export function getDefaultShortcuts(): Record + +registry.ts + export function bindAction( + action: A, + handler: TActionFunc, + ) + export function unbindAction( + action: A, + handler: TActionFunc, + ) + export const invokeAction = ( + action: A, + args?: TArgOfAction, + trigger?: TInvocationTrigger, + ) => ... + +types.ts + export type TActionArgsMap = { + "seek-forward": { seconds: number } | undefined; + "seek-backward": { seconds: number } | undef... + export type TActionWithArgs = keyof TActionArgsMap + export type TActionWithOptionalArgs = | TActionWithNoArgs + | TKeysWithValueUndefined + export type TActionWithNoArgs = Exclude + export type TArgOfAction = A extends TActionWithArgs + ? TActionArgsMap[A] + : undefined + export type TActionFunc = A extends TActionWithArgs + ? (arg: TArgOfAction, trigger?: TInvocationTrigger) => void + : (_?:... + export type TInvocationTrigger = "keypress" | "mouseclick" + export type TBoundActionList = { + [A in TAction]?: Array>; + } + export type TActionHandlerOptions = | MutableRefObject + | boolean + | undefined + +## apps/web/src/lib/auth + +server.ts + export const auth + export type Auth = typeof auth + +## apps/web/src/lib/blog + +query.ts + export function getPosts() + export function getTags() + export function getSinglePost({ slug }: { slug: string }) + export function getCategories() + export function getAuthors() + export function processHtmlContent({ + html, + }: { + html: string; + }): Promise + +## apps/web/src/lib/db + +index.ts + export const db + +schema.ts + export const users + export const sessions + export const accounts + export const verifications + +## apps/web/src/lib/gradients + +canvas.ts + export function drawCssBackground({ + ctx, + width, + height, + css, + }: { + ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D; + width: number; + height: number; + css: string; + }): void + +parser.ts + export type GradientOrientation = LinearOrientation | Array + export type Color = | { type: "hex"; value: string } + | { type: "literal"; value: string } + | { type: "rgb"; value: A... + export type ColorStop = Color & { length?: Distance } + export type GradientAst = { + type: GradientType; + orientation: GradientOrientation | undefined; + colorStops: Array => ... + export const GradientParser + +## apps/web/src/lib/media + +audio.ts + export type CollectedAudioElement = Omit< + AudioElement, + "type" | "mediaId" | "volume" | "id" | "name" | "sourceType" | "sourceUrl" + ... + export function createAudioContext(): AudioContext + export interface DecodedAudio { + samples: Float32Array + sampleRate: number + } + export function decodeAudioToFloat32({ + audioBlob, + }: { + audioBlob: Blob; + }): Promise + export function collectAudioElements({ + tracks, + mediaAssets, + audioContext, + }: { + tracks: TimelineTrack[]; + mediaAssets: MediaAsset[]; + audioContext: AudioContext; + }): Promise + export interface AudioClipSource { + id: string + sourceKey: string + file: File + startTime: number + duration: number + trimStart: number + trimEnd: number + muted: boolean + } + export function collectAudioMixSources({ + tracks, + mediaAssets, + }: { + tracks: TimelineTrack[]; + mediaAssets: MediaAsset[]; + }): Promise + export function collectAudioClips({ + tracks, + mediaAssets, + }: { + tracks: TimelineTrack[]; + mediaAssets: MediaAsset[]; + }): Promise + export function createTimelineAudioBuffer({ + tracks, + mediaAssets, + duration, + sampleRate = 44100, + audioContext, + }: { + tracks: TimelineTrack[]; + mediaAssets: MediaAsset[]; + duration: number; + sampleRate?: number; + audioContext?: AudioContext; + }): Promise + +media-utils.ts + export const SUPPORTS_AUDIO: readonly MediaType[] + export function mediaSupportsAudio({ + media, + }: { + media: MediaAsset | null | undefined; + }): boolean + export const getMediaTypeFromFile = ({ + file, + }: { + file: File; + }): MediaType | null => ... + +mediabunny.ts + export function getVideoInfo({ + videoFile, + }: { + videoFile: File; + }): Promise<{ + duration: number; + width: number; + height: number; + fps: number; + }> + export const extractTimelineAudio = ({ + tracks, + mediaAssets, + totalDuration, + onProgress, + }: { + tracks: TimelineTrack[]; + mediaAssets: MediaAsset[]; + totalDuration: number; + onProgress?: (progress: number) => void; + }): Promise => ... + +processing.ts + export interface ProcessedMediaAsset extends Omit + export function generateThumbnail({ + videoFile, + timeInSeconds, + }: { + videoFile: File; + timeInSeconds: number; + }): Promise + export function generateImageThumbnail({ + imageFile, + }: { + imageFile: File; + }): Promise + export function processMediaAssets({ + files, + onProgress, + }: { + files: FileList | File[]; + onProgress?: ({ progress }: { progress: number }) => void; + }): Promise + +## apps/web/src/lib/timeline + +bookmarks.ts + export function findBookmarkIndex({ + bookmarks, + frameTime, + }: { + bookmarks: number[]; + frameTime: number; + }): number + export function isBookmarkAtTime({ + bookmarks, + frameTime, + }: { + bookmarks: number[]; + frameTime: number; + }): boolean + export function toggleBookmarkInArray({ + bookmarks, + frameTime, + }: { + bookmarks: number[]; + frameTime: number; + }): number[] + export function removeBookmarkFromArray({ + bookmarks, + frameTime, + }: { + bookmarks: number[]; + frameTime: number; + }): number[] + export function getFrameTime({ + time, + fps, + }: { + time: number; + fps: number; + }): number + +drop-utils.ts + export function computeDropTarget({ + elementType, + mouseX, + mouseY, + tracks, + playheadTime, + isExternalDrop, + elementDuration, + pixelsPerSecond, + zoomLevel, + verticalDragDirection, + startTimeOverride, + excludeElementId, + }: ComputeDropTargetParams): DropTarget + export function getDropLineY({ + dropTarget, + tracks, + }: { + dropTarget: DropTarget; + tracks: TimelineTrack[]; + }): number + +element-utils.ts + export function canElementHaveAudio( + element: TimelineElement, + ) + export function canElementBeHidden( + element: TimelineElement, + ) + export function hasMediaId( + element: TimelineElement, + ) + export function requiresMediaId({ + element, + }: { + element: CreateTimelineElement; + }): boolean + export function checkElementOverlaps({ + elements, + }: { + elements: TimelineElement[]; + }): boolean + export function resolveElementOverlaps({ + elements, + }: { + elements: TimelineElement[]; + }): TimelineElement[] + export function wouldElementOverlap({ + elements, + startTime, + endTime, + excludeElementId, + }: { + elements: TimelineElement[]; + startTime: number; + endTime: number; + excludeElementId?: string; + }): boolean + export function buildTextElement({ + raw, + startTime, + }: { + raw: Partial>; + startTime: number; + }): CreateTimelineElement + export function buildStickerElement({ + iconName, + startTime, + }: { + iconName: string; + startTime: number; + }): CreateStickerElement + export function buildVideoElement({ + mediaId, + name, + duration, + startTime, + }: { + mediaId: string; + name: string; + duration: number; + startTime: number; + }): CreateVideoElement + export function buildImageElement({ + mediaId, + name, + duration, + startTime, + }: { + mediaId: string; + name: string; + duration: number; + startTime: number; + }): CreateImageElement + export function buildUploadAudioElement({ + mediaId, + name, + duration, + startTime, + buffer, + }: { + mediaId: string; + name: string; + duration: number; + startTime: number; + buffer?: AudioBuffer; + }): CreateUploadAudioElement + export function buildLibraryAudioElement({ + sourceUrl, + name, + duration, + startTime, + buffer, + }: { + sourceUrl: string; + name: string; + duration: number; + startTime: number; + buffer?: AudioBuffer; + }): CreateLibraryAudioElement + export function getElementsAtTime({ + tracks, + time, + }: { + tracks: TimelineTrack[]; + time: number; + }): { trackId: string; elementId: string }[] + +index.ts + export function calculateTotalDuration({ + tracks, + }: { + tracks: TimelineTrack[]; + }): number + +ruler-utils.ts + export interface RulerConfig { + labelIntervalSeconds: number + tickIntervalSeconds: number + } + export function getRulerConfig({ + zoomLevel, + fps, + }: { + zoomLevel: number; + fps: number; + }): RulerConfig + export function shouldShowLabel({ + time, + labelIntervalSeconds, + }: { + time: number; + labelIntervalSeconds: number; + }): boolean + export function formatRulerLabel({ + timeInSeconds, + fps, + }: { + timeInSeconds: number; + fps: number; + }): string + +track-utils.ts + export function canTracktHaveAudio( + track: TimelineTrack, + ) + export function canTrackBeHidden( + track: TimelineTrack, + ) + export function getTrackColor({ type }: { type: TrackType }) + export function getTrackClasses({ type }: { type: TrackType }) + export function getTrackHeight({ type }: { type: TrackType }): number + export function getCumulativeHeightBefore({ + tracks, + trackIndex, + }: { + tracks: Array<{ type: TrackType }>; + trackIndex: number; + }): number + export function getTotalTracksHeight({ + tracks, + }: { + tracks: Array<{ type: TrackType }>; + }): number + export function buildEmptyTrack({ + id, + type, + name, + }: { + id: string; + type: TrackType; + name?: string; + }): TimelineTrack + export function getDefaultInsertIndexForTrack({ + tracks, + trackType, + }: { + tracks: TimelineTrack[]; + trackType: TrackType; + }): number + export function getHighestInsertIndexForTrack({ + tracks, + trackType, + }: { + tracks: TimelineTrack[]; + trackType: TrackType; + }): number + export function isMainTrack(track: TimelineTrack) + export function getMainTrack({ + tracks, + }: { + tracks: TimelineTrack[]; + }): TimelineTrack | null + export function ensureMainTrack({ + tracks, + }: { + tracks: TimelineTrack[]; + }): TimelineTrack[] + export function canElementGoOnTrack({ + elementType, + trackType, + }: { + elementType: ElementType; + trackType: TrackType; + }): boolean + export function validateElementTrackCompatibility({ + element, + track, + }: { + element: { type: ElementType }; + track: { type: TrackType }; + }): { isValid: boolean; errorMessage?: string } + +zoom-utils.ts + export function getTimelineZoomMin({ + duration, + containerWidth, + }: { + duration: number; + containerWidth: number | null | undefined; + }): number + export function getTimelinePaddingPx({ + containerWidth, + zoomLevel, + minZoom, + }: { + containerWidth: number; + zoomLevel: number; + minZoom: number; + }): number + export function getZoomPercent({ + zoomLevel, + minZoom, + }: { + zoomLevel: number; + minZoom: number; + }): number + +## apps/web/src/lib/transcription + +caption.ts + export function buildCaptionChunks({ + segments, + wordsPerChunk = DEFAULT_WORDS_PER_CAPTION, + minDuration = MIN_CAPTION_DURATION_SECONDS, + }: { + segments: TranscriptionSegment[]; + wordsPerChunk?: number; + minDuration?: number; + }): CaptionChunk[] + +## apps/web/src/services/media + +video-cache.ts + export class VideoCache { + sinks + initPromises + async getFrameAt({ + mediaId, + file, + time, + }: { + mediaId: string; + file: File; + time: number; + }): Promise + clearVideo({ mediaId }: { mediaId: string }): void + clearAll(): void + getStats() + } + export const videoCache + +## apps/web/src/services/renderer + +canvas-renderer.ts + export type CanvasRendererParams = { + width: number; + height: number; + fps: number; + } + export class CanvasRenderer { + canvas: OffscreenCanvas | HTMLCanvasElement + context: OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D + width: number + height: number + fps: number + constructor({ width, height, fps }: CanvasRendererParams) + setSize({ width, height }: { width: number; height: number }) + async render({ node, time }: { node: BaseNode; time: number }) + async renderToCanvas({ + node, + time, + targetCanvas, + }: { + node: BaseNode; + time: number; + targetCanvas: HTMLCanvasElement; + }) + } + +scene-builder.ts + export type BuildSceneParams = { + canvasSize: TCanvasSize; + tracks: TimelineTrack[]; + mediaAssets: MediaAsset[]; + duration: numb... + export function buildScene(params: BuildSceneParams) + +scene-exporter.ts + export type ExportFormat = "mp4" | "webm" + export type ExportQuality = "low" | "medium" | "high" | "very_high" + export type SceneExporterEvents = { + progress: [progress: number]; + complete: [buffer: ArrayBuffer]; + error: [error: Error]; + cance... + export class SceneExporter extends EventEmitter { + renderer: CanvasRenderer + format: ExportFormat + quality: ExportQuality + shouldIncludeAudio: boolean + audioBuffer: AudioBuffer + isCancelled + constructor({ + width, + height, + fps, + format, + quality, + shouldIncludeAudio, + audioBuffer, + }: ExportParams) + cancel(): void + async export({ + rootNode, + }: { + rootNode: RootNode; + }): Promise + } + +## apps/web/src/services/storage + +indexeddb-adapter.ts + export class IndexedDBAdapter implements StorageAdapter { + dbName: string + storeName: string + version: number + constructor(dbName: string, storeName: string, version = 1) + async get(key: string): Promise + async set(key: string, value: T): Promise + async remove(key: string): Promise + async list(): Promise + async getAll(): Promise + async clear(): Promise + } + +opfs-adapter.ts + export class OPFSAdapter implements StorageAdapter { + directoryName: string + constructor(directoryName = "media") + async get(key: string): Promise + async set(key: string, file: File): Promise + async remove(key: string): Promise + async list(): Promise + async clear(): Promise + static isSupported(): boolean + } + +storage-service.ts + export const storageService + +types.ts + export interface StorageAdapter { + get(key: string): Promise + set(key: string, value: T): Promise + remove(key: string): Promise + list(): Promise + clear(): Promise + } + export interface MediaAssetData { + id: string + name: string + type: MediaType + size: number + lastModified: number + width?: number + height?: number + duration?: number + fps?: number + ephemeral?: boolean + thumbnailUrl?: string + sourceStickerIconName?: string + } + export type SerializedScene = Omit & { + createdAt: string; + updatedAt: string; + } + export type SerializedProjectMetadata = Omit< + TProjectMetadata, + "createdAt" | "updatedAt" + > & { + createdAt: string; + updatedAt: string; + } + export type SerializedProject = Omit & { + metadata: SerializedProjectMetadata; + scenes: Serializ... + export interface StorageConfig { + projectsDb: string + mediaDb: string + savedSoundsDb: string + version: number + } + +## apps/web/src/services/transcription + +index.ts + export const transcriptionService + +worker.ts + export type WorkerMessage = | { type: "init"; modelId: string } + | { type: "transcribe"; audio: Float32Array; language: strin... + export type WorkerResponse = | { type: "init-progress"; progress: number } + | { type: "init-complete" } + | { type: "init-error... + +## apps/web/src/stores + +assets-panel-store.tsx + export const TAB_KEYS + export type Tab = (typeof TAB_KEYS)[number] + export const tabs + export const useAssetsPanelStore + +editor-store.ts + export const useEditorStore + +keybindings-store.ts + export const defaultKeybindings: KeybindingConfig + export interface KeybindingConflict { + key: ShortcutKey + existingAction: TActionWithOptionalArgs + newAction: TActionWithOptionalArgs + } + export const useKeybindingsStore + +panel-store.ts + export interface PanelSizes { + tools: number + preview: number + properties: number + mainContent: number + timeline: number + } + export type PanelId = keyof PanelSizes + export const usePanelStore + +sounds-store.ts + export const useSoundsStore + +stickers-store.ts + export const useStickersStore + +text-properties-store.ts + export type TextPropertiesTab = "text" | "transform" + export interface TextPropertiesTabMeta { + value: TextPropertiesTab + label: string + } + export const TEXT_PROPERTIES_TABS: ReadonlyArray + export function isTextPropertiesTab(value: string) + export const useTextPropertiesStore + +timeline-store.ts + export const useTimelineStore + +## apps/web/src/types + +assets.ts + export type MediaType = "image" | "video" | "audio" + export interface MediaAsset extends Omit { + file: File + url?: string + } + +blog.ts + export type Post = { + id: string; + slug: string; + title: string; + content: string; + description: string; + coverImage... + export type Pagination = { + limit: number; + currpage: number; + nextPage: number | null; + prevPage: number | null; + totalIt... + export type MarblePostList = { + posts: Post[]; + pagination: Pagination; + } + export type MarblePost = { + post: Post; + } + export type Tag = { + id: string; + name: string; + slug: string; + } + export type MarbleTag = { + tag: Tag; + } + export type MarbleTagList = { + tags: Tag[]; + pagination: Pagination; + } + export type Category = { + id: string; + name: string; + slug: string; + } + export type MarbleCategory = { + category: Category; + } + export type MarbleCategoryList = { + categories: Category[]; + pagination: Pagination; + } + export type Author = { + id: string; + name: string; + image: string; + } + export type MarbleAuthor = { + author: Author; + } + export type MarbleAuthorList = { + authors: Author[]; + pagination: Pagination; + } + +drag.ts + export interface MediaDragData extends BaseDragData { + type: "media" + mediaType: "image" | "video" | "audio" + } + export interface TextDragData extends BaseDragData { + type: "text" + content: string + } + export interface StickerDragData extends BaseDragData { + type: "sticker" + iconName: string + } + export type TimelineDragData = MediaDragData | TextDragData | StickerDragData + +editor.ts + export type TPlatformLayout = "tiktok" + +export.ts + export const EXPORT_QUALITY_VALUES + export const EXPORT_FORMAT_VALUES + export type ExportFormat = (typeof EXPORT_FORMAT_VALUES)[number] + export type ExportQuality = (typeof EXPORT_QUALITY_VALUES)[number] + export interface ExportOptions { + format: ExportFormat + quality: ExportQuality + fps?: number + includeAudio?: boolean + onProgress?: ({ progress }: { progress: number }) => void + onCancel?: () => boolean + } + export interface ExportResult { + success: boolean + buffer?: ArrayBuffer + error?: string + cancelled?: boolean + } + +keybinding.ts + export type ModifierKeys = | "ctrl" + | "alt" + | "shift" + | "ctrl+shift" + | "alt+shift" + | "ctrl+alt" + | "ctrl+alt+shift" + export type Key = | "a" + | "b" + | "c" + | "d" + | "e" + | "f" + | "g" + | "h" + | "i" + | "j" + | "k" + | "l" + | "m" + | "n" + ... + export type ModifierBasedShortcutKey = `${ModifierKeys}+${Key}` + export type SingleCharacterShortcutKey = `${Key}` + export type ShortcutKey = ModifierBasedShortcutKey | SingleCharacterShortcutKey + export type KeybindingConfig = { + [key in ShortcutKey]?: TActionWithOptionalArgs; + } + +language.ts + export type Language = (typeof LANGUAGES)[number] + export type LanguageCode = Language["code"] + +project.ts + export type TBackground = | { + type: "color"; + color: string; + } + | { + type: "blur"; + blurIntensity: number; + } + export interface TCanvasSize { + width: number + height: number + } + export interface TProjectMetadata { + id: string + name: string + thumbnail?: string + duration: number + createdAt: Date + updatedAt: Date + } + export interface TProjectSettings { + fps: number + canvasSize: TCanvasSize + originalCanvasSize?: TCanvasSize | null + background: TBackground + } + export interface TTimelineViewState { + zoomLevel: number + scrollLeft: number + playheadTime: number + } + export interface TProject { + metadata: TProjectMetadata + scenes: TScene[] + currentSceneId: string + settings: TProjectSettings + version: number + timelineViewState?: TTimelineViewState + } + export type TProjectSortKey = "createdAt" | "updatedAt" | "name" | "duration" + export type TSortOrder = "asc" | "desc" + export type TProjectSortOption = `${TProjectSortKey}-${TSortOrder}` + +sounds.ts + export interface SoundEffect { + id: number + name: string + description: string + url: string + previewUrl?: string + downloadUrl?: string + duration: number + filesize: number + type: string + channels: number + bitrate: number + bitdepth: number + samplerate: number + username: string + tags: string[] + license: string + created: string + downloads: number + rating: number + ratingCount: number + } + export interface SavedSound { + id: number + name: string + username: string + previewUrl?: string + downloadUrl?: string + duration: number + tags: string[] + license: string + savedAt: string + } + export interface SavedSoundsData { + sounds: SavedSound[] + lastModified: string + } + +stickers.ts + export type StickerCategory = (typeof STICKER_CATEGORIES)[number] + +time.ts + export type TTimeCode = "MM:SS" | "HH:MM:SS" | "HH:MM:SS:CS" | "HH:MM:SS:FF" + +timeline.ts + export interface TScene { + id: string + name: string + isMain: boolean + tracks: TimelineTrack[] + bookmarks: number[] + createdAt: Date + updatedAt: Date + } + export type TrackType = "video" | "text" | "audio" | "sticker" + export interface VideoTrack extends BaseTrack { + type: "video" + elements: (VideoElement | ImageElement)[] + isMain: boolean + muted: boolean + hidden: boolean + } + export interface TextTrack extends BaseTrack { + type: "text" + elements: TextElement[] + hidden: boolean + } + export interface AudioTrack extends BaseTrack { + type: "audio" + elements: AudioElement[] + muted: boolean + } + export interface StickerTrack extends BaseTrack { + type: "sticker" + elements: StickerElement[] + hidden: boolean + } + export type TimelineTrack = VideoTrack | TextTrack | AudioTrack | StickerTrack + export interface Transform { + scale: number + position: { + x: number; + y: number; + } + rotate: number + } + export interface UploadAudioElement extends BaseAudioElement { + sourceType: "upload" + mediaId: string + } + export interface LibraryAudioElement extends BaseAudioElement { + sourceType: "library" + sourceUrl: string + } + export type AudioElement = UploadAudioElement | LibraryAudioElement + export interface VideoElement extends BaseTimelineElement { + type: "video" + mediaId: string + muted?: boolean + hidden?: boolean + transform: Transform + opacity: number + } + export interface ImageElement extends BaseTimelineElement { + type: "image" + mediaId: string + hidden?: boolean + transform: Transform + opacity: number + } + export interface TextElement extends BaseTimelineElement { + type: "text" + content: string + fontSize: number + fontFamily: string + color: string + backgroundColor: string + textAlign: "left" | "center" | "right" + fontWeight: "normal" | "bold" + fontStyle: "normal" | "italic" + textDecoration: "none" | "underline" | "line-through" + hidden?: boolean + transform: Transform + opacity: number + } + export interface StickerElement extends BaseTimelineElement { + type: "sticker" + iconName: string + hidden?: boolean + transform: Transform + opacity: number + color?: string + } + export type TimelineElement = | AudioElement + | VideoElement + | ImageElement + | TextElement + | StickerElement + export type ElementType = TimelineElement["type"] + export type CreateUploadAudioElement = Omit + export type CreateLibraryAudioElement = Omit + export type CreateAudioElement = | CreateUploadAudioElement + | CreateLibraryAudioElement + export type CreateVideoElement = Omit + export type CreateImageElement = Omit + export type CreateTextElement = Omit + export type CreateStickerElement = Omit + export type CreateTimelineElement = | CreateAudioElement + | CreateVideoElement + | CreateImageElement + | CreateTextElement + | CreateSt... + export interface ElementDragState { + isDragging: boolean + elementId: string | null + trackId: string | null + startMouseX: number + startMouseY: number + startElementTime: number + clickOffsetTime: number + currentTime: number + currentMouseY: number + } + export interface DropTarget { + trackIndex: number + isNewTrack: boolean + insertPosition: "above" | "below" | null + xPosition: number + } + export interface ComputeDropTargetParams { + elementType: ElementType + mouseX: number + mouseY: number + tracks: TimelineTrack[] + playheadTime: number + isExternalDrop: boolean + elementDuration: number + pixelsPerSecond: number + zoomLevel: number + verticalDragDirection?: "up" | "down" | null + startTimeOverride?: number + excludeElementId?: string + } + export interface ClipboardItem { + trackId: string + trackType: TrackType + element: CreateTimelineElement + } + +transcription.ts + export type TranscriptionLanguage = LanguageCode | "auto" + export interface TranscriptionSegment { + text: string + start: number + end: number + } + export interface TranscriptionResult { + text: string + segments: TranscriptionSegment[] + language: string + } + export type TranscriptionStatus = | "idle" + | "loading-model" + | "transcribing" + | "complete" + | "error" + export interface TranscriptionProgress { + status: TranscriptionStatus + progress: number + message?: string + } + export type TranscriptionModelId = | "whisper-tiny" + | "whisper-small" + | "whisper-medium" + | "whisper-large-v3-turbo" + export interface TranscriptionModel { + id: TranscriptionModelId + name: string + huggingFaceId: string + description: string + } + export interface CaptionChunk { + text: string + startTime: number + duration: number + } + +## apps/web/src/utils + +browser.ts + export function isTypableDOMElement({ + element, + }: { + element: HTMLElement; + }): boolean + +date.ts + export function formatDate({ date }: { date: Date }): string + +geometry.ts + export function dimensionToAspectRatio({ + width, + height, + }: { + width: number; + height: number; + }): string + +id.ts + export function generateUUID(): string + +math.ts + export function clamp({ + value, + min, + max, + }: { + value: number; + min: number; + max: number; + }): number + +platform.ts + export function getPlatformSpecialKey(): string + export function getPlatformAlternateKey(): string + export function isAppleDevice(): boolean + +string.ts + export function capitalizeFirstLetter({ string }: { string: string }) + export function uppercase({ string }: { string: string }) + +ui.ts + export function cn(...inputs: ClassValue[]): string + +## packages/ui/src/icons + +brand.tsx + export function OcVercelIcon({ className }: { className?: string }) + export function OcMarbleIcon({ + className = "", + size = 32, + }: { + className?: string; + size?: number; + }) + export function OcDataBuddyIcon({ + className = "", + size = 32, + }: { + className?: string; + size?: number; + }) + +ui.tsx + export function OcVideoIcon({ + className = "", + size = 32, + }: { + className?: string; + size?: number; + }) + +``` + +--- + +*Generated and maintained by [Twiggy](https://github.com/twiggy-tools/Twiggy)* -- cgit v1.3.1