diff --git a/.cursor/rules/codebase-index.mdc b/.cursor/rules/codebase-index.mdc index 77a5fb9b..a058c5ef 100644 --- a/.cursor/rules/codebase-index.mdc +++ b/.cursor/rules/codebase-index.mdc @@ -319,106 +319,26 @@ use-element-selection.ts ## apps/web/src/lib -audio-utils.ts - export type CollectedAudioElement = Omit< - AudioElement, - "type" | "mediaId" | "volume" | "id" | "name" | "sourceType" | "sourceU... - export function createAudioContext(): AudioContext - export interface DecodedAudio { - samples: Float32Array - sampleRate: number - } - export function decodeAudioToFloat32({ - audioBlob, +browser-utils.ts + export function isTypableDOMElement({ + element, }: { - audioBlob: Blob; - }): Promise - export function collectAudioElements({ - tracks, - mediaAssets, - audioContext, - }: { - tracks: TimelineTrack[]; - mediaAssets: MediaAsset[]; - audioContext: AudioContext; - }): Promise - export function collectAudioMixSources({ - tracks, - mediaAssets, - }: { - tracks: TimelineTrack[]; - mediaAssets: MediaAsset[]; - }): Promise - export function createTimelineAudioBuffer({ - tracks, - mediaAssets, - duration, - sampleRate = 44100, - }: { - tracks: TimelineTrack[]; - mediaAssets: MediaAsset[]; - duration: number; - sampleRate?: number; - }): Promise - -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 - -caption-utils.ts - export function buildCaptionChunks({ - segments, - wordsPerChunk = DEFAULT_WORDS_PER_CAPTION, - minDuration = MIN_CAPTION_DURATION_SECONDS, - }: { - segments: TranscriptionSegment[]; - wordsPerChunk?: number; - minDuration?: number; - }): CaptionChunk[] - -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; + element: HTMLElement; }): boolean - export function clearDragData(): void -editor-utils.ts - export function dimensionToAspectRatio({ - width, - height, - }: { - width: number; - height: number; - }): string +date-utils.ts + export function formatDate({ date }: { date: Date }): string export.ts export function getExportMimeType({ format, }: { - format: "mp4" | "webm"; + format: ExportFormat; }): string export function getExportFileExtension({ format, }: { - format: "mp4" | "webm"; + format: ExportFormat; }): string iconify-api.ts @@ -493,22 +413,21 @@ iconify-api.ts collections: Record ): string[] -keyboard-utils.ts +math-utils.ts + export function clamp({ + value, + min, + max, + }: { + value: number; + min: number; + max: number; + }): number + +platform-utils.ts export function getPlatformSpecialKey(): string export function getPlatformAlternateKey(): string - -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 => ... - -rate-limit.ts - export const baseRateLimit - export function checkRateLimit({ request }: { request: Request }) + export function isAppleDevice(): boolean scene-utils.ts export function getMainScene({ scenes }: { scenes: TScene[] }): TScene | null @@ -550,6 +469,10 @@ scene-utils.ts updates: Partial; }): TScene[] +string-utils.ts + export function capitalizeFirstLetter({ string }: { string: string }) + export function uppercase({ string }: { string: string }) + time-utils.ts export function roundToFrame({ time, @@ -612,28 +535,6 @@ time-utils.ts fps: number; }): number -utils.ts - export function cn(...inputs: ClassValue[]): string - export function formatDate({ date }: { date: Date }): string - export function capitalizeFirstLetter({ string }: { string: string }) - export function uppercase({ 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 - ## apps/web/src/lib/actions definitions.ts @@ -698,6 +599,16 @@ 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 @@ -739,6 +650,15 @@ parser.ts ## apps/web/src/lib/media +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 const initFFmpeg = (): Promise => ... export function getVideoInfo({ @@ -1006,6 +926,161 @@ zoom-utils.ts containerWidth: number | null | undefined; }): number +## apps/web/src/lib/utils + +audio.ts + export type CollectedAudioElement = Omit< + AudioElement, + "type" | "mediaId" | "volume" | "id" | "name" | "sourceType" | "sourceU... + 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 function collectAudioMixSources({ + tracks, + mediaAssets, + }: { + tracks: TimelineTrack[]; + mediaAssets: MediaAsset[]; + }): Promise + export function createTimelineAudioBuffer({ + tracks, + mediaAssets, + duration, + sampleRate = 44100, + }: { + tracks: TimelineTrack[]; + mediaAssets: MediaAsset[]; + duration: number; + sampleRate?: number; + }): Promise + +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 + +browser.ts + export function isTypableDOMElement({ + element, + }: { + element: HTMLElement; + }): boolean + +date.ts + export function formatDate({ date }: { date: Date }): string + +editor.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 + +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 + +ui.ts + export function cn(...inputs: ClassValue[]): string + ## apps/web/src/services/media video-cache.ts @@ -1584,20 +1659,19 @@ transcription.ts segments: TranscriptionSegment[] language: string } - export type TranscriptionStatus = | "idle" - | "loading-model" - | "ready" - | "transcribing" - | "complete" - | "er... + 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" + export type TranscriptionModelId = | "whisper-tiny" + | "whisper-small" + | "whisper-medium" | "whisper-large-v3-turbo" export interface TranscriptionModel { id: TranscriptionModelId @@ -1611,6 +1685,36 @@ transcription.ts duration: number } +## apps/web/src/utils + +geometry.ts + export function dimensionToAspectRatio({ + width, + height, + }: { + width: number; + height: number; + }): 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 + +ui.ts + export function cn(...inputs: ClassValue[]): string + ## packages/ui/src/icons index.tsx diff --git a/apps/web/src/app/api/sounds/search/route.ts b/apps/web/src/app/api/sounds/search/route.ts index 16eeb75f..a477c9db 100644 --- a/apps/web/src/app/api/sounds/search/route.ts +++ b/apps/web/src/app/api/sounds/search/route.ts @@ -93,14 +93,14 @@ function buildSortParameter({ query, sort }: { query?: string; sort: string }) { return sort === "score" ? "score" : `${sort}_desc`; } -function applyEffectsFilters({ - params, - min_rating, - commercial_only -}: { - params: URLSearchParams; - min_rating: number; - commercial_only: boolean; +function applyEffectsFilters({ + params, + min_rating, + commercial_only +}: { + params: URLSearchParams; + min_rating: number; + commercial_only: boolean; }) { params.append("filter", "duration:[* TO 30.0]"); params.append("filter", `avg_rating:[${min_rating} TO *]`); diff --git a/apps/web/src/app/base-page.tsx b/apps/web/src/app/base-page.tsx index 61c827c7..48303e8e 100644 --- a/apps/web/src/app/base-page.tsx +++ b/apps/web/src/app/base-page.tsx @@ -1,6 +1,6 @@ import { Header } from "@/components/header"; import { Footer } from "@/components/footer"; -import { cn } from "@/lib/utils"; +import { cn } from "@/utils/ui"; interface BasePageProps { children: React.ReactNode; diff --git a/apps/web/src/app/blog/[slug]/page.tsx b/apps/web/src/app/blog/[slug]/page.tsx index 7814edff..7325d859 100644 --- a/apps/web/src/app/blog/[slug]/page.tsx +++ b/apps/web/src/app/blog/[slug]/page.tsx @@ -1,7 +1,7 @@ import { BasePage } from "@/app/base-page"; import Prose from "@/components/ui/prose"; import { Separator } from "@/components/ui/separator"; -import { getPosts, getSinglePost, processHtmlContent } from "@/lib/blog-query"; +import { getPosts, getSinglePost, processHtmlContent } from "@/lib/blog/query"; import { Post, Author } from "@/types/blog"; import { Metadata } from "next"; import Image from "next/image"; diff --git a/apps/web/src/app/blog/page.tsx b/apps/web/src/app/blog/page.tsx index ed4f0164..6abfc46e 100644 --- a/apps/web/src/app/blog/page.tsx +++ b/apps/web/src/app/blog/page.tsx @@ -2,7 +2,7 @@ import { Metadata } from "next"; import { BasePage } from "@/app/base-page"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import Link from "next/link"; -import { getPosts } from "@/lib/blog-query"; +import { getPosts } from "@/lib/blog/query"; import { Post, Author } from "@/types/blog"; import { Separator } from "@/components/ui/separator"; diff --git a/apps/web/src/app/projects/page.tsx b/apps/web/src/app/projects/page.tsx index 929f076f..7bba1023 100644 --- a/apps/web/src/app/projects/page.tsx +++ b/apps/web/src/app/projects/page.tsx @@ -40,7 +40,7 @@ import { MigrationDialog } from "@/components/editor/migration-dialog"; import type { TProjectMetadata } from "@/types/project"; import { toast } from "sonner"; import { useEditor } from "@/hooks/use-editor"; -import { formatDate } from "@/lib/utils"; +import { formatDate } from "@/utils/date"; export default function ProjectsPage() { const [isSelectionMode, setIsSelectionMode] = useState(false); @@ -419,14 +419,12 @@ function ProjectCard({ const cardContent = (
{isSelectionMode && (
@@ -477,11 +475,10 @@ function ProjectCard({ aria-label="project options" variant="text" size="sm" - className={`ml-2 size-6 shrink-0 p-0 transition-all ${ - isDropdownOpen - ? "opacity-100" - : "opacity-0 group-hover:opacity-100" - }`} + className={`ml-2 size-6 shrink-0 p-0 transition-all ${isDropdownOpen + ? "opacity-100" + : "opacity-0 group-hover:opacity-100" + }`} onClick={(event) => event.preventDefault()} >