2131 lines
52 KiB
Plaintext
2131 lines
52 KiB
Plaintext
---
|
|
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
|
|
|
|
middleware.ts
|
|
export function middleware()
|
|
export const config
|
|
|
|
## apps/web/src/constants
|
|
|
|
captions-constants.ts
|
|
export const LANGUAGES: Language[]
|
|
|
|
editor-constants.ts
|
|
export const PLATFORM_LAYOUTS: Record<TPlatformLayout, string>
|
|
|
|
export-constants.ts
|
|
export const DEFAULT_EXPORT_OPTIONS: ExportOptions
|
|
|
|
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[] => ...
|
|
|
|
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<TextElement, "id">
|
|
|
|
timeline-constants.tsx
|
|
export const TRACK_COLORS: Record<
|
|
TrackType,
|
|
{ solid: string; background: string; border: string }
|
|
>
|
|
export const TRACK_HEIGHTS: Record<TrackType, number>
|
|
export const TRACK_GAP
|
|
export const TIMELINE_CONSTANTS
|
|
export const TRACK_ICONS: Record<TrackType, React.ReactNode>
|
|
|
|
## 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
|
|
static getInstance(): EditorCore
|
|
// ... and 2 more
|
|
}
|
|
|
|
## apps/web/src/core/managers
|
|
|
|
commands.ts
|
|
export class CommandManager {
|
|
history: Command[]
|
|
redoStack: Command[]
|
|
execute({ command }: { command: Command }): Command
|
|
undo(): void
|
|
redo(): void
|
|
canUndo(): boolean
|
|
canRedo(): boolean
|
|
clear(): void
|
|
}
|
|
|
|
media-manager.ts
|
|
export class MediaManager {
|
|
assets: MediaAsset[]
|
|
isLoading
|
|
listeners
|
|
constructor(private editor: EditorCore)
|
|
async addMediaAsset({
|
|
|
|
projectId,
|
|
|
|
asset,
|
|
|
|
}: {
|
|
|
|
projectId: string;
|
|
|
|
asset: Omit<MediaAsset, "id">;
|
|
|
|
}): Promise<void>
|
|
async removeMediaAsset({
|
|
|
|
projectId,
|
|
|
|
id,
|
|
|
|
}: {
|
|
|
|
projectId: string;
|
|
|
|
id: string;
|
|
|
|
}): Promise<void>
|
|
async loadProjectMedia({ projectId }: { projectId: string }): Promise<void>
|
|
async clearProjectMedia({ projectId }: { projectId: string }): Promise<void>
|
|
clearAllAssets(): void
|
|
getAssets(): MediaAsset[]
|
|
// ... and 3 more
|
|
}
|
|
|
|
playback-manager.ts
|
|
export class PlaybackManager {
|
|
isPlaying
|
|
currentTime
|
|
volume
|
|
muted
|
|
previousVolume
|
|
speed
|
|
listeners
|
|
playbackTimer: number | null
|
|
lastUpdate
|
|
constructor(private editor: EditorCore)
|
|
// ... and 16 more
|
|
}
|
|
|
|
project-manager.ts
|
|
export interface MigrationState {
|
|
isMigrating: boolean
|
|
fromVersion: number | null
|
|
toVersion: number | null
|
|
projectName: string | null
|
|
}
|
|
export class ProjectManager {
|
|
active: TProject | null
|
|
savedProjects: TProjectMetadata[]
|
|
isLoading
|
|
isInitialized
|
|
invalidProjectIds
|
|
storageMigrationPromise: Promise<void> | null
|
|
listeners
|
|
migrationState: MigrationState
|
|
constructor(private editor: EditorCore)
|
|
async createNewProject({ name }: { name: string }): Promise<string>
|
|
// ... and 22 more
|
|
}
|
|
|
|
renderer-manager.ts
|
|
export class RendererManager {
|
|
renderTree: RootNode | null
|
|
listeners
|
|
constructor(private editor: EditorCore)
|
|
setRenderTree({ renderTree }: { renderTree: RootNode | null }): void
|
|
getRenderTree(): RootNode | null
|
|
async exportProject({
|
|
options,
|
|
}: {
|
|
options: ExportOptions;
|
|
}): Promise<ExportResult>
|
|
subscribe(listener: () => void): () => void
|
|
}
|
|
|
|
save-manager.ts
|
|
export class SaveManager {
|
|
debounceMs: number
|
|
isPaused
|
|
isSaving
|
|
hasPendingSave
|
|
saveTimer: ReturnType<typeof setTimeout> | null
|
|
unsubscribeHandlers: Array<() => void>
|
|
constructor(
|
|
|
|
private editor: EditorCore,
|
|
|
|
{ debounceMs = 800 }: SaveManagerOptions = {},
|
|
|
|
)
|
|
start(): void
|
|
stop(): void
|
|
pause(): void
|
|
// ... and 3 more
|
|
}
|
|
|
|
scenes-manager.ts
|
|
export class ScenesManager {
|
|
active: TScene | null
|
|
list: TScene[]
|
|
listeners
|
|
constructor(private editor: EditorCore)
|
|
async createScene({
|
|
|
|
name,
|
|
|
|
isMain = false,
|
|
|
|
}: {
|
|
|
|
name: string;
|
|
|
|
isMain: boolean;
|
|
|
|
}): Promise<string>
|
|
async deleteScene({ sceneId }: { sceneId: string }): Promise<void>
|
|
async renameScene({
|
|
|
|
sceneId,
|
|
|
|
name,
|
|
|
|
}: {
|
|
|
|
sceneId: string;
|
|
|
|
name: string;
|
|
|
|
}): Promise<void>
|
|
async switchToScene({ sceneId }: { sceneId: string }): Promise<void>
|
|
async toggleBookmark({ time }: { time: number }): Promise<void>
|
|
isBookmarked({ time }: { time: number }): boolean
|
|
// ... and 9 more
|
|
}
|
|
|
|
timeline-manager.ts
|
|
export class TimelineManager {
|
|
listeners
|
|
constructor(private editor: EditorCore)
|
|
addTrack({ type, index }: { type: TrackType; index?: number }): string
|
|
removeTrack({ trackId }: { trackId: string }): void
|
|
addElementToTrack({
|
|
trackId,
|
|
element,
|
|
}: {
|
|
trackId: string;
|
|
element: CreateTimelineElement;
|
|
}): void
|
|
updateElementTrim({
|
|
elementId,
|
|
trimStart,
|
|
trimEnd,
|
|
pushHistory = true,
|
|
}: {
|
|
elementId: string;
|
|
trimStart: number;
|
|
trimEnd: number;
|
|
pushHistory?: boolean;
|
|
}): void
|
|
updateElementDuration({
|
|
trackId,
|
|
elementId,
|
|
duration,
|
|
pushHistory = true,
|
|
}: {
|
|
trackId: string;
|
|
elementId: string;
|
|
duration: number;
|
|
pushHistory?: boolean;
|
|
}): void
|
|
updateElementStartTime({
|
|
elements,
|
|
startTime,
|
|
}: {
|
|
elements: { trackId: string; elementId: string }[];
|
|
startTime: number;
|
|
}): void
|
|
moveElement({
|
|
sourceTrackId,
|
|
targetTrackId,
|
|
elementId,
|
|
newStartTime,
|
|
createTrack,
|
|
}: {
|
|
sourceTrackId: string;
|
|
targetTrackId: string;
|
|
elementId: string;
|
|
newStartTime: number;
|
|
createTrack?: { type: TrackType; index: number };
|
|
}): void
|
|
toggleTrackMute({ trackId }: { trackId: string }): void
|
|
// ... and 14 more
|
|
}
|
|
|
|
## apps/web/src/data/colors
|
|
|
|
pattern-craft.ts
|
|
export const patternCraftGradients
|
|
|
|
solid.ts
|
|
export const colors
|
|
|
|
syntax-ui.tsx
|
|
export const syntaxUIGradients
|
|
|
|
## 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()
|
|
export const bindings
|
|
|
|
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-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<A>,
|
|
|
|
isActive: TActionHandlerOptions,
|
|
|
|
)
|
|
|
|
use-editor-actions.ts
|
|
export function useEditorActions()
|
|
|
|
## 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({
|
|
|
|
rulerScrollRef,
|
|
|
|
tracksScrollRef,
|
|
|
|
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,
|
|
zoomLevel,
|
|
isSnappingEnabled = true,
|
|
}: UseTimelineDragDropProps)
|
|
|
|
use-timeline-interactions.ts
|
|
export function useTimelineInteractions({
|
|
playheadRef,
|
|
trackLabelsRef,
|
|
rulerScrollRef,
|
|
tracksScrollRef,
|
|
zoomLevel,
|
|
duration,
|
|
isSelecting,
|
|
clearSelectedElements,
|
|
seek,
|
|
}: UseTimelineInteractionsProps)
|
|
|
|
use-timeline-playhead.ts
|
|
export function useTimelinePlayhead({
|
|
zoomLevel,
|
|
rulerRef,
|
|
rulerScrollRef,
|
|
tracksScrollRef,
|
|
playheadRef,
|
|
}: UseTimelinePlayheadProps)
|
|
|
|
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,
|
|
|
|
isInTimeline = false,
|
|
|
|
}: UseTimelineZoomProps): UseTimelineZoomReturn
|
|
|
|
## apps/web/src/hooks/timeline/element
|
|
|
|
use-element-interaction.ts
|
|
export function useElementInteraction({
|
|
zoomLevel,
|
|
timelineRef,
|
|
tracksContainerRef,
|
|
tracksScrollRef,
|
|
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,
|
|
}: UseTimelineElementResizeProps)
|
|
|
|
use-element-selection.ts
|
|
export function useElementSelection()
|
|
|
|
## apps/web/src/lib
|
|
|
|
audio-utils.ts
|
|
export type CollectedAudioElement = Omit<
|
|
|
|
AudioElement,
|
|
|
|
"type" | "mediaId" | "volume" | "id" | "name" | "sourceType" | "sourceU...
|
|
export function createAudioContext(): AudioContext
|
|
export function collectAudioElements({
|
|
|
|
tracks,
|
|
|
|
mediaAssets,
|
|
|
|
audioContext,
|
|
|
|
}: {
|
|
|
|
tracks: TimelineTrack[];
|
|
|
|
mediaAssets: MediaAsset[];
|
|
|
|
audioContext: AudioContext;
|
|
|
|
}): Promise<CollectedAudioElement[]>
|
|
export function collectAudioMixSources({
|
|
|
|
tracks,
|
|
|
|
mediaAssets,
|
|
|
|
}: {
|
|
|
|
tracks: TimelineTrack[];
|
|
|
|
mediaAssets: MediaAsset[];
|
|
|
|
}): Promise<AudioMixSource[]>
|
|
export function createTimelineAudioBuffer({
|
|
|
|
tracks,
|
|
|
|
mediaAssets,
|
|
|
|
duration,
|
|
|
|
sampleRate = 44100,
|
|
|
|
}: {
|
|
|
|
tracks: TimelineTrack[];
|
|
|
|
mediaAssets: MediaAsset[];
|
|
|
|
duration: number;
|
|
|
|
sampleRate?: number;
|
|
|
|
}): Promise<AudioBuffer | null>
|
|
|
|
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<string>
|
|
|
|
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
|
|
|
|
editor-utils.ts
|
|
export function dimensionToAspectRatio({
|
|
width,
|
|
height,
|
|
}: {
|
|
width: number;
|
|
height: number;
|
|
}): string
|
|
|
|
export-utils.ts
|
|
export function exportProject({
|
|
format,
|
|
quality,
|
|
fps,
|
|
includeAudio,
|
|
onProgress,
|
|
onCancel,
|
|
}: ExportOptions): Promise<ExportResult>
|
|
export function getExportMimeType({
|
|
format,
|
|
}: {
|
|
format: "mp4" | "webm";
|
|
}): string
|
|
export function getExportFileExtension({
|
|
format,
|
|
}: {
|
|
format: "mp4" | "webm";
|
|
}): 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<string, IconSet>
|
|
}
|
|
export interface CollectionInfo {
|
|
prefix: string
|
|
total: number
|
|
title?: string
|
|
uncategorized?: string[]
|
|
categories?: Record<string, string[]>
|
|
hidden?: string[]
|
|
aliases?: Record<string, string>
|
|
}
|
|
export function getCollections(
|
|
|
|
category?: string
|
|
|
|
): Promise<Record<string, IconSet>>
|
|
export function getCollection(
|
|
|
|
prefix: string
|
|
|
|
): Promise<CollectionInfo | null>
|
|
export function searchIcons(
|
|
|
|
query: string,
|
|
|
|
limit: number = 64,
|
|
|
|
prefixes?: string[],
|
|
|
|
category?: string
|
|
|
|
): Promise<IconSearchResult>
|
|
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<typeof buildIconSvgUrl>[2]
|
|
|
|
): string
|
|
export function downloadSvgAsText(
|
|
|
|
iconName: string,
|
|
|
|
params?: Parameters<typeof getIconSvgUrl>[1]
|
|
|
|
): Promise<string>
|
|
export function svgToFile(svgText: string, fileName: string): File
|
|
export const POPULAR_COLLECTIONS
|
|
export function getCategoriesFromCollections(
|
|
|
|
collections: Record<string, IconSet>
|
|
|
|
): string[]
|
|
|
|
keyboard-utils.ts
|
|
export function getPlatformSpecialKey(): string
|
|
export function getPlatformAlternateKey(): string
|
|
|
|
media-processing-utils.ts
|
|
export interface ProcessedMediaAsset extends Omit<MediaAsset, "id">
|
|
export function generateThumbnail({
|
|
videoFile,
|
|
timeInSeconds,
|
|
}: {
|
|
videoFile: File;
|
|
timeInSeconds: number;
|
|
}): Promise<string>
|
|
export function processMediaAssets({
|
|
files,
|
|
onProgress,
|
|
}: {
|
|
files: FileList | File[];
|
|
onProgress?: ({ progress }: { progress: number }) => void;
|
|
}): Promise<ProcessedMediaAsset[]>
|
|
|
|
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-utils.ts
|
|
export const initFFmpeg = (): Promise<FFmpeg> => ...
|
|
export function getVideoInfo({
|
|
|
|
videoFile,
|
|
|
|
}: {
|
|
|
|
videoFile: File;
|
|
|
|
}): Promise<{
|
|
|
|
duration: number;
|
|
|
|
width: number;
|
|
|
|
height: number;
|
|
|
|
fps: number;
|
|
|
|
}>
|
|
export const extractTimelineAudio = (
|
|
|
|
onProgress?: (progress: number) => void,
|
|
|
|
): Promise<Blob> => ...
|
|
|
|
rate-limit.ts
|
|
export const baseRateLimit
|
|
export function checkRateLimit({ request }: { request: Request })
|
|
|
|
scene-utils.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 updateSceneInArray({
|
|
|
|
scenes,
|
|
|
|
sceneId,
|
|
|
|
updates,
|
|
|
|
}: {
|
|
|
|
scenes: TScene[];
|
|
|
|
sceneId: string;
|
|
|
|
updates: Partial<TScene>;
|
|
|
|
}): TScene[]
|
|
|
|
time-utils.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
|
|
|
|
transcription-utils.ts
|
|
export function isTranscriptionConfigured()
|
|
|
|
utils.ts
|
|
export function cn(...inputs: ClassValue[]): string
|
|
export function formatDate({ date }: { date: Date }): string
|
|
export function capitalizeFirstLetter({ string }: { string: string })
|
|
export function generateUUID(): string
|
|
export function isTypableDOMElement({
|
|
|
|
element,
|
|
|
|
}: {
|
|
|
|
element: HTMLElement;
|
|
|
|
}): boolean
|
|
export function isAppleDevice(): boolean
|
|
export function clamp({
|
|
|
|
value,
|
|
|
|
min,
|
|
|
|
max,
|
|
|
|
}: {
|
|
|
|
value: number;
|
|
|
|
min: number;
|
|
|
|
max: number;
|
|
|
|
}): number
|
|
|
|
video-cache.ts
|
|
export class VideoCache {
|
|
sinks
|
|
initPromises
|
|
async getFrameAt(
|
|
|
|
mediaId: string,
|
|
|
|
file: File,
|
|
|
|
time: number
|
|
|
|
): Promise<WrappedCanvas | null>
|
|
clearVideo(mediaId: string): void
|
|
clearAll(): void
|
|
getStats()
|
|
}
|
|
export const videoCache
|
|
|
|
zk-encryption.ts
|
|
export interface ZeroKnowledgeEncryptionResult {
|
|
encryptedData: ArrayBuffer
|
|
key: ArrayBuffer
|
|
iv: ArrayBuffer
|
|
}
|
|
export function encryptWithRandomKey(
|
|
|
|
data: ArrayBuffer
|
|
|
|
): Promise<ZeroKnowledgeEncryptionResult>
|
|
export function arrayBufferToBase64(buffer: ArrayBuffer): string
|
|
export function base64ToArrayBuffer(base64: string): ArrayBuffer
|
|
|
|
## apps/web/src/lib/actions
|
|
|
|
definitions.ts
|
|
export type TActionCategory = | "playback"
|
|
|
|
| "navigation"
|
|
|
|
| "editing"
|
|
|
|
| "selection"
|
|
|
|
| "history"
|
|
|
|
| "timeline"
|
|
|
|
...
|
|
export interface TActionDefinition {
|
|
description: string
|
|
category: TActionCategory
|
|
defaultShortcuts?: ShortcutKey[]
|
|
args?: Record<string, unknown>
|
|
}
|
|
export const ACTIONS
|
|
export type TAction = keyof typeof ACTIONS
|
|
export function getActionDefinition(action: TAction): TActionDefinition
|
|
export function getDefaultShortcuts(): Record<ShortcutKey, TAction>
|
|
|
|
registry.ts
|
|
export function bindAction(
|
|
|
|
action: A,
|
|
|
|
handler: TActionFunc<A>,
|
|
|
|
)
|
|
export function unbindAction(
|
|
|
|
action: A,
|
|
|
|
handler: TActionFunc<A>,
|
|
|
|
)
|
|
export const invokeAction = (
|
|
|
|
action: A,
|
|
|
|
args?: TArgOfAction<A>,
|
|
|
|
trigger?: TInvocationTrigger,
|
|
|
|
) => ...
|
|
|
|
types.ts
|
|
export type TActionArgsMap = {
|
|
|
|
"seek-forward": { seconds: number } | undefined;
|
|
|
|
"seek-backward": { seconds: number } | u...
|
|
export type TActionWithArgs = keyof TActionArgsMap
|
|
export type TActionWithOptionalArgs = | TActionWithNoArgs
|
|
|
|
| TKeysWithValueUndefined<TActionArgsMap>
|
|
export type TActionWithNoArgs = Exclude<TAction, TActionWithArgs>
|
|
export type TArgOfAction<A extends TAction> = A extends TActionWithArgs
|
|
|
|
? TActionArgsMap[A]
|
|
|
|
: undefined
|
|
export type TActionFunc<A extends TAction> = A extends TActionWithArgs
|
|
|
|
? (arg: TArgOfAction<A>, trigger?: TInvocationTrigger) => void
|
|
|
|
: ...
|
|
export type TInvocationTrigger = "keypress" | "mouseclick"
|
|
export type TBoundActionList = {
|
|
|
|
[A in TAction]?: Array<TActionFunc<A>>;
|
|
|
|
}
|
|
export type TActionHandlerOptions = | MutableRefObject<boolean>
|
|
|
|
| boolean
|
|
|
|
| undefined
|
|
|
|
## apps/web/src/lib/auth
|
|
|
|
server.ts
|
|
export const auth
|
|
export type Auth = typeof auth
|
|
|
|
## apps/web/src/lib/canvas
|
|
|
|
gradients.ts
|
|
export function drawCssBackground({
|
|
|
|
ctx,
|
|
|
|
width,
|
|
|
|
height,
|
|
|
|
css,
|
|
|
|
}: {
|
|
|
|
ctx: CanvasRenderingContext2D;
|
|
|
|
width: number;
|
|
|
|
height: number;
|
|
|
|
css: string;
|
|
|
|
}): void
|
|
|
|
## apps/web/src/lib/commands/media
|
|
|
|
add-media-asset.ts
|
|
export class AddMediaAssetCommand extends Command {
|
|
assetId: string
|
|
savedAssets: MediaAsset[] | null
|
|
createdAsset: MediaAsset | null
|
|
constructor(
|
|
|
|
private projectId: string,
|
|
|
|
private asset: Omit<MediaAsset, "id">,
|
|
|
|
)
|
|
execute(): void
|
|
undo(): void
|
|
getAssetId(): string
|
|
}
|
|
|
|
remove-media-asset.ts
|
|
export class RemoveMediaAssetCommand extends Command {
|
|
savedAssets: MediaAsset[] | null
|
|
savedTracks: TimelineTrack[] | null
|
|
removedAsset: MediaAsset | null
|
|
constructor(
|
|
|
|
private projectId: string,
|
|
|
|
private assetId: string,
|
|
|
|
)
|
|
execute(): void
|
|
undo(): void
|
|
}
|
|
|
|
## apps/web/src/lib/commands/project
|
|
|
|
update-project-settings.ts
|
|
export class UpdateProjectSettingsCommand extends Command {
|
|
savedSettings: TProjectSettings | null
|
|
savedUpdatedAt: Date | null
|
|
constructor(private updates: Partial<TProjectSettings>)
|
|
execute(): void
|
|
undo(): void
|
|
}
|
|
|
|
## apps/web/src/lib/commands/scene
|
|
|
|
create-scene.ts
|
|
export class CreateSceneCommand extends Command {
|
|
savedScenes: TScene[] | null
|
|
createdScene: TScene | null
|
|
constructor(
|
|
|
|
private name: string,
|
|
|
|
private isMain: boolean = false,
|
|
|
|
)
|
|
execute(): void
|
|
undo(): void
|
|
getSceneId(): string
|
|
}
|
|
|
|
delete-scene.ts
|
|
export class DeleteSceneCommand extends Command {
|
|
savedScenes: TScene[] | null
|
|
savedActiveSceneId: string | null
|
|
deletedScene: TScene | null
|
|
constructor(private sceneId: string)
|
|
execute(): void
|
|
undo(): void
|
|
}
|
|
|
|
remove-bookmark.ts
|
|
export class RemoveBookmarkCommand extends Command {
|
|
savedScenes: TScene[] | null
|
|
frameTime: number
|
|
constructor(private time: number)
|
|
execute(): void
|
|
undo(): void
|
|
}
|
|
|
|
rename-scene.ts
|
|
export class RenameSceneCommand extends Command {
|
|
savedScenes: TScene[] | null
|
|
previousName: string | null
|
|
constructor(
|
|
|
|
private sceneId: string,
|
|
|
|
private newName: string,
|
|
|
|
)
|
|
execute(): void
|
|
undo(): void
|
|
}
|
|
|
|
toggle-bookmark.ts
|
|
export class ToggleBookmarkCommand extends Command {
|
|
savedScenes: TScene[] | null
|
|
frameTime: number
|
|
constructor(private time: number)
|
|
execute(): void
|
|
undo(): void
|
|
}
|
|
|
|
## apps/web/src/lib/commands/timeline/clipboard
|
|
|
|
paste.ts
|
|
export class PasteCommand extends Command {
|
|
savedState: TimelineTrack[] | null
|
|
pastedElementIds: string[]
|
|
constructor(
|
|
private time: number,
|
|
private clipboardItems: ClipboardItem[],
|
|
)
|
|
execute(): void
|
|
undo(): void
|
|
}
|
|
|
|
## apps/web/src/lib/commands/timeline/element
|
|
|
|
add-element.ts
|
|
export class AddElementToTrackCommand extends Command {
|
|
elementId: string
|
|
savedState: TimelineTrack[] | null
|
|
constructor(
|
|
|
|
private trackId: string,
|
|
|
|
private element: CreateTimelineElement,
|
|
|
|
)
|
|
execute(): void
|
|
undo(): void
|
|
getElementId(): string
|
|
}
|
|
|
|
delete-elements.ts
|
|
export class DeleteElementsCommand extends Command {
|
|
savedState: TimelineTrack[] | null
|
|
constructor(private elements: { trackId: string; elementId: string }[])
|
|
execute(): void
|
|
undo(): void
|
|
}
|
|
|
|
duplicate-elements.ts
|
|
export class DuplicateElementsCommand extends Command {
|
|
duplicatedIds: string[]
|
|
savedState: TimelineTrack[] | null
|
|
elements: DuplicateElementsParams["elements"]
|
|
constructor({ elements }: DuplicateElementsParams)
|
|
execute(): void
|
|
undo(): void
|
|
}
|
|
|
|
move-element.ts
|
|
export class MoveElementCommand extends Command {
|
|
savedState: TimelineTrack[] | null
|
|
constructor(
|
|
private sourceTrackId: string,
|
|
private targetTrackId: string,
|
|
private elementId: string,
|
|
private newStartTime: number,
|
|
private createTrack?: { type: TrackType; index: number },
|
|
)
|
|
execute(): void
|
|
undo(): void
|
|
}
|
|
|
|
split-elements.ts
|
|
export class SplitElementsCommand extends Command {
|
|
savedState: TimelineTrack[] | null
|
|
newElementIds: string[]
|
|
affectedElementIds: Set<string>
|
|
constructor(
|
|
private elements: { trackId: string; elementId: string }[],
|
|
private splitTime: number,
|
|
private retainSide: "both" | "left" | "right" = "both",
|
|
)
|
|
splitElementIds(): string[]
|
|
execute(): void
|
|
undo(): void
|
|
}
|
|
|
|
toggle-elements-muted.ts
|
|
export class ToggleElementsMutedCommand extends Command {
|
|
savedState: TimelineTrack[] | null
|
|
constructor(private elements: { trackId: string; elementId: string }[])
|
|
execute(): void
|
|
undo(): void
|
|
}
|
|
|
|
toggle-elements-visibility.ts
|
|
export class ToggleElementsVisibilityCommand extends Command {
|
|
savedState: TimelineTrack[] | null
|
|
constructor(private elements: { trackId: string; elementId: string }[])
|
|
execute(): void
|
|
undo(): void
|
|
}
|
|
|
|
update-element-duration.ts
|
|
export class UpdateElementDurationCommand extends Command {
|
|
savedState: TimelineTrack[] | null
|
|
constructor(
|
|
private trackId: string,
|
|
private elementId: string,
|
|
private duration: number,
|
|
)
|
|
execute(): void
|
|
undo(): void
|
|
}
|
|
|
|
update-element-start-time.ts
|
|
export class UpdateElementStartTimeCommand extends Command {
|
|
savedState: TimelineTrack[] | null
|
|
constructor(
|
|
private elements: { trackId: string; elementId: string }[],
|
|
private startTime: number,
|
|
)
|
|
execute(): void
|
|
undo(): void
|
|
}
|
|
|
|
update-element-trim.ts
|
|
export class UpdateElementTrimCommand extends Command {
|
|
savedState: TimelineTrack[] | null
|
|
constructor(
|
|
private elementId: string,
|
|
private trimStart: number,
|
|
private trimEnd: number,
|
|
)
|
|
execute(): void
|
|
undo(): void
|
|
}
|
|
|
|
update-text-element.ts
|
|
export class UpdateTextElementCommand extends Command {
|
|
savedState: TimelineTrack[] | null
|
|
constructor(
|
|
private trackId: string,
|
|
private elementId: string,
|
|
private updates: Partial<
|
|
Pick<
|
|
TextElement,
|
|
| "content"
|
|
| "fontSize"
|
|
| "fontFamily"
|
|
| "color"
|
|
| "backgroundColor"
|
|
| "textAlign"
|
|
| "fontWeight"
|
|
| "fontStyle"
|
|
| "textDecoration"
|
|
>
|
|
>,
|
|
)
|
|
execute(): void
|
|
undo(): void
|
|
}
|
|
|
|
## apps/web/src/lib/commands/timeline/track
|
|
|
|
add-track.ts
|
|
export class AddTrackCommand extends Command {
|
|
trackId: string
|
|
savedState: TimelineTrack[] | null
|
|
constructor(
|
|
private type: TrackType,
|
|
private index?: number,
|
|
)
|
|
execute(): void
|
|
undo(): void
|
|
getTrackId(): string
|
|
}
|
|
|
|
remove-track.ts
|
|
export class RemoveTrackCommand extends Command {
|
|
savedState: TimelineTrack[] | null
|
|
constructor(private trackId: string)
|
|
execute(): void
|
|
undo(): void
|
|
}
|
|
|
|
toggle-track-mute.ts
|
|
export class ToggleTrackMuteCommand extends Command {
|
|
savedState: TimelineTrack[] | null
|
|
constructor(private trackId: string)
|
|
execute(): void
|
|
undo(): void
|
|
}
|
|
|
|
toggle-track-visibility.ts
|
|
export class ToggleTrackVisibilityCommand extends Command {
|
|
savedState: TimelineTrack[] | null
|
|
constructor(private trackId: string)
|
|
execute(): void
|
|
undo(): void
|
|
}
|
|
|
|
## apps/web/src/lib/db
|
|
|
|
index.ts
|
|
export const db
|
|
|
|
schema.ts
|
|
export const users
|
|
export const sessions
|
|
export const accounts
|
|
export const verifications
|
|
export const exportWaitlist
|
|
|
|
## apps/web/src/lib/schemas
|
|
|
|
waitlist.ts
|
|
export const exportWaitlistSchema
|
|
export const exportWaitlistResponseSchema
|
|
export type ExportWaitlistInput = z.infer<typeof exportWaitlistSchema>
|
|
export type ExportWaitlistResponse = z.infer<
|
|
|
|
typeof exportWaitlistResponseSchema
|
|
|
|
>
|
|
|
|
## apps/web/src/lib/storage
|
|
|
|
indexeddb-adapter.ts
|
|
export class IndexedDBAdapter implements StorageAdapter<T> {
|
|
dbName: string
|
|
storeName: string
|
|
version: number
|
|
constructor(dbName: string, storeName: string, version = 1)
|
|
async get(key: string): Promise<T | null>
|
|
async set(key: string, value: T): Promise<void>
|
|
async remove(key: string): Promise<void>
|
|
async list(): Promise<string[]>
|
|
async getAll(): Promise<T[]>
|
|
async clear(): Promise<void>
|
|
}
|
|
|
|
opfs-adapter.ts
|
|
export class OPFSAdapter implements StorageAdapter<File> {
|
|
directoryName: string
|
|
constructor(directoryName = "media")
|
|
async get(key: string): Promise<File | null>
|
|
async set(key: string, file: File): Promise<void>
|
|
async remove(key: string): Promise<void>
|
|
async list(): Promise<string[]>
|
|
async clear(): Promise<void>
|
|
static isSupported(): boolean
|
|
}
|
|
|
|
storage-service.ts
|
|
export const storageService
|
|
|
|
types.ts
|
|
export interface StorageAdapter<T> {
|
|
get(key: string): Promise<T | null>
|
|
set(key: string, value: T): Promise<void>
|
|
remove(key: string): Promise<void>
|
|
list(): Promise<string[]>
|
|
clear(): Promise<void>
|
|
}
|
|
export interface MediaAssetData {
|
|
id: string
|
|
name: string
|
|
type: MediaType
|
|
size: number
|
|
lastModified: number
|
|
width?: number
|
|
height?: number
|
|
duration?: number
|
|
fps?: number
|
|
ephemeral?: boolean
|
|
// ... and 2 more
|
|
}
|
|
export type SerializedScene = Omit<TScene, "createdAt" | "updatedAt"> & {
|
|
|
|
createdAt: string;
|
|
|
|
updatedAt: string;
|
|
|
|
}
|
|
export type SerializedProjectMetadata = Omit<
|
|
|
|
TProjectMetadata,
|
|
|
|
"createdAt" | "updatedAt"
|
|
|
|
> & {
|
|
|
|
createdAt: string;
|
|
|
|
updatedAt...
|
|
export type SerializedProject = Omit<TProject, "metadata" | "scenes"> & {
|
|
|
|
metadata: SerializedProjectMetadata;
|
|
|
|
scenes: Seri...
|
|
export interface StorageConfig {
|
|
projectsDb: string
|
|
mediaDb: string
|
|
savedSoundsDb: string
|
|
version: number
|
|
}
|
|
|
|
## apps/web/src/lib/timeline
|
|
|
|
bookmark-utils.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,
|
|
|
|
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<Omit<TextElement, "type" | "id">>;
|
|
|
|
startTime: number;
|
|
|
|
}): CreateTimelineElement
|
|
export function buildStickerElement({
|
|
|
|
iconName,
|
|
|
|
startTime,
|
|
|
|
}: {
|
|
|
|
iconName: string;
|
|
|
|
startTime: number;
|
|
|
|
}): CreateStickerElement
|
|
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
|
|
|
|
index.ts
|
|
export function calculateTotalDuration({
|
|
|
|
tracks,
|
|
|
|
}: {
|
|
|
|
tracks: TimelineTrack[];
|
|
|
|
}): number
|
|
|
|
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 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 }
|
|
|
|
## 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: ...
|
|
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];
|
|
...
|
|
export class SceneExporter extends EventEmitter<SceneExporterEvents> {
|
|
renderer: CanvasRenderer
|
|
format: ExportFormat
|
|
quality: ExportQuality
|
|
includeAudio: boolean
|
|
audioBuffer: AudioBuffer
|
|
cancelled
|
|
constructor(params: ExportParams)
|
|
cancel()
|
|
async export(rootNode: RootNode)
|
|
}
|
|
|
|
## apps/web/src/services/renderer/nodes
|
|
|
|
base-node.ts
|
|
export type BaseNodeParams = object | undefined
|
|
export class BaseNode {
|
|
params: Params
|
|
constructor(params?: Params)
|
|
children: BaseNode[]
|
|
add(child: BaseNode)
|
|
remove(child: BaseNode)
|
|
async render({ renderer, time }: { renderer: CanvasRenderer; time: number }): Promise<void>
|
|
}
|
|
|
|
blur-background-node.ts
|
|
export type BlurBackgroundNodeParams = {
|
|
blurIntensity: number;
|
|
contentNodes: BaseNode[];
|
|
}
|
|
export class BlurBackgroundNode extends BaseNode<BlurBackgroundNodeParams> {
|
|
blurIntensity: number
|
|
contentNodes: BaseNode[]
|
|
constructor(params: BlurBackgroundNodeParams)
|
|
async render({
|
|
renderer,
|
|
time,
|
|
}: {
|
|
renderer: CanvasRenderer;
|
|
time: number;
|
|
}): Promise<void>
|
|
}
|
|
|
|
color-node.ts
|
|
export type ColorNodeParams = {
|
|
|
|
color: string;
|
|
|
|
}
|
|
export class ColorNode extends BaseNode<ColorNodeParams> {
|
|
color: string
|
|
constructor(params: ColorNodeParams)
|
|
async render({ renderer }: { renderer: CanvasRenderer })
|
|
}
|
|
|
|
image-node.ts
|
|
export type ImageNodeParams = ;
|
|
export class ImageNode extends BaseNode<ImageNodeParams> {
|
|
image: HTMLImageElement
|
|
readyPromise: Promise<void>
|
|
constructor(params: ImageNodeParams)
|
|
async render({ renderer, time }: { renderer: CanvasRenderer; time: number })
|
|
}
|
|
|
|
root-node.ts
|
|
export type RootNodeParams = {
|
|
|
|
duration: number;
|
|
|
|
}
|
|
export class RootNode extends BaseNode<RootNodeParams> {
|
|
duration()
|
|
}
|
|
|
|
sticker-node.ts
|
|
export type StickerNodeParams = BaseNodeParams & {
|
|
|
|
iconName: string;
|
|
|
|
duration: number;
|
|
|
|
timeOffset: number;
|
|
|
|
trimStart:...
|
|
export class StickerNode extends BaseNode<StickerNodeParams> {
|
|
image: HTMLImageElement
|
|
readyPromise: Promise<void>
|
|
constructor(params: StickerNodeParams)
|
|
async render({ renderer, time }: { renderer: CanvasRenderer; time: number })
|
|
}
|
|
|
|
text-node.ts
|
|
export type TextNodeParams = TextElement & {
|
|
canvasCenter: { x: number; y: number };
|
|
textBaseline?: CanvasTextBaseline;
|
|
}
|
|
export class TextNode extends BaseNode<TextNodeParams> {
|
|
isInRange({ time }: { time: number })
|
|
async render({ renderer, time }: { renderer: CanvasRenderer; time: number })
|
|
}
|
|
|
|
video-node.ts
|
|
export interface BaseMediaNodeParams {
|
|
file: File
|
|
duration: number
|
|
timeOffset: number
|
|
trimStart: number
|
|
trimEnd: number
|
|
x?: number
|
|
y?: number
|
|
width?: number
|
|
height?: number
|
|
opacity?: number
|
|
}
|
|
export type VideoNodeParams = ;
|
|
export class VideoNode extends BaseNode<VideoNodeParams> {
|
|
sink: VideoSampleSink
|
|
readyPromise: Promise<void>
|
|
constructor(params: VideoNodeParams)
|
|
async render({ renderer, time }: { renderer: CanvasRenderer; time: number })
|
|
}
|
|
|
|
## apps/web/src/stores
|
|
|
|
assets-panel-store.ts
|
|
export type Tab = | "media"
|
|
|
|
| "sounds"
|
|
|
|
| "text"
|
|
|
|
| "stickers"
|
|
|
|
| "effects"
|
|
|
|
| "transitions"
|
|
|
|
| "capti...
|
|
export const tabs: { [key in Tab]: { icon: LucideIcon; label: string } }
|
|
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 type PanelPreset = | "default"
|
|
|
|
| "media"
|
|
|
|
| "inspector"
|
|
|
|
| "vertical-preview"
|
|
export const PRESET_CONFIGS: Record<PanelPreset, PanelSizes>
|
|
export const usePanelStore
|
|
|
|
sounds-store.ts
|
|
export const useSoundsStore
|
|
|
|
stickers-store.ts
|
|
export const useStickersStore
|
|
|
|
text-properties-store.ts
|
|
export type TextPropertiesTab = "transform" | "style"
|
|
export interface TextPropertiesTabMeta {
|
|
value: TextPropertiesTab
|
|
label: string
|
|
}
|
|
export const TEXT_PROPERTIES_TABS: ReadonlyArray<TextPropertiesTabMeta>
|
|
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<MediaAssetData, "size" | "lastModified"> {
|
|
file: File
|
|
url?: string
|
|
}
|
|
|
|
blog.ts
|
|
export type Post = {
|
|
|
|
id: string;
|
|
|
|
slug: string;
|
|
|
|
title: string;
|
|
|
|
content: string;
|
|
|
|
description: string;
|
|
|
|
...
|
|
export type Pagination = {
|
|
|
|
limit: number;
|
|
|
|
currpage: number;
|
|
|
|
nextPage: number | null;
|
|
|
|
prevPage: number | null;
|
|
...
|
|
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 type ExportFormat = "mp4" | "webm"
|
|
export type ExportQuality = "low" | "medium" | "high" | "very_high"
|
|
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"
|
|
|...
|
|
export type ModifierBasedShortcutKey = `${ModifierKeys}+${Key}`
|
|
export type SingleCharacterShortcutKey = `${Key}`
|
|
export type ShortcutKey = ModifierBasedShortcutKey | SingleCharacterShortcutKey
|
|
export type KeybindingConfig = {
|
|
[key in ShortcutKey]?: TActionWithOptionalArgs;
|
|
}
|
|
|
|
project.ts
|
|
export type TBackground = | {
|
|
type: "color";
|
|
color: string;
|
|
}
|
|
| {
|
|
type: "blur";
|
|
blurIntensity...
|
|
export interface TCanvasSize {
|
|
width: number
|
|
height: number
|
|
}
|
|
export interface TProjectMetadata {
|
|
id: string
|
|
name: string
|
|
thumbnail?: string
|
|
createdAt: Date
|
|
updatedAt: Date
|
|
}
|
|
export interface TProjectSettings {
|
|
fps: number
|
|
canvasSize: TCanvasSize
|
|
background: TBackground
|
|
}
|
|
export interface TProject {
|
|
metadata: TProjectMetadata
|
|
scenes: TScene[]
|
|
currentSceneId: string
|
|
settings: TProjectSettings
|
|
version: number
|
|
}
|
|
|
|
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
|
|
// ... and 10 more
|
|
}
|
|
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"
|
|
// ... and 3 more
|
|
}
|
|
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<UploadAudioElement, "id">
|
|
export type CreateLibraryAudioElement = Omit<LibraryAudioElement, "id">
|
|
export type CreateAudioElement = | CreateUploadAudioElement
|
|
| CreateLibraryAudioElement
|
|
export type CreateVideoElement = Omit<VideoElement, "id">
|
|
export type CreateImageElement = Omit<ImageElement, "id">
|
|
export type CreateTextElement = Omit<TextElement, "id">
|
|
export type CreateStickerElement = Omit<StickerElement, "id">
|
|
export type CreateTimelineElement = | CreateAudioElement
|
|
| CreateVideoElement
|
|
| CreateImageElement
|
|
| CreateTextElement
|
|
| Crea...
|
|
export interface ElementDragState {
|
|
isDragging: boolean
|
|
elementId: string | null
|
|
trackId: string | null
|
|
startMouseX: number
|
|
startMouseY: number
|
|
startElementTime: number
|
|
clickOffsetTime: number
|
|
currentTime: 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
|
|
excludeElementId?: string
|
|
}
|
|
export interface ClipboardItem {
|
|
trackType: TrackType
|
|
element: CreateTimelineElement
|
|
}
|
|
|
|
## packages/ui/src/icons
|
|
|
|
index.tsx
|
|
export function GoogleIcon({ className }: { className?: string })
|
|
export function GithubIcon({ className }: { className?: string })
|
|
export function VercelIcon({ className }: { className?: string })
|
|
export function BackgroundIcon({ className }: { className?: string })
|
|
export function MarbleIcon({
|
|
|
|
className = "",
|
|
|
|
size = 32,
|
|
|
|
}: {
|
|
|
|
className?: string;
|
|
|
|
size?: number;
|
|
|
|
})
|
|
export function DataBuddyIcon({
|
|
|
|
className = "",
|
|
|
|
size = 32,
|
|
|
|
}: {
|
|
|
|
className?: string;
|
|
|
|
size?: number;
|
|
|
|
})
|
|
export function SocialsIcon({
|
|
|
|
className = "",
|
|
|
|
size = 32,
|
|
|
|
}: {
|
|
|
|
className?: string;
|
|
|
|
size?: number;
|
|
|
|
})
|
|
export function TransitionUpIcon({
|
|
|
|
className = "",
|
|
|
|
size = 16,
|
|
|
|
}: {
|
|
|
|
className?: string;
|
|
|
|
size?: number;
|
|
|
|
})
|
|
export function MenuIcon({
|
|
|
|
className = "",
|
|
|
|
size = 16,
|
|
|
|
}: {
|
|
|
|
className?: string;
|
|
|
|
size?: number;
|
|
|
|
})
|
|
|
|
```
|
|
|
|
---
|
|
|
|
*Generated and maintained by [Twiggy](https://github.com/twiggy-tools/Twiggy)*
|