Full Code of OpenCut-app/OpenCut for AI

main 26d523ebad3d cached
510 files
33.3 MB
709.5k tokens
1793 symbols
1 requests
Download .txt
Showing preview only (2,825K chars total). Download the full file or copy to clipboard to get everything.
Repository: OpenCut-app/OpenCut
Branch: main
Commit: 26d523ebad3d
Files: 510
Total size: 33.3 MB

Directory structure:
gitextract_ph6jujtc/

├── .cursor/
│   ├── commands/
│   │   └── review.md
│   ├── rules/
│   │   ├── codebase-index.mdc
│   │   ├── comments.mdc
│   │   ├── handling-uncertainty.mdc
│   │   ├── readability.mdc
│   │   ├── separation-of-concerns.mdc
│   │   ├── ultracite.mdc
│   │   └── writing-scannable-code.mdc
│   ├── settings.json
│   └── skills/
│       └── design/
│           └── SKILL.md
├── .dockerignore
├── .github/
│   ├── CODE_OF_CONDUCT.md
│   ├── CONTRIBUTING.md
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.yml
│   │   └── feature_request.yml
│   ├── SECURITY.md
│   ├── SUPPORT.md
│   ├── copilot-instructions.md
│   ├── pull_request_template.md
│   └── workflows/
│       └── bun-ci.yml
├── .gitignore
├── .npmrc
├── .vscode/
│   └── settings.json
├── AGENTS.md
├── LICENSE
├── README.md
├── apps/
│   └── web/
│       ├── .env.example
│       ├── .gitignore
│       ├── Dockerfile
│       ├── components.json
│       ├── content/
│       │   └── changelog/
│       │       ├── 0.1.0.md
│       │       └── 0.2.0.md
│       ├── content-collections.ts
│       ├── drizzle.config.ts
│       ├── migrations/
│       │   ├── 0000_brainy_saracen.sql
│       │   └── meta/
│       │       ├── 0000_snapshot.json
│       │       └── _journal.json
│       ├── next-env.d.ts
│       ├── next.config.ts
│       ├── package.json
│       ├── postcss.config.mjs
│       ├── public/
│       │   ├── browserconfig.xml
│       │   ├── countries.json
│       │   ├── ffmpeg/
│       │   │   ├── ffmpeg-core.js
│       │   │   └── ffmpeg-core.wasm
│       │   ├── fonts/
│       │   │   ├── font-atlas.json
│       │   │   ├── font-chunk-0.avif
│       │   │   ├── font-chunk-1.avif
│       │   │   ├── font-chunk-10.avif
│       │   │   ├── font-chunk-11.avif
│       │   │   ├── font-chunk-12.avif
│       │   │   ├── font-chunk-13.avif
│       │   │   ├── font-chunk-14.avif
│       │   │   ├── font-chunk-2.avif
│       │   │   ├── font-chunk-3.avif
│       │   │   ├── font-chunk-4.avif
│       │   │   ├── font-chunk-5.avif
│       │   │   ├── font-chunk-6.avif
│       │   │   ├── font-chunk-7.avif
│       │   │   ├── font-chunk-8.avif
│       │   │   └── font-chunk-9.avif
│       │   └── manifest.json
│       ├── scripts/
│       │   └── generate-font-sprites.ts
│       ├── src/
│       │   ├── app/
│       │   │   ├── api/
│       │   │   │   ├── auth/
│       │   │   │   │   └── [...all]/
│       │   │   │   │       └── route.ts
│       │   │   │   ├── health/
│       │   │   │   │   └── route.ts
│       │   │   │   └── sounds/
│       │   │   │       └── search/
│       │   │   │           └── route.ts
│       │   │   ├── base-page.tsx
│       │   │   ├── blog/
│       │   │   │   ├── [slug]/
│       │   │   │   │   └── page.tsx
│       │   │   │   └── page.tsx
│       │   │   ├── brand/
│       │   │   │   └── page.tsx
│       │   │   ├── changelog/
│       │   │   │   ├── [version]/
│       │   │   │   │   └── page.tsx
│       │   │   │   ├── components/
│       │   │   │   │   ├── copy-markdown-button.tsx
│       │   │   │   │   └── release.tsx
│       │   │   │   ├── page.tsx
│       │   │   │   └── utils.ts
│       │   │   ├── contributors/
│       │   │   │   └── page.tsx
│       │   │   ├── editor/
│       │   │   │   └── [project_id]/
│       │   │   │       └── page.tsx
│       │   │   ├── globals.css
│       │   │   ├── layout.tsx
│       │   │   ├── metadata.ts
│       │   │   ├── page.tsx
│       │   │   ├── privacy/
│       │   │   │   └── page.tsx
│       │   │   ├── projects/
│       │   │   │   ├── page.tsx
│       │   │   │   └── store.ts
│       │   │   ├── roadmap/
│       │   │   │   └── page.tsx
│       │   │   ├── robots.ts
│       │   │   ├── rss.xml/
│       │   │   │   └── route.ts
│       │   │   ├── sitemap.ts
│       │   │   ├── sponsors/
│       │   │   │   └── page.tsx
│       │   │   └── terms/
│       │   │       └── page.tsx
│       │   ├── components/
│       │   │   ├── editable-timecode.tsx
│       │   │   ├── editor/
│       │   │   │   ├── dialogs/
│       │   │   │   │   ├── delete-project-dialog.tsx
│       │   │   │   │   ├── migration-dialog.tsx
│       │   │   │   │   ├── project-info-dialog.tsx
│       │   │   │   │   ├── rename-project-dialog.tsx
│       │   │   │   │   └── shortcuts-dialog.tsx
│       │   │   │   ├── editor-header.tsx
│       │   │   │   ├── export-button.tsx
│       │   │   │   ├── mobile-gate.tsx
│       │   │   │   ├── onboarding.tsx
│       │   │   │   ├── panels/
│       │   │   │   │   ├── assets/
│       │   │   │   │   │   ├── drag-overlay.tsx
│       │   │   │   │   │   ├── draggable-item.tsx
│       │   │   │   │   │   ├── index.tsx
│       │   │   │   │   │   ├── tabbar.tsx
│       │   │   │   │   │   └── views/
│       │   │   │   │   │       ├── assets.tsx
│       │   │   │   │   │       ├── base-view.tsx
│       │   │   │   │   │       ├── captions.tsx
│       │   │   │   │   │       ├── effects.tsx
│       │   │   │   │   │       ├── settings-legacy.tsx
│       │   │   │   │   │       ├── settings.tsx
│       │   │   │   │   │       ├── sounds.tsx
│       │   │   │   │   │       ├── stickers.tsx
│       │   │   │   │   │       └── text.tsx
│       │   │   │   │   ├── preview/
│       │   │   │   │   │   ├── bookmark-note-overlay.tsx
│       │   │   │   │   │   ├── context-menu.tsx
│       │   │   │   │   │   ├── index.tsx
│       │   │   │   │   │   ├── layout-guide-overlay.tsx
│       │   │   │   │   │   ├── preview-interaction-overlay.tsx
│       │   │   │   │   │   ├── snap-guides.tsx
│       │   │   │   │   │   ├── text-edit-overlay.tsx
│       │   │   │   │   │   ├── toolbar.tsx
│       │   │   │   │   │   └── transform-handles.tsx
│       │   │   │   │   ├── properties/
│       │   │   │   │   │   ├── audio-properties.tsx
│       │   │   │   │   │   ├── clip-effects-properties.tsx
│       │   │   │   │   │   ├── effect-param-field.tsx
│       │   │   │   │   │   ├── effect-properties.tsx
│       │   │   │   │   │   ├── empty-view.tsx
│       │   │   │   │   │   ├── hooks/
│       │   │   │   │   │   │   ├── use-element-playhead.ts
│       │   │   │   │   │   │   ├── use-keyframed-color-property.ts
│       │   │   │   │   │   │   ├── use-keyframed-number-property.ts
│       │   │   │   │   │   │   └── use-property-draft.ts
│       │   │   │   │   │   ├── index.tsx
│       │   │   │   │   │   ├── keyframe-toggle.tsx
│       │   │   │   │   │   ├── section.tsx
│       │   │   │   │   │   ├── sections/
│       │   │   │   │   │   │   ├── blending.tsx
│       │   │   │   │   │   │   ├── index.tsx
│       │   │   │   │   │   │   └── transform.tsx
│       │   │   │   │   │   ├── text-properties.tsx
│       │   │   │   │   │   └── video-properties.tsx
│       │   │   │   │   └── timeline/
│       │   │   │   │       ├── audio-waveform.tsx
│       │   │   │   │       ├── bookmarks.tsx
│       │   │   │   │       ├── drag-line.tsx
│       │   │   │   │       ├── index.tsx
│       │   │   │   │       ├── snap-indicator.tsx
│       │   │   │   │       ├── timeline-element.tsx
│       │   │   │   │       ├── timeline-playhead.tsx
│       │   │   │   │       ├── timeline-ruler.tsx
│       │   │   │   │       ├── timeline-tick.tsx
│       │   │   │   │       ├── timeline-toolbar.tsx
│       │   │   │   │       └── timeline-track.tsx
│       │   │   │   ├── scenes-view.tsx
│       │   │   │   └── selection-box.tsx
│       │   │   ├── footer.tsx
│       │   │   ├── gitHub-contribute-section.tsx
│       │   │   ├── header.tsx
│       │   │   ├── landing/
│       │   │   │   ├── handlebars.tsx
│       │   │   │   └── hero.tsx
│       │   │   ├── providers/
│       │   │   │   └── editor-provider.tsx
│       │   │   ├── storage-provider.tsx
│       │   │   ├── theme-toggle.tsx
│       │   │   └── ui/
│       │   │       ├── accordion.tsx
│       │   │       ├── alert-dialog.tsx
│       │   │       ├── alert.tsx
│       │   │       ├── aspect-ratio.tsx
│       │   │       ├── avatar.tsx
│       │   │       ├── badge.tsx
│       │   │       ├── breadcrumb.tsx
│       │   │       ├── button.tsx
│       │   │       ├── calendar.tsx
│       │   │       ├── card.tsx
│       │   │       ├── checkbox.tsx
│       │   │       ├── collapsible.tsx
│       │   │       ├── color-picker.tsx
│       │   │       ├── context-menu.tsx
│       │   │       ├── dialog.tsx
│       │   │       ├── dropdown-menu.tsx
│       │   │       ├── font-picker.tsx
│       │   │       ├── form.tsx
│       │   │       ├── hover-card.tsx
│       │   │       ├── input-with-back.tsx
│       │   │       ├── input.tsx
│       │   │       ├── label.tsx
│       │   │       ├── menubar.tsx
│       │   │       ├── navigation-menu.tsx
│       │   │       ├── number-field.tsx
│       │   │       ├── popover.tsx
│       │   │       ├── progress.tsx
│       │   │       ├── prose.tsx
│       │   │       ├── radio-group.tsx
│       │   │       ├── react-markdown-wrapper.tsx
│       │   │       ├── resizable.tsx
│       │   │       ├── scroll-area.tsx
│       │   │       ├── select.tsx
│       │   │       ├── separator.tsx
│       │   │       ├── sheet.tsx
│       │   │       ├── skeleton.tsx
│       │   │       ├── slider.tsx
│       │   │       ├── sonner.tsx
│       │   │       ├── spinner.tsx
│       │   │       ├── split-button.tsx
│       │   │       ├── switch.tsx
│       │   │       ├── table.tsx
│       │   │       ├── tabs.tsx
│       │   │       ├── textarea.tsx
│       │   │       ├── toast.tsx
│       │   │       ├── toggle-group.tsx
│       │   │       ├── toggle.tsx
│       │   │       └── tooltip.tsx
│       │   ├── constants/
│       │   │   ├── animation-constants.ts
│       │   │   ├── editor-constants.ts
│       │   │   ├── export-constants.ts
│       │   │   ├── font-constants.ts
│       │   │   ├── language-constants.ts
│       │   │   ├── project-constants.ts
│       │   │   ├── site-constants.ts
│       │   │   ├── sticker-constants.ts
│       │   │   ├── text-constants.ts
│       │   │   ├── timeline-constants.tsx
│       │   │   └── transcription-constants.ts
│       │   ├── core/
│       │   │   ├── index.ts
│       │   │   └── managers/
│       │   │       ├── audio-manager.ts
│       │   │       ├── commands.ts
│       │   │       ├── media-manager.ts
│       │   │       ├── playback-manager.ts
│       │   │       ├── project-manager.ts
│       │   │       ├── renderer-manager.ts
│       │   │       ├── save-manager.ts
│       │   │       ├── scenes-manager.ts
│       │   │       ├── selection-manager.ts
│       │   │       └── timeline-manager.ts
│       │   ├── data/
│       │   │   └── colors/
│       │   │       ├── pattern-craft.ts
│       │   │       ├── solid.ts
│       │   │       └── syntax-ui.tsx
│       │   ├── hooks/
│       │   │   ├── actions/
│       │   │   │   ├── use-action-handler.ts
│       │   │   │   └── use-editor-actions.ts
│       │   │   ├── storage/
│       │   │   │   └── use-local-storage.ts
│       │   │   ├── timeline/
│       │   │   │   ├── element/
│       │   │   │   │   ├── use-element-interaction.ts
│       │   │   │   │   ├── use-element-resize.ts
│       │   │   │   │   ├── use-element-selection.ts
│       │   │   │   │   ├── use-keyframe-drag.ts
│       │   │   │   │   └── use-keyframe-selection.ts
│       │   │   │   ├── use-bookmark-drag.ts
│       │   │   │   ├── use-edge-auto-scroll.ts
│       │   │   │   ├── use-scroll-position.ts
│       │   │   │   ├── use-scroll-sync.ts
│       │   │   │   ├── use-selection-box.ts
│       │   │   │   ├── use-snap-indicator-position.ts
│       │   │   │   ├── use-timeline-drag-drop.ts
│       │   │   │   ├── use-timeline-playhead.ts
│       │   │   │   ├── use-timeline-seek.ts
│       │   │   │   └── use-timeline-zoom.ts
│       │   │   ├── use-container-size.ts
│       │   │   ├── use-editor.ts
│       │   │   ├── use-effect-preview.ts
│       │   │   ├── use-file-upload.ts
│       │   │   ├── use-focus-lock.ts
│       │   │   ├── use-fullscreen.ts
│       │   │   ├── use-infinite-scroll.ts
│       │   │   ├── use-keybindings.ts
│       │   │   ├── use-keyboard-shortcuts-help.ts
│       │   │   ├── use-mobile.ts
│       │   │   ├── use-paste-media.ts
│       │   │   ├── use-preview-interaction.ts
│       │   │   ├── use-raf-loop.ts
│       │   │   ├── use-reveal-item.ts
│       │   │   ├── use-shift-key.ts
│       │   │   ├── use-sound-search.ts
│       │   │   └── use-transform-handles.ts
│       │   ├── lib/
│       │   │   ├── actions/
│       │   │   │   ├── definitions.ts
│       │   │   │   ├── index.ts
│       │   │   │   ├── registry.ts
│       │   │   │   └── types.ts
│       │   │   ├── animation/
│       │   │   │   ├── __tests__/
│       │   │   │   │   └── transform-keyframes.test.ts
│       │   │   │   ├── color-channel.ts
│       │   │   │   ├── effect-param-channel.ts
│       │   │   │   ├── index.ts
│       │   │   │   ├── interpolation.ts
│       │   │   │   ├── keyframe-query.ts
│       │   │   │   ├── keyframes.ts
│       │   │   │   ├── number-channel.ts
│       │   │   │   ├── property-registry.ts
│       │   │   │   └── resolve.ts
│       │   │   ├── auth/
│       │   │   │   ├── client.ts
│       │   │   │   └── server.ts
│       │   │   ├── blog/
│       │   │   │   └── query.ts
│       │   │   ├── commands/
│       │   │   │   ├── __tests__/
│       │   │   │   │   └── keyframe-aware-commands.test.ts
│       │   │   │   ├── base-command.ts
│       │   │   │   ├── batch-command.ts
│       │   │   │   ├── index.ts
│       │   │   │   ├── media/
│       │   │   │   │   ├── add-media-asset.ts
│       │   │   │   │   ├── index.ts
│       │   │   │   │   └── remove-media-asset.ts
│       │   │   │   ├── preview-tracker.ts
│       │   │   │   ├── project/
│       │   │   │   │   ├── index.ts
│       │   │   │   │   └── update-project-settings.ts
│       │   │   │   ├── scene/
│       │   │   │   │   ├── create-scene.ts
│       │   │   │   │   ├── delete-scene.ts
│       │   │   │   │   ├── index.ts
│       │   │   │   │   ├── move-bookmark.ts
│       │   │   │   │   ├── remove-bookmark.ts
│       │   │   │   │   ├── rename-scene.ts
│       │   │   │   │   ├── toggle-bookmark.ts
│       │   │   │   │   └── update-bookmark.ts
│       │   │   │   └── timeline/
│       │   │   │       ├── clipboard/
│       │   │   │       │   ├── index.ts
│       │   │   │       │   └── paste.ts
│       │   │   │       ├── element/
│       │   │   │       │   ├── delete-elements.ts
│       │   │   │       │   ├── duplicate-elements.ts
│       │   │   │       │   ├── effects/
│       │   │   │       │   │   ├── add-effect.ts
│       │   │   │       │   │   ├── index.ts
│       │   │   │       │   │   ├── remove-effect.ts
│       │   │   │       │   │   ├── reorder-effect.ts
│       │   │   │       │   │   ├── toggle-effect.ts
│       │   │   │       │   │   └── update-effect-params.ts
│       │   │   │       │   ├── index.ts
│       │   │   │       │   ├── insert-element.ts
│       │   │   │       │   ├── keyframes/
│       │   │   │       │   │   ├── index.ts
│       │   │   │       │   │   ├── remove-effect-param-keyframe.ts
│       │   │   │       │   │   ├── remove-keyframe.ts
│       │   │   │       │   │   ├── retime-keyframe.ts
│       │   │   │       │   │   ├── upsert-effect-param-keyframe.ts
│       │   │   │       │   │   └── upsert-keyframe.ts
│       │   │   │       │   ├── move-elements.ts
│       │   │   │       │   ├── split-elements.ts
│       │   │   │       │   ├── toggle-elements-muted.ts
│       │   │   │       │   ├── toggle-elements-visibility.ts
│       │   │   │       │   ├── update-element-duration.ts
│       │   │   │       │   ├── update-element-start-time.ts
│       │   │   │       │   ├── update-element-trim.ts
│       │   │   │       │   └── update-element.ts
│       │   │   │       ├── index.ts
│       │   │   │       ├── track/
│       │   │   │       │   ├── add-track.ts
│       │   │   │       │   ├── index.ts
│       │   │   │       │   ├── remove-track.ts
│       │   │   │       │   ├── toggle-track-mute.ts
│       │   │   │       │   └── toggle-track-visibility.ts
│       │   │   │       └── tracks-snapshot.ts
│       │   │   ├── db/
│       │   │   │   ├── index.ts
│       │   │   │   └── schema.ts
│       │   │   ├── drag-data.ts
│       │   │   ├── effects/
│       │   │   │   ├── definitions/
│       │   │   │   │   ├── blur.frag.glsl
│       │   │   │   │   ├── blur.ts
│       │   │   │   │   └── index.ts
│       │   │   │   ├── effect.vert.glsl
│       │   │   │   ├── index.ts
│       │   │   │   └── registry.ts
│       │   │   ├── export.ts
│       │   │   ├── fonts/
│       │   │   │   └── google-fonts.ts
│       │   │   ├── gradients/
│       │   │   │   ├── canvas.ts
│       │   │   │   ├── index.ts
│       │   │   │   └── parser.ts
│       │   │   ├── iconify-api.ts
│       │   │   ├── media/
│       │   │   │   ├── audio.ts
│       │   │   │   ├── media-utils.ts
│       │   │   │   ├── mediabunny.ts
│       │   │   │   └── processing.ts
│       │   │   ├── preview/
│       │   │   │   ├── element-bounds.ts
│       │   │   │   ├── hit-test.ts
│       │   │   │   ├── preview-coords.ts
│       │   │   │   └── preview-snap.ts
│       │   │   ├── rate-limit.ts
│       │   │   ├── scenes.ts
│       │   │   ├── stickers/
│       │   │   │   ├── __tests__/
│       │   │   │   │   └── sticker-id.test.ts
│       │   │   │   ├── index.ts
│       │   │   │   ├── providers/
│       │   │   │   │   ├── emoji.ts
│       │   │   │   │   ├── flags.ts
│       │   │   │   │   ├── icons.ts
│       │   │   │   │   ├── index.ts
│       │   │   │   │   └── shapes.ts
│       │   │   │   ├── registry.ts
│       │   │   │   ├── resolver.ts
│       │   │   │   ├── sticker-id.ts
│       │   │   │   └── types.ts
│       │   │   ├── text/
│       │   │   │   └── layout.ts
│       │   │   ├── time.ts
│       │   │   ├── timeline/
│       │   │   │   ├── bookmarks.ts
│       │   │   │   ├── drag-utils.ts
│       │   │   │   ├── drop-utils.ts
│       │   │   │   ├── element-utils.ts
│       │   │   │   ├── index.ts
│       │   │   │   ├── pixel-utils.ts
│       │   │   │   ├── ripple-utils.ts
│       │   │   │   ├── ruler-utils.ts
│       │   │   │   ├── snap-utils.ts
│       │   │   │   ├── track-element-update.ts
│       │   │   │   ├── track-utils.ts
│       │   │   │   └── zoom-utils.ts
│       │   │   └── transcription/
│       │   │       └── caption.ts
│       │   ├── proxy.ts
│       │   ├── services/
│       │   │   ├── renderer/
│       │   │   │   ├── canvas-renderer.ts
│       │   │   │   ├── canvas-utils.ts
│       │   │   │   ├── effect-preview.ts
│       │   │   │   ├── nodes/
│       │   │   │   │   ├── base-node.ts
│       │   │   │   │   ├── color-node.ts
│       │   │   │   │   ├── composite-effect-node.ts
│       │   │   │   │   ├── effect-layer-node.ts
│       │   │   │   │   ├── image-node.ts
│       │   │   │   │   ├── root-node.ts
│       │   │   │   │   ├── sticker-node.ts
│       │   │   │   │   ├── text-node.ts
│       │   │   │   │   ├── video-node.ts
│       │   │   │   │   └── visual-node.ts
│       │   │   │   ├── scene-builder.ts
│       │   │   │   ├── scene-exporter.ts
│       │   │   │   ├── webgl-effect-renderer.ts
│       │   │   │   └── webgl-utils.ts
│       │   │   ├── storage/
│       │   │   │   ├── indexeddb-adapter.ts
│       │   │   │   ├── migrations/
│       │   │   │   │   ├── __tests__/
│       │   │   │   │   │   ├── fixtures/
│       │   │   │   │   │   │   ├── index.ts
│       │   │   │   │   │   │   ├── v0.ts
│       │   │   │   │   │   │   ├── v1.ts
│       │   │   │   │   │   │   ├── v2.ts
│       │   │   │   │   │   │   ├── v3.ts
│       │   │   │   │   │   │   └── v5.ts
│       │   │   │   │   │   ├── v0-to-v1.test.ts
│       │   │   │   │   │   ├── v1-to-v2.test.ts
│       │   │   │   │   │   ├── v2-to-v3.test.ts
│       │   │   │   │   │   ├── v3-to-v4.test.ts
│       │   │   │   │   │   ├── v4-to-v5.test.ts
│       │   │   │   │   │   ├── v5-to-v6.test.ts
│       │   │   │   │   │   └── v8-to-v9.test.ts
│       │   │   │   │   ├── base.ts
│       │   │   │   │   ├── index.ts
│       │   │   │   │   ├── runner.ts
│       │   │   │   │   ├── transformers/
│       │   │   │   │   │   ├── index.ts
│       │   │   │   │   │   ├── types.ts
│       │   │   │   │   │   ├── utils.ts
│       │   │   │   │   │   ├── v0-to-v1.ts
│       │   │   │   │   │   ├── v1-to-v2.ts
│       │   │   │   │   │   ├── v2-to-v3.ts
│       │   │   │   │   │   ├── v3-to-v4.ts
│       │   │   │   │   │   ├── v4-to-v5.ts
│       │   │   │   │   │   ├── v5-to-v6.ts
│       │   │   │   │   │   ├── v6-to-v7.ts
│       │   │   │   │   │   ├── v7-to-v8.ts
│       │   │   │   │   │   └── v8-to-v9.ts
│       │   │   │   │   ├── v0-to-v1.ts
│       │   │   │   │   ├── v1-to-v2.ts
│       │   │   │   │   ├── v2-to-v3.ts
│       │   │   │   │   ├── v3-to-v4.ts
│       │   │   │   │   ├── v4-to-v5.ts
│       │   │   │   │   ├── v5-to-v6.ts
│       │   │   │   │   ├── v6-to-v7.ts
│       │   │   │   │   ├── v7-to-v8.ts
│       │   │   │   │   └── v8-to-v9.ts
│       │   │   │   ├── opfs-adapter.ts
│       │   │   │   ├── service.ts
│       │   │   │   └── types.ts
│       │   │   ├── transcription/
│       │   │   │   ├── service.ts
│       │   │   │   └── worker.ts
│       │   │   └── video-cache/
│       │   │       └── service.ts
│       │   ├── stores/
│       │   │   ├── assets-panel-store.tsx
│       │   │   ├── editor-store.ts
│       │   │   ├── keybindings/
│       │   │   │   └── migrations/
│       │   │   │       ├── index.ts
│       │   │   │       ├── v2-to-v3.ts
│       │   │   │       ├── v3-to-v4.ts
│       │   │   │       └── v4-to-v5.ts
│       │   │   ├── keybindings-store.ts
│       │   │   ├── panel-store.ts
│       │   │   ├── preview-store.ts
│       │   │   ├── properties-store.ts
│       │   │   ├── sounds-store.ts
│       │   │   ├── stickers-store.ts
│       │   │   └── timeline-store.ts
│       │   ├── types/
│       │   │   ├── animation.ts
│       │   │   ├── assets.ts
│       │   │   ├── blog.ts
│       │   │   ├── drag.ts
│       │   │   ├── editor.ts
│       │   │   ├── effects.ts
│       │   │   ├── export.ts
│       │   │   ├── eyedropper.d.ts
│       │   │   ├── fonts.ts
│       │   │   ├── glsl.d.ts
│       │   │   ├── keybinding.ts
│       │   │   ├── language.ts
│       │   │   ├── project.ts
│       │   │   ├── rendering.ts
│       │   │   ├── sounds.ts
│       │   │   ├── stickers.ts
│       │   │   ├── time.ts
│       │   │   ├── timeline.ts
│       │   │   └── transcription.ts
│       │   └── utils/
│       │       ├── browser.ts
│       │       ├── color.ts
│       │       ├── date.ts
│       │       ├── geometry.ts
│       │       ├── id.ts
│       │       ├── math.ts
│       │       ├── platform.ts
│       │       ├── string.ts
│       │       └── ui.ts
│       ├── tsconfig.json
│       └── tsconfig.tsbuildinfo
├── biome.json
├── docker-compose.yml
├── docs/
│   ├── actions.md
│   ├── countries-search.md
│   ├── effects-renderer.md
│   └── keyframes.md
├── package.json
├── packages/
│   ├── env/
│   │   ├── package.json
│   │   └── src/
│   │       ├── tools.ts
│   │       ├── types/
│   │       │   └── node-env.d.ts
│   │       └── web.ts
│   └── ui/
│       ├── package.json
│       ├── src/
│       │   └── icons/
│       │       ├── brand.tsx
│       │       ├── index.tsx
│       │       └── ui.tsx
│       └── tsconfig.json
├── tsconfig.json
└── turbo.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .cursor/commands/review.md
================================================
# Code Review Checklist

Review every point below carefully to ensure files follow consistent code style and best practices.

---

## Function Signatures & Parameters

- [ ] Every function accepts a single object parameter with destructuring in the signature (for readability and future extensibility)
  - Exception: tiny one-liner callbacks (e.g. `array.find(x => ...)`, `map`, `filter`, `sort`) do not need destructuring if it hurts readability

  ```tsx
  // ❌ wrong
  function formatTime(seconds: number, fps: number) { ... }

  // ✅ correct
  function formatTime({ seconds, fps }: { seconds: number; fps: number }) { ... }
  ```

## TypeScript & Type Safety

- [ ] No `any` references
- [ ] General interfaces are in the `types` folder, not scattered in components
  - Example: `TimelineTrack` interface belongs in `src/types/timeline.ts`, not `src/components/timeline/index.tsx`

## JSX & Components

- [ ] JSX is clean — no comments explaining what each part does
- [ ] Complex/reusable JSX is extracted into sub-components (placed below the main component)
- [ ] Components shared across multiple files are in separate files
- [ ] File order: constants specific to file (top) -> utils specific to file -> main component → sub-components (bottom)
- [ ] Components render UI only — domain logic lives in hooks, utilities, or managers
  - Simple interaction logic (gestures, modifier keys) can stay if not complex

## Code Organization & File Structure

- [ ] Each file has one single purpose/responsibility
  - Example: `timeline/index.tsx` should not define `validateElementTrackCompatibility` — that belongs in a lib file
  - Example: `lib/timeline-utils.ts` should not declare `TRACK_COLORS` — that belongs in `constants/`
- [ ] File name accurately reflects what the file contains — a misleading name is a bug waiting to happen
- [ ] Business logic lives in either `src/lib`, `src/core` or `src/services` folder

## Comments

- [ ] No AI comments — only human comments that explain _why_, not _what_
  - Bad: changelog-style comments, explaining readable code, using more words than necessary
- [ ] All comments are lowercase

## Naming Conventions

- [ ] Readability over brevity — use `element` not `el`, `event` not `e`
- [ ] Booleans are named `isSomething`, `hasSomething`, or `shouldSomething` — not `something`
- [ ] No title case for multi-word text/UI — use `Hello world` not `Hello World`

## Tailwind & Styling

- [ ] Always use `cn()` for `className` — never string interpolation with `${}` or ternaries inline
  ```tsx
  // ❌ wrong
  className={`base-class ${isActive && "active"} ${someHelper()}`}

  // ✅ correct
  className={cn("base-class", isActive && "active", someHelper())}
  ```
- [ ] Use `gap-*` instead of `mb-*` or `mt-*` for consistent spacing
- [ ] Use `size-*` instead of `h-* w-*` when width and height are the same
- [ ] When using `size-*` on icons inside `<Button>`, use `!` modifier to override default `size-4`
  ```tsx
  <Button>
    <PlusIcon className="!size-6" /> {/* ✅ correct */}
    <PlusIcon className="size-6" /> {/* ❌ wrong */}
    <PlusIcon className="!size-4" /> {/* ❌ unnecessary, size-4 is default */}
    <PlusIcon className="size-4" />{" "}
    {/* ❌ completely wrong, 1) doesn't override and 2) size-4 is default */}
  </Button>
  ```

## State Management (Zustand)

- [ ] React components never use `someStore.getState()` — use the `useSomeStore` hook instead
- [ ] High-frequency stores (timeline, playback, selections) use selectors — `useStore((s) => s.value)` not `const { value } = useStore()`
- [ ] Store/manager methods are not passed as props — sub-components access them directly

  ```tsx
  // ❌ wrong
  function Parent() {
    const { selectedElements } = useTimelineStore();
    return <Child selectedElements={selectedElements} />;
  }

  // ✅ correct
  function Parent() {
    return <Child />;
  }
  function Child() {
    const { selectedElements } = useTimelineStore();
  }
  ```

- [ ] Components and hooks should use the `useEditor` hook. Only use `EditorCore.getInstance()` if you are outside of a react component/hook. Eg: in a utility function, event handler.

## Code Quality

- [ ] Code is scannable — use variables and helper functions to make intent clear at a glance
- [ ] Complex logic is extracted into well-named variables or helpers
- [ ] No magic numbers or magic values — extract inline literals into named constants
  - Applies to colors, durations, thresholds, sizes, config values, etc.
  - If it's domain-specific to one file, a `const` at the top of that file is fine
  - If it's generic enough, it belongs in `constants/`

- [ ] No redundant single/plural function variants — if a function can operate on multiple items, it should accept an array and handle both cases. Don't create `doThing()` + `doThings()`.

  ```tsx
  // ❌ wrong — redundant variants
  function updateElement({ element }: { element: Element }) { ... }
  function updateElements({ elements }: { elements: Element[] }) { ... }

  // ✅ correct — one function, accepts array
  function updateElements({ elements }: { elements: Element[] }) { ... }
  ```

---

## Function Keywords

| Context                           | Keyword                   |
| --------------------------------- | ------------------------- |
| Next.js page components           | `export default function` |
| Main react component              | `export function`         |
| Sub-components                    | `function`                |
| Utility functions                 | `export function`         |
| Functions inside react components | `const`                   |

---

## Review Methodology

Do NOT review by reading the file top-to-bottom and noting what jumps out. Instead:

1. Go through each checklist section **one at a time**
2. For each section, scan the **entire file** for violations of that specific rule
3. Only move to the next section after you've exhausted the current one
4. After all sections are checked, do a final pass: re-read every checklist item and confirm you didn't skip it

Before outputting the review, list each checklist section and confirm you checked it:
`Signatures ✓ | TypeScript ✓ | JSX ✓ | Organization ✓ | File Names ✓ | Comments ✓ | Naming ✓ | Tailwind ✓ | State ✓ | Quality ✓ | Keywords ✓`

---

## IMPORTANT: Review Rules

- **ONLY** flag issues that are explicitly covered by a checklist item above.
- Do **NOT** invent your own rules, suggestions, or "nice to haves" as primary issues.
- The review output must be a list of issues, each one mapping to a specific checklist item.
- If something looks off but isn't covered by the checklist, you can mention it as a brief side note at the end — but keep it clearly separate from the actual review. Always default to fixing the issues covered by the checklist above, unless the user says otherwise.

> You WILL miss things if you try to review the whole file in one pass. Iterate rule by rule.

---

## Think Bigger

After the checklist review, step back and ask the hard questions. The biggest architectural problems get solved by the biggest questions.

- Does this abstraction actually need to exist? Could it be deleted entirely?
- Is this the right layer for this logic? (wrong layer = future pain)
- Is this solving a real problem, or a problem we invented?
- Would a simpler data model make this whole file unnecessary?
- Are we adding complexity to work around a bad decision made earlier?
- Could this field be derived from other existing fields? Redundant data in a model is a source of bugs.

Don't be shy about flagging these. A "why does this exist?" question is often worth more than 10 style fixes.


================================================
FILE: .cursor/rules/codebase-index.mdc
================================================
---
alwaysApply: false
---

# 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 PANEL_CONFIG

export-constants.ts
  export const DEFAULT_EXPORT_OPTIONS
  export const EXPORT_MIME_TYPES

font-constants.ts
  export const DEFAULT_FONT
  export const SYSTEM_FONTS

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[]

sticker-constants.ts
  export const STICKER_CATEGORIES

text-constants.ts
  export const MIN_FONT_SIZE
  export const MAX_FONT_SIZE
  export const FONT_SIZE_SCALE_REFERENCE
  export const DEFAULT_LETTER_SPACING
  export const DEFAULT_LINE_HEIGHT
  export const DEFAULT_TEXT_ELEMENT: Omit<TextElement, "id">

timeline-constants.tsx
  export const DEFAULT_TRANSFORM: Transform
  export const DEFAULT_OPACITY
  export const DEFAULT_BLEND_MODE: BlendMode
  export const DEFAULT_BOOKMARK_COLOR
  export const TRACK_COLORS: Record<TrackType, { background: string }>
  export const TRACK_HEIGHTS: Record<TrackType, number>
  export const TRACK_GAP
  export const DRAG_THRESHOLD_PX
  export const TIMELINE_CONSTANTS
  export const DEFAULT_TIMELINE_VIEW_STATE: TTimelineViewState
  export const TRACK_ICONS: Record<TrackType, React.ReactNode>

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-container-size.ts
  export function useContainerSize({
  	containerRef,
  }: {
  	containerRef: React.RefObject<HTMLElement | null>;
  })

use-editor.ts
  export function useEditor(): EditorCore

use-file-upload.ts
  export function useFileUpload({
  	accept,
  	multiple,
  	onFilesSelected,
  }: UseFileUploadOptions = {})

use-focus-lock.ts
  export function useFocusLock({
  	isActive,
  	onDismiss,
  	cursor = "default",
  	allowSelector,
  }: {
  	isActive: boolean;
  	onDismiss: () => void;
  	cursor?: FocusLockCursor;
  	allowSelector?: string;
  })

use-fullscreen.ts
  export function useFullscreen({
  	containerRef,
  }: {
  	containerRef: React.RefObject<HTMLElement | null>;
  })

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-paste-media.ts
  export function usePasteMedia()

use-preview-interaction.ts
  export function usePreviewInteraction({
  	canvasRef,
  }: {
  	canvasRef: React.RefObject<HTMLCanvasElement | null>;
  })

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-shift-key.ts
  export function useShiftKey(): RefObject<boolean>

use-sound-search.ts
  export function useSoundSearch({
  	query,
  	commercialOnly,
  }: {
  	query: string;
  	commercialOnly: boolean;
  })

use-transform-handles.ts
  export function useTransformHandles({

  	canvasRef,

  }: {

  	canvasRef: React.RefObject<HTMLCanvasElement | null>;

  })

## 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/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-bookmark-drag.ts
  export interface BookmarkDragState {
    isDragging: boolean
    bookmarkTime: number | null
    currentTime: number
  }
  export function useBookmarkDrag({

  	zoomLevel,

  	scrollRef,

  	snappingEnabled,

  	onSnapPointChange,

  }: UseBookmarkDragProps)

use-edge-auto-scroll.ts
  export function useEdgeAutoScroll({
  	isActive,
  	getMouseClientX,
  	rulerScrollRef,
  	tracksScrollRef,
  	contentWidth,
  	edgeThreshold = 100,
  	maxScrollSpeed = 15,
  }: UseEdgeAutoScrollParams): void

use-scroll-position.ts
  export function useScrollPosition({
  	scrollRef,
  }: {
  	scrollRef: React.RefObject<HTMLElement | null>;
  }): UseScrollPositionReturn

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" | "bookmark"
    elementId?: string
    trackId?: string
  }
  export interface SnapResult {
    snappedTime: number
    snapPoint: SnapPoint | null
    snapDistance: number
  }
  export interface UseTimelineSnappingOptions {
    snapThreshold?: number
    enableElementSnapping?: boolean
    enablePlayheadSnapping?: boolean
    enableBookmarkSnapping?: boolean
  }
  export function useTimelineSnapping({
  	snapThreshold = 10,
  	enableElementSnapping = true,
  	enablePlayheadSnapping = true,
  	enableBookmarkSnapping = 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<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[]

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>;
  }): 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<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 } | undef...
  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/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>

## 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/fonts

google-fonts.ts
  export function getCachedFontAtlas(): FontAtlas | null
  export function clearFontAtlasCache(): void
  export function prefetchFontAtlas(): Promise<FontAtlas | null>
  export function loadFullFont({
  	family,
  	weights = [400, 700],
  }: {
  	family: string;
  	weights?: number[];
  }): Promise<void>
  export function loadFonts({
  	families,
  }: {
  	families: string[];
  }): Promise<void>

## 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<RadialOrientation>
  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<ColorSto...
  export const parseGradient = ({
  	code,
  }: {
  	code: string;
  }): Array<GradientAst> => ...
  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<DecodedAudio>
  export function collectAudioElements({
  	tracks,
  	mediaAssets,
  	audioContext,
  }: {
  	tracks: TimelineTrack[];
  	mediaAssets: MediaAsset[];
  	audioContext: AudioContext;
  }): Promise<CollectedAudioElement[]>
  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<AudioMixSource[]>
  export function collectAudioClips({
  	tracks,
  	mediaAssets,
  }: {
  	tracks: TimelineTrack[];
  	mediaAssets: MediaAsset[];
  }): Promise<AudioClipSource[]>
  export function createTimelineAudioBuffer({
  	tracks,
  	mediaAssets,
  	duration,
  	sampleRate = 44100,
  	audioContext,
  }: {
  	tracks: TimelineTrack[];
  	mediaAssets: MediaAsset[];
  	duration: number;
  	sampleRate?: number;
  	audioContext?: AudioContext;
  }): Promise<AudioBuffer | null>

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<Blob> => ...

processing.ts
  export interface ProcessedMediaAsset extends Omit<MediaAsset, "id">
  export function generateThumbnail({
  	videoFile,
  	timeInSeconds,
  }: {
  	videoFile: File;
  	timeInSeconds: number;
  }): Promise<string>
  export function generateImageThumbnail({
  	imageFile,
  }: {
  	imageFile: File;
  }): Promise<string>
  export function processMediaAssets({
  	files,
  	onProgress,
  }: {
  	files: FileList | File[];
  	onProgress?: ({ progress }: { progress: number }) => void;
  }): Promise<ProcessedMediaAsset[]>

## apps/web/src/lib/preview

element-bounds.ts
  export interface ElementBounds {
    cx: number
    cy: number
    width: number
    height: number
    rotation: number
  }
  export interface ElementWithBounds {
    trackId: string
    elementId: string
    element: TimelineElement
    bounds: ElementBounds
  }
  export function getElementBounds({
  	element,
  	canvasSize,
  	mediaAsset,
  }: {
  	element: TimelineElement;
  	canvasSize: { width: number; height: number };
  	mediaAsset?: MediaAsset | null;
  }): ElementBounds | null
  export function getVisibleElementsWithBounds({
  	tracks,
  	currentTime,
  	canvasSize,
  	mediaAssets,
  }: {
  	tracks: TimelineTrack[];
  	currentTime: number;
  	canvasSize: { width: number; height: number };
  	mediaAssets: MediaAsset[];
  }): ElementWithBounds[]

hit-test.ts
  export function hitTest({

  	canvasX,

  	canvasY,

  	elementsWithBounds,

  }: {

  	canvasX: number;

  	canvasY: number;

  	elementsWithBounds: ElementWithBounds[];

  }): ElementWithBounds | null

preview-coords.ts
  export function screenToCanvas({

  	clientX,

  	clientY,

  	canvas,

  }: {

  	clientX: number;

  	clientY: number;

  	canvas: HTMLCanvasElement;

  }): { x: number; y: number }
  export function canvasToOverlay({

  	canvasX,

  	canvasY,

  	canvasRect,

  	containerRect,

  	canvasSize,

  }: {

  	canvasX: number;

  	canvasY: number;

  	canvasRect: DOMRect;

  	containerRect: DOMRect;

  	canvasSize: { width: number; height: number };

  }): { x: number; y: number }
  export function positionToOverlay({

  	positionX,

  	positionY,

  	canvasRect,

  	containerRect,

  	canvasSize,

  }: {

  	positionX: number;

  	positionY: number;

  	canvasRect: DOMRect;

  	containerRect: DOMRect;

  	canvasSize: { width: number; height: number };

  }): { x: number; y: number }
  export function getDisplayScale({

  	canvasRect,

  	canvasSize,

  }: {

  	canvasRect: DOMRect;

  	canvasSize: { width: number; height: number };

  }): { x: number; y: number }

preview-snap.ts
  export interface SnapLine {
    type: "horizontal" | "vertical"
    position: number
  }
  export const MIN_SCALE
  export interface SnapResult {
    snappedPosition: { x: number; y: number }
    activeLines: SnapLine[]
  }
  export function snapPosition({

  	proposedPosition,

  	canvasSize,

  	elementSize,

  }: {

  	proposedPosition: { x: number; y: number };

  	canvasSize: { width: number; height: number };

  	elementSize: { width: number; height: number };

  }): SnapResult
  export interface ScaleSnapResult {
    snappedScale: number
    activeLines: SnapLine[]
  }
  export function snapScale({

  	proposedScale,

  	position,

  	baseWidth,

  	baseHeight,

  	canvasSize,

  }: {

  	proposedScale: number;

  	position: { x: number; y: number };

  	baseWidth: number;

  	baseHeight: number;

  	canvasSize: { width: number; height: number };

  }): ScaleSnapResult
  export interface RotationSnapResult {
    snappedRotation: number
    isSnapped: boolean
  }
  export function snapRotation({

  	proposedRotation,

  }: {

  	proposedRotation: number;

  }): RotationSnapResult

## apps/web/src/lib/stickers

index.ts
  export function searchStickers({
  	query,
  	category,
  	limit = DEFAULT_SEARCH_LIMIT,
  }: {
  	query: string;
  	category: StickerCategory;
  	limit?: number;
  }): Promise<StickerSearchResult>
  export function browseStickers({
  	category,
  	page = 1,
  	limit = DEFAULT_SEARCH_LIMIT,
  }: {
  	category: StickerCategory;
  	page?: number;
  	limit?: number;
  }): Promise<StickerSearchResult>

registry.ts
  export function registerProvider({
  	provider,
  }: {
  	provider: StickerProvider;
  }): void
  export function hasProvider({ providerId }: { providerId: string }): boolean
  export function getProvider({
  	providerId,
  }: {
  	providerId: string;
  }): StickerProvider
  export function getAllProviders(): StickerProvider[]

resolver.ts
  export function resolveStickerId({
  	stickerId,
  	options,
  }: {
  	stickerId: string;
  	options?: StickerResolveOptions;
  }): string

sticker-id.ts
  export function parseStickerId({ stickerId }: { stickerId: string }): {
  	providerId: string;
  	providerValue: string;
  }
  export function buildStickerId({
  	providerId,
  	providerValue,
  }: {
  	providerId: string;
  	providerValue: string;
  }): string

## apps/web/src/lib/stickers/providers

emoji.ts
  export const emojiProvider: StickerProvider

flags.ts
  export const flagsProvider: StickerProvider

icons.ts
  export const iconsProvider: StickerProvider

index.ts
  export function registerDefaultStickerProviders({
  	providersToRegister = defaultProviders,
  }: {
  	providersToRegister?: StickerProvider[];
  } = {}): void

shapes.ts
  export const shapesProvider: StickerProvider

## apps/web/src/lib/timeline

bookmarks.ts
  export const BOOKMARK_TIME_EPSILON
  export function findBookmarkIndex({
  	bookmarks,
  	frameTime,
  }: {
  	bookmarks: Bookmark[];
  	frameTime: number;
  }): number
  export function isBookmarkAtTime({
  	bookmarks,
  	frameTime,
  }: {
  	bookmarks: Bookmark[];
  	frameTime: number;
  }): boolean
  export function toggleBookmarkInArray({
  	bookmarks,
  	frameTime,
  }: {
  	bookmarks: Bookmark[];
  	frameTime: number;
  }): Bookmark[]
  export function removeBookmarkFromArray({
  	bookmarks,
  	frameTime,
  }: {
  	bookmarks: Bookmark[];
  	frameTime: number;
  }): Bookmark[]
  export function updateBookmarkInArray({
  	bookmarks,
  	frameTime,
  	updates,
  }: {
  	bookmarks: Bookmark[];
  	frameTime: number;
  	updates: Partial<Omit<Bookmark, "time">>;
  }): Bookmark[]
  export function moveBookmarkInArray({
  	bookmarks,
  	fromTime,
  	toTime,
  }: {
  	bookmarks: Bookmark[];
  	fromTime: number;
  	toTime: number;
  }): Bookmark[]
  export function getFrameTime({
  	time,
  	fps,
  }: {
  	time: number;
  	fps: number;
  }): number
  export function getBookmarkAtTime({
  	bookmarks,
  	frameTime,
  }: {
  	bookmarks: Bookmark[];
  	frameTime: number;
  }): Bookmark | null
  export function getBookmarksActiveAtTime({
  	bookmarks,
  	time,
  }: {
  	bookmarks: Bookmark[];
  	time: number;
  }): Bookmark[]

drag-utils.ts
  export function getMouseTimeFromClientX({

  	clientX,

  	containerRect,

  	zoomLevel,

  	scrollLeft,

  }: {

  	clientX: number;

  	containerRect: DOMRect;

  	zoomLevel: number;

  	scrollLeft: 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 isVisualElement(
  	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({
  	stickerId,
  	name,
  	startTime,
  }: {
  	stickerId: string;
  	name?: 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 buildElementFromMedia({
  	mediaId,
  	mediaType,
  	name,
  	duration,
  	startTime,
  	buffer,
  }: {
  	mediaId: string;
  	mediaType: MediaType;
  	name: string;
  	duration: number;
  	startTime: number;
  	buffer?: AudioBuffer;
  }): CreateTimelineElement
  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 }[]
  export function collectFontFamilies({
  	tracks,
  }: {
  	tracks: TimelineTrack[];
  }): 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 }
  export function getEarliestMainTrackElement({
  	tracks,
  	excludeElementId,
  }: {
  	tracks: TimelineTrack[];
  	excludeElementId?: string;
  }): TimelineElement | null
  export function enforceMainTrackStart({
  	tracks,
  	targetTrackId,
  	requestedStartTime,
  	excludeElementId,
  }: {
  	tracks: TimelineTrack[];
  	targetTrackId: string;
  	requestedStartTime: number;
  	excludeElementId?: string;
  }): number

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
  export function sliderToZoom({
  	sliderPosition,
  	minZoom,
  	maxZoom = TIMELINE_CONSTANTS.ZOOM_MAX,
  }: {
  	sliderPosition: number;
  	minZoom: number;
  	maxZoom?: number;
  }): number
  export function zoomToSlider({
  	zoomLevel,
  	minZoom,
  	maxZoom = TIMELINE_CONSTANTS.ZOOM_MAX,
  }: {
  	zoomLevel: number;
  	minZoom: number;
  	maxZoom?: 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/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<SceneExporterEvents> {
    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<ArrayBuffer | null>
  }

## apps/web/src/services/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>
  }
  export function deleteDatabase({
  	dbName,
  }: {
  	dbName: string;
  }): 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
  }

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
    thumbnailUrl?: string
  }
  export type SerializedScene = Omit<TScene, "createdAt" | "updatedAt"> & {
  	createdAt: string;
  	updatedAt: string;
  }
  export type SerializedProjectMetadata = Omit<
  	TProjectMetadata,
  	"createdAt" | "updatedAt"
  > & {
  	createdAt: string;
  	updatedAt: string;
  }
  export type SerializedProject = Omit<TProject, "metadata" | "scenes"> & {
  	metadata: SerializedProjectMetadata;
  	scenes: Serializ...
  export interface StorageConfig {
    projectsDb: string
    mediaDb: string
    savedSoundsDb: string
    version: number
  }

## apps/web/src/services/transcription

service.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/services/video-cache

service.ts
  export class VideoCache {
    sinks
    initPromises
    async getFrameAt({
  		mediaId,
  		file,
  		time,
  	}: {
  		mediaId: string;
  		file: File;
  		time: number;
  	}): Promise<WrappedCanvas | null>
    clearVideo({ mediaId }: { mediaId: string }): void
    clearAll(): void
    getStats()
  }
  export const videoCache

## 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

preview-store.ts
  export const usePreviewStore

sounds-store.ts
  export const useSoundsStore

stickers-store.ts
  export const useStickersStore

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;
  	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"
    stickerId: 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
  }

fonts.ts
  export interface FontOption {
    value: string
    label: string
    category: "system" | "google" | "custom"
    weights?: number[]
    hasClassName?: boolean
  }
  export interface GoogleFontMeta {
    family: string
    category: string
  }
  export interface FontAtlasEntry {
    x: number
    y: number
    w: number
    ch: number
    s: string[]
  }
  export interface FontAtlas {
    fonts: Record<string, FontAtlasEntry>
  }

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}`

rendering.ts
  export type BlendMode = | "normal"

  	| "darken"

  	| "multiply"

  	| "color-burn"

  	| "lighten"

  	| "screen"

  	| "plus-ligh...

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 = keyof typeof STICKER_CATEGORIES
  export interface StickerItem {
    id: string
    provider: string
    name: string
    previewUrl: string
    metadata: Record<string, unknown>
  }
  export interface StickerSearchResult {
    items: StickerItem[]
    total: number
    hasMore: boolean
  }
  export interface StickerProviderSearchOptions {
    limit?: number
  }
  export interface StickerProviderBrowseOptions {
    page?: number
    limit?: number
  }
  export interface StickerResolveOptions {
    width?: number
    height?: number
  }
  export interface StickerProvider {
    id: string
    search({
  		query,
  		options,
  	}: {
  		query: string;
  		options?: StickerProviderSearchOptions;
  	}): Promise<StickerSearchResult>
    browse({
  		options,
  	}: {
  		options?: StickerProviderBrowseOptions;
  	}): Promise<StickerSearchResult>
    resolveUrl({
  		stickerId,
  		options,
  	}: {
  		stickerId: string;
  		options?: StickerResolveOptions;
  	}): string
  }

time.ts
  export type TTimeCode = "MM:SS" | "HH:MM:SS" | "HH:MM:SS:CS" | "HH:MM:SS:FF"

timeline.ts
  export interface Bookmark {
    time: number
    note?: string
    color?: string
    duration?: number
  }
  export interface TScene {
    id: string
    name: string
    isMain: boolean
    tracks: TimelineTrack[]
    bookmarks: Bookmark[]
    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
    blendMode?: BlendMode
  }
  export interface ImageElement extends BaseTimelineElement {
    type: "image"
    mediaId: string
    hidden?: boolean
    transform: Transform
    opacity: number
    blendMode?: BlendMode
  }
  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"
    letterSpacing?: number
    lineHeight?: number
    hidden?: boolean
    transform: Transform
    opacity: number
    blendMode?: BlendMode
  }
  export interface StickerElement extends BaseTimelineElement {
    type: "sticker"
    stickerId: string
    hidden?: boolean
    transform: Transform
    opacity: number
    blendMode?: BlendMode
  }
  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
  	| 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

color.ts
  export type ColorFormat = "hex" | "rgb" | "hsl" | "hsv"
  export function hexToHsv({ hex }: { hex: string }): [number, number, number]
  export function hsvToHex({

  	h,

  	s,

  	v,

  }: { h: number; s: number; v: number }): string
  export function parseHexAlpha({ hex }: { hex: string }): {

  	rgb: string;

  	alpha: number;

  }
  export function appendAlpha({

  	rgbHex,

  	alpha,

  }: { rgbHex: string; alpha: number }): string
  export function extractColorFromText({

  	text,

  }: { text: string }): string | null
  export function formatColorValue({

  	hex,

  	format,

  }: {

  	hex: string;

  	format: ColorFormat;

  }): string
  export function parseColorInput({

  	input,

  	format,

  }: {

  	input: string;

  	format: ColorFormat;

  }): string | null

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
  export function evaluateMathExpression({
  	input,
  }: {
  	input: string;
  }): number | null

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;
  })
  export function OcCheckerboardIcon({
  	className = "",
  	size = 32,
  }: {
  	className?: string;
  	size?: number;
  })
  export function OcFontWeightIcon({
  	className = "",
  	size = 32,
  }: {
  	className?: string;
  	size?: number;
  })
  export function OcSlidersVerticalIcon({
  	className = "",
  	size = 32,
  }: {
  	className?: string;
  	size?: number;
  })
  export function OcSocialIcon({
  	className = "",
  	size = 32,
  }: {
  	className?: string;
  	size?: number;
  })

```

---

_Generated and maintained by [Twiggy](https://github.com/twiggy-tools/Twiggy)_


================================================
FILE: .cursor/rules/comments.mdc
================================================
---
alwaysApply: false
---
# Comment Guidelines

## Good Comments (Human-style)
- Explain WHY, not WHAT
- Document non-obvious behavior or edge cases
- Warn about performance implications or side effects
- Explain business logic that isn't clear from code

Examples:
```javascript
// transfer, not copy; sender buffer detaches
// satisfies: check shape; keep literals  
// keep multibyte across chunks
// timingSafeEqual throws on length mismatch
```

## Bad Comments (AI-style)
- Don't explain what the code literally does
- Don't add changelog-style comments in code
- Don't comment every line or obvious operations

Avoid:
```javascript
// Prevent duplicate initialization
// Check if project is already loaded  
// Mark as initializing to prevent race conditions
// (changed from blah to blah)
```

## Rule
Only add comments when there's genuinely non-obvious behavior, performance considerations, or business logic that needs context. Code should be self-documenting through naming and structure.

================================================
FILE: .cursor/rules/handling-uncertainty.mdc
================================================
---
alwaysApply: false
---
# Handling Uncertainty

## Principle
If you can't confidently respond due to missing context, data access, or ambiguity (multiple interpretations), report it instead of guessing. Seek clarification to avoid errors.

Apply when: query lacks details, no access to info/tools, or unclear intent.

## How to Report
1. **Description**: Why uncertain and what you need.
2. **Questions**: 1-3 targeted ones.
3. **Assumptions** (opt.): State if proceeding; omit otherwise.

Direct and concise.

**Assumptions**: None.

Builds transparency.

================================================
FILE: .cursor/rules/readability.mdc
================================================
---
alwaysApply: false
---
# Readability First

Optimize code for AI agents to understand and modify.

Never abbreviate. `event` not `e`, `element` not `el`. If it's easy to read, it's correct. In this case, "config" is better than "configuration" because it's shorter and is **still very readable**. "El" is not very readable.


================================================
FILE: .cursor/rules/separation-of-concerns.mdc
================================================
---
alwaysApply: false
---
# Separation of Concerns

## Core Principle

Each file should have one single purpose/responsibility. Related functionality should be grouped together, unrelated functionality should be separated.

## Good Separation

- One file per major concern (auth, validation, data transformation)
- Group related utilities together
- Extract shared logic into dedicated files
- Keep API routes focused on their specific endpoint logic

Examples:

```javascript
// ✅ Good: Each file has clear responsibility
/lib/rate-limit.ts          // Rate limiting utilities
/lib/validation.ts          // Input validation schemas
/lib/freesound-api.ts       // External API integration
/api/sounds/search/route.ts // Route handler only
```

## Bad Mixing of Concerns

Avoid cramming multiple responsibilities into one file:

```javascript
// ❌ Bad: Route file doing everything
/api/sounds/search/route.ts
- Rate limiting logic
- Validation schemas
- API transformation
- External API calls
- Response formatting
- Error handling utilities
```

## When to Separate

- File is getting long (>500 lines)
- Multiple distinct responsibilities in one file
- Logic could be reused elsewhere
- Complex utilities that distract from main purpose

## Rule

One file, one responsibility. Extract shared concerns into focused utility files

================================================
FILE: .cursor/rules/ultracite.mdc
================================================
---
alwaysApply: false
---
# Project Context

Ultracite enforces strict type safety, accessibility standards, and consistent code quality for JavaScript/TypeScript projects using Biome's formatter.

## Key Principles

- Zero configuration required
- Subsecond performance
- Maximum type safety
- AI-friendly code generation

## Before Writing Code

1. Analyze existing patterns in the codebase
2. Consider edge cases and error scenarios
3. Follow the rules below strictly
4. Validate accessibility requirements
5. Avoid code duplication

## Rules

### Accessibility (a11y)

- Always include a `title` element for icons unless there's text beside the icon.
- Accompany `onClick` with at least one of: `onKeyUp`, `onKeyDown`, or `onKeyPress`.
- Accompany `onMouseOver`/`onMouseOut` with `onFocus`/`onBlur`.

### Code Complexity and Quality

- Don't use primitive type aliases or misleading types.
- Don't use the comma operator.
- Use for...of statements instead of Array.forEach.
- Don't initialize variables to undefined.
- Use .flatMap() instead of map().flat() when possible.

### React and JSX Best Practices

- Don't import `React` itself.
- Don't use both `children` and `dangerouslySetInnerHTML` props on the same element.
- Don't insert comments as text nodes.
- Use `<>...</>` instead of `<Fragment>...</Fragment>`.

### Function Parameters and Props

- Always use destructured props objects instead of individual parameters in functions.
- Example: `function helloWorld({ prop }: { prop: string })` instead of `function helloWorld(param: string)`.
- This applies to all functions, not just React components.

### Correctness and Safety

- Don't assign a value to itself.
- Avoid unused imports and variables.
- Don't use await inside loops.
- Don't hardcode sensitive data like API keys and tokens.
- Don't use the TypeScript directive @ts-ignore.
- Make sure the `preconnect` attribute is used when using Google Fonts.
- Don't use the `delete` operator.
- Don't use `require()` in TypeScript/ES modules - use proper `import` statements.

### TypeScript Best Practices

- Don't use TypeScript enums.
- Use either `T[]` or `Array<T>` consistently.
- Don't use the `any` type.

### Style and Consistency

- Don't use global `eval()`.
- Use `String.slice()` instead of `String.substr()` and `String.substring()`.
- Don't use `else` blocks when the `if` block breaks early.
- Put default function parameters and optional function parameters last.
- Use `new` when throwing an error.
- Use `String.trimStart()` and `String.trimEnd()` over `String.trimLeft()` and `String.trimRight()`.

### Next.js Specific Rules

- Don't use `<img>` elements in Next.js projects.
- Don't use `<head>` elements in Next.js projects.

## Example: Error Handling

```typescript
// ✅ Good: Comprehensive error handling
try {
  const result = await fetchData();
  return { success: true, data: result };
} catch (error) {
  console.error("API call failed:", error);
  return { success: false, error: error.message };
}

// ❌ Bad: Swallowing errors
try {
  return await fetchData();
} catch (e) {
  console.log(e);
}
```


================================================
FILE: .cursor/rules/writing-scannable-code.mdc
================================================
---
alwaysApply: false
---
# Scannable Code Guidelines/Separating Concerns.

## Core Principle

Code should be scannable through proper abstraction, not comments. Use variables and helper functions to make intent clear at a glance.

## Good Scannable Code

- Extract complex logic into well-named variables
- Create helper functions for multi-step operations
- Use descriptive names that explain intent

Examples:

```javascript
// ✅ Scannable: Intent is clear from variable names
const isValidUser = user.isActive && user.hasPermissions;
const shouldProcessPayment = amount > 0 && !order.isPaid;

// ✅ Scannable: Complex logic extracted to helper
const searchParams = buildFreesoundSearchParams({ query, filters, pagination });
const transformedResults = transformFreesoundResults({ rawResults });
```

## Bad Unscannable Code

Avoid:

```javascript
// ❌ Hard to scan: What does this condition mean?
if (type === "effects" || !type) {
  params.append("filter", "duration:[* TO 30.0]");
  params.append("filter", `avg_rating:[${min_rating} TO *]`);
  if (commercial_only) {
    params.append("filter", 'license:("Attribution" OR "Creative Commons 0")');
  }
}

// ❌ Hard to scan: Complex ternary
const sortParam = query
  ? sort === "score"
    ? "score"
    : `${sort}_desc`
  : `${sort}_desc`;
```

## Rule

Make code scannable by extracting intent into variables and helper functions. If you need to think about what code does, extract it. The reader should understand the flow without diving into implementation details.

================================================
FILE: .cursor/settings.json
================================================
{
	"plugins": {
		"figma": {
			"enabled": true
		}
	}
}


================================================
FILE: .cursor/skills/design/SKILL.md
================================================
---
name: frontend-design
description: Create distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
license: Complete terms in LICENSE.txt
---

This skill guides creation of distinctive, production-grade frontend interfaces that avoid generic "AI slop" aesthetics. Implement real working code with exceptional attention to aesthetic details and creative choices.

The user provides frontend requirements: a component, page, application, or interface to build. They may include context about the purpose, audience, or technical constraints.

## Design Thinking

Before coding, understand the context and commit to a BOLD aesthetic direction:

- **Purpose**: What problem does this interface solve? Who uses it?
- **Tone**: Pick an extreme: brutally minimal, maximalist chaos, retro-futuristic, organic/natural, luxury/refined, playful/toy-like, editorial/magazine, brutalist/raw, art deco/geometric, soft/pastel, industrial/utilitarian, etc. There are so many flavors to choose from. Use these for inspiration but design one that is true to the aesthetic direction.
- **Constraints**: Technical requirements (framework, performance, accessibility).
- **Differentiation**: What makes this UNFORGETTABLE? What's the one thing someone will remember?

**CRITICAL**: Choose a clear conceptual direction and execute it with precision. Bold maximalism and refined minimalism both work - the key is intentionality, not intensity.

Then implement working code (HTML/CSS/JS, React, Vue, etc.) that is:

- Production-grade and functional
- Visually striking and memorable
- Cohesive with a clear aesthetic point-of-view
- Meticulously refined in every detail

## Frontend Aesthetics Guidelines

Focus on:

- **Typography**: Choose fonts that are beautiful, unique, and interesting. Avoid generic fonts like Arial and Inter; opt instead for distinctive choices that elevate the frontend's aesthetics; unexpected, characterful font choices. Pair a distinctive display font with a refined body font.
- **Color & Theme**: Commit to a cohesive aesthetic. Use CSS variables for consistency. Dominant colors with sharp accents outperform timid, evenly-distributed palettes.
- **Motion**: Use animations for effects and micro-interactions. Prioritize CSS-only solutions for HTML. Use Motion library for React when available. Focus on high-impact moments: one well-orchestrated page load with staggered reveals (animation-delay) creates more delight than scattered micro-interactions. Use scroll-triggering and hover states that surprise.
- **Spatial Composition**: Unexpected layouts. Asymmetry. Overlap. Diagonal flow. Grid-breaking elements. Generous negative space OR controlled density.
- **Backgrounds & Visual Details**: Create atmosphere and depth rather than defaulting to solid colors. Add contextual effects and textures that match the overall aesthetic. Apply creative forms like gradient meshes, noise textures, geometric patterns, layered transparencies, dramatic shadows, decorative borders, custom cursors, and grain overlays.

NEVER use generic AI-generated aesthetics like overused font families (Inter, Roboto, Arial, system fonts), cliched color schemes (particularly purple gradients on white backgrounds), predictable layouts and component patterns, and cookie-cutter design that lacks context-specific character.

Interpret creatively and make unexpected choices that feel genuinely designed for the context. No design should be the same. Vary between light and dark themes, different fonts, different aesthetics. NEVER converge on common choices (Space Grotesk, for example) across generations.

**IMPORTANT**: Match implementation complexity to the aesthetic vision. Maximalist designs need elaborate code with extensive animations and effects. Minimalist or refined designs need restraint, precision, and careful attention to spacing, typography, and subtle details. Elegance comes from executing the vision well.

Remember: Claude is capable of extraordinary creative work. Don't hold back, show what can truly be created when thinking outside the box and committing fully to a distinctive vision.


================================================
FILE: .dockerignore
================================================
node_modules
.next
.git
.gitignore
*.md
.env*
!.env.example
.cursor
.vscode
diffs
docs
agent-transcripts
mcps
terminals
apps/web/.font-cache


================================================
FILE: .github/CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct

## Our Pledge

We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, caste, color, religion, or sexual
identity and orientation.

We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.

## Our Standards

Examples of behavior that contributes to a positive environment for our
community include:

- Demonstrating empathy and kindness toward other people
- Being respectful of differing opinions, viewpoints, and experiences
- Giving and gracefully accepting constructive feedback
- Accepting responsibility and apologizing to those affected by our mistakes,
  and learning from the experience
- Focusing on what is best not just for us as individuals, but for the overall
  community

Examples of unacceptable behavior include:

- The use of sexualized language or imagery, and sexual attention or advances of
  any kind
- Trolling, insulting or derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or email address,
  without their explicit permission
- Other conduct which could reasonably be considered inappropriate in a
  professional setting

## Enforcement Responsibilities

Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.

Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.

## Scope

This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.

## Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at our discord server.
All complaints will be reviewed and investigated promptly and fairly.

All community leaders are obligated to respect the privacy and security of the
reporter of any incident.

## Enforcement Guidelines

Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:

### 1. Correction

**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.

**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.

## Attribution

This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.1, available at
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].

[homepage]: https://www.contributor-covenant.org
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html


================================================
FILE: .github/CONTRIBUTING.md
================================================
# Contributing to OpenCut

⚠️ We are currently NOT accepting feature PRs while we build out the core editor.

If you want to contribute:

1. Open an issue first to discuss
2. Wait for maintainer approval
3. Only then start coding

Critical bug fixes may be accepted on a case-by-case basis.

Thank you for your interest in contributing to OpenCut! This document provides guidelines and instructions for contributing.

## Getting Started

### Prerequisites

- [Node.js](https://nodejs.org/en/) (v18 or later)
- [Bun](https://bun.sh/docs/installation)
  (for `npm` alternative)
- [Docker](https://docs.docker.com/get-docker/) and [Docker Compose](https://docs.docker.com/compose/install/)

> **Note:** Docker is optional, but it's essential for running the local database and Redis services. If you're planning to contribute to frontend features, you can skip the Docker setup. If you have followed the steps below in [Setup](#setup), you're all set to go!

### Setup

1. Fork the repository
2. Clone your fork locally
3. Navigate to the web app directory: `cd apps/web`
4. Copy `.env.example` to `.env.local`:

   ```bash
   # Unix/Linux/Mac
   cp .env.example .env.local

   # Windows Command Prompt
   copy .env.example .env.local

   # Windows PowerShell
   Copy-Item .env.example .env.local
   ```

5. Install dependencies: `bun install`
6. Start the development server: `bun run dev`

> **Note:** If you see an error like `Unsupported URL Type "workspace:*"` when running `npm install`, you have two options:
>
> 1. Upgrade to a recent npm version (v9 or later), which has full workspace protocol support.
> 2. Use an alternative package manager such as **bun** or **pnpm**.

## What to Focus On

**🎯 Good Areas to Contribute:**

- Timeline functionality and UI improvements
- Project management features
- Performance optimizations
- Bug fixes in existing functionality
- UI/UX improvements
- Documentation and testing

**⚠️ Areas to Avoid:**

- Preview panel enhancements (text fonts, stickers, effects)
- Export functionality improvements
- Preview rendering optimizations

**Why?** We're currently planning a major refactor of the preview system. The current preview renders DOM elements (HTML), but we're moving to a binary rendering approach similar to CapCut. This new system will ensure consistency between preview and export, and provide much better performance and quality.

The current HTML-based preview is essentially a prototype - the binary approach will be the "real deal." To avoid wasted effort, please focus on other areas of the application until this refactor is complete.

If you're unsure whether your idea falls into the preview category, feel free to ask us [directly in discord](https://discord.gg/zmR9N35cjK) or create a GitHub issue!

## Development Setup

### Local Development

1. Start the database and Redis services:

   ```bash
   # From project root
   docker-compose up -d
   ```

2. Navigate to the web app directory:

   ```bash
   cd apps/web
   ```

3. Copy `.env.example` to `.env.local`:

   ```bash
   # Unix/Linux/Mac
   cp .env.example .env.local

   # Windows Command Prompt
   copy .env.example .env.local

   # Windows PowerShell
   Copy-Item .env.example .env.local
   ```

4. Configure required environment variables in `.env.local`:

   **Required Variables:**

   ```bash
   # Database (matches docker-compose.yaml)
   DATABASE_URL="postgresql://opencut:opencut@localhost:5432/opencut"

   # Generate a secure secret for Better Auth
   BETTER_AUTH_SECRET="your-generated-secret-here"
   NEXT_PUBLIC_SITE_URL="http://localhost:3000"

   # Redis (matches docker-compose.yaml)
   UPSTASH_REDIS_REST_URL="http://localhost:8079"
   UPSTASH_REDIS_REST_TOKEN="example_token"

   # Development
   NODE_ENV="development"
   ```

   **Generate BETTER_AUTH_SECRET:**

   ```bash
   # Unix/Linux/Mac
   openssl rand -base64 32

   # Windows PowerShell (simple method)
   [System.Web.Security.Membership]::GeneratePassword(32, 0)

   # Cross-platform (using Node.js)
   node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"

   # Or use an online generator: https://generate-secret.vercel.app/32
   ```

5. Run database migrations: `bun run db:migrate`
6. Start the development server: `bun run dev`

## How to Contribute

### Reporting Bugs

- Use the bug report template
- Include steps to reproduce
- Provide screenshots if applicable

### Suggesting Features

- Use the feature request template
- Explain the use case
- Consider implementation details

### Code Contributions

1. Create a new branch: `git checkout -b feature/your-feature-name`
2. Make your changes
3. Navigate to the web app directory: `cd apps/web`
4. Run the linter: `bun run lint`
5. Format your code: `bunx biome format --write .`
6. Commit your changes with a descriptive message
7. Push to your fork and create a pull request

## Code Style

- We use Biome for code formatting and linting
- Run `bunx biome format --write .` from the `apps/web` directory to format code
- Run `bun run lint` from the `apps/web` directory to check for linting issues
- Follow the existing code patterns

## Pull Request Process

1. Fill out the pull request template completely
2. Link any related issues
3. Ensure CI passes
4. Request review from maintainers
5. Address any feedback

## Community

- Be respectful and inclusive
- Follow our Code of Conduct
- Help others in discussions and issues

Thank you for contributing!


================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.yml
================================================
name: Bug report
description: Create a report to help us improve
title: "[BUG] "
labels: bug
body:
  - type: input
    id: Platform
    attributes:
      label: Platform
      description: Please enter the platform on which you encountered the bug.
      placeholder: e.g. Windows 11, Ubuntu 14.04
    validations:
      required: true
  - type: input
    id: Browser
    attributes:
      label: Browser
      description: Please enter the browser on which you encountered the bug.
      placeholder: e.g. Chrome 137, Firefox 137, Safari 17
    validations:
      required: true
  - type: textarea
    id: current-behavior
    attributes:
      label: Current Behavior
      description: A concise description of what you're experiencing.
    validations:
      required: true
  - type: textarea
    id: expected-behavior
    attributes:
      label: Expected Behavior
      description: A concise description of what you expected to happen.
    validations:
      required: false
  - type: dropdown
    id: recurrence-probability
    attributes:
      label: Recurrence Probability
      description: How often does this bug occur?
      options:
        - Always
        - Usually
        - Sometimes
        - Seldom
      default: 0
    validations:
      required: true
  - type: textarea
    id: steps-to-reproduce
    attributes:
      label: Steps To Reproduce
      description: Steps to reproduce the behavior.
      placeholder: |
        1. Go to '...'
        2. Click on '....'
        3. Scroll down to '....'
        4. See error
    validations:
      required: true
  - type: textarea
    id: additional-context
    attributes:
      label: Anything else?
      description: |
        Links? References? Anything that will give us more context about the issue you are encountering!

        Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
    validations:
      required: false


================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.yml
================================================
name: Feature request
description: Suggest an idea for OpenCut
title: "[FEATURE] "
labels: enhancement
body:
  - type: markdown
    attributes:
      value: Please make sure that no duplicated issues has already been delivered.
  - type: textarea
    id: problem
    attributes:
      label: Problem
      placeholder: Is your feature request related to a problem? Please describe.
      description: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
    validations:
      required: true
  - type: textarea
    id: solution
    attributes:
      label: Solution
      placeholder: Describe the solution you'd like.
      description: A clear and concise description of what you want to happen.
    validations:
      required: true
  - type: textarea
    id: alternative
    attributes:
      label: Alternative
      placeholder: Describe alternatives you've considered.
      description: A clear and concise description of any alternative solutions or features you've considered.
    validations:
      required: true
  - type: textarea
    id: additional-context
    attributes:
      label: Anything else?
      description: |
        Links? References? Anything that will give us more context about the issue you are encountering!

        Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
    validations:
      required: false


================================================
FILE: .github/SECURITY.md
================================================
# Security Policy

## Supported Versions

| Version | Supported          |
| ------- | ------------------ |
| 1.x.x   | :white_check_mark: |

## Reporting a Vulnerability

We take security vulnerabilities seriously. If you discover a security vulnerability within OpenCut, please send an email to security@opencut.app. All security vulnerabilities will be promptly addressed.

Please do not report security vulnerabilities through public GitHub issues.

### What to include in your report

- Description of the vulnerability
- Steps to reproduce
- Potential impact
- Any suggested fixes

### Response timeline

- We will acknowledge receipt within 48 hours
- We will provide a detailed response within 5 business days
- We will keep you updated on our progress

Thank you for helping keep OpenCut secure!


================================================
FILE: .github/SUPPORT.md
================================================
# Getting Help

Thanks for using OpenCut! If you need help, here are your options:

## Documentation

- Check our [README](../README.md) for basic setup instructions
- Review the [Contributing Guidelines](CONTRIBUTING.md) for development setup

## Issues

- **Bug reports**: Use the bug report template
- **Feature requests**: Use the feature request template
- **Questions**: Use GitHub Discussions for general questions

## Community

- Join our discussions on GitHub
- Follow the [Code of Conduct](CODE_OF_CONDUCT.md)

## Response Times

- Issues are typically triaged within 2-3 business days
- Feature requests may take longer to evaluate
- Security issues are handled with priority

We appreciate your patience and contributions to making OpenCut better!


================================================
FILE: .github/copilot-instructions.md
================================================
---
applyTo: "**/*.{ts,tsx,js,jsx}"
---

# Project Context

Ultracite enforces strict type safety, accessibility standards, and consistent code quality for JavaScript/TypeScript projects using Biome's lightning-fast formatter and linter.

## Key Principles

- Zero configuration required
- Subsecond performance
- Maximum type safety
- AI-friendly code generation

## Before Writing Code

1. Analyze existing patterns in the codebase
2. Consider edge cases and error scenarios
3. Follow the rules below strictly
4. Validate accessibility requirements

## Rules

### Accessibility (a11y)

- Don't use `accessKey` attribute on any HTML element.
- Don't set `aria-hidden="true"` on focusable elements.
- Don't add ARIA roles, states, and properties to elements that don't support them.
- Don't use distracting elements like `<marquee>` or `<blink>`.
- Only use the `scope` prop on `<th>` elements.
- Don't assign non-interactive ARIA roles to interactive HTML elements.
- Make sure label elements have text content and are associated with an input.
- Don't assign interactive ARIA roles to non-interactive HTML elements.
- Don't assign `tabIndex` to non-interactive HTML elements.
- Don't use positive integers for `tabIndex` property.
- Don't include "image", "picture", or "photo" in img alt prop.
- Don't use explicit role property that's the same as the implicit/default role.
- Make static elements with click handlers use a valid role attribute.
- Always include a `title` element for SVG elements.
- Give all elements requiring alt text meaningful information for screen readers.
- Make sure anchors have content that's accessible to screen readers.
- Assign `tabIndex` to non-interactive HTML elements with `aria-activedescendant`.
- Include all required ARIA attributes for elements with ARIA roles.
- Make sure ARIA properties are valid for the element's supported roles.
- Always include a `type` attribute for button elements.
- Make elements with interactive roles and handlers focusable.
- Give heading elements content that's accessible to screen readers (not hidden with `aria-hidden`).
- Always include a `lang` attribute on the html element.
- Always include a `title` attribute for iframe elements.
- Accompany `onClick` with at least one of: `onKeyUp`, `onKeyDown`, or `onKeyPress`.
- Accompany `onMouseOver`/`onMouseOut` with `onFocus`/`onBlur`.
- Include caption tracks for audio and video elements.
- Use semantic elements instead of role attributes in JSX.
- Make sure all anchors are valid and navigable.
- Ensure all ARIA properties (`aria-*`) are valid.
- Use valid, non-abstract ARIA roles for elements with ARIA roles.
- Use valid ARIA state and property values.
- Use valid values for the `autocomplete` attribute on input elements.
- Use correct ISO language/country codes for the `lang` attribute.

### Code Complexity and Quality

- Don't use consecutive spaces in regular expression literals.
- Don't use the `arguments` object.
- Don't use primitive type aliases or misleading types.
- Don't use the comma operator.
- Don't use empty type parameters in type aliases and interfaces.
- Don't write functions that exceed a given Cognitive Complexity score.
- Don't nest describe() blocks too deeply in test files.
- Don't use unnecessary boolean casts.
- Don't use unnecessary callbacks with flatMap.
- Use for...of statements instead of Array.forEach.
- Don't create classes that only have static members (like a static namespace).
- Don't use this and super in static contexts.
- Don't use unnecessary catch clauses.
- Don't use unnecessary constructors.
- Don't use unnecessary continue statements.
- Don't export empty modules that don't change anything.
- Don't use unnecessary escape sequences in regular expression literals.
- Don't use unnecessary fragments.
- Don't use unnecessary labels.
- Don't use unnecessary nested block statements.
- Don't rename imports, exports, and destructured assignments to the same name.
- Don't use unnecessary string or template literal concatenation.
- Don't use String.raw in template literals when there are no escape sequences.
- Don't use useless case statements in switch statements.
- Don't use ternary operators when simpler alternatives exist.
- Don't use useless `this` aliasing.
- Don't use any or unknown as type constraints.
- Don't initialize variables to undefined.
- Don't use the void operators (they're not familiar).
- Use arrow functions instead of function expressions.
- Use Date.now() to get milliseconds since the Unix Epoch.
- Use .flatMap() instead of map().flat() when possible.
- Use literal property access instead of computed property access.
- Don't use parseInt() or Number.parseInt() when binary, octal, or hexadecimal literals work.
- Use concise optional chaining instead of chained logical expressions.
- Use regular expression literals instead of the RegExp constructor when possible.
- Don't use number literal object member names that aren't base 10 or use underscore separators.
- Remove redundant terms from logical expressions.
- Use while loops instead of for loops when you don't need initializer and update expressions.
- Don't pass children as props.
- Don't reassign const variables.
- Don't use constant expressions in conditions.
- Don't use `Math.min` and `Math.max` to clamp values when the result is constant.
- Don't return a value from a constructor.
- Don't use empty character classes in regular expression literals.
- Don't use empty destructuring patterns.
- Don't call global object properties as functions.
- Don't declare functions and vars that are accessible outside their block.
- Make sure builtins are correctly instantiated.
- Don't use super() incorrectly inside classes. Also check that super() is called in classes that extend other constructors.
- Don't use variables and function parameters before they're declared.
- Don't use 8 and 9 escape sequences in string literals.
- Don't use literal numbers that lose precision.

### React and JSX Best Practices

- Don't use the return value of React.render.
- Make sure all dependencies are correctly specified in React hooks.
- Make sure all React hooks are called from the top level of component functions.
- Don't forget key props in iterators and collection literals.
- Don't destructure props inside JSX components in Solid projects.
- Don't define React components inside other components.
- Don't use event handlers on non-interactive elements.
- Don't assign to React component props.
- Don't use both `children` and `dangerouslySetInnerHTML` props on the same element.
- Don't use dangerous JSX props.
- Don't use Array index in keys.
- Don't insert comments as text nodes.
- Don't assign JSX properties multiple times.
- Don't add extra closing tags for components without children.
- Use `<>...</>` instead of `<Fragment>...</Fragment>`.
- Watch out for possible "wrong" semicolons inside JSX elements.

### Correctness and Safety

- Don't assign a value to itself.
- Don't return a value from a setter.
- Don't compare expressions that modify string case with non-compliant values.
- Don't use lexical declarations in switch clauses.
- Don't use variables that haven't been declared in the document.
- Don't write unreachable code.
- Make sure super() is called exactly once on every code path in a class constructor before this is accessed if the class has a superclass.
- Don't use control flow statements in finally blocks.
- Don't use optional chaining where undefined values aren't allowed.
- Don't have unused function parameters.
- Don't have unused imports.
- Don't have unused labels.
- Don't have unused private class members.
- Don't have unused variables.
- Make sure void (self-closing) elements don't have children.
- Don't return a value from a function with the return type 'void'
- Use isNaN() when checking for NaN.
- Make sure "for" loop update clauses move the counter in the right direction.
- Make sure typeof expressions are compared to valid values.
- Make sure generator functions contain yield.
- Don't use await inside loops.
- Don't use bitwise operators.
- Don't use expressions where the operation doesn't change the value.
- Make sure Promise-like statements are handled appropriately.
- Don't use **dirname and **filename in the global scope.
- Prevent import cycles.
- Don't use configured elements.
- Don't hardcode sensitive data like API keys and tokens.
- Don't let variable declarations shadow variables from outer scopes.
- Don't use the TypeScript directive @ts-ignore.
- Prevent duplicate polyfills from Polyfill.io.
- Don't use useless backreferences in regular expressions that always match empty strings.
- Don't use unnecessary escapes in string literals.
- Don't use useless undefined.
- Make sure getters and setters for the same property are next to each other in class and object definitions.
- Make sure object literals are declared consistently (defaults to explicit definitions).
- Use static Response methods instead of new Response() constructor when possible.
- Make sure switch-case statements are exhaustive.
- Make sure the `preconnect` attribute is used when using Google Fonts.
- Use `Array#{indexOf,lastIndexOf}()` instead of `Array#{findIndex,findLastIndex}()` when looking for the index of an item.
- Make sure iterable callbacks return consistent values.
- Use `with { type: "json" }` for JSON module imports.
- Use numeric separators in numeric literals.
- Use object spread instead of `Object.assign()` when constructing new objects.
- Always use the radix argument when using `parseInt()`.
- Make sure JSDoc comment lines start with a single asterisk, except for the first one.
- Include a description parameter for `Symbol()`.
- Don't use spread (`...`) syntax on accumulators.
- Don't use the `delete` operator.
- Don't access namespace imports dynamically.
- Don't use namespace imports.
- Declare regex literals at the top level.
- Don't use `target="_blank"` without `rel="noopener"`.

### TypeScript Best Practices

- Don't use TypeScript enums.
- Don't export imported variables.
- Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions.
- Don't use TypeScript namespaces.
- Don't use non-null assertions with the `!` postfix operator.
- Don't use parameter properties in class constructors.
- Don't use user-defined types.
- Use `as const` instead of literal types and type annotations.
- Use either `T[]` or `Array<T>` consistently.
- Initialize each enum member value explicitly.
- Use `export type` for types.
- Use `import type` for types.
- Make sure all enum members are literal values.
- Don't use TypeScript const enum.
- Don't declare empty interfaces.
- Don't let variables evolve into any type through reassignments.
- Don't use the any type.
- Don't misuse the non-null assertion operator (!) in TypeScript files.
- Don't use implicit any type on variable declarations.
- Don't merge interfaces and classes unsafely.
- Don't use overload signatures that aren't next to each other.
- Use the namespace keyword instead of the module keyword to declare TypeScript namespaces.

### Style and Consistency

- Don't use global `eval()`.
- Don't use callbacks in asynchronous tests and hooks.
- Don't use negation in `if` statements that have `else` clauses.
- Don't use nested ternary expressions.
- Don't reassign function parameters.
- This rule lets you specify global variable names you don't want to use in your application.
- Don't use specified modules when loaded by import or require.
- Don't use constants whose value is the upper-case version of their name.
- Use `String.slice()` instead of `String.substr()` and `String.substring()`.
- Don't use template literals if you don't need interpolation or special-character handling.
- Don't use `else` blocks when the `if` block breaks early.
- Don't use yoda expressions.
- Don't use Array constructors.
- Use `at()` instead of integer index access.
- Follow curly brace conventions.
- Use `else if` instead of nested `if` statements in `else` clauses.
- Use single `if` statements instead of nested `if` clauses.
- Use `new` for all builtins except `String`, `Number`, and `Boolean`.
- Use consistent accessibility modifiers on class properties and methods.
- Use `const` declarations for variables that are only assigned once.
- Put default function parameters and optional function parameters last.
- Include a `default` clause in switch statements.
- Use the `**` operator instead of `Math.pow`.
- Use `for-of` loops when you need the index to extract an item from the iterated array.
- Use `node:assert/strict` over `node:assert`.
- Use the `node:` protocol for Node.js builtin modules.
- Use Number properties instead of global ones.
- Use assignment operator shorthand where possible.
- Use function types instead of object types with call signatures.
- Use template literals over string concatenation.
- Use `new` when throwing an error.
- Don't throw non-Error values.
- Use `String.trimStart()` and `String.trimEnd()` over `String.trimLeft()` and `String.trimRight()`.
- Use standard constants instead of approximated literals.
- Don't assign values in expressions.
- Don't use async functions as Promise executors.
- Don't reassign exceptions in catch clauses.
- Don't reassign class members.
- Don't compare against -0.
- Don't use labeled statements that aren't loops.
- Don't use void type outside of generic or return types.
- Don't use console.
- Don't use control characters and escape sequences that match control characters in regular expression literals.
- Don't use debugger.
- Don't assign directly to document.cookie.
- Use `===` and `!==`.
- Don't use duplicate case labels.
- Don't use duplicate class members.
- Don't use duplicate conditions in if-else-if chains.
- Don't use two keys with the same name inside objects.
- Don't use duplicate function parameter names.
- Don't have duplicate hooks in describe blocks.
- Don't use empty block statements and static blocks.
- Don't let switch clauses fall through.
- Don't reassign function declarations.
- Don't allow assignments to native objects and read-only global variables.
- Use Number.isFinite instead of global isFinite.
- Use Number.isNaN instead of global isNaN.
- Don't assign to imported bindings.
- Don't use irregular whitespace characters.
- Don't use labels that share a name with a variable.
- Don't use characters made with multiple code points in character class syntax.
- Make sure to use new and constructor properly.
- Don't use shorthand assign when the variable appears on both sides.
- Don't use octal escape sequences in string literals.
- Don't use Object.prototype builtins directly.
- Don't redeclare variables, functions, classes, and types in the same scope.
- Don't have redundant "use strict".
- Don't compare things where both sides are exactly the same.
- Don't let identifiers shadow restricted names.
- Don't use sparse arrays (arrays with holes).
- Don't use template literal placeholder syntax in regular strings.
- Don't use the then property.
- Don't use unsafe negation.
- Don't use var.
- Don't use with statements in non-strict contexts.
- Make sure async functions actually use await.
- Make sure default clauses in switch statements come last.
- Make sure to pass a message value when creating a built-in error.
- Make sure get methods always return a value.
- Use a recommended display strategy with Google Fonts.
- Make sure for-in loops include an if statement.
- Use Array.isArray() instead of instanceof Array.
- Make sure to use the digits argument with Number#toFixed().
- Make sure to use the "use strict" directive in script files.

### Next.js Specific Rules

- Don't use `<img>` elements in Next.js projects.
- Don't use `<head>` elements in Next.js projects.
- Don't import next/document outside of pages/\_document.jsx in Next.js projects.
- Don't use the next/head module in pages/\_document.js on Next.js projects.

### Testing Best Practices

- Don't use export or module.exports in test files.
- Don't use focused tests.
- Make sure the assertion function, like expect, is placed inside an it() function call.
- Don't use disabled tests.

## Common Tasks

- `npx ultracite init` - Initialize Ultracite in your project
- `npx ultracite format` - Format and fix code automatically
- `npx ultracite lint` - Check for issues without fixing

## Example: Error Handling

```typescript
// ✅ Good: Comprehensive error handling
try {
  const result = await fetchData();
  return { success: true, data: result };
} catch (error) {
  console.error("API call failed:", error);
  return { success: false, error: error.message };
}

// ❌ Bad: Swallowing errors
try {
  return await fetchData();
} catch (e) {
  console.log(e);
}
```


================================================
FILE: .github/pull_request_template.md
================================================
⚠️ READ BEFORE SUBMITTING ⚠️

We are not currently accepting PRs except for critical bugs.

If this is a bug fix:
- [ ] I've opened an issue first
- [ ] This was approved by a maintainer

If this is a feature:

This PR will be closed. Please open an issue to discuss first.

================================================
FILE: .github/workflows/bun-ci.yml
================================================
name: Bun CI

concurrency:
  group: bun-ci-${{ github.ref }}
  cancel-in-progress: true

on:
  push:
    branches: [main]
    paths-ignore:
      - "*.md"
  pull_request:
    branches: [main]
    paths-ignore:
      - "*.md"

jobs:
  build:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]

    env:
      DATABASE_URL: "postgresql://opencut:opencut@localhost:5432/opencut"
      BETTER_AUTH_SECRET: "supersecret"
      NEXT_PUBLIC_SITE_URL: "http://localhost:3000"
      UPSTASH_REDIS_REST_URL: "https://your-upstash-redis-url"
      UPSTASH_REDIS_REST_TOKEN: "your-upstash-redis-token"
      NEXT_PUBLIC_MARBLE_API_URL: "https://placeholder.example.com"
      MARBLE_WORKSPACE_KEY: "placeholder"
      FREESOUND_CLIENT_ID: "placeholder"
      FREESOUND_API_KEY: "placeholder"
      CLOUDFLARE_ACCOUNT_ID: "placeholder"
      R2_ACCESS_KEY_ID: "placeholder"
      R2_SECRET_ACCESS_KEY: "placeholder"
      R2_BUCKET_NAME: "placeholder"
      MODAL_TRANSCRIPTION_URL: "https://placeholder.example.com"
    
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Install Bun
        uses: oven-sh/setup-bun@735343b667d3e6f658f44d0eca948eb6282f2b76
        with:
          bun-version: 1.2.18

      - name: Cache Bun modules
        uses: actions/cache@v4
        with:
          path: ~/.bun/install/cache
          key: ${{ runner.os }}-bun-1.2.18-${{ hashFiles('apps/web/bun.lock') }}

      - name: Install dependencies
        working-directory: apps/web
        run: bun install

      - name: Build
        working-directory: apps/web
        run: bun run build

      - name: Run tests
        working-directory: apps/web
        run: echo "No tests implemented yet"
        continue-on-error: true


================================================
FILE: .gitignore
================================================
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# asdf version management
.tool-versions

node_modules
.cursorignore
.turbo

.env*
!*.env.example

# cursor
bun.lockb

# content-collections
.content-collections

# Twiggy
.cursor/rules/file-structure.mdc


================================================
FILE: .npmrc
================================================
install-strategy="nested"
node-linker=isolated

================================================
FILE: .vscode/settings.json
================================================
{
	"editor.defaultFormatter": "esbenp.prettier-vscode",
	"[javascript][typescript][javascriptreact][typescriptreact][json][jsonc][css][graphql]": {
		"editor.defaultFormatter": "esbenp.prettier-vscode"
	},
	"typescript.tsdk": "node_modules/typescript/lib",
	"editor.formatOnSave": true,
	"editor.formatOnPaste": true,
	"editor.codeActionsOnSave": {
		"source.fixAll.biome": "explicit",
		"source.organizeImports.biome": "explicit"
	},
	"emmet.showExpandedAbbreviation": "never",
	"[typescriptreact]": {
		"editor.defaultFormatter": "biomejs.biome"
	},
	"[typescript]": {
		"editor.defaultFormatter": "biomejs.biome"
	}
}


================================================
FILE: AGENTS.md
================================================
# AGENTS.md

## Overview

Privacy-first video editor, with a focus on simplicity and ease of use.

## Lib vs Utils

- `lib/` - domain logic (specific to this app)
- `utils/` - small helper utils (generic, could be copy-pasted into any other app)

## Core Editor System

The editor uses a **singleton EditorCore** that manages all editor state through specialized managers.

### Architecture

```
EditorCore (singleton)
├── playback: PlaybackManager
├── timeline: TimelineManager
├── scene: SceneManager
├── project: ProjectManager
├── media: MediaManager
└── renderer: RendererManager
```

### When to Use What

#### In React Components

**Always use the `useEditor()` hook:**

```typescript
import { useEditor } from '@/hooks/use-editor';

function MyComponent() {
  const editor = useEditor();
  const tracks = editor.timeline.getTracks();

  // Call methods
  editor.timeline.addTrack({ type: 'media' });

  // Display data (auto re-renders on changes)
  return <div>{tracks.length} tracks</div>;
}
```

The hook:

- Returns the singleton instance
- Subscribes to all manager changes
- Automatically re-renders when state changes

#### Outside React Components

**Use `EditorCore.getInstance()` directly:**

```typescript
// In utilities, event handlers, or non-React code
import { EditorCore } from "@/core";

const editor = EditorCore.getInstance();
await editor.export({ format: "mp4", quality: "high" });
```

## Actions System

Actions are the trigger layer for user-initiated operations. The single source of truth is `@/lib/actions/definitions.ts`.

**To add a new action:**

1. Add it to `ACTIONS` in `@/lib/actions/definitions.ts`:

```typescript
export const ACTIONS = {
  "my-action": {
    description: "What the action does",
    category: "editing",
    defaultShortcuts: ["ctrl+m"],
  },
  // ...
};
```

2. Add handler in `@/hooks/use-editor-actions.ts`:

```typescript
useActionHandler(
  "my-action",
  () => {
    // implementation
  },
  undefined,
);
```

**In components, use `invokeAction()` for user-triggered operations:**

```typescript
import { invokeAction } from '@/lib/actions';

// Good - uses action system
const handleSplit = () => invokeAction("split-selected");

// Avoid - bypasses UX layer (toasts, validation feedback)
const handleSplit = () => editor.timeline.splitElements({ ... });
```

Direct `editor.xxx()` calls are for internal use (commands, tests, complex multi-step operations).

## Commands System

Commands handle undo/redo. They live in `@/lib/commands/` organized by domain (timeline, media, scene).

Each command extends `Command` from `@/lib/commands/base-command` and implements:

- `execute()` - saves current state, then does the mutation
- `undo()` - restores the saved state

Actions and commands work together: actions are "what triggered this", commands are "how to do it (and undo it)".


================================================
FILE: LICENSE
================================================
Copyright 2025 OpenCut

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

================================================
FILE: README.md
================================================
<table width="100%">
  <tr>
    <td align="left" width="120">
      <img src="apps/web/public/logos/opencut/1k/logo-white-black.png" alt="OpenCut Logo" width="100" />
    </td>
    <td align="right">
      <h1>OpenCut</span></h1>
      <h3 style="margin-top: -10px;">A free, open-source video editor for web, desktop, and mobile.</h3>
    </td>
  </tr>
</table>

## Sponsors

Thanks to [Vercel](https://vercel.com?utm_source=github-opencut&utm_campaign=oss) and [fal.ai](https://fal.ai?utm_source=github-opencut&utm_campaign=oss) for their support of open-source software.

<a href="https://vercel.com/oss">
  <img alt="Vercel OSS Program" src="https://vercel.com/oss/program-badge.svg" />
</a>

<a href="https://fal.ai">
  <img alt="Powered by fal.ai" src="https://img.shields.io/badge/Powered%20by-fal.ai-000000?style=flat&logo=data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTEyIDJMMTMuMDkgOC4yNkwyMCAxMEwxMy4wOSAxNS43NEwxMiAyMkwxMC45MSAxNS43NEw0IDEwTDEwLjkxIDguMjZMMTIgMloiIGZpbGw9IndoaXRlIi8+Cjwvc3ZnPgo=" />
</a>

## Why?

- **Privacy**: Your videos stay on your device
- **Free features**: Most basic CapCut features are now paywalled 
- **Simple**: People want editors that are easy to use - CapCut proved that

## Features

- Timeline-based editing
- Multi-track support
- Real-time preview
- No watermarks or subscriptions
- Analytics provided by [Databuddy](https://www.databuddy.cc?utm_source=opencut), 100% Anonymized & Non-invasive.
- Blog powered by [Marble](https://marblecms.com?utm_source=opencut), Headless CMS.

## Project Structure

- `apps/web/` – Main Next.js web application
- `src/components/` – UI and editor components
- `src/hooks/` – Custom React hooks
- `src/lib/` – Utility and API logic
- `src/stores/` – State management (Zustand, etc.)
- `src/types/` – TypeScript types

## Getting Started

### Prerequisites

- [Bun](https://bun.sh/docs/installation)
- [Docker](https://docs.docker.com/get-docker/) and [Docker Compose](https://docs.docker.com/compose/install/)

> **Note:** Docker is optional but recommended for running the local database and Redis. If you only want to work on frontend features, you can skip it.

### Setup

1. Fork and clone the repository

2. Copy the environment file:

   ```bash
   # Unix/Linux/Mac
   cp apps/web/.env.example apps/web/.env.local

   # Windows PowerShell
   Copy-Item apps/web/.env.example apps/web/.env.local
   ```

3. Start the database and Redis:

   ```bash
   docker compose up -d db redis serverless-redis-http
   ```

4. Install dependencies and start the dev server:

   ```bash
   bun install
   bun dev:web
   ```

The application will be available at [http://localhost:3000](http://localhost:3000).

The `.env.example` has sensible defaults that match the Docker Compose config — it should work out of the box.

### Self-Hosting with Docker

To run everything (including a production build of the app) in Docker:

```bash
docker compose up -d
```

The app will be available at [http://localhost:3100](http://localhost:3100).

## Contributing

We welcome contributions! While we're actively developing and refactoring certain areas, there are plenty of opportunities to contribute effectively.

**🎯 Focus areas:** Timeline functionality, project management, performance, bug fixes, and UI improvements outside the preview panel.

**⚠️ Avoid for now:** Preview panel enhancements (fonts, stickers, effects) and export functionality - we're refactoring these with a new binary rendering approach.

See our [Contributing Guide](.github/CONTRIBUTING.md) for detailed setup instructions, development guidelines, and complete focus area guidance.

**Quick start for contributors:**

- Fork the repo and clone locally
- Follow the setup instructions in CONTRIBUTING.md
- Create a feature branch and submit a PR

## License

[MIT LICENSE](LICENSE)

---

![Star History Chart](https://api.star-history.com/svg?repos=opencut-app/opencut&type=Date)


================================================
FILE: apps/web/.env.example
================================================
# Environment variables example
# Copy this file to .env.local and update the values as needed

# Node
NODE_ENV=development

# Public
NEXT_PUBLIC_SITE_URL=http://localhost:3000
NEXT_PUBLIC_MARBLE_API_URL=https://api.marblecms.com

# Server
DATABASE_URL="postgresql://opencut:opencut@localhost:5432/opencut"
BETTER_AUTH_SECRET=your_better_auth_secret

UPSTASH_REDIS_REST_URL=http://localhost:8079
UPSTASH_REDIS_REST_TOKEN=example_token

MARBLE_WORKSPACE_KEY=your_workspace_key_here

FREESOUND_CLIENT_ID=your_client_id_here
FREESOUND_API_KEY=your_api_key_here

CLOUDFLARE_ACCOUNT_ID=your_account_id_here
R2_ACCESS_KEY_ID=your_access_key_here
R2_SECRET_ACCESS_KEY=your_secret_key_here
R2_BUCKET_NAME=opencut-transcription # whatever you named your r2 bucket

MODAL_TRANSCRIPTION_URL=your_modal_url_here

================================================
FILE: apps/web/.gitignore
================================================
# Turborepo
.turbo

# Env vars
.env*
!.env.example

.next/

.font-cache/

================================================
FILE: apps/web/Dockerfile
================================================
FROM oven/bun:alpine AS base

FROM base AS builder

WORKDIR /app

ARG FREESOUND_CLIENT_ID
ARG FREESOUND_API_KEY

COPY package.json package.json
COPY bun.lock bun.lock
COPY turbo.json turbo.json

COPY apps/web/package.json apps/web/package.json
COPY packages/env/package.json packages/env/package.json
COPY packages/ui/package.json packages/ui/package.json

RUN bun install

COPY apps/web/ apps/web/
COPY packages/env/ packages/env/
COPY packages/ui/ packages/ui/

ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1

# Build-time env stubs to pass zod validation
ENV DATABASE_URL="postgresql://opencut:opencut@localhost:5432/opencut"
ENV BETTER_AUTH_SECRET="build-time-secret"
ENV UPSTASH_REDIS_REST_URL="http://localhost:8079"
ENV UPSTASH_REDIS_REST_TOKEN="example_token"
ENV NEXT_PUBLIC_SITE_URL="http://localhost:3000"
ENV NEXT_PUBLIC_MARBLE_API_URL="https://api.marblecms.com"
ENV MARBLE_WORKSPACE_KEY="build-placeholder"
ENV CLOUDFLARE_ACCOUNT_ID="build-placeholder"
ENV R2_ACCESS_KEY_ID="build-placeholder"
ENV R2_SECRET_ACCESS_KEY="build-placeholder"
ENV R2_BUCKET_NAME="build-placeholder"
ENV MODAL_TRANSCRIPTION_URL="http://localhost:0"

ENV FREESOUND_CLIENT_ID=$FREESOUND_CLIENT_ID
ENV FREESOUND_API_KEY=$FREESOUND_API_KEY

WORKDIR /app/apps/web
RUN bun run build

# Production image
FROM base AS runner
WORKDIR /app

ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1

RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs

COPY --from=builder --chown=nextjs:nodejs /app/apps/web/public ./apps/web/public
COPY --from=builder --chown=nextjs:nodejs /app/apps/web/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/apps/web/.next/static ./apps/web/.next/static

RUN chown nextjs:nodejs apps

USER nextjs

EXPOSE 3000

ENV PORT=3000
ENV HOSTNAME="0.0.0.0"

CMD ["bun", "apps/web/server.js"]


================================================
FILE: apps/web/components.json
================================================
{
	"$schema": "https://ui.shadcn.com/schema.json",
	"style": "new-york",
	"rsc": true,
	"tsx": true,
	"tailwind": {
		"config": "",
		"css": "src/app/globals.css",
		"baseColor": "neutral",
		"cssVariables": true,
		"prefix": ""
	},
	"aliases": {
		"components": "@/components",
		"utils": "@/lib/utils",
		"ui": "@/components/ui",
		"lib": "@/lib",
		"hooks": "@/hooks"
	},
	"iconLibrary": "lucide"
}


================================================
FILE: apps/web/content/changelog/0.1.0.md
================================================
---
version: "0.1.0"
date: "2026-02-23"
title: "Editor foundation"
description: "This first release focuses on making editing faster, clearer, and more reliable for day-to-day use."
changes:
  - type: new
    text: "Rebuilt the properties panel from scratch. Number inputs now support math expressions and click-drag scrubbing instead of just typing values in."
  - type: new
    text: "The properties panel went from a flat list of basic text styles to organized sections: transform, blending, typography, spacing, and background controls. Text elements finally have position, scale, and rotation."
  - type: new
    text: "New color picker with eyedropper, opacity control, and multiple color formats."
  - type: new
    text: "Bookmarks now support notes, color labels, and optional duration to plan scenes more clearly."
  - type: new
    text: "Move, scale, and rotate elements directly in the preview panel."
  - type: new
    text: "Blend modes (Multiply, Screen, Overlay, etc). Only available on text elements for now."
  - type: new
    text: "Paste images, videos, or audio from your clipboard straight into the editor."
  - type: new
    text: "Hold Shift while moving or resizing to temporarily turn off snapping."
  - type: improved
    text: "Expanded font selection from 7 to over 1,000."
  - type: improved
    text: "Fonts load much faster."
  - type: improved
    text: "All asset panel tabs now share a consistent layout."
  - type: improved
    text: "Text rendering properly supports multiple lines, line height, and letter spacing."
  - type: improved
    text: "Better contrast in the dark theme."
  - type: fixed
    text: "Fixed undo/redo for drag-and-drop so dropped items are reliably undoable."
---


================================================
FILE: apps/web/content/changelog/0.2.0.md
================================================
---
version: "0.2.0"
date: "2026-03-01"
title: "Motion & effects"
description: "This release adds the foundation for motion and effects, alongside many improvements & fixes."
changes:
  - type: new
    text: "Keyframe animation system for animating element properties over time."
  - type: new
    text: "Entirely new efects system. Effects can be applied per-clip or added as standalone elements on the timeline (with our first effect: Blur!)"
  - type: new
    text: "Added a changelog page to the site."
  - type: new
    text: "Ripple editing mode."
  - type: fixed
    text: "Fixed an issue where click-and-drag selection on one timeline track could accidentally select items from the track below."
  - type: fixed
    text: "Audio could flicker or cut out at the start of playback when certain elements were selected. The scheduler now recovers from timing slips without losing audio."
  - type: fixed
    text: "Dragging a clip onto the timeline and pressing Ctrl+Z now removes both the clip and the track it created in one step, instead of requiring two undos."
  - type: fixed
    text: "A few values in the properties panel were previously showing incorrect values. This has been fixed."
  - type: improved
    text: "New text elements no longer have a black background by default."
  - type: improved
    text: "Selection outline in the preview is now dashed."
  - type: improved
    text: "Clips sitting next to each other on the timeline now have a small gap between them so selected clips are always clearly visible."
  - type: fixed
    text: "Timeline trim handles now sit outside the clip instead of inside them, so the clip's visible area accurately represents where it starts and ends."
  - type: fixed
    text: "Clips being dragged on the timeline now render on top of other clips instead of underneath them."
  - type: fixed
    text: "Undoing a trim-from-start operation now correctly restores the clip to its original position and length, instead of stretching it to the right."
  - type: fixed
    text: "Resizing a clip on the timeline would deselect it when the mouse was released. The clip now stays selected after a resize."
  - type: improved
    text: "Press Escape to deselect the current element."
  - type: fixed
    text: "Resizing a clip could extend it past another clip on the same track, causing an overlap. The resize now stops at the next clip's edge."
  - type: fixed
    text: "Dragging a clip to a different track would deselect it on drop. The clip stays selected after moving."
  - type: fixed
    text: "Finishing a resize or drag with the mouse over empty track space would deselect the clip."
---


================================================
FILE: apps/web/content-collections.ts
================================================
import { defineCollection, defineConfig } from "@content-collections/core";
import { z } from "zod";

const changelog = defineCollection({
	name: "changelog",
	directory: "content/changelog",
	include: "*.md",
	schema: z.object({
		content: z.string(),
		version: z.string(),
		date: z.string(),
		title: z.string(),
		description: z.string().optional(),
		changes: z.array(
			z.object({
				type: z.string(),
				text: z.string(),
			}),
		),
	}),
	transform: async (doc, { collection }) => {
		const allDocs = await collection.documents();
		const sorted = [...allDocs].sort((a, b) =>
			b.version.localeCompare(a.version, undefined, { numeric: true }),
		);
		const isLatest = sorted[0]?.version === doc.version;
		return { ...doc, isLatest };
	},
});

export default defineConfig({
	content: [changelog],
});


================================================
FILE: apps/web/drizzle.config.ts
================================================
import type { Config } from "drizzle-kit";
import * as dotenv from "dotenv";
import { webEnv } from "@opencut/env/web";

// Load the right env file based on environment
if (webEnv.NODE_ENV === "production") {
	dotenv.config({ path: ".env.production" });
} else {
	dotenv.config({ path: ".env.local" });
}

export default {
	schema: "./src/schema.ts",
	dialect: "postgresql",
	migrations: {
		table: "drizzle_migrations",
	},
	dbCredentials: {
		url: webEnv.DATABASE_URL,
	},
	out: "./migrations",
	strict: webEnv.NODE_ENV === "production",
} satisfies Config;


================================================
FILE: apps/web/migrations/0000_brainy_saracen.sql
================================================
CREATE TABLE "accounts" (
	"id" text PRIMARY KEY NOT NULL,
	"account_id" text NOT NULL,
	"provider_id" text NOT NULL,
	"user_id" text NOT NULL,
	"access_token" text,
	"refresh_token" text,
	"id_token" text,
	"access_token_expires_at" timestamp,
	"refresh_token_expires_at" timestamp,
	"scope" text,
	"password" text,
	"created_at" timestamp NOT NULL,
	"updated_at" timestamp NOT NULL
);
--> statement-breakpoint
ALTER TABLE "accounts" ENABLE ROW LEVEL SECURITY;--> statement-breakpoint
CREATE TABLE "sessions" (
	"id" text PRIMARY KEY NOT NULL,
	"expires_at" timestamp NOT NULL,
	"token" text NOT NULL,
	"created_at" timestamp NOT NULL,
	"updated_at" timestamp NOT NULL,
	"ip_address" text,
	"user_agent" text,
	"user_id" text NOT NULL,
	CONSTRAINT "sessions_token_unique" UNIQUE("token")
);
--> statement-breakpoint
ALTER TABLE "sessions" ENABLE ROW LEVEL SECURITY;--> statement-breakpoint
CREATE TABLE "users" (
	"id" text PRIMARY KEY NOT NULL,
	"name" text NOT NULL,
	"email" text NOT NULL,
	"email_verified" boolean NOT NULL,
	"image" text,
	"created_at" timestamp NOT NULL,
	"updated_at" timestamp NOT NULL,
	CONSTRAINT "users_email_unique" UNIQUE("email")
);
--> statement-breakpoint
ALTER TABLE "users" ENABLE ROW LEVEL SECURITY;--> statement-breakpoint
CREATE TABLE "verifications" (
	"id" text PRIMARY KEY NOT NULL,
	"identifier" text NOT NULL,
	"value" text NOT NULL,
	"expires_at" timestamp NOT NULL,
	"created_at" timestamp,
	"updated_at" timestamp
);
--> statement-breakpoint
ALTER TABLE "verifications" ENABLE ROW LEVEL SECURITY;--> statement-breakpoint
CREATE TABLE "waitlist" (
	"id" text PRIMARY KEY NOT NULL,
	"email" text NOT NULL,
	"created_at" timestamp NOT NULL,
	CONSTRAINT "waitlist_email_unique" UNIQUE("email")
);
--> statement-breakpoint
ALTER TABLE "waitlist" ENABLE ROW LEVEL SECURITY;--> statement-breakpoint
ALTER TABLE "accounts" ADD CONSTRAINT "accounts_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "sessions" ADD CONSTRAINT "sessions_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;

================================================
FILE: apps/web/migrations/meta/0000_snapshot.json
================================================
{
	"id": "33a6742f-89da-4ac5-958f-421aa1cf9bd6",
	"prevId": "00000000-0000-0000-0000-000000000000",
	"version": "7",
	"dialect": "postgresql",
	"tables": {
		"public.accounts": {
			"name": "accounts",
			"schema": "",
			"columns": {
				"id": {
					"name": "id",
					"type": "text",
					"primaryKey": true,
					"notNull": true
				},
				"account_id": {
					"name": "account_id",
					"type": "text",
					"primaryKey": false,
					"notNull": true
				},
				"provider_id": {
					"name": "provider_id",
					"type": "text",
					"primaryKey": false,
					"notNull": true
				},
				"user_id": {
					"name": "user_id",
					"type": "text",
					"primaryKey": false,
					"notNull": true
				},
				"access_token": {
					"name": "access_token",
					"type": "text",
					"primaryKey": false,
					"notNull": false
				},
				"refresh_token": {
					"name": "refresh_token",
					"type": "text",
					"primaryKey": false,
					"notNull": false
				},
				"id_token": {
					"name": "id_token",
					"type": "text",
					"primaryKey": false,
					"notNull": false
				},
				"access_token_expires_at": {
					"name": "access_token_expires_at",
					"type": "timestamp",
					"primaryKey": false,
					"notNull": false
				},
				"refresh_token_expires_at": {
					"name": "refresh_token_expires_at",
					"type": "timestamp",
					"primaryKey": false,
					"notNull": false
				},
				"scope": {
					"name": "scope",
					"type": "text",
					"primaryKey": false,
					"notNull": false
				},
				"password": {
					"name": "password",
					"type": "text",
					"primaryKey": false,
					"notNull": false
				},
				"created_at": {
					"name": "created_at",
					"type": "timestamp",
					"primaryKey": false,
					"notNull": true
				},
				"updated_at": {
					"name": "updated_at",
					"type": "timestamp",
					"primaryKey": false,
					"notNull": true
				}
			},
			"indexes": {},
			"foreignKeys": {
				"accounts_user_id_users_id_fk": {
					"name": "accounts_user_id_users_id_fk",
					"tableFrom": "accounts",
					"tableTo": "users",
					"columnsFrom": ["user_id"],
					"columnsTo": ["id"],
					"onDelete": "cascade",
					"onUpdate": "no action"
				}
			},
			"compositePrimaryKeys": {},
			"uniqueConstraints": {},
			"policies": {},
			"checkConstraints": {},
			"isRLSEnabled": true
		},
		"public.sessions": {
			"name": "sessions",
			"schema": "",
			"columns": {
				"id": {
					"name": "id",
					"type": "text",
					"primaryKey": true,
					"notNull": true
				},
				"expires_at": {
					"name": "expires_at",
					"type": "timestamp",
					"primaryKey": false,
					"notNull": true
				},
				"token": {
					"name": "token",
					"type": "text",
					"primaryKey": false,
					"notNull": true
				},
				"created_at": {
					"name": "created_at",
					"type": "timestamp",
					"primaryKey": false,
					"notNull": true
				},
				"updated_at": {
					"name": "updated_at",
					"type": "timestamp",
					"primaryKey": false,
					"notNull": true
				},
				"ip_address": {
					"name": "ip_address",
					"type": "text",
					"primaryKey": false,
					"notNull": false
				},
				"user_agent": {
					"name": "user_agent",
					"type": "text",
					"primaryKey": false,
					"notNull": false
				},
				"user_id": {
					"name": "user_id",
					"type": "text",
					"primaryKey": false,
					"notNull": true
				}
			},
			"indexes": {},
			"foreignKeys": {
				"sessions_user_id_users_id_fk": {
					"name": "sessions_user_id_users_id_fk",
					"tableFrom": "sessions",
					"tableTo": "users",
					"columnsFrom": ["user_id"],
					"columnsTo": ["id"],
					"onDelete": "cascade",
					"onUpdate": "no action"
				}
			},
			"compositePrimaryKeys": {},
			"uniqueConstraints": {
				"sessions_token_unique": {
					"name": "sessions_token_unique",
					"nullsNotDistinct": false,
					"columns": ["token"]
				}
			},
			"policies": {},
			"checkConstraints": {},
			"isRLSEnabled": true
		},
		"public.users": {
			"name": "users",
			"schema": "",
			"columns": {
				"id": {
					"name": "id",
					"type": "text",
					"primaryKey": true,
					"notNull": true
				},
				"name": {
					"name": "name",
					"type": "text",
					"primaryKey": false,
					"notNull": true
				},
				"email": {
					"name": "email",
					"type": "text",
					"primaryKey": false,
					"notNull": true
				},
				"email_verified": {
					"name": "email_verified",
					"type": "boolean",
					"primaryKey": false,
					"notNull": true
				},
				"image": {
					"name": "image",
					"type": "text",
					"primaryKey": false,
					"notNull": false
				},
				"created_at": {
					"name": "created_at",
					"type": "timestamp",
					"primaryKey": false,
					"notNull": true
				},
				"updated_at": {
					"name": "updated_at",
					"type": "timestamp",
					"primaryKey": false,
					"notNull": true
				}
			},
			"indexes": {},
			"foreignKeys": {},
			"compositePrimaryKeys": {},
			"uniqueConstraints": {
				"users_email_unique": {
					"name": "users_email_unique",
					"nullsNotDistinct": false,
					"columns": ["email"]
				}
			},
			"policies": {},
			"checkConstraints": {},
			"isRLSEnabled": true
		},
		"public.verifications": {
			"name": "verifications",
			"schema": "",
			"columns": {
				"id": {
					"name": "id",
					"type": "text",
					"primaryKey": true,
					"notNull": true
				},
				"identifier": {
					"name": "identifier",
					"type": "text",
					"primaryKey": false,
					"notNull": true
				},
				"value": {
					"name": "value",
					"type": "text",
					"primaryKey": false,
					"notNull": true
				},
				"expires_at": {
					"name": "expires_at",
					"type": "timestamp",
					"primaryKey": false,
					"notNull": true
				},
				"created_at": {
					"name": "created_at",
					"type": "timestamp",
					"primaryKey": false,
					"notNull": false
				},
				"updated_at": {
					"name": "updated_at",
					"type": "timestamp",
					"primaryKey": false,
					"notNull": false
				}
			},
			"indexes": {},
			"foreignKeys": {},
			"compositePrimaryKeys": {},
			"uniqueConstraints": {},
			"policies": {},
			"checkConstraints": {},
			"isRLSEnabled": true
		},
		"public.waitlist": {
			"name": "waitlist",
			"schema": "",
			"columns": {
				"id": {
					"name": "id",
					"type": "text",
					"primaryKey": true,
					"notNull": true
				},
				"email": {
					"name": "email",
					"type": "text",
					"primaryKey": false,
					"notNull": true
				},
				"created_at": {
					"name": "created_at",
					"type": "timestamp",
					"primaryKey": false,
					"notNull": true
				}
			},
			"indexes": {},
			"foreignKeys": {},
			"compositePrimaryKeys": {},
			"uniqueConstraints": {
				"waitlist_email_unique": {
					"name": "waitlist_email_unique",
					"nullsNotDistinct": false,
					"columns": ["email"]
				}
			},
			"policies": {},
			"checkConstraints": {},
			"isRLSEnabled": true
		}
	},
	"enums": {},
	"schemas": {},
	"sequences": {},
	"roles": {},
	"policies": {},
	"views": {},
	"_meta": {
		"columns": {},
		"schemas": {},
		"tables": {}
	}
}


================================================
FILE: apps/web/migrations/meta/_journal.json
================================================
{
	"version": "7",
	"dialect": "postgresql",
	"entries": [
		{
			"idx": 0,
			"version": "7",
			"when": 1750753385927,
			"tag": "0000_brainy_saracen",
			"breakpoints": true
		}
	]
}


================================================
FILE: apps/web/next-env.d.ts
================================================
/// <reference types="next" />
/// <reference types="next/image-types/global" />
import "./.next/dev/types/routes.d.ts";

// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.


================================================
FILE: apps/web/next.config.ts
================================================
import type { NextConfig } from "next";
import { withBotId } from "botid/next/config";
import { withContentCollections } from "@content-collections/next";

const nextConfig: NextConfig = {
	turbopack: {
		rules: {
			"*.glsl": {
				loaders: [require.resolve("raw-loader")],
				as: "*.js",
			},
		},
	},
	compiler: {
		removeConsole: process.env.NODE_ENV === "production",
	},
	reactStrictMode: true,
	productionBrowserSourceMaps: true,
	output: "standalone",
	images: {
		remotePatterns: [
			{
				protocol: "https",
				hostname: "plus.unsplash.com",
			},
			{
				protocol: "https",
				hostname: "images.unsplash.com",
			},
			{
				protocol: "https",
				hostname: "images.marblecms.com",
			},
			{
				protocol: "https",
				hostname: "lh3.googleusercontent.com",
			},
			{
				protocol: "https",
				hostname: "avatars.githubusercontent.com",
			},
			{
				protocol: "https",
				hostname: "api.iconify.design",
			},
			{
				protocol: "https",
				hostname: "api.simplesvg.com",
			},
			{
				protocol: "https",
				hostname: "api.unisvg.com",
			},
		],
	},
};

export default withContentCollections(withBotId(nextConfig));


================================================
FILE: apps/web/package.json
================================================
{
  "name": "@opencut/web",
  "version": "0.1.0",
  "private": true,
  "packageManager": "bun@1.2.18",
  "scripts": {
    "dev": "next dev --turbopack",
    "build": "next build",
    "start": "next start",
    "lint": "biome check src/",
    "lint:fix": "biome check src/ --write",
    "format": "biome format src/ --write",
    "db:generate": "drizzle-kit generate",
    "db:migrate": "drizzle-kit migrate",
    "db:push:local": "cross-env NODE_ENV=development drizzle-kit push",
    "db:push:prod": "cross-env NODE_ENV=production drizzle-kit push"
  },
  "dependencies": {
    "@ffmpeg/core": "^0.12.10",
    "@ffmpeg/ffmpeg": "^0.12.15",
    "@ffmpeg/util": "^0.12.2",
    "@hello-pangea/dnd": "^18.0.1",
    "@hookform/resolvers": "^3.9.1",
    "@hugeicons/core-free-icons": "^3.1.1",
    "@hugeicons/react": "^1.1.4",
    "@huggingface/transformers": "^3.8.1",
    "@opencut/env": "workspace:*",
    "@opencut/ui": "workspace:*",
    "@radix-ui/react-accordion": "^1.2.12",
    "@radix-ui/react-checkbox": "^1.3.3",
    "@radix-ui/react-dialog": "^1.1.15",
    "@radix-ui/react-dropdown-menu": "^2.1.16",
    "@radix-ui/react-primitive": "^2.1.4",
    "@radix-ui/react-select": "^2.2.6",
    "@radix-ui/react-separator": "^1.1.8",
    "@radix-ui/react-slot": "^1.2.4",
    "@radix-ui/react-tooltip": "^1.2.8",
    "@types/culori": "^4.0.1",
    "@upstash/ratelimit": "^2.0.6",
    "@upstash/redis": "^1.35.4",
    "@vercel/analytics": "^1.4.1",
    "aws4fetch": "^1.0.20",
    "better-auth": "^1.2.7",
    "botid": "^1.4.2",
    "class-variance-authority": "^0.7.1",
    "clsx": "^2.1.1",
    "cmdk": "^1.0.0",
    "culori": "^4.0.2",
    "dayjs": "^1.11.13",
    "drizzle-orm": "^0.44.2",
    "embla-carousel-react": "^8.5.1",
    "eventemitter3": "^5.0.1",
    "feed": "^5.1.0",
    "input-otp": "^1.4.1",
    "lucide-react": "^0.562.0",
    "mediabunny": "^1.29.1",
    "motion": "^12.18.1",
    "nanoid": "^5.1.5",
    "next": "16.1.3",
    "next-themes": "^0.4.4",
    "pg": "^8.16.2",
    "postgres": "^3.4.5",
    "radix-ui": "^1.4.3",
    "react": "^19.0.0",
    "react-day-picker": "^8.10.1",
    "react-dom": "^19.0.0",
    "react-hook-form": "^7.54.0",
    "react-icons": "^5.4.0",
    "react-markdown": "^10.1.0",
    "react-phone-number-input": "^3.4.11",
    "react-resizable-panels": "^2.1.7",
    "react-window": "^2.2.7",
    "recharts": "^2.14.1",
    "rehype-autolink-headings": "^7.1.0",
    "rehype-parse": "^9.0.1",
    "rehype-sanitize": "^6.0.0",
    "rehype-slug": "^6.0.0",
    "rehype-stringify": "^10.0.1",
    "sonner": "^1.7.1",
    "tailwind-merge": "^3.5.0",
    "tailwindcss-animate": "^1.0.7",
    "unified": "^11.0.5",
    "use-deep-compare-effect": "^1.8.1",
    "vaul": "^1.1.2",
    "wavesurfer.js": "^7.9.8",
    "zod": "^3.25.67",
    "zustand": "^5.0.2"
  },
  "devDependencies": {
    "@content-collections/core": "^0.14.1",
    "@content-collections/next": "^0.2.11",
    "@napi-rs/canvas": "^0.1.92",
    "@tailwindcss/postcss": "^4.2.1",
    "@tailwindcss/typography": "^0.5.19",
    "@types/bun": "latest",
    "@types/node": "^24.2.1",
    "@types/pg": "^8.15.4",
    "@types/react": "^19",
    "@types/react-dom": "^19",
    "cross-env": "^7.0.3",
    "dotenv": "^16.5.0",
    "drizzle-kit": "^0.31.4",
    "postcss": "^8",
    "raw-loader": "^4.0.2",
    "sharp": "^0.34.5",
    "tailwindcss": "^4.2.1",
    "tsx": "^4.7.1",
    "typescript": "^5.8.3"
  }
}


================================================
FILE: apps/web/postcss.config.mjs
================================================
/** @type {import('postcss-load-config').Config} */
const config = {
	plugins: {
		"@tailwindcss/postcss": {},
	},
};

export default config;


================================================
FILE: apps/web/public/browserconfig.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
    <msapplication>
        <tile>
            <square70x70logo src="/icons/ms-icon-70x70.png"/>
            <square150x150logo src="/icons/ms-icon-150x150.png"/>
            <square310x310logo src="/icons/ms-icon-310x310.png"/>
            <TileColor>#ffffff</TileColor>
        </tile>
    </msapplication>
</browserconfig>

================================================
FILE: apps/web/public/countries.json
================================================
[
	{
		"name": "Andorra",
		"code": "AD",
		"languages": ["catalan"],
		"flag_colors": ["blue", "yellow", "red"],
		"region": "Western Europe"
	},
	{
		"name": "United Arab Emirates",
		"code": "AE",
		"languages": ["arabic"],
		"flag_colors": ["red", "green", "white", "black"],
		"region": "Middle East"
	},
	{
		"name": "Afghanistan",
		"code": "AF",
		"languages": ["dari", "pashto"],
		"flag_colors": ["black", "red", "green"],
		"region": "South Asia"
	},
	{
		"name": "Antigua and Barbuda",
		"code": "AG",
		"languages": ["english"],
		"flag_colors": ["red", "black", "blue", "white", "yellow"],
		"region": "Caribbean"
	},
	{
		"name": "Anguilla",
		"code": "AI",
		"languages": ["english"],
		"flag_colors": ["blue", "white", "orange"],
		"region": "Caribbean"
	},
	{
		"name": "Albania",
		"code": "AL",
		"languages": ["albanian"],
		"flag_colors": ["red", "black"],
		"region": "Southern Europe"
	},
	{
		"name": "Armenia",
		"code": "AM",
		"languages": ["armenian"],
		"flag_colors": ["red", "blue", "orange"],
		"region": "Middle East"
	},
	{
		"name": "Angola",
		"code": "AO",
		"languages": ["portuguese"],
		"flag_colors": ["red", "black", "yellow"],
		"region": "Sub-Saharan Africa"
	},
	{
		"name": "Antarctica",
		"code": "AQ",
		"languages": [],
		"flag_colors": [],
		"region": "Antarctica"
	},
	{
		"name": "Argentina",
		"code": "AR",
		"languages": ["spanish"],
		"flag_colors": ["blue", "white", "yellow"],
		"region": "South America"
	},
	{
		"name": "American Samoa",
		"code": "AS",
		"languages": ["english", "samoan"],
		"flag_colors": ["red", "white", "blue"],
		"region": "Oceania"
	},
	{
		"name": "Austria",
		"code": "AT",
		"languages": ["german"],
		"flag_colors": ["red", "white"],
		"region": "Western Europe"
	},
	{
		"name": "Australia",
		"code": "AU",
		"languages": ["english"],
		"flag_colors": ["blue", "white", "red"],
		"region": "Oceania"
	},
	{
		"name": "Aruba",
		"code": "AW",
		"languages": ["dutch", "papiamento"],
		"flag_colors": ["blue", "yellow", "red", "white"],
		"region": "Caribbean"
	},
	{
		"name": "Aland Islands",
		"code": "AX",
		"languages": ["swedish"],
		"flag_colors": ["blue", "yellow", "red"],
		"region": "Northern Europe"
	},
	{
		"name": "Azerbaijan",
		"code": "AZ",
		"languages": ["azerbaijani"],
		"flag_colors": ["blue", "red", "green", "white"],
		"region": "Middle East"
	},
	{
		"name": "Bosnia and Herzegovina",
		"code": "BA",
		"languages": ["bosnian", "croatian", "serbian"],
		"flag_colors": ["blue", "yellow", "white"],
		"region": "Southern Europe"
	},
	{
		"name": "Barbados",
		"code": "BB",
		"languages": ["english"],
		"flag_colors": ["blue", "yellow", "black"],
		"region": "Caribbean"
	},
	{
		"name": "Bangladesh",
		"code": "BD",
		"languages": ["bengali"],
		"flag_colors": ["green", "red"],
		"region": "South Asia"
	},
	{
		"name": "Belgium",
		"code": "BE",
		"languages": ["french", "dutch", "german"],
		"flag_colors": ["black", "yellow", "red"],
		"region": "Western Europe"
	},
	{
		"name": "Burkina Faso",
		"code": "BF",
		"languages": ["french"],
		"flag_colors": ["red", "green", "yellow"],
		"region": "Sub-Saharan Africa"
	},
	{
		"name": "Bulgaria",
		"code": "BG",
		"languages": ["bulgarian"],
		"flag_colors": ["white", "green", "red"],
		"region": "Eastern Europe"
	},
	{
		"name": "Bahrain",
		"code": "BH",
		"languages": ["arabic"],
		"flag_colors": ["red", "white"],
		"region": "Middle East"
	},
	{
		"name": "Burundi",
		"code": "BI",
		"languages": ["kirundi", "french", "english"],
		"flag_colors": ["red", "green", "white"],
		"region": "Sub-Saharan Africa"
	},
	{
		"name": "Benin",
		"code": "BJ",
		"languages": ["french"],
		"flag_colors": ["green", "yellow", "red"],
		"region": "Sub-Saharan Africa"
	},
	{
		"name": "Saint Barthelemy",
		"code": "BL",
		"languages": ["french"],
		"flag_colors": ["blue", "white", "red"],
		"region": "Caribbean"
	},
	{
		"name": "Bermuda",
		"code": "BM",
		"languages": ["english"],
		"flag_colors": ["blue", "white", "red"],
		"region": "North Atlantic"
	},
	{
		"name": "Brunei",
		"code": "BN",
		"languages": ["malay"],
		"flag_colors": ["yellow", "white", "black", "red"],
		"region": "Southeast Asia"
	},
	{
		"name": "Bolivia",
		"code": "BO",
		"languages": ["spanish", "quechua", "aymara"],
		"flag_colors": ["red", "yellow", "green"],
		"region": "South America"
	},
	{
		"name": "Sint Eustatius",
		"code": "BQ-SE",
		"languages": ["dutch", "english"],
		"flag_colors": ["blue", "white", "red", "yellow"],
		"region": "Caribbean"
	},
	{
		"name": "Caribbean Netherlands",
		"code": "BQ",
		"languages": ["dutch", "papiamento", "english"],
		"flag_colors": ["blue", "white", "red"],
		"region": "Caribbean"
	},
	{
		"name": "Brazil",
		"code": "BR",
		"languages": ["portuguese"],
		"flag_colors": ["green", "yellow", "blue", "white"],
		"region": "South America"
	},
	{
		"name": "Bahamas",
		"code": "BS",
		"languages": ["english"],
		"flag_colors": ["blue", "yellow", "black"],
		"region": "Caribbean"
	},
	{
		"name": "Bhutan",
		"code": "BT",
		"languages": ["dzongkha"],
		"flag_colors": ["yellow", "orange", "white"],
		"region": "South Asia"
	},
	{
		"name": "Bouvet Island",
		"code": "BV",
		"languages": [],
		"flag_colors": ["red", "white", "blue"],
		"region": "Antarctica"
	},
	{
		"name": "Botswana",
		"code": "BW",
		"languages": ["english", "tswana"],
		"flag_colors": ["blue", "black", "white"],
		"region": "Sub-Saharan Africa"
	},
	{
		"name": "Belarus",
		"code": "BY",
		"languages": ["belarusian", "russian"],
		"flag_colors": ["red", "green", "white"],
		"region": "Eastern Europe"
	},
	{
		"name": "Belize",
		"code": "BZ",
		"languages": ["english"],
		"flag_colors": ["blue", "red", "white"],
		"region": "Central America"
	},
	{
		"name": "Canada",
		"code": "CA",
		"languages": ["english", "french"],
		"flag_colors": ["red", "white"],
		"region": "North America"
	},
	{
		"name": "Cocos (Keeling) Islands",
		"code": "CC",
		"languages": ["english"],
		"flag_colors": ["green", "red", "yellow", "white"],
		"region": "Southeast Asia"
	},
	{
		"name": "Democratic Republic of the Congo",
		"code": "CD",
		"languages": ["french", "lingala", "swahili", "tshiluba", "kikongo"],
		"flag_colors": ["blue", "red", "yellow"],
		"region": "Sub-Saharan Africa"
	},
	{
		"name": "Central African Republic",
		"code": "CF",
		"languages": ["french", "sango"],
		"flag_colors": ["blue", "white", "green", "yellow", "red"],
		"region": "Sub-Saharan Africa"
	},
	{
		"name": "Republic of the Congo",
		"code": "CG",
		"languages": ["french", "lingala", "kituba"],
		"flag_colors": ["green", "yellow", "red"],
		"region": "Sub-Saharan Africa"
	},
	{
		"name": "Switzerland",
		"code": "CH",
		"languages": ["german", "french", "italian"],
		"flag_colors": ["red", "white"],
		"region": "Western Europe"
	},
	{
		"name": "Cote d'Ivoire",
		"code": "CI",
		"languages": ["french"],
		"flag_colors": ["orange", "white", "green"],
		"region": "Sub-Saharan Africa"
	},
	{
		"name": "Cook Islands",
		"code": "CK",
		"languages": ["english", "cook islands maori"],
		"flag_colors": ["blue", "white"],
		"region": "Oceania"
	},
	{
		"name": "Chile",
		"code": "CL",
		"languages": ["spanish"],
		"flag_colors": ["white", "red", "blue"],
		"region": "South America"
	},
	{
		"name": "Cameroon",
		"code": "CM",
		"languages": ["english", "french"],
		"flag_colors": ["green", "red", "yellow"],
		"region": "Sub-Saharan Africa"
	},
	{
		"name": "China",
		"code": "CN",
		"languages": ["mandarin"],
		"flag_colors": ["red", "yellow"],
		"region": "East Asia"
	},
	{
		"name": "Colombia",
		"code": "CO",
		"languages": ["spanish"],
		"flag_colors": ["yellow", "blue", "red"],
		"region": "South America"
	},
	{
		"name": "Costa Rica",
		"code": "CR",
		"languages": ["spanish"],
		"flag_colors": ["blue", "white", "red"],
		"region": "Central America"
	},
	{
		"name": "Cuba",
		"code": "CU",
		"languages": ["spanish"],
		"flag_colors": ["blue", "white", "red"],
		"region": "Caribbean"
	},
	{
		"name": "Cape Verde",
		"code": "CV",
		"languages": ["portuguese"],
		"flag_colors": ["blue", "white", "red", "yellow"],
		"region": "Sub-Saharan Africa"
	},
	{
		"name": "Curacao",
		"code": "CW",
		"languages": ["dutch", "papiamento", "english"],
		"flag_colors": ["blue", "yellow", "white"],
		"region": "Caribbean"
	},
	{
		"name": "Christmas Island",
		"code": "CX",
		"languages": ["english"],
		"flag_colors": ["green", "blue", "yellow", "white"],
		"region": "Oceania"
	},
	{
		"name": "Cyprus",
		"code": "CY",
		"languages": ["greek", "turkish"],
		"flag_colors": ["white", "orange", "green"],
		"region": "Middle East"
	},
	{
		"name": "Czechia",
		"code": "CZ",
		"languages": ["czech"],
		"flag_colors": ["white", "red", "blue"],
		"region": "Eastern Europe"
	},
	{
		"name": "Germany",
		"code": "DE",
		"languages": ["german"],
		"flag_colors": ["black", "red", "yellow"],
		"region": "Western Europe"
	},
	{
		"name": "Djibouti",
		"code": "DJ",
		"languages": ["arabic", "french", "afar", "somali"],
		"flag_colors": ["blue", "green", "white", "red"],
		"region": "Sub-Saharan Africa"
	},
	{
		"name": "Denmark",
		"code": "DK",
		"languages": ["danish"],
		"flag_colors": ["red", "white"],
		"region": "Northern Europe"
	},
	{
		"name": "Dominica",
		"code": "DM",
		"languages": ["english"],
		"flag_colors": ["green", "yellow", "black", "white", "red"],
		"region": "Caribbean"
	},
	{
		"name": "Dominican Republic",
		"code": "DO",
		"languages": ["spanish"],
		"flag_colors": ["red", "white", "blue"],
		"region": "Caribbean"
	},
	{
		"name": "Algeria",
		"code": "DZ",
		"languages": ["arabic", "tamazight"],
		"flag_colors": ["green", "white", "red"],
		"region": "North Africa"
	},
	{
		"name": "Ecuador",
		"code": "EC",
		"languages": ["spanish"],
		"flag_colors": ["yellow", "blue", "red"],
		"region": "South America"
	},
	{
		"name": "Estonia",
		"code": "EE",
		"languages": ["estonian"],
		"flag_colors": ["blue", "black", "white"],
		"region": "Northern Europe"
	},
	{
		"name": "Egypt",
		"code": "EG",
		"languages": ["arabic"],
		"flag_colors": ["red", "white", "black", "yellow"],
		"region": "North Africa"
	},
	{
		"name": "Western Sahara",
		"code": "EH",
		"languages": ["arabic", "spanish"],
		"flag_colors": ["black", "white", "green", "red"],
		"region": "North Africa"
	},
	{
		"name": "Eritrea",
		"code": "ER",
		"languages": ["tigrinya", "arabic", "english"],
		"flag_colors": ["red", "green", "blue", "yellow"],
		"region": "Sub-Saharan Africa"
	},
	{
		"name": "Spain",
		"code": "ES",
		"languages": ["spanish"],
		"flag_colors": ["red", "yellow"],
		"region": "Southern Europe"
	},
	{
		"name": "Ethiopia",
		"code": "ET",
		"languages": ["amharic", "oromo"],
		"flag_colors": ["green", "yellow", "red", "blue"],
		"region": "Sub-Saharan Africa"
	},
	{
		"name": "European Union",
		"code": "EU",
		"languages": ["english", "french", "german"],
		"flag_colors": ["blue", "yellow"],
		"region": "Western Europe"
	},
	{
		"name": "Finland",
		"code": "FI",
		"languages": ["finnish", "swedish"],
		"flag_colors": ["white", "blue"],
		"region": "Northern Europe"
	},
	{
		"name": "Fiji",
		"code": "FJ",
		"languages": ["english", "fijian", "hindi"],
		"flag_colors": ["blue", "white", "red", "yellow"],
		"region": "Oceania"
	},
	{
		"name": "Falkland Islands",
		"code": "FK",
		"languages": ["english"],
		"flag_colors": ["blue", "white", "red", "yellow"],
		"region": "South America"
	},
	{
		"name": "Micronesia",
		"code": "FM",
		"languages": ["english"],
		"flag_colors": ["blue", "white"],
		"region": "Oceania"
	},
	{
		"name": "Faroe Islands",
		"code": "FO",
		"languages": ["faroese", "danish"],
		"flag_colors": ["white", "red", "blue"],
		"region": "Northern Europe"
	},
	{
		"name": "France",
		"code": "FR",
		"languages": ["french"],
		"flag_colors": ["blue", "white", "red"],
		"region": "Western Europe"
	},
	{
		"name": "Gabon",
		"code": "GA",
		"languages": ["french"],
		"flag_colors": ["green", "yellow", "blue"],
		"region": "Sub-Saharan Africa"
	},
	{
		"name": "England",
		"code": "GB-ENG",
		"languages": ["english"],
		"flag_colors": ["red", "white"],
		"region": "Western Europe"
	},
	{
		"name": "Northern Ireland",
		"code": "GB-NIR",
		"languages": ["english", "irish"],
		"flag_colors": ["white", "red"],
		"region": "Western Europe"
	},
	{
		"name": "Scotland",
		"code": "GB-SCT",
		"languages": ["english", "scottish gaelic"],
		"flag_colors": ["blue", "white"],
		"region": "Western Europe"
	},
	{
		"name": "Wales",
		"code": "GB-WLS",
		"languages": ["english", "welsh"],
		"flag_colors": ["green", "white", "red"],
		"region": "Western Europe"
	},
	{
		"name": "United Kingdom",
		"code": "GB",
		"languages": ["english"],
		"flag_colors": ["red", "white", "blue"],
		"region": "Western Europe"
	},
	{
		"name": "Grenada",
		"code": "GD",
		"languages": ["english"],
		"flag_colors": ["red", "yellow", "green", "black"],
		"region": "Caribbean"
	},
	{
		"name": "Georgia",
		"code": "GE",
		"languages": ["georgian"],
		"flag_colors": ["white", "red"],
		"region": "Middle East"
	},
	{
		"name": "French Guiana",
		"code": "GF",
		"languages": ["french"],
		"flag_colors": ["green", "yellow", "red"],
		"region": "South America"
	},
	{
		"name": "Guernsey",
		"code": "GG",
		"languages": ["english", "french"],
		"flag_colors": ["white", "red", "yellow"],
		"region": "Western Europe"
	},
	{
		"name": "Ghana",
		"code": "GH",
		"languages": ["english"],
		"flag_colors": ["red", "yellow", "green", "black"],
		"region": "Sub-Saharan Africa"
	},
	{
		"name": "Gibraltar",
		"code": "GI",
		"languages": ["english"],
		"flag_colors": ["red", "white", "yellow"],
		"region": "Southern Europe"
	},
	{
		"name": "Greenland",
		"code": "GL",
		"languages": ["kalaallisut", "danish"],
		"flag_colors": ["red", "white"],
		"region": "North America"
	},
	{
		"name": "Gambia",
		"code": "GM",
		"languages": ["english"],
		"flag_colors": ["red", "blue", "green", "white"],
		"region": "Sub-Saharan Africa"
	},
	{
		"name": "Guinea",
		"code": "GN",
		"languages": ["french"],
		"flag_colors": ["red", "yellow", "green"],
		"region": "Sub-Saharan Africa"
	},
	{
		"name": "Guadeloupe",
		"code": "GP",
		"languages": ["french"],
		"flag_colors": ["blue", "white", "red"],
		"region": "Caribbean"
	},
	{
		"name": "Equatorial Guinea",
		"code": "GQ",
		"languages": ["spanish", "french", "portuguese"],
		"flag_colors": ["green", "white", "red", "blue", "yellow"],
		"region": "Sub-Saharan Africa"
	},
	{
		"name": "Greece",
		"code": "GR",
		"languages": ["greek"],
		"flag_colors": ["blue", "white"],
		"region": "Southern Europe"
	},
	{
		"name": "South Georgia and the South Sandwich Islands",
		"code": "GS",
		"languages": [],
		"flag_colors": ["blue", "white", "yellow"],
		"region": "Atlantic Ocean"
	},
	{
		"name": "Guatemala",
		"code": "GT",
		"languages": ["spanish"],
		"flag_colors": ["blue", "white"],
		"region": "Central America"
	},
	{
		"name": "Guam",
		"code": "GU",
		"languages": ["english", "chamorro"],
		"flag_colors": ["blue", "red"],
		"region": "Oceania"
	},
	{
		"name": "Guinea-Bissau",
		"code": "GW",
		"languages": ["portuguese"],
		"flag_colors": ["red", "yellow", "green", "black"],
		"region": "Sub-Saharan Africa"
	},
	{
		"name": "Guyana",
		"code": "GY",
		"languages": ["english"],
		"flag_colors": ["green", "yellow", "red", "black", "white"],
		"region": "South America"
	},
	{
		"name": "Hong Kong",
		"code": "HK",
		"languages": ["cantonese", "english"],
		"flag_colors": ["red", "white"],
		"region": "East Asia"
	},
	{
		"name": "Heard Island and McDonald Islands",
		"code": "HM",
		"languages": [],
		"flag_colors": ["blue", "white", "red"],
		"region": "Oceania"
	},
	{
		"name": "Honduras",
		"code": "HN",
		"languages": ["spanish"],
		"flag_colors": ["blue", "white"],
		"region": "Central America"
	},
	{
		"name": "Croatia",
		"code": "HR",
		"languages": ["croatian"],
		"flag_colors": ["red", "white", "blue"],
		"region": "Southern Europe"
	},
	{
		"name": "Haiti",
		"code": "HT",
		"languages": ["haitian creole", "french"],
		"flag_colors": ["blue", "red", "white"],
		"region": "Caribbean"
	},
	{
		"name": "Hungary",
		"code": "HU",
		"languages": ["hungarian"],
		"flag_colors": ["red", "white", "green"],
		"region": "Eastern Europe"
	},
	{
		"name": "Indonesia",
		"code": "ID",
		"languages": ["indonesian"],
		"flag_colors": ["red", "white"],
		"region": "Southeast Asia"
	},
	{
		"name": "Ireland",
		"code": "IE",
		"languages": ["english", "irish"],
		"flag_colors": ["green", "white", "orange"],
		"region": "Western Europe"
	},
	{
		"name": "Israel",
		"code": "IL",
		"languages": ["hebrew", "arabic"],
		"flag_colors": ["blue", "white"],
		"region": "Middle East"
	},
	{
		"name": "Isle of Man",
		"code": "IM",
		"languages": ["english", "manx"],
		"flag_colors": ["red", "yellow"],
		"region": "Western Europe"
	},
	{
		"name": "India",
		"code": "IN",
		"languages": ["hindi", "english"],
		"flag_colors": ["orange", "white", "green", "blue"],
		"region": "South Asia"
	},
	{
		"name": "British Indian Ocean Territory",
		"code": "IO",
		"languages": ["english"],
		"flag_colors": ["blue", "white", "red", "yellow"],
		"region": "South Asia"
	},
	{
		"name": "Iraq",
		"code": "IQ",
		"languages": ["arabic", "kurdish"],
		"flag_colors": ["red", "white", "black", "green"],
		"region": "Middle East"
	},
	{
		"name": "Iran",
		"code": "IR",
		"languages": ["persian"],
		"flag_colors": ["green", "white", "red"],
		"region": "Middle East"
	},
	{
		"name": "Iceland",
		"code": "IS",
		"languages": ["icelandic"],
		"flag_colors": ["blue", "white", "red"],
		"region": "Northern Europe"
	},
	{
		"name": "Italy",
		"code": "IT",
		"languages": ["italian"],
		"flag_colors": ["green", "white", "red"],
		"region": "Southern Europe"
	},
	{
		"name": "Jersey",
		"code": "JE",
		"languages": ["english", "french"],
		"flag_colors": ["white", "red", "yellow"],
		"region": "Western Europe"
	},
	{
		"name": "Jamaica",
		"code": "JM",
		"languages": ["english"],
		"flag_colors": ["green", "yellow", "black"],
		"region": "Caribbean"
	},
	{
		"name": "Jordan",
		"code": "JO",
		"languages": ["arabic"],
		"flag_colors": ["black", "white", "green", "red"],
		"region": "Middle East"
	},
	{
		"name": "Japan",
		"code": "JP",
		"languages": ["japanese"],
		"flag_colors": ["red", "white"],
		"region": "East Asia"
	},
	{
		"name": "Kenya",
		"code": "KE",
		"languages": ["english", "swahili"],
		"flag_colors": ["black", "red", "green", "white"],
		"region": "Sub-Saharan Africa"
	},
	{
		"name": "Kyrgyzstan",
		"code": "KG",
		"languages": ["kyrgyz", "russian"],
		"flag_colors": ["red", "yellow"],
		"region": "Central Asia"
	},
	{
		"name": "Cambodia",
		"code": "KH",
		"languages": ["khmer"],
		"flag_colors": ["blue", "red", "white"],
		"region": "Southeast Asia"
	},
	{
		"name": "Kiribati",
		"code": "KI",
		"languages": ["english", "kiribati"],
		"flag_colors": ["red", "blue", "white", "yellow"],
		"region": "Oceania"
	},
	{
		"name": "Comoros",
		"code": "KM",
		"languages": ["comorian", "arabic", "french"],
		"flag_colors": ["green", "yellow", "white", "red", "blue"],
		"region": "Sub-Saharan Africa"
	},
	{
		"name": "Saint Kitts and Nevis",
		"code": "KN",
		"languages": ["english"],
		"flag_colors": ["green", "red", "black", "yellow", "white"],
		"region": "Caribbean"
	},
	{
		"name": "North Korea",
		"code": "KP",
		"languages": ["korean"],
		"flag_colors": ["red", "blue", "white"],
		"region": "East Asia"
	},
	{
		"name": "South Korea",
		"code": "KR",
		"languages": ["korean"],
		"flag_colors": ["white", "red", "blue", "black"],
		"region": "East Asia"
	},
	{
		"name": "Kuwait",
		"code": "KW",
		"languages": ["arabic"],
		"flag_colors": ["green", "white", "red", "black"],
		"region": "Middle East"
	},
	{
		"name": "Cayman Islands",
		"code": "KY",
		"languages": ["english"],
		"flag_colors": ["blue", "white", "red"],
		"region": "Caribbean"
	},
	{
		"name": "Kazakhstan",
		"code": "KZ",
		"languages": ["kazakh", "russian"],
		"flag_colors": ["blue", "yellow"],
		"region": "Central Asia"
	},
	{
		"name": "Laos",
		"code": "LA",
		"languages": ["lao"],
		"flag_colors": ["red", "blue", "white"],
		"region": "Southeast Asia"
	},
	{
		"name": "Lebanon",
		"code": "LB",
		"languages": ["arabic"],
		"flag_colors": ["red", "white", "green"],
		"region": "Middle East"
	},
	{
		"name": "Saint Lucia",
		"code": "LC",
		"languages": ["english"],
		"flag_colors": ["blue", "yellow", "black", "white"],
		"region": "Caribbean"
	},
	{
		"name": "Liechtenstein",
		"code": "LI",
		"languages": ["german"],
		"flag_colors": ["blue", "red", "yellow"],
		"region": "Western Europe"
	},
	{
		"name": "Sri Lanka",
		"code": "LK",
		"languages": ["sinhala", "tamil"],
		"flag_colors": ["yellow", "green", "orange", "red"],
		"region": "South Asia"
	},
	{
		"name": "Liberia",
		"code": "LR",
		"languages": ["english"],
		"flag_colors": ["red", "white", "blue"],
		"region": "Sub-Saharan Africa"
	},
	{
		"name": "Lesotho",
		"code": "LS",
		"languages": ["sesotho", "english"],
		"flag_colors": ["blue", "white", "green", "black"],
		"region": "Sub-Saharan Africa"
	},
	{
		"name": "Lithuania",
		"code": "LT",
		"languages": ["lithuanian"],
		"flag_colors": ["yellow", "green", "red"],
		"region": "Northern Europe"
	},
	{
		"name": "Luxembourg",
		"code": "LU",
		"languages": ["luxembourgish", "french", "german"],
		"flag_colors": ["red", "white", "blue"],
		"region": "Western Europe"
	},
	{
		"name": "Latvia",
		"code": "LV",
		"languages": ["latvian"],
		"flag_colors": ["red", "white"],
		"region": "Northern Europe"
	},
	{
		"name": "Libya",
		"code": "LY",
		"languages": ["arabic"],
		"flag_colors": ["red", "black", "green", "white"],
		"region": "North Africa"
	},
	{
		"name": "Morocco",
		"code": "MA",
		"languages": ["arabic", "tamazight"],
		"flag_colors": ["red", "green"],
		"region": "North Africa"
	},
	{
		"name": "Monaco",
		"code": "MC",
		"languages": ["french"],
		"flag_colors": ["red", "white"],
		"region": "Western Europe"
	},
	{
		"name": "Moldova",
		"code": "MD",
		"languages": ["romanian"],
		"flag_colors": ["blue", "yellow", "red"],
		"region": "Eastern Europe"
	},
	{
		"name": "Montenegro",
		"code": "ME",
		"languages": ["montenegrin"],
		"flag_colors": ["red", "yellow"],
		"region": "Southern Europe"
	},
	{
		"name": "Saint Martin",
		"code": "MF",
		"languages": ["french"],
		"flag_colors": ["blue", "white", "red"],
		"region": "Caribbean"
	},
	{
		"name": "Madagascar",
		"code": "MG",
		"languages": ["malagasy", "french"],
		"flag_colors": ["white", "red", "green"],
		"region": "Sub-Saharan Africa"
	},
	{
		"name": "Marshall Islands",
		"code": "MH",
		"languages": ["marshallese", "english"],
		"flag_colors": ["blue", "white", "orange"],
		"region": "Oceania"
	},
	{
		"name": "North Macedonia",
		"code": "MK",
		"languages": ["macedonian", "albanian"],
		"flag_colors": ["red", "yellow"],
		"region": "Southern Europe"
	},
	{
		"name": "Mali",
		"code": "ML",
		"languages": ["french"],
		"flag_colors": ["green", "yellow", "red"],
		"region": "Sub-Saharan Africa"
	},
	{
		"name": "Myanmar",
		"code": "MM",
		"languages": ["burmese"],
		"flag_colors": ["yellow", "green", "red", "white"],
		"region": "Southeast Asia"
	},
	{
		"name": "Mongolia",
		"code": "MN",
		"languages": ["mongolian"],
		"flag_colors": ["red", "blue", "yellow"],
		"region": "East Asia"
	},
	{
		"name": "Macao",
		"code": "MO",
		"languages": ["cantonese", "portuguese"],
		"flag_colors": ["green", "white", "yellow"],
		"region": "East Asia"
	},
	{
		"name": "Northern Mariana Islands",
		"code": "MP",
		"languages": ["english", "chamorro", "carolinian"],
		"flag_colors": ["blue", "white"],
		"region": "Oceania"
	},
	{
		"name": "Martinique",
		"code": "MQ",
		"languages": ["french"],
		"flag_colors": ["blue", "white", "red"],
		"region": "Caribbean"
	},
	{
		"name": "Mauritania",
		"code": "MR",
		"languages": ["arabic"],
		"flag_colors": ["green", "yellow", "red"],
		"region": "North Africa"
	},
	{
		"name": "Montserrat",
		"code": "MS",
		"languages": ["english"],
		"flag_colors": ["blue", "green", "white", "yellow", "red"],
		"region": "Caribbean"
	},
	{
		"name": "Malta",
		"code": "MT",
		"languages": ["maltese", "english"],
		"flag_colors": ["white", "red"],
		"region": "Southern Europe"
	},
	{
		"name": "Mauritius",
		"code": "MU",
		"languages": ["english", "french", "mauritian creole"],
		"flag_colors": ["red", "blue", "yellow", "green"],
		"region": "Sub-Saharan Africa"
	},
	{
		"name": "Maldives",
		"code": "MV",
		"languages": ["divehi"],
		"flag_colors": ["red", "green", "white"],
		"region": "South Asia"
	},
	{
		"name": "Malawi",
		"code": "MW",
		"languages": ["english", "chichewa"],
		"flag_colors": ["black", "red", "green"],
		"region": "Sub-Saharan Africa"
	},
	{
		"name": "Mexico",
		"code": "MX",
		"languages": ["spanish"],
		"flag_colors": ["green", "white", "red"],
		"region": "North America"
	},
	{
		"name": "Malaysia",
		"code": "MY",
		"languages": ["malay"],
		"flag_colors": ["red", "white", "blue", "yellow"],
		"region": "Southeast Asia"
	},
	{
		"name": "Mozambique",
		"code": "MZ",
		"languages": ["portuguese"],
		"flag_colors": ["green", "black", "yellow", "white", "red"],
		"region": "Sub-Saharan Africa"
	},
	{
		"name": "Namibia",
		"code": "NA",
		"languages": ["english"],
		"flag_colors": ["blue", "red", "green", "white", "yellow"],
		"region": "Sub-Saharan Africa"
	},
	{
		"name": "New Caledonia",
		"code": "NC",
		"languages": ["french"],
		"flag_colors": ["blue", "red", "green", "yellow", "black"],
		"region": "Oceania"
	},
	{
		"name": "Niger",
		"code": "NE",
		"languages": ["french"],
		"flag_colors": ["orange", "white", "green"],
		"region": "Sub-Saharan Africa"
	},
	{
		"name": "Norfolk Island",
		"code": "NF",
		"languages": ["english", "norfuk"],
		"flag_colors": ["green", "white"],
		"region": "Oceania"
	},
	{
		"name": "Nigeria",
		"code": "NG",
		"languages": ["english"],
		"flag_colors": ["green", "white"],
		"region": "Sub-Saharan Africa"
	},
	{
		"name": "Nicaragua",
		"code": "NI",
		"languages": ["spanish"],
		"flag_colors": ["blue", "white"],
		"region": "Central America"
	},
	{
		"name": "Netherlands",
		"code": "NL",
		"languages": ["dutch"],
		"flag_colors": ["red", "white", "blue"],
		"region": "Western Europe"
	},
	{
		"name": "Norway",
		"code": "NO",
		"languages": ["norwegian"],
		"flag_colors": ["red", "white", "blue"],
		"region": "Northern Europe"
	},
	{
		"name": "Nepal",
		"code": "NP",
		"languages": ["nepali"],
		"flag_colors": ["red", "blue", "white"],
		"region": "South Asia"
	},
	{
		"name": "Nauru",
		"code": "NR",
		"languages": ["nauruan", "english"],
		"flag_colors": ["blue", "yellow", "white"],
		"region": "Oceania"
	},
	{
		"name": "Niue",
		"code": "NU",
		"languages": ["niuean", "english"],
		"flag_colors": ["yellow", "blue", "white", "red"],
		"region": "Oceania"
	},
	{
		"name": "New Zealand",
		"code": "NZ",
		"languages": ["english", "maori"],
		"flag_colors": ["blue", "red", "white"],
		"region": "Oceania"
	},
	{
		"name": "Oman",
		"code": "OM",
		"languages": ["arabic"],
		"flag_colors": ["red", "white", "green"],
		"region": "Middle East"
	},
	{
		"name": "Panama",
		"code": "PA",
		"languages": ["spanish"],
		"flag_colors": ["red", "white", "blue"],
		"region": "Central America"
	},
	{
		"name": "Peru",
		"code": "PE",
		"languages": ["spanish", "quechua", "aymara"],
		"flag_colors": ["red", "white"],
		"region": "South America"
	},
	{
		"name": "French Polynesia",
		"code": "PF",
		"languages": ["french", "tahitian"],
		"flag_colors": ["red", "white"],
		"region": "Oceania"
	},
	{
		"name": "Papua New Guinea",
		"code": "PG",
		"languages": ["english", "tok pisin", "hiri motu"],
		"flag_colors": ["red", "black", "yellow", "white"],
		"region": "Oceania"
	},
	{
		"name": "Philippines",
		"code": "PH",
		"languages": ["filipino", "english"],
		"flag_colors": ["blue", "red", "white", "yellow"],
		"region": "Southeast Asia"
	},
	{
		"name": "Pakistan",
		"code": "PK",
		"languages": ["urdu", "english"],
		"flag_colors": ["green", "white"],
		"region": "South Asia"
	},
	{
		"name": "Poland",
		"code": "PL",
		"languages": ["polish"],
		"flag_colors": ["white", "red"],
		"region": "Eastern Europe"
	},
	{
		"name": "Saint Pierre and Miquelon",
		"code": "PM",
		"languages": ["french"],
		"flag_colors": ["blue", "white", "red", "yellow", "black"],
		"region": "North America"
	},
	{
		"name": "Pitcairn Islands",
		"code": "PN",
		"languages": ["english", "pitkern"],
		"flag_colors": ["blue", "green", "yellow", "red", "white"],
		"region": "Oceania"
	},
	{
		"name": "Puerto Rico",
		"code": "PR",
		"languages": ["spanish", "english"],
		"flag_colors": ["red", "white", "blue"],
		"region": "Caribbean"
	},
	{
		"name": "Palestine",
		"code": "PS",
		"languages": ["arabic"],
		"flag_colors": ["red", "green", "white", "black"],
		"region": "Middle East"
	},
	{
		"name": "Portugal",
		"code": "PT",
		"languages": ["portuguese"],
		"flag_colors": ["green", "red", "yellow"],
		"region": "Southern Europe"
	},
	{
		"name": "Palau",
		"code": "PW",
		"languages": ["palauan", "english"],
		"flag_colors": ["blue", "yellow"],
		"region": "Oceania"
	},
	{
		"name": "Paraguay",
		"code": "PY",
		"languages": ["spanish", "guarani"],
		"flag_colors": ["red", "white", "blue", "yellow"],
		"region": "South America"
	},
	{
		"name": "Qatar",
		"code": "QA",
		"languages": ["arabic"],
		"flag_colors": ["red", "white"],
		"region": "Middle East"
	},
	{
		"name": "Reunion",
		"code": "RE",
		"languages": ["french", "reunion creole"],
		"flag_colors": ["blue", "white", "red"],
		"region": "Sub-Saharan Africa"
	},
	{
		"name": "Romania",
		"code": "RO",
		"languages": ["romanian"],
		"flag_colors": ["blue", "yellow", "red"],
		"region": "Eastern Europe"
	},
	{
		"name": "Serbia",
		"code": "RS",
		"languages": ["serbian"],
		"flag_colors": ["red", "blue", "white", "yellow"],
		"region": "Eastern Europe"
	},
	{
		"name": "Russia",
		"code": "RU",
		"languages": ["russian"],
		"flag_colors": ["white", "blue", "red"],
		"region": "Eastern Europe"
	},
	{
		"name": "Rwanda",
		"code": "RW",
		"languages": ["kinyarwanda", "french", "english"],
		"flag_colors": ["blue", "yellow", "green"],
		"region": "Sub-Saharan Africa"
	},
	{
		"name": "Saudi Arabia",
		"code": "SA",
		"languages": ["arabic"],
		"flag_colors": ["green", "white"],
		"region": "Middle East"
	},
	{
		"name": "Solomon Islands",
		"code": "SB",
		"languages": ["english"],
		"flag_colors": ["blue", "green", "yellow", "white"],
		"region": "Oceania"
	},
	{
		"name": "Seychelles",
		"code": "SC",
		"languages": ["seychellois creole", "english", "french"],
		"flag_colors": ["blue", "yellow", "red", "white", "green"],
		"region": "Sub-Saharan Africa"
	},
	{
		"name": "Sudan",
		"code": "SD",
		"languages": ["arabic", "english"],
		"flag_colors": ["red", "white", "black", "green"],
		"region": "North Africa"
	},
	{
		"name": "Sweden",
		"code": "SE",
		"languages": ["swedish"],
		"flag_colors": ["blue", "yellow"],
		"region": "Northern Europe"
	},
	{
		"name": "Singapore",
		"code": "SG",
		"languages": ["english", "malay", "mandarin", "tamil"],
		"flag_colors": ["red", "white"],
		"region": "Southeast Asia"
	},
	{
		"name": "Saint Helena",
		"code": "SH",
		"languages": ["english"],
		"flag_colors": ["blue", "red", "white", "yellow", "green"],
		"region": "Atlantic Ocean"
	},
	{
		"name": "Slovenia",
		"code": "SI",
		"languages": ["slovenian"],
		"flag_colors": ["white", "blue", "red"],
		"region": "Southern Europe"
	},
	{
		"name": "Svalbard and Jan Mayen",
		"code": "SJ",
		"languages": ["norwegian"],
		"flag_colors": ["red", "white", "blue"],
		"region": "Northern Europe"
	},
	{
		"name": "Slovakia",
		"code": "SK",
		"languages": ["slovak"],
		"flag_colors": ["white", "blue", "red"],
		"region": "Eastern Europe"
	},
	{
		"name": "Sierra Leone",
		"code": "SL",
		"languages": ["english"],
		"flag_colors": ["green", "white", "blue"],
		"region": "Sub-Saharan Africa"
	},
	{
		"name": "San Marino",
		"code": "SM",
		"languages": ["italian"],
		"flag_colors": ["white", "blue"],
		"region": "Southern Europe"
	},
	{
		"name": "Senegal",
		"code": "SN",
		"languages": ["french"],
		"flag_colors": ["green", "yellow", "red"],
		"region": "Sub-Saharan Africa"
	},
	{
		"name": "Somalia",
		"code": "SO",
		"languages": ["somali", "arabic"],
		"flag_colors": ["blue", "white"],
		"region": "Sub-Saharan Africa"
	},
	{
		"name": "Suriname",
		"code": "SR",
		"languages": ["dutch"],
		"flag_colors": ["green", "white", "red", "yellow"],
		"region": "South America"
	},
	{
		"name": "South Sudan",
		"code": "SS",
		"languages": ["english"],
		"flag_colors": ["black", "red", "green", "blue", "white", "yellow"],
		"region": "Sub-Saharan Africa"
	},
	{
		"name": "Sao Tome and Principe",
		"code": "ST",
		"languages": ["portuguese"],
		"flag_colors": ["green", "yellow", "red", "black"],
		"region": "Sub-Saharan Africa"
	},
	{
		"name": "El Salvador",
		"code": "SV",
		"languages": ["spanish"],
		"flag_colors": ["blue", "white"],
		"region": "Central America"
	},
	{
		"name": "Sint Maarten",
		"code": "SX",
		"languages": ["dutch", "english"],
		"flag_colors": ["red", "white", "blue"],
		"region": "Caribbean"
	},
	{
		"name": "Syria",
		"code": "SY",
		"languages": ["arabic"],
		"flag_colors": ["red", "white", "black", "green"],
		"region": "Middle East"
	},
	{
		"name": "Eswatini",
		"code": "SZ",
		"languages": ["swazi", "english"],
		"flag_colors": ["blue", "red", "yellow", "black", "white"],
		"region": "Sub-Saharan Africa"
	},
	{
		"name": "Turks and Caicos Islands",
		"code": "TC",
		"languages": ["english"],
		"flag_colors": ["blue", "red", "white", "yellow", "green"],
		"region": "Caribbean"
	},
	{
		"name": "Chad",
		"code": "TD",
		"languages": ["french", "arabic"],
		"flag_colors": ["blue", "yellow", "red"],
		"region": "Sub-Saharan Africa"
	},
	{
		"name": "French Southern Territories",
		"code": "TF",
		"languages": ["french"],
		"flag_colors": ["blue", "white", "red"],
		"region": "Oceania"
	},
	{
		"name": "Togo",
		"code": "TG",
		"languages": ["french"],
		"flag_colors": ["green", "yellow", "red", "white"],
		"region": "Sub-Saharan Africa"
	},
	{
		"name": "Thailand",
		"code": "TH",
		"languages": ["thai"],
		"flag_colors": ["red", "white", "blue"],
		"region": "Southeast Asia"
	},
	{
		"name": "Tajikistan",
		"code": "TJ",
		"languages": ["tajik"],
		"flag_colors": ["red", "white", "green", "yellow"],
		"region": "Central Asia"
	},
	{
		"name": "Tokelau",
		"code": "TK",
		"languages": ["tokelauan", "english"],
		"flag_colors": ["blue", "yellow", "white"],
		"region": "Oceania"
	},
	{
		"name": "Timor-Leste",
		"code": "TL",
		"languages": ["tetum", "portuguese"],
		"flag_colors": ["red", "yellow", "black", "white"],
		"region": "Southeast Asia"
	},
	{
		"name": "Turkmenistan",
		"code": "TM",
		"languages": ["turkmen"],
		"flag_colors": ["green", "red", "white"],
		"region": "Central Asia"
	},
	{
		"name": "Tunisia",
		"code": "TN",
		"languages": ["arabic"],
		"flag_colors": ["red", "white"],
		"region": "North Africa"
	},
	{
		"name": "Tonga",
		"code": "TO",
		"languages": ["tongan", "english"],
		"flag_colors": ["red", "white"],
		"region": "Oceania"
	},
	{
		"name": "Turkey",
		"code": "TR",
		"languages": ["turkish"],
		"flag_colors": ["red", "white"],
		"region": "Middle East"
	},
	{
		"name": "Trinidad and Tobago",
		"code": "TT",
		"languages": ["english"],
		"flag_colors": ["red", "black", "white"],
		"region": "Caribbean"
	},
	{
		"name": "Tuvalu",
		"code": "TV",
		"languages": ["tuvaluan", "english"],
		"flag_colors": ["blue", "yellow", "red", "white"],
		"region": "Oceania"
	},
	{
		"name": "Taiwan",
		"code": "TW",
		"languages": ["mandarin"],
		"flag_colors": ["red", "blue", "white"],
		"region": "East Asia"
	},
	{
		"name": "Tanzania",
		"code": "TZ",
		"languages": ["swahili", "english"],
		"flag_colors": ["green", "yellow", "black", "blue"],
		"region": "Sub-Saharan Africa"
	},
	{
		"name": "Ukraine",
		"code": "UA",
		"languages": ["ukrainian"],
		"flag_colors": ["blue", "yellow"],
		"region": "Eastern Europe"
	},
	{
		"name": "Uganda",
		"code": "UG",
		"languages": ["english", "swahili"],
		"flag_colors": ["black", "yellow", "red", "white"],
		"region": "Sub-Saharan Africa"
	},
	{
		"name": "United States Minor Outlying Islands",
		"code": "UM",
		"languages": ["english"],
		"flag_colors": ["red", "white", "blue"],
		"region": "Oceania"
	},
	{
		"name": "United States",
		"code": "US",
		"languages": ["english"],
		"flag_colors": ["red", "white", "blue"],
		"region": "North America"
	},
	{
		"name": "Uruguay",
		"code": "UY",
		"languages": ["spanish"],
		"flag_colors": ["white", "blue", "yellow"],
		"region": "South America"
	},
	{
		"name": "Uzbekistan",
		"code": "UZ",
		"languages": ["uzbek"],
		"flag_colors": ["blue", "white", "green", "red"],
		"region": "Central Asia"
	},
	{
		"name": "Vatican City",
		"code": "VA",
		"languages": ["italian", "latin"],
		"flag_colors": ["yellow", "white"],
		"region": "Southern Europe"
	},
	{
		"name": "Saint Vincent and the Grenadines",
		"code": "VC",
		"languages": ["english"],
		"flag_colors": ["blue", "yellow", "green", "white"],
		"region": "Caribbean"
	},
	{
		"name": "Venezuela",
		"code": "VE",
		"languages": ["spanish"],
		"flag_colors": ["yellow", "blue", "red", "white"],
		"region": "South America"
	},
	{
		"name": "British Virgin Islands",
		"code": "VG",
		"languages": ["english"],
		"flag_colors": ["blue", "white", "red"],
		"region": "Caribbean"
	},
	{
		"name": "U.S. Virgin Islands",
		"code": "VI",
		"languages": ["english"],
		"flag_colors": ["red", "white", "blue", "yellow"],
		"region": "Caribbean"
	},
	{
		"name": "Vietnam",
		"code": "VN",
		"languages": ["vietnamese"],
		"flag_colors": ["red", "yellow"],
		"region": "Southeast Asia"
	},
	{
		"name": "Vanuatu",
		"code": "VU",
		"languages": ["bislama", "english", "french"],
		"flag_colors": ["red", "green", "black", "yellow"],
		"region": "Oceania"
	},
	{
		"name": "Wallis and Futuna",
		"code": "WF",
		"languages": ["french", "wallisian", "futunan"],
		"flag_colors": ["red", "white", "blue"],
		"region": "Oceania"
	},
	{
		"name": "Samoa",
		"code": "WS",
		"languages": ["samoan", "english"],
		"flag_colors": ["red", "blue", "white"],
		"region": "Oceania"
	},
	{
		"name": "Kosovo",
		"code": "XK",
		"languages": ["albanian", "serbian"],
		"flag_colors": ["blue", "yellow", "white"],
		"region": "Eastern Europe"
	},
	{
		"name": "Yemen",
		"code": "YE",
		"languages": ["arabic"],
		"flag_colors": ["red", "white", "black"],
		"region": "Middle East"
	},
	{
		"name": "Mayotte",
		"code": "YT",
		"languages": ["french", "shimaore", "kibushi"],
		"flag_colors": ["white"],
		"region": "Sub-Saharan Africa"
	},
	{
		"name": "South Africa",
		"code": "ZA",
		"languages": ["zulu", "xhosa", "afrikaans", "english"],
		"flag_colors": ["red", "blue", "green", "yellow", "black", "white"],
		"region": "Sub-Saharan Africa"
	},
	{
		"name": "Zambia",
		"code": "ZM",
		"languages": ["english"],
		"flag_colors": ["green", "red", "black", "orange"],
		"region": "Sub-Saharan Africa"
	},
	{
		"name": "Zimbabwe",
		"code": "ZW",
		"languages": ["english", "shona", "ndebele"],
		"flag_colors": ["green", "yellow", "red", "black", "white"],
		"region": "Sub-Saharan Africa"
	}
]


================================================
FILE: apps/web/public/ffmpeg/ffmpeg-core.js
================================================
var createFFmpegCore = (() => {
  var _scriptDir =
    typeof document !== "undefined" && document.currentScript
      ? document.currentScript.src
      : undefined;

  return (createFFmpegCore = {}) => {
    var Module = typeof createFFmpegCore != "undefined" ? createFFmpegCore : {}; // Ensure that createFFmpegCore is defined and available.var readyPromiseResolve,readyPromiseReject;Module["ready"]=new Promise((resolve,reject)=>{readyPromiseResolve=resolve;readyPromiseReject=reject});const NULL=0;const SIZE_I32=Uint32Array.BYTES_PER_ELEMENT;const DEFAULT_ARGS=["./ffmpeg","-nostdin","-y"];const DEFAULT_ARGS_FFPROBE=["./ffprobe"];Module["NULL"]=NULL;Module["SIZE_I32"]=SIZE_I32;Module["DEFAULT_ARGS"]=DEFAULT_ARGS;Module["DEFAULT_ARGS_FFPROBE"]=DEFAULT_ARGS_FFPROBE;Module["ret"]=-1;Module["timeout"]=-1;Module["logger"]=()=>{};Module["progress"]=()=>{};function stringToPtr(str){const len=Module["lengthBytesUTF8"](str)+1;const ptr=Module["_malloc"](len);Module["stringToUTF8"](str,ptr,len);return ptr}function stringsToPtr(strs){const len=strs.length;const ptr=Module["_malloc"](len*SIZE_I32);for(let i=0;i<len;i++){Module["setValue"](ptr+SIZE_I32*i,stringToPtr(strs[i]),"i32")}return ptr}function print(message){Module["logger"]({type:"stdout",message:message})}function printErr(message){if(!message.startsWith("Aborted(native code called abort())"))Module["logger"]({type:"stderr",message:message})}function exec(..._args){const args=[...Module["DEFAULT_ARGS"],..._args];try{Module["_ffmpeg"](args.length,stringsToPtr(args))}catch(e){if(!e.message.startsWith("Aborted")){throw e}}return Module["ret"]}function ffprobe(..._args){const args=[...Module["DEFAULT_ARGS_FFPROBE"],..._args];try{Module["_ffprobe"](args.length,stringsToPtr(args))}catch(e){if(!e.message.startsWith("Aborted")){throw e}}return Module["ret"]}function setLogger(logger){Module["logger"]=logger}function setTimeout(timeout){Module["timeout"]=timeout}function setProgress(handler){Module["progress"]=handler}function receiveProgress(progress,time){Module["progress"]({progress:progress,time:time})}function reset(){Module["ret"]=-1;Module["timeout"]=-1}function _locateFile(path,prefix){const mainScriptUrlOrBlob=Module["mainScriptUrlOrBlob"];if(mainScriptUrlOrBlob){const{wasmURL:wasmURL,workerURL:workerURL}=JSON.parse(atob(mainScriptUrlOrBlob.slice(mainScriptUrlOrBlob.lastIndexOf("#")+1)));if(path.endsWith(".wasm"))return wasmURL;if(path.endsWith(".worker.js"))return workerURL}return prefix+path}Module["stringToPtr"]=stringToPtr;Module["stringsToPtr"]=stringsToPtr;Module["print"]=print;Module["printErr"]=printErr;Module["locateFile"]=_locateFile;Module["exec"]=exec;Module["ffprobe"]=ffprobe;Module["setLogger"]=setLogger;Module["setTimeout"]=setTimeout;Module["setProgress"]=setProgress;Module["reset"]=reset;Module["receiveProgress"]=receiveProgress;var moduleOverrides=Object.assign({},Module);var arguments_=[];var thisProgram="./this.program";var quit_=(status,toThrow)=>{throw toThrow};var ENVIRONMENT_IS_WEB=false;var ENVIRONMENT_IS_WORKER=true;var ENVIRONMENT_IS_NODE=false;var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var read_,readAsync,readBinary,setWindowTitle;if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(typeof document!="undefined"&&document.currentScript){scriptDirectory=document.currentScript.src}if(_scriptDir){scriptDirectory=_scriptDir}if(scriptDirectory.indexOf("blob:")!==0){scriptDirectory=scriptDirectory.substr(0,scriptDirectory.replace(/[?#].*/,"").lastIndexOf("/")+1)}else{scriptDirectory=""}{read_=url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText};if(ENVIRONMENT_IS_WORKER){readBinary=url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=(url,onload,onerror)=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=()=>{if(xhr.status==200||xhr.status==0&&xhr.response){onload(xhr.response);return}onerror()};xhr.onerror=onerror;xhr.send(null)}}setWindowTitle=title=>document.title=title}else{}var out=Module["print"]||console.log.bind(console);var err=Module["printErr"]||console.error.bind(console);Object.assign(Module,moduleOverrides);moduleOverrides=null;if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["quit"])quit_=Module["quit"];var wasmBinary;if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];var noExitRuntime=Module["noExitRuntime"]||true;if(typeof WebAssembly!="object"){abort("no native wasm support detected")}var wasmMemory;var ABORT=false;var EXITSTATUS;function assert(condition,text){if(!condition){abort(text)}}var HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAP64,HEAPU64,HEAPF64;function updateMemoryViews(){var b=wasmMemory.buffer;Module["HEAP8"]=HEAP8=new Int8Array(b);Module["HEAP16"]=HEAP16=new Int16Array(b);Module["HEAP32"]=HEAP32=new Int32Array(b);Module["HEAPU8"]=HEAPU8=new Uint8Array(b);Module["HEAPU16"]=HEAPU16=new Uint16Array(b);Module["HEAPU32"]=HEAPU32=new Uint32Array(b);Module["HEAPF32"]=HEAPF32=new Float32Array(b);Module["HEAPF64"]=HEAPF64=new Float64Array(b);Module["HEAP64"]=HEAP64=new BigInt64Array(b);Module["HEAPU64"]=HEAPU64=new BigUint64Array(b)}var wasmTable;var __ATPRERUN__=[];var __ATINIT__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;var runtimeKeepaliveCounter=0;function keepRuntimeAlive(){return noExitRuntime||runtimeKeepaliveCounter>0}function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function initRuntime(){runtimeInitialized=true;if(!Module["noFSInit"]&&!FS.init.initialized)FS.init();FS.ignorePermissions=false;TTY.init();SOCKFS.root=FS.mount(SOCKFS,{},null);callRuntimeCallbacks(__ATINIT__)}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnInit(cb){__ATINIT__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function getUniqueRunDependency(id){return id}function addRunDependency(id){runDependencies++;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}}function removeRunDependency(id){runDependencies--;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}function abort(what){if(Module["onAbort"]){Module["onAbort"](what)}what="Aborted("+what+")";err(what);ABORT=true;EXITSTATUS=1;what+=". Build with -sASSERTIONS for more info.";var e=new WebAssembly.RuntimeError(what);readyPromiseReject(e);throw e}var dataURIPrefix="data:application/octet-stream;base64,";function isDataURI(filename){return filename.startsWith(dataURIPrefix)}var wasmBinaryFile;wasmBinaryFile="ffmpeg-core.wasm";if(!isDataURI(wasmBinaryFile)){wasmBinaryFile=locateFile(wasmBinaryFile)}function getBinary(file){try{if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(file)}throw"both async and sync fetching of the wasm failed"}catch(err){abort(err)}}function getBinaryPromise(binaryFile){if(!wasmBinary&&(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER)){if(typeof fetch=="function"){return fetch(binaryFile,{credentials:"same-origin"}).then(response=>{if(!response["ok"]){throw"failed to load wasm binary file at '"+binaryFile+"'"}return response["arrayBuffer"]()}).catch(()=>getBinary(binaryFile))}}return Promise.resolve().then(()=>getBinary(binaryFile))}function instantiateArrayBuffer(binaryFile,imports,receiver){return getBinaryPromise(binaryFile).then(binary=>{return WebAssembly.instantiate(binary,imports)}).then(instance=>{return instance}).then(receiver,reason=>{err("failed to asynchronously prepare wasm: "+reason);abort(reason)})}function instantiateAsync(binary,binaryFile,imports,callback){if(!binary&&typeof WebAssembly.instantiateStreaming=="function"&&!isDataURI(binaryFile)&&typeof fetch=="function"){return fetch(binaryFile,{credentials:"same-origin"}).then(response=>{var result=WebAssembly.instantiateStreaming(response,imports);return result.then(callback,function(reason){err("wasm streaming compile failed: "+reason);err("falling back to ArrayBuffer instantiation");return instantiateArrayBuffer(binaryFile,imports,callback)})})}else{return instantiateArrayBuffer(binaryFile,imports,callback)}}function createWasm(){var info={"a":wasmImports};function receiveInstance(instance,module){var exports=instance.exports;Module["asm"]=exports;wasmMemory=Module["asm"]["ra"];updateMemoryViews();wasmTable=Module["asm"]["ua"];addOnInit(Module["asm"]["sa"]);removeRunDependency("wasm-instantiate");return exports}addRunDependency("wasm-instantiate");function receiveInstantiationResult(result){receiveInstance(result["instance"])}if(Module["instantiateWasm"]){try{return Module["instantiateWasm"](info,receiveInstance)}catch(e){err("Module.instantiateWasm callback failed with error: "+e);readyPromiseReject(e)}}instantiateAsync(wasmBinary,wasmBinaryFile,info,receiveInstantiationResult).catch(readyPromiseReject);return{}}var ASM_CONSTS={6077464:$0=>{Module.ret=$0}};function send_progress(progress,time){Module.receiveProgress(progress,time)}function is_timeout(diff){if(Module.timeout===-1)return 0;else{return Module.timeout<=diff}}function ExitStatus(status){this.name="ExitStatus";this.message=`Program terminated with exit(${status})`;this.status=status}function callRuntimeCallbacks(callbacks){while(callbacks.length>0){callbacks.shift()(Module)}}var wasmTableMirror=[];function getWasmTableEntry(funcPtr){var func=wasmTableMirror[funcPtr];if(!func){if(funcPtr>=wasmTableMirror.length)wasmTableMirror.length=funcPtr+1;wasmTableMirror[funcPtr]=func=wasmTable.get(funcPtr)}return func}function getValue(ptr,type="i8"){if(type.endsWith("*"))type="*";switch(type){case"i1":return HEAP8[ptr>>0];case"i8":return HEAP8[ptr>>0];case"i16":return HEAP16[ptr>>1];case"i32":return HEAP32[ptr>>2];case"i64":return HEAP64[ptr>>3];case"float":return HEAPF32[ptr>>2];case"double":return HEAPF64[ptr>>3];case"*":return HEAPU32[ptr>>2];default:abort(`invalid type for getValue: ${type}`)}}function setValue(ptr,value,type="i8"){if(type.endsWith("*"))type="*";switch(type){case"i1":HEAP8[ptr>>0]=value;break;case"i8":HEAP8[ptr>>0]=value;break;case"i16":HEAP16[ptr>>1]=value;break;case"i32":HEAP32[ptr>>2]=value;break;case"i64":HEAP64[ptr>>3]=BigInt(value);break;case"float":HEAPF32[ptr>>2]=value;break;case"double":HEAPF64[ptr>>3]=value;break;case"*":HEAPU32[ptr>>2]=value;break;default:abort(`invalid type for setValue: ${type}`)}}var UTF8Decoder=typeof TextDecoder!="undefined"?new TextDecoder("utf8"):undefined;function UTF8ArrayToString(heapOrArray,idx,maxBytesToRead){var endIdx=idx+maxBytesToRead;var endPtr=idx;while(heapOrArray[endPtr]&&!(endPtr>=endIdx))++endPtr;if(endPtr-idx>16&&heapOrArray.buffer&&UTF8Decoder){return UTF8Decoder.decode(heapOrArray.subarray(idx,endPtr))}var str="";while(idx<endPtr){var u0=heapOrArray[idx++];if(!(u0&128)){str+=String.fromCharCode(u0);continue}var u1=heapOrArray[idx++]&63;if((u0&224)==192){str+=String.fromCharCode((u0&31)<<6|u1);continue}var u2=heapOrArray[idx++]&63;if((u0&240)==224){u0=(u0&15)<<12|u1<<6|u2}else{u0=(u0&7)<<18|u1<<12|u2<<6|heapOrArray[idx++]&63}if(u0<65536){str+=String.fromCharCode(u0)}else{var ch=u0-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}}return str}function UTF8ToString(ptr,maxBytesToRead){return ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):""}function ___assert_fail(condition,filename,line,func){abort(`Assertion failed: ${UTF8ToString(condition)}, at: `+[filename?UTF8ToString(filename):"unknown filename",line,func?UTF8ToString(func):"unknown function"])}function ExceptionInfo(excPtr){this.excPtr=excPtr;this.ptr=excPtr-24;this.set_type=function(type){HEAPU32[this.ptr+4>>2]=type};this.get_type=function(){return HEAPU32[this.ptr+4>>2]};this.set_destructor=function(destructor){HEAPU32[this.ptr+8>>2]=destructor};this.get_destructor=function(){return HEAPU32[this.ptr+8>>2]};this.set_caught=function(caught){caught=caught?1:0;HEAP8[this.ptr+12>>0]=caught};this.get_caught=function(){return HEAP8[this.ptr+12>>0]!=0};this.set_rethrown=function(rethrown){rethrown=rethrown?1:0;HEAP8[this.ptr+13>>0]=rethrown};this.get_rethrown=function(){return HEAP8[this.ptr+13>>0]!=0};this.init=function(type,destructor){this.set_adjusted_ptr(0);this.set_type(type);this.set_destructor(destructor)};this.set_adjusted_ptr=function(adjustedPtr){HEAPU32[this.ptr+16>>2]=adjustedPtr};this.get_adjusted_ptr=function(){return HEAPU32[this.ptr+16>>2]};this.get_exception_ptr=function(){var isPointer=___cxa_is_pointer_type(this.get_type());if(isPointer){return HEAPU32[this.excPtr>>2]}var adjusted=this.get_adjusted_ptr();if(adjusted!==0)return adjusted;return this.excPtr}}var exceptionLast=0;var uncaughtExceptionCount=0;function ___cxa_throw(ptr,type,destructor){var info=new ExceptionInfo(ptr);info.init(type,destructor);exceptionLast=ptr;uncaughtExceptionCount++;throw exceptionLast}var dlopenMissingError="To use dlopen, you need enable dynamic linking, see https://emscripten.org/docs/compiling/Dynamic-Linking.html";function ___dlsym(handle,symbol){abort(dlopenMissingError)}var PATH={isAbs:path=>path.charAt(0)==="/",splitPath:filename=>{var splitPathRe=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;return splitPathRe.exec(filename).slice(1)},normalizeArray:(parts,allowAboveRoot)=>{var up=0;for(var i=parts.length-1;i>=0;i--){var last=parts[i];if(last==="."){parts.splice(i,1)}else if(last===".."){parts.splice(i,1);up++}else if(up){parts.splice(i,1);up--}}if(allowAboveRoot){for(;up;up--){parts.unshift("..")}}return parts},normalize:path=>{var isAbsolute=PATH.isAbs(path),trailingSlash=path.substr(-1)==="/";path=PATH.normalizeArray(path.split("/").filter(p=>!!p),!isAbsolute).join("/");if(!path&&!isAbsolute){path="."}if(path&&trailingSlash){path+="/"}return(isAbsolute?"/":"")+path},dirname:path=>{var result=PATH.splitPath(path),root=result[0],dir=result[1];if(!root&&!dir){return"."}if(dir){dir=dir.substr(0,dir.length-1)}return root+dir},basename:path=>{if(path==="/")return"/";path=PATH.normalize(path);path=path.replace(/\/$/,"");var lastSlash=path.lastIndexOf("/");if(lastSlash===-1)return path;return path.substr(lastSlash+1)},join:function(){var paths=Array.prototype.slice.call(arguments);return PATH.normalize(paths.join("/"))},join2:(l,r)=>{return PATH.normalize(l+"/"+r)}};function initRandomFill(){if(typeof crypto=="object"&&typeof crypto["getRandomValues"]=="function"){return view=>crypto.getRandomValues(view)}else abort("initRandomDevice")}function randomFill(view){return(randomFill=initRandomFill())(view)}var PATH_FS={resolve:function(){var resolvedPath="",resolvedAbsolute=false;for(var i=arguments.length-1;i>=-1&&!resolvedAbsolute;i--){var path=i>=0?arguments[i]:FS.cwd();if(typeof path!="string"){throw new TypeError("Arguments to path.resolve must be strings")}else if(!path){return""}resolvedPath=path+"/"+resolvedPath;resolvedAbsolute=PATH.isAbs(path)}res
Download .txt
gitextract_ph6jujtc/

├── .cursor/
│   ├── commands/
│   │   └── review.md
│   ├── rules/
│   │   ├── codebase-index.mdc
│   │   ├── comments.mdc
│   │   ├── handling-uncertainty.mdc
│   │   ├── readability.mdc
│   │   ├── separation-of-concerns.mdc
│   │   ├── ultracite.mdc
│   │   └── writing-scannable-code.mdc
│   ├── settings.json
│   └── skills/
│       └── design/
│           └── SKILL.md
├── .dockerignore
├── .github/
│   ├── CODE_OF_CONDUCT.md
│   ├── CONTRIBUTING.md
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.yml
│   │   └── feature_request.yml
│   ├── SECURITY.md
│   ├── SUPPORT.md
│   ├── copilot-instructions.md
│   ├── pull_request_template.md
│   └── workflows/
│       └── bun-ci.yml
├── .gitignore
├── .npmrc
├── .vscode/
│   └── settings.json
├── AGENTS.md
├── LICENSE
├── README.md
├── apps/
│   └── web/
│       ├── .env.example
│       ├── .gitignore
│       ├── Dockerfile
│       ├── components.json
│       ├── content/
│       │   └── changelog/
│       │       ├── 0.1.0.md
│       │       └── 0.2.0.md
│       ├── content-collections.ts
│       ├── drizzle.config.ts
│       ├── migrations/
│       │   ├── 0000_brainy_saracen.sql
│       │   └── meta/
│       │       ├── 0000_snapshot.json
│       │       └── _journal.json
│       ├── next-env.d.ts
│       ├── next.config.ts
│       ├── package.json
│       ├── postcss.config.mjs
│       ├── public/
│       │   ├── browserconfig.xml
│       │   ├── countries.json
│       │   ├── ffmpeg/
│       │   │   ├── ffmpeg-core.js
│       │   │   └── ffmpeg-core.wasm
│       │   ├── fonts/
│       │   │   ├── font-atlas.json
│       │   │   ├── font-chunk-0.avif
│       │   │   ├── font-chunk-1.avif
│       │   │   ├── font-chunk-10.avif
│       │   │   ├── font-chunk-11.avif
│       │   │   ├── font-chunk-12.avif
│       │   │   ├── font-chunk-13.avif
│       │   │   ├── font-chunk-14.avif
│       │   │   ├── font-chunk-2.avif
│       │   │   ├── font-chunk-3.avif
│       │   │   ├── font-chunk-4.avif
│       │   │   ├── font-chunk-5.avif
│       │   │   ├── font-chunk-6.avif
│       │   │   ├── font-chunk-7.avif
│       │   │   ├── font-chunk-8.avif
│       │   │   └── font-chunk-9.avif
│       │   └── manifest.json
│       ├── scripts/
│       │   └── generate-font-sprites.ts
│       ├── src/
│       │   ├── app/
│       │   │   ├── api/
│       │   │   │   ├── auth/
│       │   │   │   │   └── [...all]/
│       │   │   │   │       └── route.ts
│       │   │   │   ├── health/
│       │   │   │   │   └── route.ts
│       │   │   │   └── sounds/
│       │   │   │       └── search/
│       │   │   │           └── route.ts
│       │   │   ├── base-page.tsx
│       │   │   ├── blog/
│       │   │   │   ├── [slug]/
│       │   │   │   │   └── page.tsx
│       │   │   │   └── page.tsx
│       │   │   ├── brand/
│       │   │   │   └── page.tsx
│       │   │   ├── changelog/
│       │   │   │   ├── [version]/
│       │   │   │   │   └── page.tsx
│       │   │   │   ├── components/
│       │   │   │   │   ├── copy-markdown-button.tsx
│       │   │   │   │   └── release.tsx
│       │   │   │   ├── page.tsx
│       │   │   │   └── utils.ts
│       │   │   ├── contributors/
│       │   │   │   └── page.tsx
│       │   │   ├── editor/
│       │   │   │   └── [project_id]/
│       │   │   │       └── page.tsx
│       │   │   ├── globals.css
│       │   │   ├── layout.tsx
│       │   │   ├── metadata.ts
│       │   │   ├── page.tsx
│       │   │   ├── privacy/
│       │   │   │   └── page.tsx
│       │   │   ├── projects/
│       │   │   │   ├── page.tsx
│       │   │   │   └── store.ts
│       │   │   ├── roadmap/
│       │   │   │   └── page.tsx
│       │   │   ├── robots.ts
│       │   │   ├── rss.xml/
│       │   │   │   └── route.ts
│       │   │   ├── sitemap.ts
│       │   │   ├── sponsors/
│       │   │   │   └── page.tsx
│       │   │   └── terms/
│       │   │       └── page.tsx
│       │   ├── components/
│       │   │   ├── editable-timecode.tsx
│       │   │   ├── editor/
│       │   │   │   ├── dialogs/
│       │   │   │   │   ├── delete-project-dialog.tsx
│       │   │   │   │   ├── migration-dialog.tsx
│       │   │   │   │   ├── project-info-dialog.tsx
│       │   │   │   │   ├── rename-project-dialog.tsx
│       │   │   │   │   └── shortcuts-dialog.tsx
│       │   │   │   ├── editor-header.tsx
│       │   │   │   ├── export-button.tsx
│       │   │   │   ├── mobile-gate.tsx
│       │   │   │   ├── onboarding.tsx
│       │   │   │   ├── panels/
│       │   │   │   │   ├── assets/
│       │   │   │   │   │   ├── drag-overlay.tsx
│       │   │   │   │   │   ├── draggable-item.tsx
│       │   │   │   │   │   ├── index.tsx
│       │   │   │   │   │   ├── tabbar.tsx
│       │   │   │   │   │   └── views/
│       │   │   │   │   │       ├── assets.tsx
│       │   │   │   │   │       ├── base-view.tsx
│       │   │   │   │   │       ├── captions.tsx
│       │   │   │   │   │       ├── effects.tsx
│       │   │   │   │   │       ├── settings-legacy.tsx
│       │   │   │   │   │       ├── settings.tsx
│       │   │   │   │   │       ├── sounds.tsx
│       │   │   │   │   │       ├── stickers.tsx
│       │   │   │   │   │       └── text.tsx
│       │   │   │   │   ├── preview/
│       │   │   │   │   │   ├── bookmark-note-overlay.tsx
│       │   │   │   │   │   ├── context-menu.tsx
│       │   │   │   │   │   ├── index.tsx
│       │   │   │   │   │   ├── layout-guide-overlay.tsx
│       │   │   │   │   │   ├── preview-interaction-overlay.tsx
│       │   │   │   │   │   ├── snap-guides.tsx
│       │   │   │   │   │   ├── text-edit-overlay.tsx
│       │   │   │   │   │   ├── toolbar.tsx
│       │   │   │   │   │   └── transform-handles.tsx
│       │   │   │   │   ├── properties/
│       │   │   │   │   │   ├── audio-properties.tsx
│       │   │   │   │   │   ├── clip-effects-properties.tsx
│       │   │   │   │   │   ├── effect-param-field.tsx
│       │   │   │   │   │   ├── effect-properties.tsx
│       │   │   │   │   │   ├── empty-view.tsx
│       │   │   │   │   │   ├── hooks/
│       │   │   │   │   │   │   ├── use-element-playhead.ts
│       │   │   │   │   │   │   ├── use-keyframed-color-property.ts
│       │   │   │   │   │   │   ├── use-keyframed-number-property.ts
│       │   │   │   │   │   │   └── use-property-draft.ts
│       │   │   │   │   │   ├── index.tsx
│       │   │   │   │   │   ├── keyframe-toggle.tsx
│       │   │   │   │   │   ├── section.tsx
│       │   │   │   │   │   ├── sections/
│       │   │   │   │   │   │   ├── blending.tsx
│       │   │   │   │   │   │   ├── index.tsx
│       │   │   │   │   │   │   └── transform.tsx
│       │   │   │   │   │   ├── text-properties.tsx
│       │   │   │   │   │   └── video-properties.tsx
│       │   │   │   │   └── timeline/
│       │   │   │   │       ├── audio-waveform.tsx
│       │   │   │   │       ├── bookmarks.tsx
│       │   │   │   │       ├── drag-line.tsx
│       │   │   │   │       ├── index.tsx
│       │   │   │   │       ├── snap-indicator.tsx
│       │   │   │   │       ├── timeline-element.tsx
│       │   │   │   │       ├── timeline-playhead.tsx
│       │   │   │   │       ├── timeline-ruler.tsx
│       │   │   │   │       ├── timeline-tick.tsx
│       │   │   │   │       ├── timeline-toolbar.tsx
│       │   │   │   │       └── timeline-track.tsx
│       │   │   │   ├── scenes-view.tsx
│       │   │   │   └── selection-box.tsx
│       │   │   ├── footer.tsx
│       │   │   ├── gitHub-contribute-section.tsx
│       │   │   ├── header.tsx
│       │   │   ├── landing/
│       │   │   │   ├── handlebars.tsx
│       │   │   │   └── hero.tsx
│       │   │   ├── providers/
│       │   │   │   └── editor-provider.tsx
│       │   │   ├── storage-provider.tsx
│       │   │   ├── theme-toggle.tsx
│       │   │   └── ui/
│       │   │       ├── accordion.tsx
│       │   │       ├── alert-dialog.tsx
│       │   │       ├── alert.tsx
│       │   │       ├── aspect-ratio.tsx
│       │   │       ├── avatar.tsx
│       │   │       ├── badge.tsx
│       │   │       ├── breadcrumb.tsx
│       │   │       ├── button.tsx
│       │   │       ├── calendar.tsx
│       │   │       ├── card.tsx
│       │   │       ├── checkbox.tsx
│       │   │       ├── collapsible.tsx
│       │   │       ├── color-picker.tsx
│       │   │       ├── context-menu.tsx
│       │   │       ├── dialog.tsx
│       │   │       ├── dropdown-menu.tsx
│       │   │       ├── font-picker.tsx
│       │   │       ├── form.tsx
│       │   │       ├── hover-card.tsx
│       │   │       ├── input-with-back.tsx
│       │   │       ├── input.tsx
│       │   │       ├── label.tsx
│       │   │       ├── menubar.tsx
│       │   │       ├── navigation-menu.tsx
│       │   │       ├── number-field.tsx
│       │   │       ├── popover.tsx
│       │   │       ├── progress.tsx
│       │   │       ├── prose.tsx
│       │   │       ├── radio-group.tsx
│       │   │       ├── react-markdown-wrapper.tsx
│       │   │       ├── resizable.tsx
│       │   │       ├── scroll-area.tsx
│       │   │       ├── select.tsx
│       │   │       ├── separator.tsx
│       │   │       ├── sheet.tsx
│       │   │       ├── skeleton.tsx
│       │   │       ├── slider.tsx
│       │   │       ├── sonner.tsx
│       │   │       ├── spinner.tsx
│       │   │       ├── split-button.tsx
│       │   │       ├── switch.tsx
│       │   │       ├── table.tsx
│       │   │       ├── tabs.tsx
│       │   │       ├── textarea.tsx
│       │   │       ├── toast.tsx
│       │   │       ├── toggle-group.tsx
│       │   │       ├── toggle.tsx
│       │   │       └── tooltip.tsx
│       │   ├── constants/
│       │   │   ├── animation-constants.ts
│       │   │   ├── editor-constants.ts
│       │   │   ├── export-constants.ts
│       │   │   ├── font-constants.ts
│       │   │   ├── language-constants.ts
│       │   │   ├── project-constants.ts
│       │   │   ├── site-constants.ts
│       │   │   ├── sticker-constants.ts
│       │   │   ├── text-constants.ts
│       │   │   ├── timeline-constants.tsx
│       │   │   └── transcription-constants.ts
│       │   ├── core/
│       │   │   ├── index.ts
│       │   │   └── managers/
│       │   │       ├── audio-manager.ts
│       │   │       ├── commands.ts
│       │   │       ├── media-manager.ts
│       │   │       ├── playback-manager.ts
│       │   │       ├── project-manager.ts
│       │   │       ├── renderer-manager.ts
│       │   │       ├── save-manager.ts
│       │   │       ├── scenes-manager.ts
│       │   │       ├── selection-manager.ts
│       │   │       └── timeline-manager.ts
│       │   ├── data/
│       │   │   └── colors/
│       │   │       ├── pattern-craft.ts
│       │   │       ├── solid.ts
│       │   │       └── syntax-ui.tsx
│       │   ├── hooks/
│       │   │   ├── actions/
│       │   │   │   ├── use-action-handler.ts
│       │   │   │   └── use-editor-actions.ts
│       │   │   ├── storage/
│       │   │   │   └── use-local-storage.ts
│       │   │   ├── timeline/
│       │   │   │   ├── element/
│       │   │   │   │   ├── use-element-interaction.ts
│       │   │   │   │   ├── use-element-resize.ts
│       │   │   │   │   ├── use-element-selection.ts
│       │   │   │   │   ├── use-keyframe-drag.ts
│       │   │   │   │   └── use-keyframe-selection.ts
│       │   │   │   ├── use-bookmark-drag.ts
│       │   │   │   ├── use-edge-auto-scroll.ts
│       │   │   │   ├── use-scroll-position.ts
│       │   │   │   ├── use-scroll-sync.ts
│       │   │   │   ├── use-selection-box.ts
│       │   │   │   ├── use-snap-indicator-position.ts
│       │   │   │   ├── use-timeline-drag-drop.ts
│       │   │   │   ├── use-timeline-playhead.ts
│       │   │   │   ├── use-timeline-seek.ts
│       │   │   │   └── use-timeline-zoom.ts
│       │   │   ├── use-container-size.ts
│       │   │   ├── use-editor.ts
│       │   │   ├── use-effect-preview.ts
│       │   │   ├── use-file-upload.ts
│       │   │   ├── use-focus-lock.ts
│       │   │   ├── use-fullscreen.ts
│       │   │   ├── use-infinite-scroll.ts
│       │   │   ├── use-keybindings.ts
│       │   │   ├── use-keyboard-shortcuts-help.ts
│       │   │   ├── use-mobile.ts
│       │   │   ├── use-paste-media.ts
│       │   │   ├── use-preview-interaction.ts
│       │   │   ├── use-raf-loop.ts
│       │   │   ├── use-reveal-item.ts
│       │   │   ├── use-shift-key.ts
│       │   │   ├── use-sound-search.ts
│       │   │   └── use-transform-handles.ts
│       │   ├── lib/
│       │   │   ├── actions/
│       │   │   │   ├── definitions.ts
│       │   │   │   ├── index.ts
│       │   │   │   ├── registry.ts
│       │   │   │   └── types.ts
│       │   │   ├── animation/
│       │   │   │   ├── __tests__/
│       │   │   │   │   └── transform-keyframes.test.ts
│       │   │   │   ├── color-channel.ts
│       │   │   │   ├── effect-param-channel.ts
│       │   │   │   ├── index.ts
│       │   │   │   ├── interpolation.ts
│       │   │   │   ├── keyframe-query.ts
│       │   │   │   ├── keyframes.ts
│       │   │   │   ├── number-channel.ts
│       │   │   │   ├── property-registry.ts
│       │   │   │   └── resolve.ts
│       │   │   ├── auth/
│       │   │   │   ├── client.ts
│       │   │   │   └── server.ts
│       │   │   ├── blog/
│       │   │   │   └── query.ts
│       │   │   ├── commands/
│       │   │   │   ├── __tests__/
│       │   │   │   │   └── keyframe-aware-commands.test.ts
│       │   │   │   ├── base-command.ts
│       │   │   │   ├── batch-command.ts
│       │   │   │   ├── index.ts
│       │   │   │   ├── media/
│       │   │   │   │   ├── add-media-asset.ts
│       │   │   │   │   ├── index.ts
│       │   │   │   │   └── remove-media-asset.ts
│       │   │   │   ├── preview-tracker.ts
│       │   │   │   ├── project/
│       │   │   │   │   ├── index.ts
│       │   │   │   │   └── update-project-settings.ts
│       │   │   │   ├── scene/
│       │   │   │   │   ├── create-scene.ts
│       │   │   │   │   ├── delete-scene.ts
│       │   │   │   │   ├── index.ts
│       │   │   │   │   ├── move-bookmark.ts
│       │   │   │   │   ├── remove-bookmark.ts
│       │   │   │   │   ├── rename-scene.ts
│       │   │   │   │   ├── toggle-bookmark.ts
│       │   │   │   │   └── update-bookmark.ts
│       │   │   │   └── timeline/
│       │   │   │       ├── clipboard/
│       │   │   │       │   ├── index.ts
│       │   │   │       │   └── paste.ts
│       │   │   │       ├── element/
│       │   │   │       │   ├── delete-elements.ts
│       │   │   │       │   ├── duplicate-elements.ts
│       │   │   │       │   ├── effects/
│       │   │   │       │   │   ├── add-effect.ts
│       │   │   │       │   │   ├── index.ts
│       │   │   │       │   │   ├── remove-effect.ts
│       │   │   │       │   │   ├── reorder-effect.ts
│       │   │   │       │   │   ├── toggle-effect.ts
│       │   │   │       │   │   └── update-effect-params.ts
│       │   │   │       │   ├── index.ts
│       │   │   │       │   ├── insert-element.ts
│       │   │   │       │   ├── keyframes/
│       │   │   │       │   │   ├── index.ts
│       │   │   │       │   │   ├── remove-effect-param-keyframe.ts
│       │   │   │       │   │   ├── remove-keyframe.ts
│       │   │   │       │   │   ├── retime-keyframe.ts
│       │   │   │       │   │   ├── upsert-effect-param-keyframe.ts
│       │   │   │       │   │   └── upsert-keyframe.ts
│       │   │   │       │   ├── move-elements.ts
│       │   │   │       │   ├── split-elements.ts
│       │   │   │       │   ├── toggle-elements-muted.ts
│       │   │   │       │   ├── toggle-elements-visibility.ts
│       │   │   │       │   ├── update-element-duration.ts
│       │   │   │       │   ├── update-element-start-time.ts
│       │   │   │       │   ├── update-element-trim.ts
│       │   │   │       │   └── update-element.ts
│       │   │   │       ├── index.ts
│       │   │   │       ├── track/
│       │   │   │       │   ├── add-track.ts
│       │   │   │       │   ├── index.ts
│       │   │   │       │   ├── remove-track.ts
│       │   │   │       │   ├── toggle-track-mute.ts
│       │   │   │       │   └── toggle-track-visibility.ts
│       │   │   │       └── tracks-snapshot.ts
│       │   │   ├── db/
│       │   │   │   ├── index.ts
│       │   │   │   └── schema.ts
│       │   │   ├── drag-data.ts
│       │   │   ├── effects/
│       │   │   │   ├── definitions/
│       │   │   │   │   ├── blur.frag.glsl
│       │   │   │   │   ├── blur.ts
│       │   │   │   │   └── index.ts
│       │   │   │   ├── effect.vert.glsl
│       │   │   │   ├── index.ts
│       │   │   │   └── registry.ts
│       │   │   ├── export.ts
│       │   │   ├── fonts/
│       │   │   │   └── google-fonts.ts
│       │   │   ├── gradients/
│       │   │   │   ├── canvas.ts
│       │   │   │   ├── index.ts
│       │   │   │   └── parser.ts
│       │   │   ├── iconify-api.ts
│       │   │   ├── media/
│       │   │   │   ├── audio.ts
│       │   │   │   ├── media-utils.ts
│       │   │   │   ├── mediabunny.ts
│       │   │   │   └── processing.ts
│       │   │   ├── preview/
│       │   │   │   ├── element-bounds.ts
│       │   │   │   ├── hit-test.ts
│       │   │   │   ├── preview-coords.ts
│       │   │   │   └── preview-snap.ts
│       │   │   ├── rate-limit.ts
│       │   │   ├── scenes.ts
│       │   │   ├── stickers/
│       │   │   │   ├── __tests__/
│       │   │   │   │   └── sticker-id.test.ts
│       │   │   │   ├── index.ts
│       │   │   │   ├── providers/
│       │   │   │   │   ├── emoji.ts
│       │   │   │   │   ├── flags.ts
│       │   │   │   │   ├── icons.ts
│       │   │   │   │   ├── index.ts
│       │   │   │   │   └── shapes.ts
│       │   │   │   ├── registry.ts
│       │   │   │   ├── resolver.ts
│       │   │   │   ├── sticker-id.ts
│       │   │   │   └── types.ts
│       │   │   ├── text/
│       │   │   │   └── layout.ts
│       │   │   ├── time.ts
│       │   │   ├── timeline/
│       │   │   │   ├── bookmarks.ts
│       │   │   │   ├── drag-utils.ts
│       │   │   │   ├── drop-utils.ts
│       │   │   │   ├── element-utils.ts
│       │   │   │   ├── index.ts
│       │   │   │   ├── pixel-utils.ts
│       │   │   │   ├── ripple-utils.ts
│       │   │   │   ├── ruler-utils.ts
│       │   │   │   ├── snap-utils.ts
│       │   │   │   ├── track-element-update.ts
│       │   │   │   ├── track-utils.ts
│       │   │   │   └── zoom-utils.ts
│       │   │   └── transcription/
│       │   │       └── caption.ts
│       │   ├── proxy.ts
│       │   ├── services/
│       │   │   ├── renderer/
│       │   │   │   ├── canvas-renderer.ts
│       │   │   │   ├── canvas-utils.ts
│       │   │   │   ├── effect-preview.ts
│       │   │   │   ├── nodes/
│       │   │   │   │   ├── base-node.ts
│       │   │   │   │   ├── color-node.ts
│       │   │   │   │   ├── composite-effect-node.ts
│       │   │   │   │   ├── effect-layer-node.ts
│       │   │   │   │   ├── image-node.ts
│       │   │   │   │   ├── root-node.ts
│       │   │   │   │   ├── sticker-node.ts
│       │   │   │   │   ├── text-node.ts
│       │   │   │   │   ├── video-node.ts
│       │   │   │   │   └── visual-node.ts
│       │   │   │   ├── scene-builder.ts
│       │   │   │   ├── scene-exporter.ts
│       │   │   │   ├── webgl-effect-renderer.ts
│       │   │   │   └── webgl-utils.ts
│       │   │   ├── storage/
│       │   │   │   ├── indexeddb-adapter.ts
│       │   │   │   ├── migrations/
│       │   │   │   │   ├── __tests__/
│       │   │   │   │   │   ├── fixtures/
│       │   │   │   │   │   │   ├── index.ts
│       │   │   │   │   │   │   ├── v0.ts
│       │   │   │   │   │   │   ├── v1.ts
│       │   │   │   │   │   │   ├── v2.ts
│       │   │   │   │   │   │   ├── v3.ts
│       │   │   │   │   │   │   └── v5.ts
│       │   │   │   │   │   ├── v0-to-v1.test.ts
│       │   │   │   │   │   ├── v1-to-v2.test.ts
│       │   │   │   │   │   ├── v2-to-v3.test.ts
│       │   │   │   │   │   ├── v3-to-v4.test.ts
│       │   │   │   │   │   ├── v4-to-v5.test.ts
│       │   │   │   │   │   ├── v5-to-v6.test.ts
│       │   │   │   │   │   └── v8-to-v9.test.ts
│       │   │   │   │   ├── base.ts
│       │   │   │   │   ├── index.ts
│       │   │   │   │   ├── runner.ts
│       │   │   │   │   ├── transformers/
│       │   │   │   │   │   ├── index.ts
│       │   │   │   │   │   ├── types.ts
│       │   │   │   │   │   ├── utils.ts
│       │   │   │   │   │   ├── v0-to-v1.ts
│       │   │   │   │   │   ├── v1-to-v2.ts
│       │   │   │   │   │   ├── v2-to-v3.ts
│       │   │   │   │   │   ├── v3-to-v4.ts
│       │   │   │   │   │   ├── v4-to-v5.ts
│       │   │   │   │   │   ├── v5-to-v6.ts
│       │   │   │   │   │   ├── v6-to-v7.ts
│       │   │   │   │   │   ├── v7-to-v8.ts
│       │   │   │   │   │   └── v8-to-v9.ts
│       │   │   │   │   ├── v0-to-v1.ts
│       │   │   │   │   ├── v1-to-v2.ts
│       │   │   │   │   ├── v2-to-v3.ts
│       │   │   │   │   ├── v3-to-v4.ts
│       │   │   │   │   ├── v4-to-v5.ts
│       │   │   │   │   ├── v5-to-v6.ts
│       │   │   │   │   ├── v6-to-v7.ts
│       │   │   │   │   ├── v7-to-v8.ts
│       │   │   │   │   └── v8-to-v9.ts
│       │   │   │   ├── opfs-adapter.ts
│       │   │   │   ├── service.ts
│       │   │   │   └── types.ts
│       │   │   ├── transcription/
│       │   │   │   ├── service.ts
│       │   │   │   └── worker.ts
│       │   │   └── video-cache/
│       │   │       └── service.ts
│       │   ├── stores/
│       │   │   ├── assets-panel-store.tsx
│       │   │   ├── editor-store.ts
│       │   │   ├── keybindings/
│       │   │   │   └── migrations/
│       │   │   │       ├── index.ts
│       │   │   │       ├── v2-to-v3.ts
│       │   │   │       ├── v3-to-v4.ts
│       │   │   │       └── v4-to-v5.ts
│       │   │   ├── keybindings-store.ts
│       │   │   ├── panel-store.ts
│       │   │   ├── preview-store.ts
│       │   │   ├── properties-store.ts
│       │   │   ├── sounds-store.ts
│       │   │   ├── stickers-store.ts
│       │   │   └── timeline-store.ts
│       │   ├── types/
│       │   │   ├── animation.ts
│       │   │   ├── assets.ts
│       │   │   ├── blog.ts
│       │   │   ├── drag.ts
│       │   │   ├── editor.ts
│       │   │   ├── effects.ts
│       │   │   ├── export.ts
│       │   │   ├── eyedropper.d.ts
│       │   │   ├── fonts.ts
│       │   │   ├── glsl.d.ts
│       │   │   ├── keybinding.ts
│       │   │   ├── language.ts
│       │   │   ├── project.ts
│       │   │   ├── rendering.ts
│       │   │   ├── sounds.ts
│       │   │   ├── stickers.ts
│       │   │   ├── time.ts
│       │   │   ├── timeline.ts
│       │   │   └── transcription.ts
│       │   └── utils/
│       │       ├── browser.ts
│       │       ├── color.ts
│       │       ├── date.ts
│       │       ├── geometry.ts
│       │       ├── id.ts
│       │       ├── math.ts
│       │       ├── platform.ts
│       │       ├── string.ts
│       │       └── ui.ts
│       ├── tsconfig.json
│       └── tsconfig.tsbuildinfo
├── biome.json
├── docker-compose.yml
├── docs/
│   ├── actions.md
│   ├── countries-search.md
│   ├── effects-renderer.md
│   └── keyframes.md
├── package.json
├── packages/
│   ├── env/
│   │   ├── package.json
│   │   └── src/
│   │       ├── tools.ts
│   │       ├── types/
│   │       │   └── node-env.d.ts
│   │       └── web.ts
│   └── ui/
│       ├── package.json
│       ├── src/
│       │   └── icons/
│       │       ├── brand.tsx
│       │       ├── index.tsx
│       │       └── ui.tsx
│       └── tsconfig.json
├── tsconfig.json
└── turbo.json
Download .txt
SYMBOL INDEX (1793 symbols across 360 files)

FILE: apps/web/migrations/0000_brainy_saracen.sql
  type "accounts" (line 1) | CREATE TABLE "accounts" (
  type "sessions" (line 18) | CREATE TABLE "sessions" (
  type "users" (line 31) | CREATE TABLE "users" (
  type "verifications" (line 43) | CREATE TABLE "verifications" (
  type "waitlist" (line 53) | CREATE TABLE "waitlist" (

FILE: apps/web/scripts/generate-font-sprites.ts
  constant FONT_SIZE (line 20) | const FONT_SIZE = 24;
  constant ROW_HEIGHT (line 21) | const ROW_HEIGHT = 40;
  constant CANVAS_WIDTH (line 22) | const CANVAS_WIDTH = 1200;
  constant MAX_CHUNK_HEIGHT (line 23) | const MAX_CHUNK_HEIGHT = 800;
  constant PADDING_X (line 24) | const PADDING_X = 14;
  constant WIDTH_BUFFER (line 25) | const WIDTH_BUFFER = 8;
  constant CONCURRENT_DOWNLOADS (line 26) | const CONCURRENT_DOWNLOADS = 30;
  constant OUTPUT_DIR (line 28) | const OUTPUT_DIR = join(__dirname, "..", "public", "fonts");
  constant CACHE_DIR (line 29) | const CACHE_DIR = join(__dirname, "..", ".font-cache");
  constant FONTSOURCE_API (line 30) | const FONTSOURCE_API = "https://api.fontsource.org/v1/fonts";
  type FontsourceFont (line 32) | interface FontsourceFont {
  type MeasuredFont (line 42) | interface MeasuredFont {
  type PackedFont (line 48) | interface PackedFont {
  type AtlasEntry (line 55) | interface AtlasEntry {
  function fetchFontList (line 63) | async function fetchFontList(): Promise<FontsourceFont[]> {
  function downloadFont (line 82) | async function downloadFont({ id }: { id: string }): Promise<Buffer | nu...
  function downloadAllFonts (line 102) | async function downloadAllFonts({
  function measureFonts (line 134) | function measureFonts({
  function packIntoChunks (line 175) | function packIntoChunks({ measured }: { measured: MeasuredFont[] }): {
  function renderChunks (line 221) | async function renderChunks({
  function main (line 253) | async function main() {

FILE: apps/web/src/app/api/health/route.ts
  function GET (line 1) | async function GET() {

FILE: apps/web/src/app/api/sounds/search/route.ts
  function buildSortParameter (line 91) | function buildSortParameter({ query, sort }: { query?: string; sort: str...
  function applyEffectsFilters (line 96) | function applyEffectsFilters({
  function transformFreesoundResult (line 121) | function transformFreesoundResult(
  function GET (line 150) | async function GET(request: NextRequest) {

FILE: apps/web/src/app/base-page.tsx
  type BasePageProps (line 5) | interface BasePageProps {
  function BasePage (line 15) | function BasePage({

FILE: apps/web/src/app/blog/[slug]/page.tsx
  type PageProps (line 10) | type PageProps = {
  function generateMetadata (line 15) | async function generateMetadata({
  function generateStaticParams (line 58) | async function generateStaticParams() {
  function BlogPostPage (line 67) | async function BlogPostPage({ params }: PageProps) {
  function PostHeader (line 83) | function PostHeader({ post }: { post: Post }) {
  function PostCoverImage (line 99) | function PostCoverImage({ post }: { post: Post }) {
  function PostMeta (line 113) | function PostMeta({ date, publishedAt }: { date: string; publishedAt: Da...
  function PostTitle (line 121) | function PostTitle({ title }: { title: string }) {
  function PostContent (line 129) | function PostContent({ html }: { html: string }) {

FILE: apps/web/src/app/blog/page.tsx
  function BlogPage (line 20) | async function BlogPage() {
  function BlogPostItem (line 41) | function BlogPostItem({ post }: { post: Post }) {

FILE: apps/web/src/app/brand/page.tsx
  function downloadAsset (line 14) | function downloadAsset(src: string) {
  function copyAsset (line 22) | async function copyAsset(src: string) {
  type AssetTheme (line 30) | type AssetTheme = "dark" | "light" | "icon";
  type AssetVariant (line 32) | interface AssetVariant {
  type AssetSection (line 40) | interface AssetSection {
  constant ASSET_SECTIONS (line 47) | const ASSET_SECTIONS: AssetSection[] = [
  function BrandPage (line 108) | function BrandPage() {
  constant CHECKER_STYLES (line 213) | const CHECKER_STYLES: Record<"dark" | "light", CSSProperties> = {
  function AssetCard (line 230) | function AssetCard({ variant }: { variant: AssetVariant }) {

FILE: apps/web/src/app/changelog/[version]/page.tsx
  type Props (line 17) | type Props = { params: Promise<{ version: string }> };
  function generateStaticParams (line 19) | async function generateStaticParams() {
  function generateMetadata (line 23) | async function generateMetadata({ params }: Props): Promise<Metadata> {
  function ReleaseDetailPage (line 33) | async function ReleaseDetailPage({ params }: Props) {

FILE: apps/web/src/app/changelog/components/copy-markdown-button.tsx
  function buildMarkdown (line 10) | function buildMarkdown({
  function CopyMarkdownButton (line 36) | function CopyMarkdownButton({

FILE: apps/web/src/app/changelog/components/release.tsx
  function ReleaseArticle (line 7) | function ReleaseArticle({
  function ReleaseMeta (line 37) | function ReleaseMeta({ release }: { release: Release }) {
  function ReleaseTitle (line 50) | function ReleaseTitle({
  function ReleaseDescription (line 72) | function ReleaseDescription({ children }: { children: ReactNode }) {
  function ReleaseChanges (line 80) | function ReleaseChanges({ release }: { release: Release }) {

FILE: apps/web/src/app/changelog/page.tsx
  function ChangelogPage (line 37) | function ChangelogPage() {
  function ReleaseEntry (line 65) | function ReleaseEntry({ release }: { release: ReleaseType }) {

FILE: apps/web/src/app/changelog/utils.ts
  type Change (line 3) | type Change = { type: string; text: string };
  type Release (line 4) | type Release = (typeof allChangelogs)[number];
  function getSectionTitle (line 15) | function getSectionTitle(type: string): string {
  function groupAndOrderChanges (line 21) | function groupAndOrderChanges({ changes }: { changes: Change[] }) {
  function getSortedReleases (line 39) | function getSortedReleases() {

FILE: apps/web/src/app/contributors/page.tsx
  type Contributor (line 21) | interface Contributor {
  function getContributors (line 30) | async function getContributors(): Promise<Contributor[]> {
  function ContributorsPage (line 61) | async function ContributorsPage() {
  function StatItem (line 97) | function StatItem({ value, label }: { value: number; label: string }) {
  function TopContributorsSection (line 107) | function TopContributorsSection({
  function TopContributorCard (line 130) | function TopContributorCard({ contributor }: { contributor: Contributor ...
  function AllContributorsSection (line 162) | function AllContributorsSection({
  function ExternalToolsSection (line 209) | function ExternalToolsSection() {

FILE: apps/web/src/app/editor/[project_id]/page.tsx
  function Editor (line 21) | function Editor() {
  function EditorLayout (line 41) | function EditorLayout() {

FILE: apps/web/src/app/layout.tsx
  function RootLayout (line 22) | function RootLayout({

FILE: apps/web/src/app/page.tsx
  function Home (line 13) | async function Home() {

FILE: apps/web/src/app/privacy/page.tsx
  function PrivacyPage (line 24) | function PrivacyPage() {

FILE: apps/web/src/app/projects/page.tsx
  constant VIEW_MODE_OPTIONS (line 81) | const VIEW_MODE_OPTIONS = [
  function ProjectsPage (line 86) | function ProjectsPage() {
  function ProjectsHeader (line 137) | function ProjectsHeader() {
  constant SORT_LABELS (line 192) | const SORT_LABELS: Record<TProjectSortKey, string> = {
  function ProjectsToolbar (line 199) | function ProjectsToolbar({ projectIds }: { projectIds: string[] }) {
  function SearchBar (line 302) | function SearchBar({
  constant PROJECT_ACTIONS (line 343) | const PROJECT_ACTIONS = [
  function deleteProjects (line 358) | async function deleteProjects({
  function duplicateProjects (line 368) | async function duplicateProjects({
  function renameProject (line 378) | async function renameProject({
  function ProjectActions (line 390) | function ProjectActions() {
  function SortDropdown (line 468) | function SortDropdown({ children }: { children: React.ReactNode }) {
  function NewProjectButton (line 504) | function NewProjectButton() {
  function ProjectItem (line 527) | function ProjectItem({
  function ProjectContextMenuContent (line 760) | function ProjectContextMenuContent({
  function ProjectMenu (line 803) | function ProjectMenu({
  function ProjectsSkeleton (line 916) | function ProjectsSkeleton() {
  function EmptyState (line 947) | function EmptyState() {

FILE: apps/web/src/app/projects/store.ts
  type ProjectsViewMode (line 5) | type ProjectsViewMode = "grid" | "list";
  type ProjectsState (line 7) | interface ProjectsState {

FILE: apps/web/src/app/roadmap/page.tsx
  constant LAST_UPDATED (line 8) | const LAST_UPDATED = "February 25, 2026";
  type StatusType (line 10) | type StatusType = "complete" | "pending" | "default" | "info";
  type Status (line 12) | interface Status {
  type RoadmapItem (line 17) | interface RoadmapItem {
  function RoadmapPage (line 89) | function RoadmapPage() {
  function RoadmapItem (line 112) | function RoadmapItem({ item, index }: { item: RoadmapItem; index: number...
  function StatusBadge (line 127) | function StatusBadge({

FILE: apps/web/src/app/robots.ts
  function robots (line 4) | function robots(): MetadataRoute.Robots {

FILE: apps/web/src/app/rss.xml/route.ts
  function GET (line 5) | async function GET() {

FILE: apps/web/src/app/sitemap.ts
  function sitemap (line 5) | async function sitemap(): Promise<MetadataRoute.Sitemap> {

FILE: apps/web/src/app/sponsors/page.tsx
  function SponsorsPage (line 23) | function SponsorsPage() {
  function SponsorsGrid (line 40) | function SponsorsGrid() {
  function SponsorCard (line 50) | function SponsorCard({ sponsor }: { sponsor: Sponsor }) {

FILE: apps/web/src/app/terms/page.tsx
  function TermsPage (line 24) | function TermsPage() {

FILE: apps/web/src/components/editable-timecode.tsx
  type EditableTimecodeProps (line 8) | interface EditableTimecodeProps {
  function EditableTimecode (line 18) | function EditableTimecode({

FILE: apps/web/src/components/editor/dialogs/delete-project-dialog.tsx
  function DeleteProjectDialog (line 14) | function DeleteProjectDialog({

FILE: apps/web/src/components/editor/dialogs/migration-dialog.tsx
  function MigrationDialog (line 13) | function MigrationDialog() {

FILE: apps/web/src/components/editor/dialogs/project-info-dialog.tsx
  function InfoRow (line 14) | function InfoRow({
  function ProjectInfoDialog (line 29) | function ProjectInfoDialog({

FILE: apps/web/src/components/editor/dialogs/rename-project-dialog.tsx
  function RenameProjectDialog (line 14) | function RenameProjectDialog({

FILE: apps/web/src/components/editor/dialogs/shortcuts-dialog.tsx
  function ShortcutsDialog (line 20) | function ShortcutsDialog({
  function ShortcutItem (line 148) | function ShortcutItem({
  function EditableShortcutKey (line 202) | function EditableShortcutKey({

FILE: apps/web/src/components/editor/editor-header.tsx
  function EditorHeader (line 28) | function EditorHeader() {
  function ProjectDropdown (line 43) | function ProjectDropdown() {
  function EditableProjectName (line 170) | function EditableProjectName() {

FILE: apps/web/src/components/editor/export-button.tsx
  function isExportFormat (line 34) | function isExportFormat(value: string): value is ExportFormat {
  function isExportQuality (line 38) | function isExportQuality(value: string): value is ExportQuality {
  function ExportButton (line 42) | function ExportButton() {
  function ExportPopover (line 88) | function ExportPopover({
  function ExportError (line 281) | function ExportError({

FILE: apps/web/src/components/editor/mobile-gate.tsx
  constant STORAGE_KEY (line 10) | const STORAGE_KEY = "mobile-acknowledged";
  type MobileGateProps (line 12) | interface MobileGateProps {
  function MobileGate (line 16) | function MobileGate({ children }: MobileGateProps) {

FILE: apps/web/src/components/editor/onboarding.tsx
  function Onboarding (line 11) | function Onboarding() {
  function Title (line 94) | function Title({ title }: { title: string }) {
  function Description (line 98) | function Description({ description }: { description: string }) {
  function NextButton (line 122) | function NextButton({

FILE: apps/web/src/components/editor/panels/assets/drag-overlay.tsx
  type MediaDragOverlayProps (line 4) | interface MediaDragOverlayProps {
  function MediaDragOverlay (line 11) | function MediaDragOverlay({

FILE: apps/web/src/components/editor/panels/assets/draggable-item.tsx
  type DraggableItemProps (line 18) | interface DraggableItemProps {
  function DraggableItem (line 35) | function DraggableItem({
  function PlusButton (line 207) | function PlusButton({

FILE: apps/web/src/components/editor/panels/assets/index.tsx
  function AssetsPanel (line 14) | function AssetsPanel() {

FILE: apps/web/src/components/editor/panels/assets/tabbar.tsx
  function TabBar (line 17) | function TabBar() {
  function FadeOverlay (line 93) | function FadeOverlay({

FILE: apps/web/src/components/editor/panels/assets/views/assets.tsx
  function MediaView (line 53) | function MediaView() {
  function MediaAssetDraggable (line 217) | function MediaAssetDraggable({
  function MediaItemWithContextMenu (line 278) | function MediaItemWithContextMenu({
  function MediaItemList (line 303) | function MediaItemList({
  function formatDuration (line 347) | function formatDuration({ duration }: { duration: number }) {
  function MediaDurationBadge (line 353) | function MediaDurationBadge({ duration }: { duration?: number }) {
  function MediaDurationLabel (line 363) | function MediaDurationLabel({ duration }: { duration?: number }) {
  function MediaTypePlaceholder (line 371) | function MediaTypePlaceholder({
  function MediaPreview (line 398) | function MediaPreview({
  function MediaActions (line 469) | function MediaActions({
  function SortMenuItem (line 582) | function SortMenuItem({

FILE: apps/web/src/components/editor/panels/assets/views/base-view.tsx
  type PanelViewProps (line 3) | interface PanelViewProps extends React.HTMLAttributes<HTMLDivElement> {
  function PanelView (line 14) | function PanelView({

FILE: apps/web/src/components/editor/panels/assets/views/captions.tsx
  function Captions (line 25) | function Captions() {

FILE: apps/web/src/components/editor/panels/assets/views/effects.tsx
  function EffectsView (line 15) | function EffectsView() {
  function EffectsGrid (line 25) | function EffectsGrid({ effects }: { effects: EffectDefinition[] }) {
  function EffectPreviewCanvas (line 38) | function EffectPreviewCanvas({ effectType }: { effectType: string }) {
  function EffectItem (line 59) | function EffectItem({ effect }: { effect: EffectDefinition }) {

FILE: apps/web/src/components/editor/panels/assets/views/settings-legacy.tsx
  function SettingsView (line 35) | function SettingsView() {
  function ProjectInfoView (line 46) | function ProjectInfoView() {
  function BackgroundView (line 252) | function BackgroundView() {

FILE: apps/web/src/components/editor/panels/assets/views/settings.tsx
  constant ORIGINAL_PRESET_VALUE (line 28) | const ORIGINAL_PRESET_VALUE = "original";
  function findPresetIndexByAspectRatio (line 30) | function findPresetIndexByAspectRatio({
  function SettingsView (line 50) | function SettingsView() {
  function ProjectInfoContent (line 80) | function ProjectInfoContent() {

FILE: apps/web/src/components/editor/panels/assets/views/sounds.tsx
  function SoundsView (line 38) | function SoundsView() {
  function SoundEffectsView (line 73) | function SoundEffectsView() {
  function SavedSoundsView (line 313) | function SavedSoundsView() {
  function SongsView (line 488) | function SongsView() {
  type AudioItemProps (line 492) | interface AudioItemProps {
  function AudioItem (line 498) | function AudioItem({ sound, isPlaying, onPlay }: AudioItemProps) {

FILE: apps/web/src/components/editor/panels/assets/views/stickers.tsx
  function StickersView (line 42) | function StickersView() {
  function StickerGrid (line 83) | function StickerGrid({
  function EmptyView (line 110) | function EmptyView({ message }: { message: string }) {
  function StickersContentView (line 125) | function StickersContentView() {
  type StickerItemProps (line 212) | interface StickerItemProps {
  function StickerItem (line 217) | function StickerItem({ item, shouldCapSize = false }: StickerItemProps) {
  function getStickerNameFromId (line 303) | function getStickerNameFromId({ stickerId }: { stickerId: string }): str...
  function toRecentStickerItem (line 314) | function toRecentStickerItem({

FILE: apps/web/src/components/editor/panels/assets/views/text.tsx
  function TextView (line 7) | function TextView() {

FILE: apps/web/src/components/editor/panels/preview/bookmark-note-overlay.tsx
  function BookmarkNoteOverlay (line 7) | function BookmarkNoteOverlay() {

FILE: apps/web/src/components/editor/panels/preview/context-menu.tsx
  function PreviewContextMenu (line 11) | function PreviewContextMenu({

FILE: apps/web/src/components/editor/panels/preview/index.tsx
  function usePreviewSize (line 20) | function usePreviewSize() {
  function PreviewPanel (line 30) | function PreviewPanel() {
  function RenderTreeController (line 54) | function RenderTreeController() {
  function PreviewCanvas (line 81) | function PreviewCanvas({

FILE: apps/web/src/components/editor/panels/preview/layout-guide-overlay.tsx
  function TikTokGuide (line 6) | function TikTokGuide() {
  function LayoutGuideOverlay (line 20) | function LayoutGuideOverlay() {

FILE: apps/web/src/components/editor/panels/preview/preview-interaction-overlay.tsx
  function PreviewInteractionOverlay (line 6) | function PreviewInteractionOverlay({

FILE: apps/web/src/components/editor/panels/preview/snap-guides.tsx
  function SnapGuides (line 7) | function SnapGuides({

FILE: apps/web/src/components/editor/panels/preview/text-edit-overlay.tsx
  constant TEXT_BACKGROUND_PADDING (line 15) | const TEXT_BACKGROUND_PADDING = "4px 8px";
  constant TEXT_EDIT_VERTICAL_OFFSET_EM (line 16) | const TEXT_EDIT_VERTICAL_OFFSET_EM = 0.06;
  function TextEditOverlay (line 18) | function TextEditOverlay({

FILE: apps/web/src/components/editor/panels/preview/toolbar.tsx
  function PreviewToolbar (line 17) | function PreviewToolbar({

FILE: apps/web/src/components/editor/panels/preview/transform-handles.tsx
  constant HANDLE_SIZE (line 13) | const HANDLE_SIZE = 10;
  constant ROTATION_HANDLE_OFFSET (line 14) | const ROTATION_HANDLE_OFFSET = 24;
  constant ROTATION_HANDLE_RADIUS (line 15) | const ROTATION_HANDLE_RADIUS = 10;
  constant CORNER_HIT_AREA_SIZE (line 16) | const CORNER_HIT_AREA_SIZE = 18;
  type Corner (line 18) | type Corner = "top-left" | "top-right" | "bottom-left" | "bottom-right";
  constant CORNERS (line 19) | const CORNERS: Corner[] = [
  function getCornerPosition (line 26) | function getCornerPosition({
  function getRotationHandlePosition (line 50) | function getRotationHandlePosition({ bounds }: { bounds: ElementBounds }...
  function getOverlayContext (line 64) | function getOverlayContext({
  function TransformHandles (line 80) | function TransformHandles({
  function BoundingBoxOutline (line 178) | function BoundingBoxOutline({
  function CornerHandle (line 207) | function CornerHandle({
  function RotationHandle (line 251) | function RotationHandle({

FILE: apps/web/src/components/editor/panels/properties/audio-properties.tsx
  function AudioProperties (line 1) | function AudioProperties() {

FILE: apps/web/src/components/editor/panels/properties/clip-effects-properties.tsx
  function ClipEffectsProperties (line 27) | function ClipEffectsProperties({
  function ClipEffectSection (line 120) | function ClipEffectSection({

FILE: apps/web/src/components/editor/panels/properties/effect-param-field.tsx
  function EffectParamField (line 18) | function EffectParamField({
  function EffectParamInput (line 36) | function EffectParamInput({
  function NumberParamField (line 108) | function NumberParamField({

FILE: apps/web/src/components/editor/panels/properties/effect-properties.tsx
  function EffectProperties (line 15) | function EffectProperties({

FILE: apps/web/src/components/editor/panels/properties/empty-view.tsx
  function EmptyView (line 4) | function EmptyView() {

FILE: apps/web/src/components/editor/panels/properties/hooks/use-element-playhead.ts
  function useElementPlayhead (line 5) | function useElementPlayhead({

FILE: apps/web/src/components/editor/panels/properties/hooks/use-keyframed-color-property.ts
  function useKeyframedColorProperty (line 10) | function useKeyframedColorProperty({

FILE: apps/web/src/components/editor/panels/properties/hooks/use-keyframed-number-property.ts
  function useKeyframedNumberProperty (line 11) | function useKeyframedNumberProperty({

FILE: apps/web/src/components/editor/panels/properties/hooks/use-property-draft.ts
  function looksLikeExpression (line 4) | function looksLikeExpression({ input }: { input: string }): boolean {
  function usePropertyDraft (line 12) | function usePropertyDraft<T>({

FILE: apps/web/src/components/editor/panels/properties/index.tsx
  function ElementProperties (line 16) | function ElementProperties({
  function PropertiesPanel (line 42) | function PropertiesPanel() {

FILE: apps/web/src/components/editor/panels/properties/keyframe-toggle.tsx
  function KeyframeToggle (line 6) | function KeyframeToggle({

FILE: apps/web/src/components/editor/panels/properties/section.tsx
  type SectionContext (line 11) | interface SectionContext {
  function useSectionContext (line 19) | function useSectionContext() {
  type SectionProps (line 23) | interface SectionProps {
  function Section (line 33) | function Section({
  type SectionHeaderProps (line 76) | interface SectionHeaderProps {
  function SectionHeader (line 85) | function SectionHeader({
  function SectionTitle (line 160) | function SectionTitle({
  function SectionFields (line 201) | function SectionFields({
  function SectionField (line 213) | function SectionField({
  function SectionContent (line 235) | function SectionContent({

FILE: apps/web/src/components/editor/panels/properties/sections/blending.tsx
  type BlendingElement (line 30) | type BlendingElement = {
  constant BLEND_MODE_GROUPS (line 40) | const BLEND_MODE_GROUPS = [
  function BlendingSection (line 70) | function BlendingSection({

FILE: apps/web/src/components/editor/panels/properties/sections/transform.tsx
  function parseNumericInput (line 28) | function parseNumericInput({ input }: { input: string }): number | null {
  function isPropertyAtDefault (line 33) | function isPropertyAtDefault({
  function TransformSection (line 56) | function TransformSection({

FILE: apps/web/src/components/editor/panels/properties/text-properties.tsx
  function TextProperties (line 47) | function TextProperties({
  function ContentSection (line 66) | function ContentSection({
  function TypographySection (line 106) | function TypographySection({
  function SpacingSection (line 225) | function SpacingSection({
  function BackgroundSection (line 333) | function BackgroundSection({

FILE: apps/web/src/components/editor/panels/properties/video-properties.tsx
  function VideoProperties (line 8) | function VideoProperties({

FILE: apps/web/src/components/editor/panels/timeline/audio-waveform.tsx
  type AudioWaveformProps (line 4) | interface AudioWaveformProps {
  function extractPeaks (line 11) | function extractPeaks({
  function AudioWaveform (line 42) | function AudioWaveform({

FILE: apps/web/src/components/editor/panels/timeline/bookmarks.tsx
  constant MIN_BOOKMARK_WIDTH_PX (line 30) | const MIN_BOOKMARK_WIDTH_PX = 2;
  constant BOOKMARK_MARKER_WIDTH_PX (line 31) | const BOOKMARK_MARKER_WIDTH_PX = 12;
  constant BOOKMARK_MARKER_HEIGHT_PX (line 32) | const BOOKMARK_MARKER_HEIGHT_PX = 15;
  constant BOOKMARK_HALF_WIDTH_PX (line 33) | const BOOKMARK_HALF_WIDTH_PX = BOOKMARK_MARKER_WIDTH_PX / 2;
  constant BOOKMARK_MARKER_CLIP_PATH (line 34) | const BOOKMARK_MARKER_CLIP_PATH =
  function seekToBookmarkTime (line 37) | function seekToBookmarkTime({
  type TimelineBookmarksRowProps (line 51) | interface TimelineBookmarksRowProps {
  function TimelineBookmarksRow (line 65) | function TimelineBookmarksRow({
  function TimelineBookmark (line 112) | function TimelineBookmark({
  function BookmarkPopoverContent (line 279) | function BookmarkPopoverContent({

FILE: apps/web/src/components/editor/panels/timeline/drag-line.tsx
  type DragLineProps (line 4) | interface DragLineProps {
  function DragLine (line 11) | function DragLine({

FILE: apps/web/src/components/editor/panels/timeline/index.tsx
  constant TRACKS_CONTAINER_MAX_HEIGHT (line 58) | const TRACKS_CONTAINER_MAX_HEIGHT = 800;
  constant FALLBACK_CONTAINER_WIDTH (line 59) | const FALLBACK_CONTAINER_WIDTH = 1000;
  function Timeline (line 61) | function Timeline() {
  function TrackIcon (line 546) | function TrackIcon({ track }: { track: TimelineTrack }) {
  function TrackToggleIcon (line 550) | function TrackToggleIcon({

FILE: apps/web/src/components/editor/panels/timeline/snap-indicator.tsx
  type SnapIndicatorProps (line 11) | interface SnapIndicatorProps {
  function SnapIndicator (line 21) | function SnapIndicator({

FILE: apps/web/src/components/editor/panels/timeline/timeline-element.tsx
  constant KEYFRAME_INDICATOR_MIN_WIDTH_PX (line 64) | const KEYFRAME_INDICATOR_MIN_WIDTH_PX = 40;
  constant ELEMENT_RING_WIDTH_PX (line 65) | const ELEMENT_RING_WIDTH_PX = 1.5;
  type KeyframeIndicator (line 67) | interface KeyframeIndicator {
  function buildKeyframeIndicator (line 73) | function buildKeyframeIndicator({
  function getKeyframeIndicators (line 109) | function getKeyframeIndicators({
  function getDisplayShortcut (line 156) | function getDisplayShortcut({ action }: { action: TAction }) {
  type TimelineElementProps (line 167) | interface TimelineElementProps {
  function TimelineElement (line 186) | function TimelineElement({
  function ElementInner (line 370) | function ElementInner({
  function ResizeHandle (line 470) | function ResizeHandle({
  function KeyframeIndicators (line 498) | function KeyframeIndicators({
  type ElementContentProps (line 581) | interface ElementContentProps {
  type ElementContentRendererProps (line 587) | interface ElementContentRendererProps extends ElementContentProps {
  type ElementContentRenderer (line 591) | type ElementContentRenderer = (props: ElementContentRendererProps) => Re...
  function renderTiledMedia (line 593) | function renderTiledMedia({
  function EffectsButton (line 627) | function EffectsButton({
  constant ELEMENT_CONTENT_RENDERERS (line 662) | const ELEMENT_CONTENT_RENDERERS: Record<
  function ElementContent (line 775) | function ElementContent({ element, track, isSelected }: ElementContentPr...
  function CopyMenuItem (line 790) | function CopyMenuItem() {
  function MuteMenuItem (line 801) | function MuteMenuItem({
  function VisibilityMenuItem (line 828) | function VisibilityMenuItem({
  function DeleteMenuItem (line 860) | function DeleteMenuItem({
  function ActionMenuItem (line 884) | function ActionMenuItem({

FILE: apps/web/src/components/editor/panels/timeline/timeline-playhead.tsx
  type TimelinePlayheadProps (line 12) | interface TimelinePlayheadProps {
  function TimelinePlayhead (line 22) | function TimelinePlayhead({

FILE: apps/web/src/components/editor/panels/timeline/timeline-ruler.tsx
  type TimelineRulerProps (line 9) | interface TimelineRulerProps {
  function TimelineRuler (line 20) | function TimelineRuler({

FILE: apps/web/src/components/editor/panels/timeline/timeline-tick.tsx
  type TimelineTickProps (line 6) | interface TimelineTickProps {
  function TimelineTick (line 13) | function TimelineTick({

FILE: apps/web/src/components/editor/panels/timeline/timeline-toolbar.tsx
  function TimelineToolbar (line 40) | function TimelineToolbar({
  function ToolbarLeftSection (line 78) | function ToolbarLeftSection() {
  function SceneSelector (line 164) | function SceneSelector() {
  function ToolbarRightSection (line 183) | function ToolbarRightSection({
  function ToolbarButton (line 249) | function ToolbarButton({

FILE: apps/web/src/components/editor/panels/timeline/timeline-track.tsx
  type TimelineTrackContentProps (line 13) | interface TimelineTrackContentProps {
  function TimelineTrackContent (line 38) | function TimelineTrackContent({

FILE: apps/web/src/components/editor/scenes-view.tsx
  function ScenesView (line 28) | function ScenesView({ children }: { children: React.ReactNode }) {
  function DeleteDialog (line 175) | function DeleteDialog({

FILE: apps/web/src/components/editor/selection-box.tsx
  type SelectionBoxProps (line 5) | interface SelectionBoxProps {
  function SelectionBox (line 12) | function SelectionBox({

FILE: apps/web/src/components/footer.tsx
  type Category (line 8) | type Category = "resources" | "company";
  type FooterLink (line 10) | interface FooterLink {
  type CategoryLinks (line 15) | type CategoryLinks = Record<Category, FooterLink[]>;
  function Footer (line 33) | function Footer() {

FILE: apps/web/src/components/gitHub-contribute-section.tsx
  function GitHubContributeSection (line 7) | function GitHubContributeSection({

FILE: apps/web/src/components/header.tsx
  function Header (line 27) | function Header() {

FILE: apps/web/src/components/landing/handlebars.tsx
  type HandlebarsProps (line 5) | type HandlebarsProps = PropsWithChildren;
  function Handlebars (line 7) | function Handlebars({ children }: HandlebarsProps) {

FILE: apps/web/src/components/landing/hero.tsx
  function Hero (line 9) | function Hero() {

FILE: apps/web/src/components/providers/editor-provider.tsx
  type EditorProviderProps (line 14) | interface EditorProviderProps {
  function EditorProvider (line 19) | function EditorProvider({ projectId, children }: EditorProviderProps) {
  function EditorRuntimeBindings (line 121) | function EditorRuntimeBindings() {

FILE: apps/web/src/components/storage-provider.tsx
  type StorageContextType (line 8) | interface StorageContextType {
  function useStorage (line 17) | function useStorage() {
  type StorageProviderProps (line 25) | interface StorageProviderProps {
  function StorageProvider (line 29) | function StorageProvider({ children }: StorageProviderProps) {

FILE: apps/web/src/components/theme-toggle.tsx
  type ThemeToggleProps (line 9) | interface ThemeToggleProps {
  function ThemeToggle (line 15) | function ThemeToggle({

FILE: apps/web/src/components/ui/badge.tsx
  type BadgeProps (line 26) | interface BadgeProps
  function Badge (line 30) | function Badge({ className, variant, ...props }: BadgeProps) {

FILE: apps/web/src/components/ui/breadcrumb.tsx
  function Breadcrumb (line 6) | function Breadcrumb({ ...props }: React.ComponentProps<"nav">) {
  function BreadcrumbList (line 10) | function BreadcrumbList({ className, ...props }: React.ComponentProps<"o...
  function BreadcrumbItem (line 23) | function BreadcrumbItem({ className, ...props }: React.ComponentProps<"l...
  function BreadcrumbLink (line 33) | function BreadcrumbLink({
  function BreadcrumbPage (line 51) | function BreadcrumbPage({ className, ...props }: React.ComponentProps<"s...
  function BreadcrumbSeparator (line 62) | function BreadcrumbSeparator({
  function BreadcrumbEllipsis (line 80) | function BreadcrumbEllipsis({

FILE: apps/web/src/components/ui/button.tsx
  type ButtonProps (line 39) | interface ButtonProps

FILE: apps/web/src/components/ui/calendar.tsx
  type CalendarProps (line 10) | type CalendarProps = React.ComponentProps<typeof DayPicker>;
  function Calendar (line 12) | function Calendar({

FILE: apps/web/src/components/ui/color-picker.tsx
  type ColorPickerProps (line 31) | interface ColorPickerProps {

FILE: apps/web/src/components/ui/font-picker.tsx
  constant FONT_TABS (line 31) | const FONT_TABS = [
  type FontTab (line 37) | type FontTab = (typeof FONT_TABS)[number]["key"];
  constant ROW_HEIGHT (line 39) | const ROW_HEIGHT = 40;
  constant SPRITE_ROW_HEIGHT (line 40) | const SPRITE_ROW_HEIGHT = 40;
  constant PREVIEW_SCALE (line 41) | const PREVIEW_SCALE = 0.8;
  constant LIST_WIDTH (line 42) | const LIST_WIDTH = 288;
  constant MAX_LIST_HEIGHT (line 43) | const MAX_LIST_HEIGHT = 288;
  constant OVERSCAN (line 44) | const OVERSCAN = 15;
  type FontPickerProps (line 46) | interface FontPickerProps {
  function FontPicker (line 52) | function FontPicker({
  function FontSpritePreview (line 247) | function FontSpritePreview({ entry }: { entry: FontAtlasEntry }) {
  type FontRowProps (line 268) | type FontRowProps = {
  function FontRow (line 275) | function FontRow({

FILE: apps/web/src/components/ui/form.tsx
  type FormFieldContextValue (line 20) | type FormFieldContextValue<
  type FormItemContextValue (line 67) | type FormItemContextValue = {

FILE: apps/web/src/components/ui/input-with-back.tsx
  type InputWithBackProps (line 9) | interface InputWithBackProps {
  function InputWithBack (line 18) | function InputWithBack({

FILE: apps/web/src/components/ui/input.tsx
  type InputProps (line 33) | interface InputProps

FILE: apps/web/src/components/ui/number-field.tsx
  constant DRAG_SENSITIVITIES (line 10) | const DRAG_SENSITIVITIES = {
  type DragSensitivity (line 15) | type DragSensitivity = "default" | "slow";
  type NumberFieldProps (line 17) | interface NumberFieldProps
  function NumberField (line 28) | function NumberField({

FILE: apps/web/src/components/ui/prose.tsx
  type ProseProps (line 7) | type ProseProps = React.HTMLAttributes<HTMLElement> & {
  type HastTextNode (line 12) | type HastTextNode = {
  type HastElementNode (line 17) | type HastElementNode = {
  type HastRootNode (line 24) | type HastRootNode = {
  type HastNode (line 29) | type HastNode = HastTextNode | HastElementNode | HastRootNode;
  function toReactProps (line 31) | function toReactProps({
  function renderHastNode (line 69) | function renderHastNode({
  function renderHtmlNodes (line 91) | function renderHtmlNodes({ html }: { html: string }): React.ReactNode {
  function Prose (line 98) | function Prose({ children, html, className }: ProseProps) {

FILE: apps/web/src/components/ui/react-markdown-wrapper.tsx
  function ReactMarkdownWrapper (line 4) | function ReactMarkdownWrapper({ children }: { children: string }) {

FILE: apps/web/src/components/ui/sheet.tsx
  type SheetContentProps (line 52) | interface SheetContentProps

FILE: apps/web/src/components/ui/skeleton.tsx
  function Skeleton (line 3) | function Skeleton({

FILE: apps/web/src/components/ui/sonner.tsx
  type ToasterProps (line 6) | type ToasterProps = React.ComponentProps<typeof Sonner>;

FILE: apps/web/src/components/ui/spinner.tsx
  function Spinner (line 5) | function Spinner({ className, ...props }: Omit<HugeiconsIconProps, "icon...

FILE: apps/web/src/components/ui/split-button.tsx
  type SplitButtonProps (line 6) | interface SplitButtonProps {
  type SplitButtonSideProps (line 11) | interface SplitButtonSideProps extends Omit<ButtonProps, "variant" | "si...

FILE: apps/web/src/components/ui/toast.tsx
  type ToastProps (line 115) | type ToastProps = React.ComponentPropsWithoutRef<typeof Toast>;
  type ToastActionElement (line 117) | type ToastActionElement = React.ReactElement<typeof ToastAction>;

FILE: apps/web/src/components/ui/tooltip.tsx
  type TooltipContentProps (line 41) | interface TooltipContentProps

FILE: apps/web/src/constants/animation-constants.ts
  constant TIME_EPSILON_SECONDS (line 1) | const TIME_EPSILON_SECONDS = 1 / 1000;
  constant MIN_TRANSFORM_SCALE (line 2) | const MIN_TRANSFORM_SCALE = 0.01;

FILE: apps/web/src/constants/editor-constants.ts
  constant PANEL_CONFIG (line 1) | const PANEL_CONFIG = {

FILE: apps/web/src/constants/export-constants.ts
  constant DEFAULT_EXPORT_OPTIONS (line 3) | const DEFAULT_EXPORT_OPTIONS = {
  constant EXPORT_MIME_TYPES (line 9) | const EXPORT_MIME_TYPES = {

FILE: apps/web/src/constants/font-constants.ts
  constant DEFAULT_FONT (line 1) | const DEFAULT_FONT = "Arial";
  constant SYSTEM_FONTS (line 3) | const SYSTEM_FONTS = new Set([

FILE: apps/web/src/constants/language-constants.ts
  constant LANGUAGES (line 1) | const LANGUAGES = [

FILE: apps/web/src/constants/project-constants.ts
  constant DEFAULT_CANVAS_PRESETS (line 3) | const DEFAULT_CANVAS_PRESETS: TCanvasSize[] = [
  constant FPS_PRESETS (line 10) | const FPS_PRESETS = [
  constant BLUR_INTENSITY_PRESETS (line 18) | const BLUR_INTENSITY_PRESETS: { label: string; value: number }[] = [
  constant DEFAULT_CANVAS_SIZE (line 24) | const DEFAULT_CANVAS_SIZE: TCanvasSize = { width: 1920, height: 1080 };
  constant DEFAULT_FPS (line 25) | const DEFAULT_FPS = 30;
  constant DEFAULT_BLUR_INTENSITY (line 26) | const DEFAULT_BLUR_INTENSITY = 8;
  constant DEFAULT_COLOR (line 27) | const DEFAULT_COLOR = "#000000";

FILE: apps/web/src/constants/site-constants.ts
  constant SITE_URL (line 3) | const SITE_URL = "https://opencut.app";
  constant SITE_INFO (line 5) | const SITE_INFO = {
  type ExternalTool (line 15) | type ExternalTool = {
  constant EXTERNAL_TOOLS (line 22) | const EXTERNAL_TOOLS: ExternalTool[] = [
  constant DEFAULT_LOGO_URL (line 38) | const DEFAULT_LOGO_URL = "/logos/opencut/svg/logo.svg";
  constant SOCIAL_LINKS (line 40) | const SOCIAL_LINKS = {
  type Sponsor (line 46) | type Sponsor = {
  constant SPONSORS (line 54) | const SPONSORS: Sponsor[] = [

FILE: apps/web/src/constants/sticker-constants.ts
  constant STICKER_CATEGORIES (line 1) | const STICKER_CATEGORIES = {

FILE: apps/web/src/constants/text-constants.ts
  constant MIN_FONT_SIZE (line 8) | const MIN_FONT_SIZE = 5;
  constant MAX_FONT_SIZE (line 9) | const MAX_FONT_SIZE = 300;
  constant FONT_SIZE_SCALE_REFERENCE (line 15) | const FONT_SIZE_SCALE_REFERENCE = 90;
  constant DEFAULT_LETTER_SPACING (line 17) | const DEFAULT_LETTER_SPACING = 0;
  constant DEFAULT_LINE_HEIGHT (line 18) | const DEFAULT_LINE_HEIGHT = 1.2;
  constant CORNER_RADIUS_MIN (line 20) | const CORNER_RADIUS_MIN = 0;
  constant CORNER_RADIUS_MAX (line 21) | const CORNER_RADIUS_MAX = 100;
  constant DEFAULT_TEXT_BACKGROUND (line 23) | const DEFAULT_TEXT_BACKGROUND = {
  constant DEFAULT_TEXT_ELEMENT (line 33) | const DEFAULT_TEXT_ELEMENT: Omit<TextElement, "id"> = {

FILE: apps/web/src/constants/timeline-constants.tsx
  constant DEFAULT_TRANSFORM (line 13) | const DEFAULT_TRANSFORM: Transform = {
  constant DEFAULT_OPACITY (line 19) | const DEFAULT_OPACITY = 1;
  constant DEFAULT_BLEND_MODE (line 20) | const DEFAULT_BLEND_MODE: BlendMode = "normal";
  constant DEFAULT_BOOKMARK_COLOR (line 21) | const DEFAULT_BOOKMARK_COLOR = "#009dff";
  constant TRACK_CONFIG (line 23) | const TRACK_CONFIG: Record<
  constant TRACK_GAP (line 84) | const TRACK_GAP = 4;
  constant DRAG_THRESHOLD_PX (line 86) | const DRAG_THRESHOLD_PX = 5;
  constant TIMELINE_CONSTANTS (line 88) | const TIMELINE_CONSTANTS = {
  constant DEFAULT_TIMELINE_VIEW_STATE (line 98) | const DEFAULT_TIMELINE_VIEW_STATE: TTimelineViewState = {

FILE: apps/web/src/constants/transcription-constants.ts
  constant SUPPORTED_TRANSCRIPTION_LANGS (line 8) | const SUPPORTED_TRANSCRIPTION_LANGS: ReadonlyArray<LanguageCode> = [
  constant TRANSCRIPTION_LANGUAGES (line 20) | const TRANSCRIPTION_LANGUAGES = LANGUAGES.filter((language) =>
  constant TRANSCRIPTION_MODELS (line 24) | const TRANSCRIPTION_MODELS: TranscriptionModel[] = [
  constant DEFAULT_TRANSCRIPTION_MODEL (line 51) | const DEFAULT_TRANSCRIPTION_MODEL: TranscriptionModelId =
  constant DEFAULT_CHUNK_LENGTH_SECONDS (line 54) | const DEFAULT_CHUNK_LENGTH_SECONDS = 30;
  constant DEFAULT_STRIDE_SECONDS (line 55) | const DEFAULT_STRIDE_SECONDS = 5;
  constant DEFAULT_WORDS_PER_CAPTION (line 57) | const DEFAULT_WORDS_PER_CAPTION = 3;
  constant MIN_CAPTION_DURATION_SECONDS (line 58) | const MIN_CAPTION_DURATION_SECONDS = 0.8;

FILE: apps/web/src/core/index.ts
  class EditorCore (line 13) | class EditorCore {
    method constructor (line 27) | private constructor() {
    method getInstance (line 42) | static getInstance(): EditorCore {
    method reset (line 49) | static reset(): void {

FILE: apps/web/src/core/managers/audio-manager.ts
  class AudioManager (line 12) | class AudioManager {
    method constructor (line 35) | constructor(private editor: EditorCore) {
    method dispose (line 48) | dispose(): void {
    method ensureAudioContext (line 111) | private ensureAudioContext(): AudioContext | null {
    method updateGain (line 122) | private updateGain(): void {
    method getPlaybackTime (line 127) | private getPlaybackTime(): number {
    method startPlayback (line 134) | private async startPlayback({ time }: { time: number }): Promise<void> {
    method scheduleUpcomingClips (line 167) | private scheduleUpcomingClips(): void {
    method stopPlayback (line 190) | private stopPlayback(): void {
    method runClipIterator (line 211) | private async runClipIterator({
    method waitUntilCaughtUp (line 314) | private waitUntilCaughtUp({
    method disposeSinks (line 338) | private disposeSinks(): void {
    method getAudioSink (line 352) | private async getAudioSink({

FILE: apps/web/src/core/managers/commands.ts
  class CommandManager (line 3) | class CommandManager {
    method execute (line 7) | execute({ command }: { command: Command }): Command {
    method push (line 14) | push({ command }: { command: Command }): void {
    method undo (line 19) | undo(): void {
    method redo (line 28) | redo(): void {
    method canUndo (line 37) | canUndo(): boolean {
    method canRedo (line 41) | canRedo(): boolean {
    method clear (line 45) | clear(): void {

FILE: apps/web/src/core/managers/media-manager.ts
  class MediaManager (line 8) | class MediaManager {
    method constructor (line 13) | constructor(private editor: EditorCore) {}
    method addMediaAsset (line 15) | async addMediaAsset({
    method removeMediaAsset (line 39) | async removeMediaAsset({
    method loadProjectMedia (line 82) | async loadProjectMedia({ projectId }: { projectId: string }): Promise<...
    method clearProjectMedia (line 100) | async clearProjectMedia({ projectId }: { projectId: string }): Promise...
    method clearAllAssets (line 125) | clearAllAssets(): void {
    method getAssets (line 141) | getAssets(): MediaAsset[] {
    method setAssets (line 145) | setAssets({ assets }: { assets: MediaAsset[] }): void {
    method isLoadingMedia (line 150) | isLoadingMedia(): boolean {
    method subscribe (line 154) | subscribe(listener: () => void): () => void {
    method notify (line 159) | private notify(): void {

FILE: apps/web/src/core/managers/playback-manager.ts
  class PlaybackManager (line 3) | class PlaybackManager {
    method constructor (line 14) | constructor(private editor: EditorCore) {}
    method play (line 16) | play(): void {
    method pause (line 30) | pause(): void {
    method toggle (line 36) | toggle(): void {
    method seek (line 44) | seek({ time }: { time: number }): void {
    method setVolume (line 56) | setVolume({ volume }: { volume: number }): void {
    method mute (line 66) | mute(): void {
    method unmute (line 75) | unmute(): void {
    method toggleMute (line 81) | toggleMute(): void {
    method getIsPlaying (line 89) | getIsPlaying(): boolean {
    method getCurrentTime (line 93) | getCurrentTime(): number {
    method getVolume (line 97) | getVolume(): number {
    method isMuted (line 101) | isMuted(): boolean {
    method setScrubbing (line 105) | setScrubbing({ isScrubbing }: { isScrubbing: boolean }): void {
    method getIsScrubbing (line 110) | getIsScrubbing(): boolean {
    method subscribe (line 114) | subscribe(listener: () => void): () => void {
    method notify (line 119) | private notify(): void {
    method startTimer (line 123) | private startTimer(): void {
    method stopTimer (line 132) | private stopTimer(): void {

FILE: apps/web/src/core/managers/project-manager.ts
  type MigrationState (line 33) | interface MigrationState {
  class ProjectManager (line 40) | class ProjectManager {
    method constructor (line 61) | constructor(private editor: EditorCore) {}
    method ensureStorageMigrations (line 63) | private async ensureStorageMigrations(): Promise<void> {
    method createNewProject (line 82) | async createNewProject({ name }: { name: string }): Promise<string> {
    method loadProject (line 126) | async loadProject({ id }: { id: string }): Promise<void> {
    method saveCurrentProject (line 176) | async saveCurrentProject(): Promise<void> {
    method export (line 199) | async export({ options }: { options: ExportOptions }): Promise<ExportR...
    method cancelExport (line 223) | cancelExport(): void {
    method clearExportState (line 227) | clearExportState(): void {
    method getExportState (line 232) | getExportState(): ExportState {
    method loadAllProjects (line 236) | async loadAllProjects(): Promise<void> {
    method deleteProjects (line 256) | async deleteProjects({ ids }: { ids: string[] }): Promise<void> {
    method closeProject (line 290) | closeProject(): void {
    method renameProject (line 298) | async renameProject({
    method duplicateProjects (line 340) | async duplicateProjects({ ids }: { ids: string[] }): Promise<string[]> {
    method updateSettings (line 466) | async updateSettings({
    method updateThumbnail (line 484) | async updateThumbnail({ thumbnail }: { thumbnail: string }): Promise<v...
    method prepareExit (line 497) | async prepareExit(): Promise<void> {
    method getFilteredAndSortedProjects (line 510) | getFilteredAndSortedProjects({
    method isInvalidProjectId (line 543) | isInvalidProjectId({ id }: { id: string }): boolean {
    method markProjectIdAsInvalid (line 547) | markProjectIdAsInvalid({ id }: { id: string }): void {
    method clearInvalidProjectIds (line 552) | clearInvalidProjectIds(): void {
    method getActive (line 557) | getActive(): TProject {
    method getActiveOrNull (line 569) | getActiveOrNull(): TProject | null {
    method getTimelineViewState (line 573) | getTimelineViewState(): TTimelineViewState {
    method setTimelineViewState (line 577) | setTimelineViewState({ viewState }: { viewState: TTimelineViewState })...
    method getSavedProjects (line 586) | getSavedProjects(): TProjectMetadata[] {
    method getIsLoading (line 590) | getIsLoading(): boolean {
    method getIsInitialized (line 594) | getIsInitialized(): boolean {
    method getMigrationState (line 598) | getMigrationState(): MigrationState {
    method setActiveProject (line 602) | setActiveProject({ project }: { project: TProject }): void {
    method subscribe (line 607) | subscribe(listener: () => void): () => void {
    method updateThumbnailFromTimeline (line 612) | private async updateThumbnailFromTimeline(): Promise<boolean> {
    method updateMetadata (line 653) | private updateMetadata(project: TProject): void {
    method notify (line 667) | private notify(): void {

FILE: apps/web/src/core/managers/renderer-manager.ts
  class RendererManager (line 11) | class RendererManager {
    method constructor (line 15) | constructor(private editor: EditorCore) {}
    method setRenderTree (line 17) | setRenderTree({ renderTree }: { renderTree: RootNode | null }): void {
    method getRenderTree (line 22) | getRenderTree(): RootNode | null {
    method saveSnapshot (line 26) | async saveSnapshot(): Promise<{ success: boolean; error?: string }> {
    method exportProject (line 89) | async exportProject({
    method subscribe (line 190) | subscribe(listener: () => void): () => void {
    method notify (line 195) | private notify(): void {

FILE: apps/web/src/core/managers/save-manager.ts
  type SaveManagerOptions (line 3) | type SaveManagerOptions = {
  class SaveManager (line 7) | class SaveManager {
    method constructor (line 15) | constructor(
    method start (line 22) | start(): void {
    method stop (line 35) | stop(): void {
    method pause (line 43) | pause(): void {
    method resume (line 47) | resume(): void {
    method markDirty (line 54) | markDirty({ force = false }: { force?: boolean } = {}): void {
    method flush (line 60) | async flush(): Promise<void> {
    method getIsDirty (line 65) | getIsDirty(): boolean {
    method queueSave (line 69) | private queueSave(): void {
    method saveNow (line 79) | private async saveNow(): Promise<void> {
    method clearTimer (line 102) | private clearTimer(): void {

FILE: apps/web/src/core/managers/scenes-manager.ts
  class ScenesManager (line 26) | class ScenesManager {
    method constructor (line 31) | constructor(private editor: EditorCore) {}
    method createScene (line 33) | async createScene({
    method deleteScene (line 49) | async deleteScene({ sceneId }: { sceneId: string }): Promise<void> {
    method renameScene (line 69) | async renameScene({
    method switchToScene (line 84) | async switchToScene({ sceneId }: { sceneId: string }): Promise<void> {
    method toggleBookmark (line 110) | async toggleBookmark({ time }: { time: number }): Promise<void> {
    method isBookmarked (line 115) | isBookmarked({ time }: { time: number }): boolean {
    method removeBookmark (line 129) | async removeBookmark({ time }: { time: number }): Promise<void> {
    method updateBookmark (line 134) | async updateBookmark({
    method moveBookmark (line 145) | async moveBookmark({
    method getBookmarkAtTime (line 156) | getBookmarkAtTime({ time }: { time: number }) {
    method loadProjectScenes (line 173) | async loadProjectScenes({ projectId }: { projectId: string }): Promise...
    method initializeScenes (line 214) | initializeScenes({
    method clearScenes (line 254) | clearScenes(): void {
    method getActiveScene (line 260) | getActiveScene(): TScene {
    method getScenes (line 267) | getScenes(): TScene[] {
    method setScenes (line 271) | setScenes({
    method subscribe (line 299) | subscribe(listener: () => void): () => void {
    method notify (line 304) | private notify(): void {
    method updateSceneTracks (line 308) | updateSceneTracks({ tracks }: { tracks: TimelineTrack[] }): void {
    method ensureScenesHaveMainTrack (line 337) | private ensureScenesHaveMainTrack({ scenes }: { scenes: TScene[] }): {

FILE: apps/web/src/core/managers/selection-manager.ts
  type ElementRef (line 4) | type ElementRef = { trackId: string; elementId: string };
  class SelectionManager (line 6) | class SelectionManager {
    method constructor (line 12) | constructor(editor: EditorCore) {
    method getSelectedElements (line 16) | getSelectedElements(): ElementRef[] {
    method getSelectedKeyframes (line 20) | getSelectedKeyframes(): SelectedKeyframeRef[] {
    method getKeyframeSelectionAnchor (line 24) | getKeyframeSelectionAnchor(): SelectedKeyframeRef | null {
    method setSelectedElements (line 28) | setSelectedElements({ elements }: { elements: ElementRef[] }): void {
    method setSelectedKeyframes (line 35) | setSelectedKeyframes({
    method clearSelection (line 51) | clearSelection(): void {
    method clearKeyframeSelection (line 58) | clearKeyframeSelection(): void {
    method subscribe (line 64) | subscribe(listener: () => void): () => void {
    method notify (line 69) | private notify(): void {

FILE: apps/web/src/core/managers/timeline-manager.ts
  class TimelineManager (line 47) | class TimelineManager {
    method constructor (line 51) | constructor(private editor: EditorCore) {}
    method addTrack (line 53) | addTrack({ type, index }: { type: TrackType; index?: number }): string {
    method removeTrack (line 59) | removeTrack({ trackId }: { trackId: string }): void {
    method insertElement (line 64) | insertElement({ element, placement }: InsertElementParams): void {
    method updateElementTrim (line 69) | updateElementTrim({
    method updateElementDuration (line 101) | updateElementDuration({
    method updateElementStartTime (line 124) | updateElementStartTime({
    method moveElement (line 138) | moveElement({
    method toggleTrackMute (line 164) | toggleTrackMute({ trackId }: { trackId: string }): void {
    method toggleTrackVisibility (line 169) | toggleTrackVisibility({ trackId }: { trackId: string }): void {
    method splitElements (line 174) | splitElements({
    method getTotalDuration (line 195) | getTotalDuration(): number {
    method getTrackById (line 199) | getTrackById({ trackId }: { trackId: string }): TimelineTrack | null {
    method getElementsWithTracks (line 203) | getElementsWithTracks({
    method pasteAtTime (line 225) | pasteAtTime({
    method deleteElements (line 237) | deleteElements({
    method updateElements (line 248) | updateElements({
    method addClipEffect (line 276) | addClipEffect({
    method removeClipEffect (line 294) | removeClipEffect({
    method updateClipEffectParams (line 311) | updateClipEffectParams({
    method toggleClipEffect (line 337) | toggleClipEffect({
    method reorderClipEffects (line 354) | reorderClipEffects({
    method upsertKeyframes (line 374) | upsertKeyframes({
    method removeKeyframes (line 416) | removeKeyframes({
    method retimeKeyframe (line 444) | retimeKeyframe({
    method upsertEffectParamKeyframe (line 467) | upsertEffectParamKeyframe({
    method removeEffectParamKeyframe (line 499) | removeEffectParamKeyframe({
    method isPreviewActive (line 522) | isPreviewActive(): boolean {
    method previewElements (line 526) | previewElements({
    method commitPreview (line 553) | commitPreview(): void {
    method discardPreview (line 561) | discardPreview(): void {
    method duplicateElements (line 568) | duplicateElements({
    method toggleElementsVisibility (line 578) | toggleElementsVisibility({
    method toggleElementsMuted (line 587) | toggleElementsMuted({
    method getTracks (line 596) | getTracks(): TimelineTrack[] {
    method subscribe (line 600) | subscribe(listener: () => void): () => void {
    method notify (line 605) | private notify(): void {
    method updateTracks (line 609) | updateTracks(newTracks: TimelineTrack[]): void {

FILE: apps/web/src/hooks/actions/use-action-handler.ts
  function useActionHandler (line 11) | function useActionHandler<A extends TAction>(

FILE: apps/web/src/hooks/actions/use-editor-actions.ts
  function useEditorActions (line 10) | function useEditorActions() {

FILE: apps/web/src/hooks/storage/use-local-storage.ts
  function useLocalStorage (line 3) | function useLocalStorage<T>({

FILE: apps/web/src/hooks/timeline/element/use-element-interaction.ts
  type UseElementInteractionProps (line 32) | interface UseElementInteractionProps {
  constant MOUSE_BUTTON_RIGHT (line 42) | const MOUSE_BUTTON_RIGHT = 2;
  type PendingDragState (line 56) | interface PendingDragState {
  function getClickOffsetTime (line 65) | function getClickOffsetTime({
  function getVerticalDragDirection (line 78) | function getVerticalDragDirection({
  function getDragDropTarget (line 90) | function getDragDropTarget({
  type StartDragParams (line 149) | interface StartDragParams
  function useElementInteraction (line 158) | function useElementInteraction({

FILE: apps/web/src/hooks/timeline/element/use-element-resize.ts
  type ResizeState (line 14) | interface ResizeState {
  type UseTimelineElementResizeProps (line 24) | interface UseTimelineElementResizeProps {
  function useTimelineElementResize (line 32) | function useTimelineElementResize({

FILE: apps/web/src/hooks/timeline/element/use-element-selection.ts
  type ElementRef (line 4) | type ElementRef = { trackId: string; elementId: string };
  function useElementSelection (line 6) | function useElementSelection() {

FILE: apps/web/src/hooks/timeline/element/use-keyframe-drag.ts
  type KeyframeDragState (line 21) | interface KeyframeDragState {
  type PendingKeyframeDrag (line 33) | interface PendingKeyframeDrag {
  function useKeyframeDrag (line 38) | function useKeyframeDrag({

FILE: apps/web/src/hooks/timeline/element/use-keyframe-selection.ts
  function getSelectedKeyframeId (line 5) | function getSelectedKeyframeId({
  function mergeUniqueKeyframes (line 13) | function mergeUniqueKeyframes({
  function useKeyframeSelection (line 25) | function useKeyframeSelection() {

FILE: apps/web/src/hooks/timeline/use-bookmark-drag.ts
  type BookmarkDragState (line 20) | interface BookmarkDragState {
  type PendingBookmarkDrag (line 26) | interface PendingBookmarkDrag {
  type UseBookmarkDragProps (line 32) | interface UseBookmarkDragProps {
  function useBookmarkDrag (line 39) | function useBookmarkDrag({

FILE: apps/web/src/hooks/timeline/use-edge-auto-scroll.ts
  type UseEdgeAutoScrollParams (line 3) | interface UseEdgeAutoScrollParams {
  function useEdgeAutoScroll (line 13) | function useEdgeAutoScroll({

FILE: apps/web/src/hooks/timeline/use-scroll-position.ts
  type UseScrollPositionReturn (line 3) | interface UseScrollPositionReturn {
  function useScrollPosition (line 8) | function useScrollPosition({

FILE: apps/web/src/hooks/timeline/use-scroll-sync.ts
  type UseScrollSyncProps (line 3) | interface UseScrollSyncProps {
  function useScrollSync (line 10) | function useScrollSync({

FILE: apps/web/src/hooks/timeline/use-selection-box.ts
  type UseSelectionBoxProps (line 6) | interface UseSelectionBoxProps {
  type SelectionBoxState (line 17) | interface SelectionBoxState {
  type SelectionRectangle (line 23) | interface SelectionRectangle {
  function getNormalizedRectangle (line 30) | function getNormalizedRectangle({
  function getSelectionRectangleInContent (line 45) | function getSelectionRectangleInContent({
  function isRectangleIntersecting (line 75) | function isRectangleIntersecting({
  function useSelectionBox (line 90) | function useSelectionBox({

FILE: apps/web/src/hooks/timeline/use-snap-indicator-position.ts
  type UseSnapIndicatorPositionParams (line 5) | interface UseSnapIndicatorPositionParams {
  type SnapIndicatorPosition (line 14) | interface SnapIndicatorPosition {
  function useSnapIndicatorPosition (line 20) | function useSnapIndicatorPosition({

FILE: apps/web/src/hooks/timeline/use-timeline-drag-drop.ts
  type UseTimelineDragDropProps (line 26) | interface UseTimelineDragDropProps {
  function useTimelineDragDrop (line 33) | function useTimelineDragDrop({

FILE: apps/web/src/hooks/timeline/use-timeline-playhead.ts
  type UseTimelinePlayheadProps (line 12) | interface UseTimelinePlayheadProps {
  function useTimelinePlayhead (line 20) | function useTimelinePlayhead({

FILE: apps/web/src/hooks/timeline/use-timeline-seek.ts
  type UseTimelineSeekProps (line 7) | interface UseTimelineSeekProps {
  function resetMouseTracking (line 19) | function resetMouseTracking({
  function setMouseTracking (line 37) | function setMouseTracking({
  function useTimelineSeek (line 57) | function useTimelineSeek({

FILE: apps/web/src/hooks/timeline/use-timeline-zoom.ts
  type UseTimelineZoomProps (line 14) | interface UseTimelineZoomProps {
  type UseTimelineZoomReturn (line 24) | interface UseTimelineZoomReturn {
  function useTimelineZoom (line 31) | function useTimelineZoom({

FILE: apps/web/src/hooks/use-container-size.ts
  function useContainerSize (line 3) | function useContainerSize({

FILE: apps/web/src/hooks/use-editor.ts
  function useEditor (line 4) | function useEditor(): EditorCore {

FILE: apps/web/src/hooks/use-effect-preview.ts
  function useEffectPreview (line 5) | function useEffectPreview({

FILE: apps/web/src/hooks/use-file-upload.ts
  type UseFileUploadOptions (line 4) | interface UseFileUploadOptions {
  function containsFiles (line 10) | function containsFiles(dataTransfer: DataTransfer): boolean {
  function useFileUpload (line 14) | function useFileUpload({

FILE: apps/web/src/hooks/use-focus-lock.ts
  type FocusLockCursor (line 3) | type FocusLockCursor = "text" | "default" | "pointer" | "crosshair";
  constant DATA_ATTR (line 5) | const DATA_ATTR = "data-focus-locked";
  function buildFocusLockCSS (line 7) | function buildFocusLockCSS({
  function useFocusLock (line 28) | function useFocusLock<T extends HTMLElement = HTMLElement>({

FILE: apps/web/src/hooks/use-fullscreen.ts
  function useFullscreen (line 3) | function useFullscreen({

FILE: apps/web/src/hooks/use-infinite-scroll.ts
  type UseInfiniteScrollOptions (line 3) | interface UseInfiniteScrollOptions {
  function useInfiniteScroll (line 11) | function useInfiniteScroll({

FILE: apps/web/src/hooks/use-keybindings.ts
  function useKeybindingsListener (line 10) | function useKeybindingsListener() {
  function useKeybindingDisabler (line 68) | function useKeybindingDisabler() {

FILE: apps/web/src/hooks/use-keyboard-shortcuts-help.ts
  type KeyboardShortcut (line 11) | interface KeyboardShortcut {
  function formatKey (line 20) | function formatKey({ key }: { key: string }): string {
  function useKeyboardShortcutsHelp (line 38) | function useKeyboardShortcutsHelp() {
  function isAction (line 80) | function isAction(id: string): id is TAction {

FILE: apps/web/src/hooks/use-mobile.ts
  constant MOBILE_BREAKPOINT (line 3) | const MOBILE_BREAKPOINT = 768;
  function useIsMobile (line 5) | function useIsMobile() {

FILE: apps/web/src/hooks/use-paste-media.ts
  constant MEDIA_MIME_PREFIXES (line 13) | const MEDIA_MIME_PREFIXES: MediaType[] = ["image", "video", "audio"];
  function isMediaMimeType (line 15) | function isMediaMimeType({ type }: { type: string }): boolean {
  function extractMediaFilesFromClipboard (line 19) | function extractMediaFilesFromClipboard({
  function usePasteMedia (line 37) | function usePasteMedia() {

FILE: apps/web/src/hooks/use-preview-interaction.ts
  constant MIN_DRAG_DISTANCE (line 18) | const MIN_DRAG_DISTANCE = 0.5;
  type DragState (line 20) | interface DragState {
  function usePreviewInteraction (line 34) | function usePreviewInteraction({

FILE: apps/web/src/hooks/use-raf-loop.ts
  function useRafLoop (line 3) | function useRafLoop(callback: ({ time }: { time: number }) => void) {

FILE: apps/web/src/hooks/use-reveal-item.ts
  function useRevealItem (line 3) | function useRevealItem(

FILE: apps/web/src/hooks/use-shift-key.ts
  function useShiftKey (line 3) | function useShiftKey(): RefObject<boolean> {

FILE: apps/web/src/hooks/use-sound-search.ts
  function useSoundSearch (line 4) | function useSoundSearch({

FILE: apps/web/src/hooks/use-transform-handles.ts
  type Corner (line 28) | type Corner = "top-left" | "top-right" | "bottom-left" | "bottom-right";
  type HandleType (line 29) | type HandleType = Corner | "rotation";
  type ScaleState (line 31) | interface ScaleState {
  type RotationState (line 44) | interface RotationState {
  function areSnapLinesEqual (line 53) | function areSnapLinesEqual({
  function getCornerDistance (line 75) | function getCornerDistance({
  function useTransformHandles (line 104) | function useTransformHandles({

FILE: apps/web/src/lib/actions/definitions.ts
  type TActionCategory (line 3) | type TActionCategory =
  type TActionDefinition (line 12) | interface TActionDefinition {
  constant ACTIONS (line 19) | const ACTIONS = {
  type TAction (line 151) | type TAction = keyof typeof ACTIONS;
  function getActionDefinition (line 153) | function getActionDefinition({ action }: { action: TAction }): TActionDe...
  function getDefaultShortcuts (line 157) | function getDefaultShortcuts(): Record<ShortcutKey, TAction> {

FILE: apps/web/src/lib/actions/registry.ts
  type ActionHandler (line 11) | type ActionHandler = (arg: unknown, trigger?: TInvocationTrigger) => void;
  function bindAction (line 14) | function bindAction<A extends TAction>(
  function unbindAction (line 27) | function unbindAction<A extends TAction>(
  type InvokeActionFunc (line 42) | type InvokeActionFunc = {

FILE: apps/web/src/lib/actions/types.ts
  type TActionArgsMap (line 6) | type TActionArgsMap = {
  type TKeysWithValueUndefined (line 13) | type TKeysWithValueUndefined<T> = {
  type TActionWithArgs (line 17) | type TActionWithArgs = keyof TActionArgsMap;
  type TActionWithOptionalArgs (line 19) | type TActionWithOptionalArgs =
  type TActionWithNoArgs (line 23) | type TActionWithNoArgs = Exclude<TAction, TActionWithArgs>;
  type TArgOfAction (line 25) | type TArgOfAction<A extends TAction> = A extends TActionWithArgs
  type TActionFunc (line 29) | type TActionFunc<A extends TAction> = A extends TActionWithArgs
  type TInvocationTrigger (line 33) | type TInvocationTrigger = "keypress" | "mouseclick";
  type TBoundActionList (line 35) | type TBoundActionList = {
  type TActionHandlerOptions (line 39) | type TActionHandlerOptions =

FILE: apps/web/src/lib/animation/color-channel.ts
  function getColorChannelForPath (line 7) | function getColorChannelForPath({

FILE: apps/web/src/lib/animation/effect-param-channel.ts
  constant EFFECT_PARAM_PATH_PREFIX (line 14) | const EFFECT_PARAM_PATH_PREFIX = "effects.";
  constant EFFECT_PARAM_PATH_SUFFIX (line 15) | const EFFECT_PARAM_PATH_SUFFIX = ".params.";
  function buildEffectParamPath (line 17) | function buildEffectParamPath({
  function resolveEffectParamsAtTime (line 27) | function resolveEffectParamsAtTime({
  constant EMPTY_NUMBER_CHANNEL (line 55) | const EMPTY_NUMBER_CHANNEL: NumberAnimationChannel = {
  function upsertEffectParamKeyframe (line 60) | function upsertEffectParamKeyframe({
  function removeEffectParamKeyframe (line 98) | function removeEffectParamKeyframe({

FILE: apps/web/src/lib/animation/interpolation.ts
  function byTimeAscending (line 11) | function byTimeAscending({
  function isWithinTimePair (line 21) | function isWithinTimePair({
  function clamp01 (line 36) | function clamp01({ value }: { value: number }): number {
  function parseHexChannel (line 40) | function parseHexChannel({ hex }: { hex: string }): number | null {
  function parseHexColor (line 45) | function parseHexColor({
  function formatRgbaColor (line 95) | function formatRgbaColor({
  function lerpNumber (line 113) | function lerpNumber({
  function interpolateColor (line 125) | function interpolateColor({
  function normalizeChannel (line 164) | function normalizeChannel<TChannel extends AnimationChannel>({
  function evaluateChannelValueAtTime (line 180) | function evaluateChannelValueAtTime<TKeyframe extends { time: number; va...
  function getNumberChannelValueAtTime (line 253) | function getNumberChannelValueAtTime({
  function getColorValueAtTime (line 280) | function getColorValueAtTime({
  function getDiscreteValueAtTime (line 307) | function getDiscreteValueAtTime({
  function getChannelValueAtTime (line 324) | function getChannelValueAtTime({

FILE: apps/web/src/lib/animation/keyframe-query.ts
  function getElementKeyframes (line 8) | function getElementKeyframes({
  function hasKeyframesForPath (line 34) | function hasKeyframesForPath({
  function getKeyframeAtTime (line 45) | function getKeyframeAtTime({

FILE: apps/web/src/lib/animation/keyframes.ts
  function isNearlySameTime (line 23) | function isNearlySameTime({
  function toAnimation (line 33) | function toAnimation({
  function toChannel (line 47) | function toChannel({
  function getChannel (line 62) | function getChannel({
  function getInterpolationForChannel (line 72) | function getInterpolationForChannel({
  function buildKeyframe (line 90) | function buildKeyframe({
  function createEmptyChannel (line 143) | function createEmptyChannel({
  function upsertKeyframe (line 169) | function upsertKeyframe({
  function removeKeyframe (line 244) | function removeKeyframe({
  function retimeKeyframe (line 268) | function retimeKeyframe({
  function setChannel (line 300) | function setChannel({
  function cloneAnimations (line 325) | function cloneAnimations({
  function clampAnimationsToDuration (line 364) | function clampAnimationsToDuration({
  function splitAnimationsAtTime (line 405) | function splitAnimationsAtTime({
  function upsertElementKeyframe (line 519) | function upsertElementKeyframe({
  function removeElementKeyframe (line 568) | function removeElementKeyframe({
  function retimeElementKeyframe (line 589) | function retimeElementKeyframe({

FILE: apps/web/src/lib/animation/number-channel.ts
  function getNumberChannelForPath (line 7) | function getNumberChannelForPath({

FILE: apps/web/src/lib/animation/property-registry.ts
  type NumericRange (line 17) | interface NumericRange {
  type AnimationPropertyDefinition (line 22) | interface AnimationPropertyDefinition {
  constant ANIMATION_PROPERTY_REGISTRY (line 37) | const ANIMATION_PROPERTY_REGISTRY: Record<
  function isAnimationPropertyPath (line 237) | function isAnimationPropertyPath({
  function getAnimationPropertyDefinition (line 245) | function getAnimationPropertyDefinition({
  function supportsAnimationProperty (line 253) | function supportsAnimationProperty({
  function getElementBaseValueForProperty (line 264) | function getElementBaseValueForProperty({
  function withElementBaseValueForProperty (line 278) | function withElementBaseValueForProperty({
  function getDefaultInterpolationForProperty (line 298) | function getDefaultInterpolationForProperty({
  function clampNumericRange (line 307) | function clampNumericRange({
  function coerceAnimationValueForProperty (line 323) | function coerceAnimationValueForProperty({

FILE: apps/web/src/lib/animation/resolve.ts
  function getElementLocalTime (line 7) | function getElementLocalTime({
  function resolveTransformAtTime (line 28) | function resolveTransformAtTime({
  function resolveOpacityAtTime (line 76) | function resolveOpacityAtTime({
  function resolveNumberAtTime (line 95) | function resolveNumberAtTime({
  function resolveColorAtTime (line 113) | function resolveColorAtTime({
  function resolveVolumeAtTime (line 131) | function resolveVolumeAtTime({

FILE: apps/web/src/lib/auth/server.ts
  type Auth (line 43) | type Auth = typeof auth;

FILE: apps/web/src/lib/blog/query.ts
  function fetchFromMarble (line 19) | async function fetchFromMarble<T>({
  function getPosts (line 38) | async function getPosts() {
  function getTags (line 42) | async function getTags() {
  function getSinglePost (line 46) | async function getSinglePost({ slug }: { slug: string }) {
  function getCategories (line 50) | async function getCategories() {
  function getAuthors (line 54) | async function getAuthors() {
  function processHtmlContent (line 58) | async function processHtmlContent({

FILE: apps/web/src/lib/commands/__tests__/keyframe-aware-commands.test.ts
  type MockEditor (line 13) | type MockEditor = {
  function mockEditorCore (line 30) | function mockEditorCore({ editor }: { editor: MockEditor }): void {
  function restoreEditorCore (line 38) | function restoreEditorCore(): void {
  function buildVideoElement (line 46) | function buildVideoElement(): VideoElement {
  function buildTracks (line 73) | function buildTracks({ element }: { element: VideoElement }): TimelineTr...

FILE: apps/web/src/lib/commands/base-command.ts
  method undo (line 4) | undo(): void {
  method redo (line 8) | redo(): void {

FILE: apps/web/src/lib/commands/batch-command.ts
  class BatchCommand (line 3) | class BatchCommand extends Command {
    method constructor (line 4) | constructor(private commands: Command[]) {
    method execute (line 8) | execute(): void {
    method undo (line 14) | undo(): void {
    method redo (line 20) | redo(): void {

FILE: apps/web/src/lib/commands/media/add-media-asset.ts
  class AddMediaAssetCommand (line 7) | class AddMediaAssetCommand extends Command {
    method constructor (line 12) | constructor(
    method execute (line 20) | execute(): void {
    method undo (line 43) | undo(): void {
    method getAssetId (line 58) | getAssetId(): string {

FILE: apps/web/src/lib/commands/media/remove-media-asset.ts
  class RemoveMediaAssetCommand (line 9) | class RemoveMediaAssetCommand extends Command {
    method constructor (line 14) | constructor(
    method execute (line 21) | execute(): void {
    method undo (line 63) | undo(): void {

FILE: apps/web/src/lib/commands/preview-tracker.ts
  class PreviewTracker (line 1) | class PreviewTracker<T> {
    method begin (line 4) | begin({ state }: { state: T }): void {
    method isActive (line 10) | isActive(): boolean {
    method getSnapshot (line 14) | getSnapshot(): T | null {
    method end (line 18) | end(): T | null {

FILE: apps/web/src/lib/commands/project/update-project-settings.ts
  class UpdateProjectSettingsCommand (line 5) | class UpdateProjectSettingsCommand extends Command {
    method constructor (line 9) | constructor(private updates: Partial<TProjectSettings>) {
    method execute (line 13) | execute(): void {
    method undo (line 31) | undo(): void {

FILE: apps/web/src/lib/commands/scene/create-scene.ts
  class CreateSceneCommand (line 6) | class CreateSceneCommand extends Command {
    method constructor (line 10) | constructor(
    method execute (line 17) | execute(): void {
    method undo (line 30) | undo(): void {
    method getSceneId (line 37) | getSceneId(): string {

FILE: apps/web/src/lib/commands/scene/delete-scene.ts
  class DeleteSceneCommand (line 6) | class DeleteSceneCommand extends Command {
    method constructor (line 11) | constructor(private sceneId: string) {
    method execute (line 15) | execute(): void {
    method undo (line 50) | undo(): void {

FILE: apps/web/src/lib/commands/scene/move-bookmark.ts
  class MoveBookmarkCommand (line 7) | class MoveBookmarkCommand extends Command {
    method constructor (line 10) | constructor(
    method execute (line 17) | execute(): void {
    method undo (line 53) | undo(): void {

FILE: apps/web/src/lib/commands/scene/remove-bookmark.ts
  class RemoveBookmarkCommand (line 10) | class RemoveBookmarkCommand extends Command {
    method constructor (line 14) | constructor(private time: number) {
    method execute (line 18) | execute(): void {
    method undo (line 53) | undo(): void {

FILE: apps/web/src/lib/commands/scene/rename-scene.ts
  class RenameSceneCommand (line 6) | class RenameSceneCommand extends Command {
    method constructor (line 10) | constructor(
    method execute (line 17) | execute(): void {
    method undo (line 40) | undo(): void {

FILE: apps/web/src/lib/commands/scene/toggle-bookmark.ts
  class ToggleBookmarkCommand (line 7) | class ToggleBookmarkCommand extends Command {
    method constructor (line 11) | constructor(private time: number) {
    method execute (line 15) | execute(): void {
    method undo (line 46) | undo(): void {

FILE: apps/web/src/lib/commands/scene/update-bookmark.ts
  class UpdateBookmarkCommand (line 7) | class UpdateBookmarkCommand extends Command {
    method constructor (line 10) | constructor(
    method execute (line 17) | execute(): void {
    method undo (line 49) | undo(): void {

FILE: apps/web/src/lib/commands/timeline/clipboard/paste.ts
  class PasteCommand (line 18) | class PasteCommand extends Command {
    method constructor (line 23) | constructor(
    method execute (line 30) | execute(): void {
    method undo (line 130) | undo(): void {
    method getPastedElements (line 140) | getPastedElements(): { trackId: string; elementId: string }[] {
  function groupClipboardItemsByTrackId (line 145) | function groupClipboardItemsByTrackId({
  function buildPastedElements (line 160) | function buildPastedElements({
  function resolveTargetTrackIndex (line 190) | function resolveTargetTrackIndex({
  function resolveInsertIndexForNewTrack (line 235) | function resolveInsertIndexForNewTrack({
  function canPlaceElementsOnTrack (line 258) | function canPlaceElementsOnTrack({
  function buildTrackWithElements (line 280) | function buildTrackWithElements({

FILE: apps/web/src/lib/commands/timeline/element/delete-elements.ts
  class DeleteElementsCommand (line 6) | class DeleteElementsCommand extends Command {
    method constructor (line 11) | constructor({
    method execute (line 23) | execute(): void {
    method undo (line 73) | undo(): void {

FILE: apps/web/src/lib/commands/timeline/element/duplicate-elements.ts
  type DuplicateElementsParams (line 11) | interface DuplicateElementsParams {
  class DuplicateElementsCommand (line 15) | class DuplicateElementsCommand extends Command {
    method constructor (line 21) | constructor({ elements }: DuplicateElementsParams) {
    method execute (line 26) | execute(): void {
    method undo (line 94) | undo(): void {
    method getDuplicatedElements (line 104) | getDuplicatedElements(): { trackId: string; elementId: string }[] {
  function buildDuplicateElement (line 109) | function buildDuplicateElement({

FILE: apps/web/src/lib/commands/timeline/element/effects/add-effect.ts
  function addEffectToElement (line 7) | function addEffectToElement({
  class AddClipEffectCommand (line 19) | class AddClipEffectCommand extends Command {
    method constructor (line 26) | constructor({
    method execute (line 41) | execute(): void {
    method undo (line 64) | undo(): void {
    method getEffectId (line 71) | getEffectId(): string | null {

FILE: apps/web/src/lib/commands/timeline/element/effects/remove-effect.ts
  function removeEffectFromElement (line 6) | function removeEffectFromElement({
  class RemoveClipEffectCommand (line 18) | class RemoveClipEffectCommand extends Command {
    method constructor (line 24) | constructor({
    method execute (line 39) | execute(): void {
    method undo (line 59) | undo(): void {

FILE: apps/web/src/lib/commands/timeline/element/effects/reorder-effect.ts
  function reorderEffectsOnElement (line 6) | function reorderEffectsOnElement({
  class ReorderClipEffectsCommand (line 21) | class ReorderClipEffectsCommand extends Command {
    method constructor (line 28) | constructor({
    method execute (line 46) | execute(): void {
    method undo (line 67) | undo(): void {

FILE: apps/web/src/lib/commands/timeline/element/effects/toggle-effect.ts
  function toggleEffectOnElement (line 6) | function toggleEffectOnElement({
  class ToggleClipEffectCommand (line 20) | class ToggleClipEffectCommand extends Command {
    method constructor (line 26) | constructor({
    method execute (line 41) | execute(): void {
    method undo (line 61) | undo(): void {

FILE: apps/web/src/lib/commands/timeline/element/effects/update-effect-params.ts
  function updateEffectParamsOnElement (line 7) | function updateEffectParamsOnElement({
  class UpdateClipEffectParamsCommand (line 34) | class UpdateClipEffectParamsCommand extends Command {
    method constructor (line 41) | constructor({
    method execute (line 59) | execute(): void {
    method undo (line 80) | undo(): void {

FILE: apps/web/src/lib/commands/timeline/element/insert-element.ts
  type InsertElementPlacement (line 25) | type InsertElementPlacement =
  type InsertElementParams (line 29) | interface InsertElementParams {
  class InsertElementCommand (line 34) | class InsertElementCommand extends Command {
    method constructor (line 39) | constructor({ element, placement }: InsertElementParams) {
    method execute (line 49) | execute(): void {
    method undo (line 117) | undo(): void {
    method getElementId (line 124) | getElementId(): string {
    method getTrackId (line 128) | getTrackId(): string | null {
    method buildElement (line 132) | private buildElement({
    method validateElementBasics (line 147) | private validateElementBasics({
    method resolveTracksWithElement (line 184) | private resolveTracksWithElement({
    method getAutoInsertIndex (line 303) | private getAutoInsertIndex({
    method adjustElementForMainTrack (line 325) | private adjustElementForMainTrack({
    method getTrackTypeForElement (line 342) | private getTrackTypeForElement({

FILE: apps/web/src/lib/commands/timeline/element/keyframes/remove-effect-param-keyframe.ts
  class RemoveEffectParamKeyframeCommand (line 8) | class RemoveEffectParamKeyframeCommand extends Command {
    method constructor (line 16) | constructor({
    method execute (line 37) | execute(): void {
    method undo (line 60) | undo(): void {

FILE: apps/web/src/lib/commands/timeline/element/keyframes/remove-keyframe.ts
  function sampleValueBeforeRemoval (line 15) | function sampleValueBeforeRemoval({
  function removeKeyframeAndPersist (line 48) | function removeKeyframeAndPersist({
  class RemoveKeyframeCommand (line 84) | class RemoveKeyframeCommand extends Command {
    method constructor (line 91) | constructor({
    method execute (line 109) | execute(): void {
    method undo (line 133) | undo(): void {

FILE: apps/web/src/lib/commands/timeline/element/keyframes/retime-keyframe.ts
  class RetimeKeyframeCommand (line 8) | class RetimeKeyframeCommand extends Command {
    method constructor (line 16) | constructor({
    method execute (line 37) | execute(): void {
    method undo (line 68) | undo(): void {

FILE: apps/web/src/lib/commands/timeline/element/keyframes/upsert-effect-param-keyframe.ts
  class UpsertEffectParamKeyframeCommand (line 8) | class UpsertEffectParamKeyframeCommand extends Command {
    method constructor (line 19) | constructor({
    method execute (line 49) | execute(): void {
    method undo (line 76) | undo(): void {

FILE: apps/web/src/lib/commands/timeline/element/keyframes/upsert-keyframe.ts
  class UpsertKeyframeCommand (line 12) | class UpsertKeyframeCommand extends Command {
    method constructor (line 22) | constructor({
    method execute (line 49) | execute(): void {
    method undo (line 81) | undo(): void {

FILE: apps/web/src/lib/commands/timeline/element/move-elements.ts
  class MoveElementCommand (line 16) | class MoveElementCommand extends Command {
    method constructor (line 25) | constructor({
    method execute (line 49) | execute(): void {
    method undo (line 155) | undo(): void {

FILE: apps/web/src/lib/commands/timeline/element/split-elements.ts
  class SplitElementsCommand (line 8) | class SplitElementsCommand extends Command {
    method constructor (line 17) | constructor({
    method getRightSideElements (line 35) | getRightSideElements(): { trackId: string; elementId: string }[] {
    method execute (line 39) | execute(): void {
    method undo (line 165) | undo(): void {

FILE: apps/web/src/lib/commands/timeline/element/toggle-elements-muted.ts
  class ToggleElementsMutedCommand (line 6) | class ToggleElementsMutedCommand extends Command {
    method constructor (line 9) | constructor(private elements: { trackId: string; elementId: string }[]) {
    method execute (line 13) | execute(): void {
    method undo (line 51) | undo(): void {

FILE: apps/web/src/lib/commands/timeline/element/toggle-elements-visibility.ts
  class ToggleElementsVisibilityCommand (line 6) | class ToggleElementsVisibilityCommand extends Command {
    method constructor (line 9) | constructor(private elements: { trackId: string; elementId: string }[]) {
    method execute (line 13) | execute(): void {
    method undo (line 41) | undo(): void {

FILE: apps/web/src/lib/commands/timeline/element/update-element-duration.ts
  class UpdateElementDurationCommand (line 6) | class UpdateElementDurationCommand extends Command {
    method constructor (line 12) | constructor({
    method execute (line 27) | execute(): void {
    method undo (line 51) | undo(): void {

FILE: apps/web/src/lib/commands/timeline/element/update-element-start-time.ts
  class UpdateElementStartTimeCommand (line 6) | class UpdateElementStartTimeCommand extends Command {
    method constructor (line 11) | constructor({
    method execute (line 23) | execute(): void {
    method undo (line 63) | undo(): void {

FILE: apps/web/src/lib/commands/timeline/element/update-element-trim.ts
  class UpdateElementTrimCommand (line 7) | class UpdateElementTrimCommand extends Command {
    method constructor (line 16) | constructor({
    method execute (line 40) | execute(): void {
    method undo (line 96) | undo(): void {

FILE: apps/web/src/lib/commands/timeline/element/update-element.ts
  class UpdateElementCommand (line 6) | class UpdateElementCommand extends Command {
    method constructor (line 12) | constructor({
    method execute (line 27) | execute(): void {
    method undo (line 41) | undo(): void {

FILE: apps/web/src/lib/commands/timeline/track/add-track.ts
  class AddTrackCommand (line 10) | class AddTrackCommand extends Command {
    method constructor (line 14) | constructor(
    method execute (line 22) | execute(): void {
    method undo (line 43) | undo(): void {
    method getTrackId (line 50) | getTrackId(): string {

FILE: apps/web/src/lib/commands/timeline/track/remove-track.ts
  class RemoveTrackCommand (line 6) | class RemoveTrackCommand extends Command {
    method constructor (line 9) | constructor(private trackId: string) {
    method execute (line 13) | execute(): void {
    method undo (line 29) | undo(): void {

FILE: apps/web/src/lib/commands/timeline/track/toggle-track-mute.ts
  class ToggleTrackMuteCommand (line 6) | class ToggleTrackMuteCommand extends Command {
    method constructor (line 9) | constructor(private trackId: string) {
    method execute (line 13) | execute(): void {
    method undo (line 33) | undo(): void {

FILE: apps/web/src/lib/commands/timeline/track/toggle-track-visibility.ts
  class ToggleTrackVisibilityCommand (line 6) | class ToggleTrackVisibilityCommand extends Command {
    method constructor (line 9) | constructor(private trackId: string) {
    method execute (line 13) | execute(): void {
    method undo (line 34) | undo(): void {

FILE: apps/web/src/lib/commands/timeline/tracks-snapshot.ts
  class TracksSnapshotCommand (line 5) | class TracksSnapshotCommand extends Command {
    method constructor (line 6) | constructor(
    method execute (line 13) | execute(): void {
    method undo (line 17) | undo(): void {

FILE: apps/web/src/lib/db/index.ts
  function getDb (line 8) | function getDb() {

FILE: apps/web/src/lib/drag-data.ts
  constant MIME_TYPE (line 3) | const MIME_TYPE = "application/x-timeline-drag";
  function setDragData (line 6) | function setDragData({
  function getDragData (line 18) | function getDragData({
  function hasDragData (line 38) | function hasDragData({
  function clearDragData (line 46) | function clearDragData(): void {

FILE: apps/web/src/lib/effects/definitions/index.ts
  function registerDefaultEffects (line 6) | function registerDefaultEffects(): void {

FILE: apps/web/src/lib/effects/index.ts
  constant EFFECT_TARGET_ELEMENT_TYPES (line 9) | const EFFECT_TARGET_ELEMENT_TYPES: VisualElement["type"][] = [
  function buildDefaultEffectInstance (line 16) | function buildDefaultEffectInstance({

FILE: apps/web/src/lib/effects/registry.ts
  function registerEffect (line 5) | function registerEffect({
  function hasEffect (line 13) | function hasEffect({ effectType }: { effectType: string }): boolean {
  function getEffect (line 17) | function getEffect({
  function getAllEffects (line 29) | function getAllEffects(): EffectDefinition[] {

FILE: apps/web/src/lib/export.ts
  function getExportMimeType (line 4) | function getExportMimeType({
  function getExportFileExtension (line 12) | function getExportFileExtension({
  function downloadBuffer (line 20) | function downloadBuffer({

FILE: apps/web/src/lib/fonts/google-fonts.ts
  constant GOOGLE_FONTS_CSS (line 4) | const GOOGLE_FONTS_CSS = "https://fonts.googleapis.com/css2";
  function encodeFamily (line 11) | function encodeFamily(family: string): string {
  function getCachedFontAtlas (line 15) | function getCachedFontAtlas(): FontAtlas | null {
  function clearFontAtlasCache (line 19) | function clearFontAtlasCache(): void {
  function fetchAtlas (line 24) | async function fetchAtlas(): Promise<FontAtlas | null> {
  function preloadChunkImages (line 40) | function preloadChunkImages({ atlas }: { atlas: FontAtlas }): void {
  function prefetchFontAtlas (line 50) | function prefetchFontAtlas(): Promise<FontAtlas | null> {
  function loadFullFont (line 57) | async function loadFullFont({
  function loadFonts (line 83) | async function loadFonts({

FILE: apps/web/src/lib/gradients/canvas.ts
  type BackgroundLayer (line 9) | type BackgroundLayer =
  type LinearPoints (line 13) | type LinearPoints = {
  type RadialDimensions (line 21) | type RadialDimensions = {
  type PositionKeyword (line 28) | type PositionKeyword = "left" | "center" | "right" | "top" | "bottom";
  type Distance (line 30) | type Distance =
  type Position (line 37) | type Position = { type: "position"; value: { x?: Distance; y?: Distance ...
  type Shape (line 39) | type Shape = {
  type DefaultRadial (line 46) | type DefaultRadial = { type: "default-radial"; at: Position };
  type ExtentKeyword (line 48) | type ExtentKeyword = { type: "extent-keyword"; value: string; at?: Posit...
  type RadialOrientation (line 50) | type RadialOrientation = Shape | ExtentKeyword | DefaultRadial;
  function drawCssBackground (line 55) | function drawCssBackground({

FILE: apps/web/src/lib/gradients/parser.ts
  type GradientType (line 5) | type GradientType =
  type DirectionalOrientation (line 11) | type DirectionalOrientation = { type: "directional"; value: string };
  type AngularOrientation (line 12) | type AngularOrientation = { type: "angular"; value: string };
  type LinearOrientation (line 13) | type LinearOrientation = DirectionalOrientation | AngularOrientation;
  type Distance (line 15) | type Distance =
  type PositionValue (line 22) | type PositionValue = { x?: Distance; y?: Distance };
  type Position (line 23) | type Position = { type: "position"; value: PositionValue };
  type ExtentKeyword (line 25) | type ExtentKeyword = { type: "extent-keyword"; value: string };
  type ShapeValue (line 27) | type ShapeValue = "circle" | "ellipse";
  type Shape (line 28) | type Shape = {
  type DefaultRadial (line 35) | type DefaultRadial = { type: "default-radial"; at: Position };
  type RadialOrientation (line 37) | type RadialOrientation =
  type GradientOrientation (line 42) | type GradientOrientation = LinearOrientation | Array<RadialOrientation>;
  type Color (line 44) | type Color =
  type ColorStop (line 53) | type ColorStop = Color & { length?: Distance };
  type GradientAst (line 55) | type GradientAst = {
  type Tokens (line 61) | type Tokens = {

FILE: apps/web/src/lib/iconify-api.ts
  constant ICONIFY_HOSTS (line 1) | const ICONIFY_HOSTS = [
  function fetchWithFallback (line 9) | async function fetchWithFallback(path: string): Promise<Response> {
  type IconSet (line 26) | interface IconSet {
  type IconSearchResult (line 44) | interface IconSearchResult {
  type CollectionInfo (line 52) | interface CollectionInfo {
  function getCollections (line 62) | async function getCollections(
  function getCollection (line 85) | async function getCollection(
  function searchIcons (line 99) | async function searchIcons(
  function buildIconSvgUrl (line 134) | function buildIconSvgUrl(
  function getIconSvgUrl (line 179) | function getIconSvgUrl(
  function downloadSvgAsText (line 186) | async function downloadSvgAsText(
  function svgToFile (line 198) | function svgToFile(svgText: string, fileName: string): File {
  constant POPULAR_COLLECTIONS (line 203) | const POPULAR_COLLECTIONS = {
  function getCategoriesFromCollections (line 231) | function getCategoriesFromCollections(

FILE: apps/web/src/lib/media/audio.ts
  constant MAX_AUDIO_CHANNELS (line 13) | const MAX_AUDIO_CHANNELS = 2;
  constant EXPORT_SAMPLE_RATE (line 14) | const EXPORT_SAMPLE_RATE = 44100;
  type CollectedAudioElement (line 16) | type CollectedAudioElement = Omit<
  function createAudioContext (line 21) | function createAudioContext({ sampleRate }: { sampleRate?: number } = {}...
  type DecodedAudio (line 30) | interface DecodedAudio {
  function decodeAudioToFloat32 (line 35) | async function decodeAudioToFloat32({
  function collectAudioElements (line 60) | async function collectAudioElements({
  function resolveAudioBufferForElement (line 137) | async function resolveAudioBufferForElement({
  function resolveAudioBufferForVideoElement (line 170) | async function resolveAudioBufferForVideoElement({
  type AudioMixSource (line 238) | interface AudioMixSource {
  type AudioClipSource (line 246) | interface AudioClipSource {
  function fetchLibraryAudioSource (line 257) | async function fetchLibraryAudioSource({
  function fetchLibraryAudioClip (line 286) | async function fetchLibraryAudioClip({
  function collectMediaAudioSource (line 320) | function collectMediaAudioSource({
  function collectMediaAudioClip (line 336) | function collectMediaAudioClip({
  function collectAudioMixSources (line 357) | async function collectAudioMixSources({
  function collectAudioClips (line 411) | async function collectAudioClips({
  function createTimelineAudioBuffer (line 477) | async function createTimelineAudioBuffer({
  function mixAudioChannels (line 522) | function mixAudioChannels({

FILE: apps/web/src/lib/media/media-utils.ts
  constant SUPPORTS_AUDIO (line 3) | const SUPPORTS_AUDIO: readonly MediaType[] = ["audio", "video"];
  function mediaSupportsAudio (line 5) | function mediaSupportsAudio({

FILE: apps/web/src/lib/media/mediabunny.ts
  function getVideoInfo (line 6) | async function getVideoInfo({
  constant SAMPLE_RATE (line 39) | const SAMPLE_RATE = 44100;
  constant NUM_CHANNELS (line 40) | const NUM_CHANNELS = 2;
  function decodeAndMixAudioSource (line 118) | async function decodeAndMixAudioSource({
  function createWavBlob (line 174) | function createWavBlob({ samples }: { samples: Float32Array }): Blob {
  function writeString (line 214) | function writeString({

FILE: apps/web/src/lib/media/processing.ts
  type ProcessedMediaAsset (line 7) | interface ProcessedMediaAsset extends Omit<MediaAsset, "id"> {}
  constant THUMBNAIL_MAX_WIDTH (line 9) | const THUMBNAIL_MAX_WIDTH = 1280;
  constant THUMBNAIL_MAX_HEIGHT (line 10) | const THUMBNAIL_MAX_HEIGHT = 720;
  function generateThumbnail (line 66) | async function generateThumbnail({
  function generateImageThumbnail (line 109) | async function generateImageThumbnail({
  function processMediaAssets (line 148) | async function processMediaAssets({

FILE: apps/web/src/lib/preview/element-bounds.ts
  type ElementBounds (line 17) | interface ElementBounds {
  type ElementWithBounds (line 25) | interface ElementWithBounds {
  function getVisualElementBounds (line 32) | function getVisualElementBounds({
  function getElementBounds (line 67) | function getElementBounds({
  function getVisibleElementsWithBounds (line 226) | function getVisibleElementsWithBounds({

FILE: apps/web/src/lib/preview/hit-test.ts
  function pointInRotatedRect (line 3) | function pointInRotatedRect({
  function hitTest (line 34) | function hitTest({

FILE: apps/web/src/lib/preview/preview-coords.ts
  function screenToCanvas (line 1) | function screenToCanvas({
  function canvasToOverlay (line 19) | function canvasToOverlay({
  function positionToOverlay (line 40) | function positionToOverlay({
  function getDisplayScale (line 65) | function getDisplayScale({
  function screenPixelsToLogicalThreshold (line 78) | function screenPixelsToLogicalThreshold({

FILE: apps/web/src/lib/preview/preview-snap.ts
  type SnapLine (line 1) | interface SnapLine {
  constant ROTATION_SNAP_STEP_DEGREES (line 6) | const ROTATION_SNAP_STEP_DEGREES = 90;
  constant ROTATION_SNAP_THRESHOLD_DEGREES (line 7) | const ROTATION_SNAP_THRESHOLD_DEGREES = 5;
  constant MIN_SCALE (line 8) | const MIN_SCALE = 0.01;
  constant SNAP_THRESHOLD_SCREEN_PIXELS (line 9) | const SNAP_THRESHOLD_SCREEN_PIXELS = 8;
  type SnapResult (line 11) | interface SnapResult {
  function snapPosition (line 16) | function snapPosition({
  type ScaleSnapResult (line 126) | interface ScaleSnapResult {
  function snapScale (line 131) | function snapScale({
  type RotationSnapResult (line 284) | interface RotationSnapResult {
  function snapRotation (line 289) | function snapRotation({

FILE: apps/web/src/lib/rate-limit.ts
  function checkRateLimit (line 17) | async function checkRateLimit({ request }: { request: Request }) {

FILE: apps/web/src/lib/scenes.ts
  function getMainScene (line 6) | function getMainScene({ scenes }: { scenes: TScene[] }): TScene | null {
  function ensureMainScene (line 10) | function ensureMainScene({ scenes }: { scenes: TScene[] }): TScene[] {
  function buildDefaultScene (line 19) | function buildDefaultScene({
  function canDeleteScene (line 38) | function canDeleteScene({ scene }: { scene: TScene }): {
  function getFallbackSceneAfterDelete (line 48) | function getFallbackSceneAfterDelete({
  function findCurrentScene (line 63) | function findCurrentScene({
  function getProjectDurationFromScenes (line 78) | function getProjectDurationFromScenes({
  function updateSceneInArray (line 91) | function updateSceneInArray({

FILE: apps/web/src/lib/stickers/index.ts
  constant DEFAULT_SEARCH_LIMIT (line 8) | const DEFAULT_SEARCH_LIMIT = 100;
  function mergeSearchResults (line 10) | function mergeSearchResults({
  function getProviderByCategory (line 39) | function getProviderByCategory({
  function searchStickers (line 55) | async function searchStickers({
  function browseStickers (line 113) | async function browseStickers({

FILE: apps/web/src/lib/stickers/providers/emoji.ts
  constant EMOJI_PROVIDER_ID (line 13) | const EMOJI_PROVIDER_ID = "emoji";
  constant DEFAULT_SEARCH_LIMIT (line 14) | const DEFAULT_SEARCH_LIMIT = 100;
  constant EMOJI_PREFIXES (line 16) | const EMOJI_PREFIXES = POPULAR_COLLECTIONS.emoji.map(
  constant DEFAULT_EMOJI_BROWSE (line 20) | const DEFAULT_EMOJI_BROWSE = [
  function getDisplayNameFromIconName (line 35) | function getDisplayNameFromIconName({
  function toStickerItem (line 45) | function toStickerItem({ iconName }: { iconName: string }): StickerItem {
  function computeHasMore (line 58) | function computeHasMore({
  method search (line 72) | async search({
  method browse (line 91) | async browse({
  method resolveUrl (line 106) | resolveUrl({

FILE: apps/web/src/lib/stickers/providers/flags.ts
  constant FLAGS_PROVIDER_ID (line 8) | const FLAGS_PROVIDER_ID = "flags";
  constant FLAGS_DATASET_URL (line 9) | const FLAGS_DATASET_URL = "/countries.json";
  constant DEFAULT_SEARCH_LIMIT (line 10) | const DEFAULT_SEARCH_LIMIT = 100;
  constant DEFAULT_FLAGS_BASE_URL (line 11) | const DEFAULT_FLAGS_BASE_URL = "/flags";
  type CountryRecord (line 13) | type CountryRecord = {
  function getFlagsBaseUrl (line 23) | function getFlagsBaseUrl(): string {
  function buildFlagUrl (line 27) | function buildFlagUrl({ code }: { code: string }): string {
  function loadCountries (line 32) | async function loadCountries(): Promise<CountryRecord[]> {
  function toStickerItem (line 52) | function toStickerItem({ country }: { country: CountryRecord }): Sticker...
  function normalizeQuery (line 71) | function normalizeQuery({ query }: { query: string }): string {
  function filterCountriesByQuery (line 75) | function filterCountriesByQuery({
  function paginateCountries (line 98) | function paginateCountries({
  method search (line 119) | async search({
  method browse (line 145) | async browse({
  method resolveUrl (line 158) | resolveUrl({

FILE: apps/web/src/lib/stickers/providers/icons.ts
  constant ICONS_PROVIDER_ID (line 13) | const ICONS_PROVIDER_ID = "icons";
  constant DEFAULT_SEARCH_LIMIT (line 14) | const DEFAULT_SEARCH_LIMIT = 100;
  constant ICONS_PREFIXES (line 16) | const ICONS_PREFIXES = Array.from(
  constant DEFAULT_ICONS_BROWSE (line 24) | const DEFAULT_ICONS_BROWSE = [
  function getDisplayNameFromIconName (line 39) | function getDisplayNameFromIconName({
  function toStickerItem (line 48) | function toStickerItem({ iconName }: { iconName: string }): StickerItem {
  function computeHasMore (line 61) | function computeHasMore({
  method search (line 75) | async search({
  method browse (line 94) | async browse({
  method resolveUrl (line 109) | resolveUrl({

FILE: apps/web/src/lib/stickers/providers/index.ts
  function registerDefaultStickerProviders (line 15) | function registerDefaultStickerProviders({

FILE: apps/web/src/lib/stickers/providers/shapes.ts
  constant SHAPES_PROVIDER_ID (line 8) | const SHAPES_PROVIDER_ID = "shapes";
  constant SHAPES (line 10) | const SHAPES = [
  function buildShapeUrl (line 19) | function buildShapeUrl({ shapeKey }: { shapeKey: string }): string {
  function toStickerItem (line 23) | function toStickerItem({
  function filterShapesByQuery (line 40) | function filterShapesByQuery({
  function paginateShapes (line 55) | function paginateShapes({
  method search (line 76) | async search({
  method browse (line 94) | async browse({
  method resolveUrl (line 109) | resolveUrl({

FILE: apps/web/src/lib/stickers/registry.ts
  function registerProvider (line 5) | function registerProvider({
  function hasProvider (line 13) | function hasProvider({ providerId }: { providerId: string }): boolean {
  function getProvider (line 17) | function getProvider({
  function getAllProviders (line 29) | function getAllProviders(): StickerProvider[] {

FILE: apps/web/src/lib/stickers/resolver.ts
  function resolveStickerId (line 6) | function resolveStickerId({

FILE: apps/web/src/lib/stickers/sticker-id.ts
  function parseStickerId (line 1) | function parseStickerId({ stickerId }: { stickerId: string }): {
  function buildStickerId (line 26) | function buildStickerId({

FILE: apps/web/src/lib/text/layout.ts
  type TextRect (line 4) | type TextRect = {
  type TextBlockMeasurement (line 11) | interface TextBlockMeasurement {
  function getMetricAscent (line 17) | function getMetricAscent({
  function getMetricDescent (line 27) | function getMetricDescent({
  function measureTextBlock (line 37) | function measureTextBlock({
  function getTextRect (line 70) | function getTextRect({
  function isTextBackgroundVisible (line 92) | function isTextBackgroundVisible({
  function getTextBackgroundRect (line 104) | function getTextBackgroundRect({
  function getTextVisualRect (line 135) | function getTextVisualRect({

FILE: apps/web/src/lib/time.ts
  function roundToFrame (line 3) | function roundToFrame({
  function formatTimeCode (line 13) | function formatTimeCode({
  function parseTimeCode (line 41) | function parseTimeCode({
  function guessTimeCodeFormat (line 145) | function guessTimeCodeFormat({
  function timeToFrame (line 164) | function timeToFrame({
  function frameToTime (line 174) | function frameToTime({
  function snapTimeToFrame (line 184) | function snapTimeToFrame({
  function getSnappedSeekTime (line 196) | function getSnappedSeekTime({
  function getLastFrameTime (line 209) | function getLastFrameTime({

FILE: apps/web/src/lib/timeline/bookmarks.ts
  constant BOOKMARK_TIME_EPSILON (line 4) | const BOOKMARK_TIME_EPSILON = 0.001;
  function bookmarkTimeEqual (line 6) | function bookmarkTimeEqual({
  function findBookmarkIndex (line 16) | function findBookmarkIndex({
  function isBookmarkAtTime (line 28) | function isBookmarkAtTime({
  function toggleBookmarkInArray (line 40) | function toggleBookmarkInArray({
  function removeBookmarkFromArray (line 57) | function removeBookmarkFromArray({
  function updateBookmarkInArray (line 70) | function updateBookmarkInArray({
  function moveBookmarkInArray (line 90) | function moveBookmarkInArray({
  function getFrameTime (line 110) | function getFrameTime({
  function getBookmarkAtTime (line 120) | function getBookmarkAtTime({
  function getBookmarksActiveAtTime (line 131) | function getBookmarksActiveAtTime({

FILE: apps/web/src/lib/timeline/drag-utils.ts
  function getMouseTimeFromClientX (line 3) | function getMouseTimeFromClientX({

FILE: apps/web/src/lib/timeline/drop-utils.ts
  function findElementAtPosition (line 11) | function findElementAtPosition({
  function getTrackAtY (line 40) | function getTrackAtY({
  function isCompatible (line 81) | function isCompatible({
  function getMainTrackIndex (line 98) | function getMainTrackIndex({ tracks }: { tracks: TimelineTrack[] }): num...
  function findInsertIndex (line 102) | function findInsertIndex({
  constant EMPTY_TARGET_ELEMENT (line 137) | const EMPTY_TARGET_ELEMENT = null;
  function computeDropTarget (line 139) | function computeDropTarget({
  function getDropLineY (line 299) | function getDropLineY({

FILE: apps/web/src/lib/timeline/element-utils.ts
  function canElementHaveAudio (line 30) | function canElementHaveAudio(
  function isVisualElement (line 36) | function isVisualElement(
  function canElementBeHidden (line 47) | function canElementBeHidden(
  function hasMediaId (line 53) | function hasMediaId(
  function requiresMediaId (line 59) | function requiresMediaId({
  function checkElementOverlaps (line 71) | function checkElementOverlaps({
  function resolveElementOverlaps (line 92) | function resolveElementOverlaps({
  function wouldElementOverlap (line 120) | function wouldElementOverlap({
  function buildTextBackground (line 138) | function buildTextBackground(
  function buildTextElement (line 155) | function buildTextElement({
  function buildEffectElement (line 191) | function buildEffectElement({
  function buildStickerElement (line 213) | function buildStickerElement({
  function buildVideoElement (line 238) | function buildVideoElement({
  function buildImageElement (line 266) | function buildImageElement({
  function buildUploadAudioElement (line 292) | function buildUploadAudioElement({
  function buildElementFromMedia (line 324) | function buildElementFromMedia({
  function buildLibraryAudioElement (line 355) | function buildLibraryAudioElement({
  function getElementsAtTime (line 387) | function getElementsAtTime({
  function collectFontFamilies (line 410) | function collectFontFamilies({

FILE: apps/web/src/lib/timeline/index.ts
  function calculateTotalDuration (line 11) | function calculateTotalDuration({

FILE: apps/web/src/lib/timeline/pixel-utils.ts
  constant TIMELINE_INDICATOR_LINE_WIDTH_PX (line 3) | const TIMELINE_INDICATOR_LINE_WIDTH_PX = 2;
  function getDevicePixelRatio (line 5) | function getDevicePixelRatio({
  function getTimelinePixelsPerSecond (line 29) | function getTimelinePixelsPerSecond({
  function timelineTimeToPixels (line 37) | function timelineTimeToPixels({
  function snapPixelToDeviceGrid (line 47) | function snapPixelToDeviceGrid({
  function timelineTimeToSnappedPixels (line 58) | function timelineTimeToSnappedPixels({
  function getCenteredLineLeft (line 71) | function getCenteredLineLeft({

FILE: apps/web/src/lib/timeline/ripple-utils.ts
  function rippleShiftElements (line 3) | function rippleShiftElements({

FILE: apps/web/src/lib/timeline/ruler-utils.ts
  constant LABEL_FRAME_INTERVALS (line 8) | const LABEL_FRAME_INTERVALS = [2, 3, 5, 10, 15] as const;
  constant TICK_FRAME_INTERVALS (line 13) | const TICK_FRAME_INTERVALS = [1, 2, 3, 5, 10, 15] as const;
  constant SECOND_MULTIPLIERS (line 18) | const SECOND_MULTIPLIERS = [
  constant MIN_LABEL_SPACING_PX (line 25) | const MIN_LABEL_SPACING_PX = 120;
  constant MIN_TICK_SPACING_PX (line 30) | const MIN_TICK_SPACING_PX = 18;
  type RulerConfig (line 32) | interface RulerConfig {
  function getRulerConfig (line 52) | function getRulerConfig({
  function ensureTickDividesLabel (line 94) | function ensureTickDividesLabel({
  function findOptimalInterval (line 142) | function findOptimalInterval({
  function shouldShowLabel (line 177) | function shouldShowLabel({
  function formatRulerLabel (line 195) | function formatRulerLabel({
  function isSecondBoundary (line 213) | function isSecondBoundary({
  function getFrameWithinSecond (line 226) | function getFrameWithinSecond({
  function formatTimestamp (line 240) | function formatTimestamp({ timeInSeconds }: { timeInSeconds: number }): ...

FILE: apps/web/src/lib/timeline/snap-utils.ts
  type SnapPoint (line 6) | interface SnapPoint {
  type SnapResult (line 13) | interface SnapResult {
  constant DEFAULT_SNAP_THRESHOLD_PX (line 19) | const DEFAULT_SNAP_THRESHOLD_PX = 10;
  function findSnapPoints (line 21) | function findSnapPoints({
  function snapToNearestPoint (line 99) | function snapToNearestPoint({
  function snapElementEdge (line 131) | function snapElementEdge({

FILE: apps/web/src/lib/timeline/track-element-update.ts
  function updateElementInTracks (line 3) | function updateElementInTracks({

FILE: apps/web/src/lib/timeline/track-utils.ts
  function canTracktHaveAudio (line 18) | function canTracktHaveAudio(
  function canTrackBeHidden (line 24) | function canTrackBeHidden(
  function getTrackColor (line 30) | function getTrackColor({ type }: { type: TrackType }) {
  function getTrackClasses (line 34) | function getTrackClasses({ type }: { type: TrackType }) {
  function getTrackHeight (line 38) | function getTrackHeight({ type }: { type: TrackType }): number {
  function getCumulativeHeightBefore (line 42) | function getCumulativeHeightBefore({
  function getTotalTracksHeight (line 57) | function getTotalTracksHeight({
  function buildEmptyTrack (line 70) | function buildEmptyTrack({
  function getDefaultInsertIndexForTrack (line 129) | function getDefaultInsertIndexForTrack({
  function getHighestInsertIndexForTrack (line 159) | function getHighestInsertIndexForTrack({
  function isMainTrack (line 175) | function isMainTrack(track: TimelineTrack): track is VideoTrack {
  function getMainTrack (line 179) | function getMainTrack({
  function ensureMainTrack (line 187) | function ensureMainTrack({
  function canElementGoOnTrack (line 210) | function canElementGoOnTrack({
  function validateElementTrackCompatibility (line 227) | function validateElementTrackCompatibility({
  function getEarliestMainTrackElement (line 249) | function getEarliestMainTrackElement({
  function enforceMainTrackStart (line 274) | function enforceMainTrackStart({

FILE: apps/web/src/lib/timeline/zoom-utils.ts
  constant PADDING_MAX_RATIO (line 3) | const PADDING_MAX_RATIO = 0.75;
  constant PADDING_MIN_RATIO (line 4) | const PADDING_MIN_RATIO = 0.15;
  constant PADDING_MIN_AT_ZOOM_PERCENT (line 5) | const PADDING_MIN_AT_ZOOM_PERCENT = 0.2;
  function getTimelineZoomMin (line 7) | function getTimelineZoomMin({
  function getTimelinePaddingPx (line 24) | function getTimelinePaddingPx({
  function getZoomPercent (line 45) | function getZoomPercent({
  function sliderToZoom (line 59) | function sliderToZoom({
  function zoomToSlider (line 75) | function zoomToSlider({

FILE: apps/web/src/lib/transcription/caption.ts
  function buildCaptionChunks (line 7) | function buildCaptionChunks({

FILE: apps/web/src/proxy.ts
  function proxy (line 3) | async function proxy() {

FILE: apps/web/src/services/renderer/canvas-renderer.ts
  type CanvasRendererParams (line 3) | type CanvasRendererParams = {
  class CanvasRenderer (line 9) | class CanvasRenderer {
    method constructor (line 16) | constructor({ width, height, fps }: CanvasRendererParams) {
    method setSize (line 39) | setSize({ width, height }: { width: number; height: number }) {
    method clear (line 59) | private clear() {
    method render (line 64) | async render({ node, time }: { node: BaseNode; time: number }) {
    method renderToCanvas (line 69) | async renderToCanvas({

FILE: apps/web/src/services/renderer/canvas-utils.ts
  function createOffscreenCanvas (line 1) | function createOffscreenCanvas({

FILE: apps/web/src/services/renderer/effect-preview.ts
  constant PREVIEW_SIZE (line 7) | const PREVIEW_SIZE = 160;
  constant PREVIEW_IMAGE_PATH (line 8) | const PREVIEW_IMAGE_PATH = "/effects/preview.jpg";
  function onPreviewImageReady (line 17) | function onPreviewImageReady({
  function loadPreviewImage (line 26) | function loadPreviewImage(): void {
  function buildDefaultParams (line 41) | function buildDefaultParams({
  function createTestSource (line 54) | function createTestSource({
  function getOrCreatePreviewContext (line 80) | function getOrCreatePreviewContext({
  function getTestSource (line 103) | function getTestSource({
  function applyWebGlEffect (line 120) | function applyWebGlEffect({
  function renderPreview (line 146) | function renderPreview({

FILE: apps/web/src/services/renderer/nodes/base-node.ts
  type BaseNodeParams (line 3) | type BaseNodeParams = object | undefined;
  class BaseNode (line 5) | class BaseNode<Params extends BaseNodeParams = BaseNodeParams> {
    method constructor (line 8) | constructor(params?: Params) {
    method add (line 14) | add(child: BaseNode) {
    method remove (line 19) | remove(child: BaseNode) {
    method render (line 24) | async render({

FILE: apps/web/src/services/renderer/nodes/color-node.ts
  type ColorNodeParams (line 5) | type ColorNodeParams = {
  class ColorNode (line 9) | class ColorNode extends BaseNode<ColorNodeParams> {
    method constructor (line 12) | constructor(params: ColorNodeParams) {
    method render (line 17) | async render({ renderer }: { renderer: CanvasRenderer }) {

FILE: apps/web/src/services/renderer/nodes/composite-effect-node.ts
  type CompositeEffectNodeParams (line 8) | type CompositeEffectNodeParams = {
  class CompositeEffectNode (line 15) | class CompositeEffectNode extends BaseNode<CompositeEffectNodeParams> {
    method render (line 16) | async render({

FILE: apps/web/src/services/renderer/nodes/effect-layer-node.ts
  constant TIME_EPSILON (line 7) | const TIME_EPSILON = 1e-6;
  type EffectLayerNodeParams (line 9) | type EffectLayerNodeParams = {
  function isInRange (line 16) | function isInRange({
  class EffectLayerNode (line 32) | class EffectLayerNode extends BaseNode<EffectLayerNodeParams> {
    method render (line 33) | async render({

FILE: apps/web/src/services/renderer/nodes/image-node.ts
  type ImageNodeParams (line 4) | interface ImageNodeParams extends VisualNodeParams {
  type CachedImageSource (line 9) | interface CachedImageSource {
  function loadImageSource (line 17) | function loadImageSource(
  class ImageNode (line 65) | class ImageNode extends VisualNode<ImageNodeParams> {
    method constructor (line 68) | constructor(params: ImageNodeParams) {
    method render (line 73) | async render({ renderer, time }: { renderer: CanvasRenderer; time: num...

FILE: apps/web/src/services/renderer/nodes/root-node.ts
  type RootNodeParams (line 3) | type RootNodeParams = {
  class RootNode (line 7) | class RootNode extends BaseNode<RootNodeParams> {
    method duration (line 8) | get duration() {

FILE: apps/web/src/services/renderer/nodes/sticker-node.ts
  type StickerNodeParams (line 5) | interface StickerNodeParams extends VisualNodeParams {
  type CachedStickerSource (line 9) | interface CachedStickerSource {
  function loadStickerSource (line 17) | function loadStickerSource(stickerId: string): Promise<CachedStickerSour...
  class StickerNode (line 43) | class StickerNode extends VisualNode<StickerNodeParams> {
    method constructor (line 46) | constructor(params: StickerNodeParams) {
    method render (line 51) | async render({ renderer, time }: { renderer: CanvasRenderer; time: num...

FILE: apps/web/src/services/renderer/nodes/text-node.ts
  function scaleFontSize (line 31) | function scaleFontSize({
  function quoteFontFamily (line 41) | function quoteFontFamily({ fontFamily }: { fontFamily: string }): string {
  constant TEXT_DECORATION_THICKNESS_RATIO (line 45) | const TEXT_DECORATION_THICKNESS_RATIO = 0.07;
  constant STRIKETHROUGH_VERTICAL_RATIO (line 46) | const STRIKETHROUGH_VERTICAL_RATIO = 0.35;
  function drawTextDecoration (line 48) | function drawTextDecoration({
  type TextNodeParams (line 86) | type TextNodeParams = TextElement & {
  class TextNode (line 92) | class TextNode extends BaseNode<TextNodeParams> {
    method isInRange (line 93) | isInRange({ time }: { time: number }) {
    method render (line 100) | async render({ renderer, time }: { renderer: CanvasRenderer; time: num...

FILE: apps/web/src/services/renderer/nodes/video-node.ts
  type VideoNodeParams (line 5) | interface VideoNodeParams extends VisualNodeParams {
  class VideoNode (line 11) | class VideoNode extends VisualNode<VideoNodeParams> {
    method render (line 12) | async render({ renderer, time }: { renderer: CanvasRenderer; time: num...

FILE: apps/web/src/services/renderer/nodes/visual-node.ts
  type VisualNodeParams (line 18) | interface VisualNodeParams {
  method getSourceLocalTime (line 33) | protected getSourceLocalTime({ time }: { time: number }): number {
  method getAnimationLocalTime (line 37) | protected getAnimationLocalTime({ time }: { time: number }): number {
  method isInRange (line 45) | protected isInRange({ time }: { time: number }): boolean {
  method renderVisual (line 53) | protected renderVisual({

FILE: apps/web/src/services/renderer/scene-builder.ts
  constant PREVIEW_MAX_IMAGE_SIZE (line 16) | const PREVIEW_MAX_IMAGE_SIZE = 2048;
  constant BLUR_BACKGROUND_ZOOM_SCALE (line 17) | const BLUR_BACKGROUND_ZOOM_SCALE = 1.4;
  function getVisibleSortedElements (line 19) | function getVisibleSortedElements({
  function buildTrackNodes (line 33) | function buildTrackNodes({
  type BuildSceneParams (line 141) | type BuildSceneParams = {
  function buildScene (line 150) | function buildScene({

FILE: apps/web/src/services/renderer/scene-exporter.ts
  type ExportParams (line 19) | type ExportParams = {
  type SceneExporterEvents (line 36) | type SceneExporterEvents = {
  class SceneExporter (line 43) | class SceneExporter extends EventEmitter<SceneExporterEvents> {
    method constructor (line 52) | constructor({
    method cancel (line 74) | cancel(): void {
    method export (line 78) | async export({

FILE: apps/web/src/services/renderer/webgl-effect-renderer.ts
  type ApplyEffectParams (line 5) | interface ApplyEffectParams {
  function getOrCreateCanvas (line 16) | function getOrCreateCanvas({
  function applyEffect (line 39) | function applyEffect({

FILE: apps/web/src/services/renderer/webgl-utils.ts
  type EffectPassData (line 3) | interface EffectPassData {
  constant QUAD_POSITIONS (line 8) | const QUAD_POSITIONS = new Float32Array([
  function compileProgram (line 12) | function compileProgram({
  function compileShader (line 53) | function compileShader({
  function createTexture (line 76) | function createTexture({
  function setUniforms (line 121) | function setUniforms({
  function drawFullscreenQuad (line 148) | function drawFullscreenQuad({
  function createFramebufferTexture (line 172) | function createFramebufferTexture({
  function applyMultiPassEffect (line 232) | function applyMultiPassEffect({

FILE: apps/web/src/services/storage/indexeddb-adapter.ts
  class IndexedDBAdapter (line 3) | class IndexedDBAdapter<T> implements StorageAdapter<T> {
    method constructor (line 8) | constructor(dbName: string, storeName: string, version = 1) {
    method getDB (line 14) | private async getDB(): Promise<IDBDatabase> {
    method get (line 30) | async get(key: string): Promise<T | null> {
    method set (line 42) | async set(key: string, value: T): Promise<void> {
    method remove (line 54) | async remove(key: string): Promise<void> {
    method list (line 66) | async list(): Promise<string[]> {
    method getAll (line 78) | async getAll(): Promise<T[]> {
    method clear (line 90) | async clear(): Promise<void> {
  function deleteDatabase (line 103) | async function deleteDatabase({

FILE: apps/web/src/services/storage/migrations/index.ts
  constant CURRENT_PROJECT_VERSION (line 14) | const CURRENT_PROJECT_VERSION = 9;

FILE: apps/web/src/services/storage/migrations/runner.ts
  type StorageMigrationResult (line 9) | interface StorageMigrationResult {
  type MigrationProgress (line 13) | interface MigrationProgress {
  constant MIN_MIGRATION_DISPLAY_MS (line 22) | const MIN_MIGRATION_DISPLAY_MS = 1000;
  function runStorageMigrations (line 24) | async function runStorageMigrations({
  function getProjectVersion (line 121) | function getProjectVersion({ project }: { project: ProjectRecord }): num...
  function getProjectName (line 139) | function getProjectName({

FILE: apps/web/src/services/storage/migrations/transformers/types.ts
  type ProjectRecord (line 7) | type ProjectRecord = Record<string, unknown>;
  type MigrationResult (line 9) | interface MigrationResult<T> {

FILE: apps/web/src/services/storage/migrations/transformers/utils.ts
  function isRecord (line 3) | function isRecord(value: unknown): value is ProjectRecord {
  function getProjectId (line 7) | function getProjectId({

FILE: apps/web/src/services/storage/migrations/transformers/v0-to-v1.ts
  type TransformV0ToV1Options (line 6) | interface TransformV0ToV1Options {
  function transformProjectV0ToV1 (line 10) | function transformProjectV0ToV1({

FILE: apps/web/src/services/storage/migrations/transformers/v1-to-v2.ts
  type LegacyTimelineData (line 20) | interface LegacyTimelineData {
  type LegacyMediaElement (line 25) | interface LegacyMediaElement {
  type LegacyTextElement (line 32) | interface LegacyTextElement {
  type LegacyAudioElement (line 41) | interface LegacyAudioElement {
  type LegacyMediaTrack (line 47) | interface LegacyMediaTrack {
  type TransformV1ToV2Options (line 53) | interface TransformV1ToV2Options {
  function transformProjectV1ToV2 (line 61) | async function transformProjectV1ToV2({
  function migrateProject (line 85) | async function migrateProject({
  function loadTracksFromLegacyDB (line 212) | async function loadTracksFromLegacyDB({
  function transformTracks (line 252) | async function transformTracks({
  function transformMediaTrack (line 306) | async function transformMediaTrack({
  function transformTextTrack (line 405) | function transformTextTrack({
  function transformAudioTrack (line 501) | function transformAudioTrack({
  function getCurrentSceneId (line 546) | function getCurrentSceneId({
  function findMainSceneId (line 565) | function findMainSceneId({ scenes }: { scenes: unknown[] }): string | nu...
  function applyLegacyBookmarks (line 589) | function applyLegacyBookmarks({
  function getBackgroundValue (line 622) | function getBackgroundValue({
  function getCanvasSizeValue (line 671) | function getCanvasSizeValue({
  function getNumberValue (line 694) | function getNumberValue({
  function getStringValue (line 718) | function getStringValue({
  function normalizeDateString (line 732) | function normalizeDateString({ value }: { value: unknown }): string {
  function isV2Project (line 744) | function isV2Project({ project }: { project: ProjectRecord }): boolean {

FILE: apps/web/src/services/storage/migrations/transformers/v2-to-v3.ts
  function transformProjectV2ToV3 (line 6) | function transformProjectV2ToV3({
  function getScenes (line 39) | function getScenes({ project }: { project: ProjectRecord }): TScene[] {
  function isV3Project (line 48) | function isV3Project({ project }: { project: ProjectRecord }): boolean {

FILE: apps/web/src/services/storage/migrations/transformers/v3-to-v4.ts
  constant LEGACY_FONT_WEIGHT_MAP (line 4) | const LEGACY_FONT_WEIGHT_MAP = {
  constant VALID_NUMERIC_FONT_WEIGHTS (line 9) | const VALID_NUMERIC_FONT_WEIGHTS = new Set([
  function transformProjectV3ToV4 (line 21) | function transformProjectV3ToV4({
  function normalizeProjectTextFontWeights (line 46) | function normalizeProjectTextFontWeights({
  function normalizeSceneTextFontWeights (line 75) | function normalizeSceneTextFontWeights({ scene }: { scene: unknown }): u...
  function normalizeTrackTextFontWeights (line 104) | function normalizeTrackTextFontWeights({ track }: { track: unknown }): u...
  function normalizeTextElementFontWeight (line 137) | function normalizeTextElementFontWeight({
  function normalizeFontWeight (line 157) | function normalizeFontWeight({ value }: { value: unknown }): unknown {
  function isV4Project (line 186) | function isV4Project({ project }: { project: ProjectRecord }): boolean {

FILE: apps/web/src/services/storage/migrations/transformers/v4-to-v5.ts
  constant KNOWN_STICKER_PROVIDER_IDS (line 4) | const KNOWN_STICKER_PROVIDER_IDS = new Set([
  function transformProjectV4ToV5 (line 11) | function transformProjectV4ToV5({
  function migrateProjectStickerElements (line 36) | function migrateProjectStickerElements({
  function migrateSceneStickerElements (line 65) | function migrateSceneStickerElements({ scene }: { scene: unknown }): unk...
  function migrateTrackStickerElements (line 94) | function migrateTrackStickerElements({ track }: { track: unknown }): unk...
  function migrateStickerElement (line 123) | function migrateStickerElement({ element }: { element: unknown }): unkno...
  function normalizeStickerId (line 156) | function normalizeStickerId({ value }: { value: unknown }): string | null {
  function isV5Project (line 174) | function isV5Project({ project }: { project: ProjectRecord }): boolean {

FILE: apps/web/src/services/storage/migrations/transformers/v5-to-v6.ts
  function transformProjectV5ToV6 (line 4) | function transformProjectV5ToV6({
  function migrateProjectBookmarks (line 29) | function migrateProjectBookmarks({
  function migrateSceneBookmarks (line 58) | function migrateSceneBookmarks({ scene }: { scene: unknown }): unknown {
  function isV6Project (line 85) | function isV6Project({ project }: { project: ProjectRecord }): boolean {

FILE: apps/web/src/services/storage/migrations/transformers/v6-to-v7.ts
  function transformProjectV6ToV7 (line 4) | function transformProjectV6ToV7({
  function migrateProjectTextElements (line 26) | function migrateProjectTextElements({
  function migrateSceneTextElements (line 45) | function migrateSceneTextElements({ scene }: { scene: unknown }): unknown {
  function migrateTrackTextElements (line 62) | function migrateTrackTextElements({ track }: { track: unknown }): unknown {
  function migrateTextElement (line 79) | function migrateTextElement({ element }: { element: unknown }): unknown {
  function isV7Project (line 104) | function isV7Project({ project }: { project: ProjectRecord }): boolean {

FILE: apps/web/src/services/storage/migrations/transformers/v7-to-v8.ts
  function transformProjectV7ToV8 (line 4) | function transformProjectV7ToV8({
  function migrateProjectElements (line 26) | function migrateProjectElements({
  function migrateSceneElements (line 45) | function migrateSceneElements({ scene }: { scene: unknown }): unknown {
  function migrateTrackElements (line 62) | function migrateTrackElements({ track }: { track: unknown }): unknown {
  function migrateElement (line 79) | function migrateElement({ element }: { element: unknown }): unknown {
  function isV8Project (line 94) | function isV8Project({ project }: { project: ProjectRecord }): boolean {

FILE: apps/web/src/services/storage/migrations/transformers/v8-to-v9.ts
  function transformProjectV8ToV9 (line 4) | function transformProjectV8ToV9({
  function migrateProjectTextElements (line 26) | function migrateProjectTextElements({
  function migrateSceneTextElements (line 45) | function migrateSceneTextElements({ scene }: { scene: unknown }): unknown {
  function migrateTrackTextElements (line 62) | function migrateTrackTextElements({ track }: { track: unknown }): unknown {
  function migrateTextElement (line 80) | function migrateTextElement({ element }: { element: unknown }): unknown {
  function isV9Project (line 97) | function isV9Project({ project }: { project: ProjectRecord }): boolean {

FILE: apps/web/src/services/storage/migrations/v0-to-v1.ts
  class V0toV1Migration (line 5) | class V0toV1Migration extends StorageMigration {
    method transform (line 9) | async transform(project: ProjectRecord): Promise<{

FILE: apps/web/src/services/storage/migrations/v1-to-v2.ts
  class V1toV2Migration (line 14) | class V1toV2Migration extends StorageMigration {
    method transform (line 18) | async transform(project: ProjectRecord): Promise<{
  function createMediaAssetLoader (line 46) | function createMediaAssetLoader({
  function cleanupLegacyTimelineDBs (line 62) | function cleanupLegacyTimelineDBs({

FILE: apps/web/src/services/storage/migrations/v2-to-v3.ts
  class V2toV3Migration (line 5) | class V2toV3Migration extends StorageMigration {
    method transform (line 9) | async transform(project: ProjectRecord): Promise<{

FILE: apps/web/src/services/storage/migrations/v3-to-v4.ts
  class V3toV4Migration (line 5) | class V3toV4Migration extends StorageMigration {
    method transform (line 9) | async transform(project: ProjectRecord): Promise<{

FILE: apps/web/src/services/storage/migrations/v4-to-v5.ts
  class V4toV5Migration (line 5) | class V4toV5Migration extends StorageMigration {
    method transform (line 9) | async transform(project: ProjectRecord): Promise<{

FILE: apps/web/src/services/storage/migrations/v5-to-v6.ts
  class V5toV6Migration (line 5) | class V5toV6Migration extends StorageMigration {
    method transform (line 9) | async transform(project: ProjectRecord): Promise<{

FILE: apps/web/src/services/storage/migrations/v6-to-v7.ts
  class V6toV7Migration (line 5) | class V6toV7Migration extends StorageMigration {
    method transform (line 9) | async transform(project: ProjectRecord): Promise<{

FILE: apps/web/src/services/storage/migrations/v7-to-v8.ts
  class V7toV8Migration (line 5) | class V7toV8Migration extends StorageMigration {
    method transform (line 9) | async transform(project: ProjectRecord): Promise<{

FILE: apps/web/src/services/storage/migrations/v8-to-v9.ts
  class V8toV9Migration (line 5) | class V8toV9Migration extends StorageMigration {
    method transform (line 9) | async transform(project: ProjectRecord): Promise<{

FILE: apps/web/src/services/storage/opfs-adapter.ts
  class OPFSAdapter (line 3) | class OPFSAdapter implements StorageAdapter<File> {
    method constructor (line 6) | constructor(directoryName = "media") {
    method getDirectory (line 10) | private async getDirectory(): Promise<FileSystemDirectoryHandle> {
    method get (line 17) | async get(key: string): Promise<File | null> {
    method set (line 30) | async set(key: string, file: File): Promise<void> {
    method remove (line 39) | async remove(key: string): Promise<void> {
    method list (line 50) | async list(): Promise<string[]> {
    method clear (line 61) | async clear(): Promise<void> {
    method isSupported (line 70) | static isSupported(): boolean {

FILE: apps/web/src/services/storage/service.ts
  function normalizeBookmarks (line 19) | function normalizeBookmarks({ raw }: { raw: unknown }): Bookmark[] {
  class StorageService (line 42) | class StorageService {
    method constructor (line 48) | constructor() {
    method ensureMigrations (line 69) | private async ensureMigrations(): Promise<void> {
    method getProjectMediaAdapters (line 81) | private getProjectMediaAdapters({ projectId }: { projectId: string }) {
    method stripAudioBuffers (line 93) | private stripAudioBuffers({
    method saveProject (line 110) | async saveProject({ project }: { project: TProject }): Promise<void> {
    method loadProject (line 143) | async loadProject({
    method loadAllProjects (line 189) | async loadAllProjects(): Promise<TProject[]> {
    method loadAllProjectsMetadata (line 205) | async loadAllProjectsMetadata(): Promise<TProjectMetadata[]> {
    method deleteProject (line 227) | async deleteProject({ id }: { id: string }): Promise<void> {
    method saveMediaAsset (line 231) | async saveMediaAsset({
    method loadMediaAsset (line 259) | async loadMediaAsset({
    method loadAllMediaAssets (line 307) | async loadAllMediaAssets({
    method deleteMediaAsset (line 329) | async deleteMediaAsset({
    method deleteProjectMedia (line 345) | async deleteProjectMedia({
    method clearAllData (line 359) | async clearAllData(): Promise<void> {
    method getStorageInfo (line 364) | async getStorageInfo(): Promise<{
    method getProjectStorageInfo (line 378) | async getProjectStorageInfo({ projectId }: { projectId: string }): Pro...
    method loadSavedSounds (line 392) | async loadSavedSounds(): Promise<SavedSoundsData> {
    method saveSoundEffect (line 407) | async saveSoundEffect({
    method removeSavedSound (line 443) | async removeSavedSound({ soundId }: { soundId: number }): Promise<void> {
    method isSoundSaved (line 459) | async isSoundSaved({ soundId }: { soundId: number }): Promise<boolean> {
    method clearSavedSounds (line 469) | async clearSavedSounds(): Promise<void> {
    method isOPFSSupported (line 478) | isOPFSSupported(): boolean {
    method isIndexedDBSupported (line 482) | isIndexedDBSupported(): boolean {
    method isFullySupported (line 486) | isFullySupported(): boolean {

FILE: apps/web/src/services/storage/types.ts
  type StorageAdapter (line 9) | interface StorageAdapter<T> {
  type MediaAssetData (line 17) | interface MediaAssetData {
  type SerializedScene (line 31) | type SerializedScene = Omit<TScene, "createdAt" | "updatedAt"> & {
  type SerializedProjectMetadata (line 36) | type SerializedProjectMetadata = Omit<
  type SerializedProject (line 44) | type SerializedProject = Omit<TProject, "metadata" | "scenes"> & {
  type StorageConfig (line 50) | interface StorageConfig {
  type FileSystemDirectoryHandle (line 60) | interface FileSystemDirectoryHandle {

FILE: apps/web/src/services/transcription/service.ts
  type ProgressCallback (line 13) | type ProgressCallback = (progress: TranscriptionProgress) => void;
  class TranscriptionService (line 15) | class TranscriptionService {
    method transcribe (line 21) | async transcribe({
    method cancel (line 83) | cancel() {
    method ensureWorker (line 87) | private async ensureWorker({
    method waitForInit (line 162) | private waitForInit(): Promise<void> {
    method terminate (line 177) | terminate() {

FILE: apps/web/src/services/transcription/worker.ts
  type WorkerMessage (line 12) | type WorkerMessage =
  type WorkerResponse (line 17) | type WorkerResponse =
  function handleInit (line 55) | async function handleInit({ modelId }: { modelId: string }) {
  function handleTranscribe (line 119) | async function handleTranscribe({

FILE: apps/web/src/services/video-cache/service.ts
  type VideoSinkData (line 9) | interface VideoSinkData {
  class VideoCache (line 19) | class VideoCache {
    method getFrameAt (line 23) | async getFrameAt({
    method isFrameValid (line 75) | private isFrameValid({
    method iterateToTime (line 84) | private async iterateToTime({
    method seekToTime (line 133) | private async seekToTime({
    method startPrefetch (line 180) | private startPrefetch({ sinkData }: { sinkData: VideoSinkData }): void {
    method prefetchNextFrame (line 189) | private async prefetchNextFrame({
    method ensureSink (line 219) | private async ensureSink({
    method initializeSink (line 242) | private async initializeSink({
    method clearVideo (line 285) | clearVideo({ mediaId }: { mediaId: string }): void {
    method clearAll (line 298) | clearAll(): void {
    method getStats (line 304) | getStats() {

FILE: apps/web/src/stores/assets-panel-store.tsx
  constant TAB_KEYS (line 18) | const TAB_KEYS = [
  type Tab (line 31) | type Tab = (typeof TAB_KEYS)[number];
  type MediaViewMode (line 85) | type MediaViewMode = "grid" | "list";
  type MediaSortKey (line 86) | type MediaSortKey = "name" | "type" | "duration" | "size";
  type MediaSortOrder (line 87) | type MediaSortOrder = "asc" | "desc";
  type AssetsPanelStore (line 89) | interface AssetsPanelStore {

FILE: apps/web/src/stores/editor-store.ts
  type EditorState (line 5) | interface EditorState {

FILE: apps/web/src/stores/keybindings-store.ts
  type KeybindingConflict (line 14) | interface KeybindingConflict {
  type KeybindingsState (line 20) | interface KeybindingsState {
  function isDOMElement (line 42) | function isDOMElement(element: EventTarget | null): element is HTMLEleme...
  function generateKeybindingString (line 154) | function generateKeybindingString(ev: KeyboardEvent): ShortcutKey | null {
  function getPressedKey (line 178) | function getPressedKey(ev: KeyboardEvent): string | null {
  function getActiveModifier (line 213) | function getActiveModifier(ev: KeyboardEvent): string | null {

FILE: apps/web/src/stores/keybindings/migrations/index.ts
  type MigrationFn (line 5) | type MigrationFn = ({ state }: { state: unknown }) => unknown;
  constant CURRENT_VERSION (line 13) | const CURRENT_VERSION = 5;
  function runMigrations (line 15) | function runMigrations({

FILE: apps/web/src/stores/keybindings/migrations/v2-to-v3.ts
  type V2State (line 4) | interface V2State {
  function v2ToV3 (line 9) | function v2ToV3({ state }: { state: unknown }): unknown {

FILE: apps/web/src/stores/keybindings/migrations/v3-to-v4.ts
  type V3State (line 5) | interface V3State {
  function v3ToV4 (line 10) | function v3ToV4({ state }: { state: unknown }): unknown {

FILE: apps/web/src/stores/keybindings/migrations/v4-to-v5.ts
  type V4State (line 3) | interface V4State {
  function v4ToV5 (line 8) | function v4ToV5({ state }: { state: unknown }): unknown {

FILE: apps/web/src/stores/panel-store.ts
  type PanelSizes (line 5) | interface PanelSizes {
  type PanelId (line 13) | type PanelId = keyof PanelSizes;
  type PanelState (line 15) | interface PanelState {

FILE: apps/web/src/stores/preview-store.ts
  type LayoutGuideSettings (line 5) | interface LayoutGuideSettings {
  type PreviewOverlaysState (line 9) | interface PreviewOverlaysState {
  type PreviewState (line 13) | interface PreviewState {
  constant DEFAULT_PREVIEW_OVERLAYS (line 32) | const DEFAULT_PREVIEW_OVERLAYS: PreviewOverlaysState = {

FILE: apps/web/src/stores/properties-store.ts
  type ClipEffectsTarget (line 3) | interface ClipEffectsTarget {
  type PropertiesState (line 8) | interface PropertiesState {

FILE: apps/web/src/stores/sounds-store.ts
  type SoundsStore (line 8) | interface SoundsStore {

FILE: apps/web/src/stores/stickers-store.ts
  constant MAX_RECENT_STICKERS (line 13) | const MAX_RECENT_STICKERS = 50;
  function isValidStickerId (line 15) | function isValidStickerId(value: unknown): value is string {
  function sanitizeRecentStickers (line 28) | function sanitizeRecentStickers({
  type ViewMode (line 56) | type ViewMode = "search" | "browse";
  type StickersStore (line 58) | interface StickersStore {

FILE: apps/web/src/stores/timeline-store.ts
  type TimelineStore (line 10) | interface TimelineStore {

FILE: apps/web/src/types/animation.ts
  constant ANIMATION_PROPERTY_PATHS (line 1) | const ANIMATION_PROPERTY_PATHS = [
  type AnimationPropertyPath (line 17) | type AnimationPropertyPath = (typeof ANIMATION_PROPERTY_PATHS)[number];
  type AnimationValueKind (line 19) | type AnimationValueKind = "number" | "color" | "discrete";
  type DiscreteValue (line 20) | type DiscreteValue = boolean | string;
  type AnimationValue (line 21) | type AnimationValue = number | string | boolean;
  type ContinuousKeyframeInterpolation (line 23) | type ContinuousKeyframeInterpolation = "linear" | "hold";
  type DiscreteKeyframeInterpolation (line 24) | type DiscreteKeyframeInterpolation = "hold";
  type AnimationInterpolation (line 25) | type AnimationInterpolation =
  type BaseAnimationKeyframe (line 29) | interface BaseAnimationKeyframe<
  type NumberKeyframe (line 39) | interface NumberKeyframe
  type ColorKeyframe (line 42) | interface ColorKeyframe
  type DiscreteKeyframe (line 45) | interface DiscreteKeyframe
  type AnimationKeyframe (line 48) | type AnimationKeyframe =
  type BaseAnimationChannel (line 53) | interface BaseAnimationChannel<
  type NumberAnimationChannel (line 61) | interface NumberAnimationChannel
  type ColorAnimationChannel (line 64) | interface ColorAnimationChannel
  type DiscreteAnimationChannel (line 67) | interface DiscreteAnimationChannel
  type AnimationChannel (line 70) | type AnimationChannel =
  type ElementAnimationChannelMap (line 75) | type ElementAnimationChannelMap = Record<
  type ElementAnimations (line 80) | interface ElementAnimations {
  type ElementKeyframe (line 84) | interface ElementKeyframe {
  type SelectedKeyframeRef (line 92) | interface SelectedKeyframeRef {

FILE: apps/web/src/types/assets.ts
  type MediaType (line 3) | type MediaType = "image" | "video" | "audio";
  type MediaAsset (line 5) | interface MediaAsset

FILE: apps/web/src/types/blog.ts
  type Post (line 1) | type Post = {
  type Pagination (line 31) | type Pagination = {
  type MarblePostList (line 40) | type MarblePostList = {
  type MarblePost (line 45) | type MarblePost = {
  type Tag (line 49) | type Tag = {
  type MarbleTag (line 55) | type MarbleTag = {
  type MarbleTagList (line 59) | type MarbleTagList = {
  type Category (line 64) | type Category = {
  type MarbleCategory (line 70) | type MarbleCategory = {
  type MarbleCategoryList (line 74) | type MarbleCategoryList = {
  type Author (line 79) | type Author = {
  type MarbleAuthor (line 85) | type MarbleAuthor = {
  type MarbleAuthorList (line 89) | type MarbleAuthorList = {

FILE: apps/web/src/types/drag.ts
  type BaseDragData (line 3) | interface BaseDragData {
  type MediaDragData (line 8) | interface MediaDragData extends BaseDragData {
  type TextDragData (line 14) | interface TextDragData extends BaseDragData {
  type StickerDragData (line 19) | interface StickerDragData extends BaseDragData {
  type EffectDragData (line 24) | interface EffectDragData extends BaseDragData {
  type TimelineDragData (line 30) | type TimelineDragData =

FILE: apps/web/src/types/editor.ts
  type TPlatformLayout (line 1) | type TPlatformLayout = "tiktok";

FILE: apps/web/src/types/effects.ts
  type Effect (line 1) | interface Effect {
  type EffectParamType (line 8) | type EffectParamType = "number" | "boolean" | "select" | "color";
  type EffectParamValues (line 10) | type EffectParamValues = Record<string, number | string | boolean>;
  type BaseEffectParamDefinition (line 12) | interface BaseEffectParamDefinition {
  type NumberEffectParamDefinition (line 17) | interface NumberEffectParamDefinition extends BaseEffectParamDefinition {
  type BooleanEffectParamDefinition (line 25) | interface BooleanEffectParamDefinition extends BaseEffectParamDefinition {
  type SelectEffectParamDefinition (line 30) | interface SelectEffectParamDefinition extends BaseEffectParamDefinition {
  type ColorEffectParamDefinition (line 36) | interface ColorEffectParamDefinition extends BaseEffectParamDefinition {
  type EffectParamDefinition (line 41) | type EffectParamDefinition =
  type WebGLEffectPass (line 47) | interface WebGLEffectPass {
  type WebGLEffectRenderer (line 56) | interface WebGLEffectRenderer {
  type EffectRenderer (line 61) | type EffectRenderer = WebGLEffectRenderer;
  type EffectDefinition (line 63) | interface EffectDefinition {

FILE: apps/web/src/types/export.ts
  constant EXPORT_QUALITY_VALUES (line 1) | const EXPORT_QUALITY_VALUES = [
  constant EXPORT_FORMAT_VALUES (line 8) | const EXPORT_FORMAT_VALUES = ["mp4", "webm"] as const;
  type ExportFormat (line 10) | type ExportFormat = (typeof EXPORT_FORMAT_VALUES)[number];
  type ExportQuality (line 11) | type ExportQuality = (typeof EXPORT_QUALITY_VALUES)[number];
  type ExportOptions (line 13) | interface ExportOptions {
  type ExportResult (line 20) | interface ExportResult {
  type ExportState (line 27) | interface ExportState {

FILE: apps/web/src/types/eyedropper.d.ts
  type EyeDropperResult (line 1) | interface EyeDropperResult {
  type EyeDropper (line 5) | interface EyeDropper {

FILE: apps/web/src/types/fonts.ts
  type FontOption (line 1) | interface FontOption {
  type GoogleFontMeta (line 9) | interface GoogleFontMeta {
  type FontAtlasEntry (line 14) | interface FontAtlasEntry {
  type FontAtlas (line 22) | interface FontAtlas {

FILE: apps/web/src/types/keybinding.ts
  type ModifierKeys (line 7) | type ModifierKeys =
  type Key (line 16) | type Key =
  type ModifierBasedShortcutKey (line 71) | type ModifierBasedShortcutKey = `${ModifierKeys}+${Key}`;
  type SingleCharacterShortcutKey (line 73) | type SingleCharacterShortcutKey = `${Key}`;
  type ShortcutKey (line 75) | type ShortcutKey = ModifierBasedShortcutKey | SingleCharacterShortcutKey;
  type KeybindingConfig (line 77) | type KeybindingConfig = {

FILE: apps/web/src/types/language.ts
  type Language (line 3) | type Language = (typeof LANGUAGES)[number];
  type LanguageCode (line 4) | type LanguageCode = Language["code"];

FILE: apps/web/src/types/project.ts
  type TBackground (line 3) | type TBackground =
  type TCanvasSize (line 13) | interface TCanvasSize {
  type TProjectMetadata (line 18) | interface TProjectMetadata {
  type TProjectSettings (line 27) | interface TProjectSettings {
  type TTimelineViewState (line 34) | interface TTimelineViewState {
  type TProject (line 40) | interface TProject {
  type TProjectSortKey (line 49) | type TProjectSortKey = "createdAt" | "updatedAt" | "name" | "duration";
  type TSortOrder (line 50) | type TSortOrder = "asc" | "desc";
  type TProjectSortOption (line 51) | type TProjectSortOption = `${TProjectSortKey}-${TSortOrder}`;

FILE: apps/web/src/types/rendering.ts
  type Transform (line 1) | interface Transform {
  type BlendMode (line 10) | type BlendMode =

FILE: apps/web/src/types/sounds.ts
  type SoundEffect (line 1) | interface SoundEffect {
  type SavedSound (line 24) | interface SavedSound {
  type SavedSoundsData (line 36) | interface SavedSoundsData {

FILE: apps/web/src/types/stickers.ts
  type StickerCategory (line 3) | type StickerCategory = keyof typeof STICKER_CATEGORIES;
  type StickerItem (line 5) | interface StickerItem {
  type StickerSearchResult (line 13) | interface StickerSearchResult {
  type StickerProviderSearchOptions (line 19) | interface StickerProviderSearchOptions {
  type StickerProviderBrowseOptions (line 23) | interface StickerProviderBrowseOptions {
  type StickerResolveOptions (line 28) | interface StickerResolveOptions {
  type StickerProvider (line 33) | interface StickerProvider {

FILE: apps/web/src/types/time.ts
  type TTimeCode (line 1) | type TTimeCode = "MM:SS" | "HH:MM:SS" | "HH:MM:SS:CS" | "HH:MM:SS:FF";

FILE: apps/web/src/types/timeline.ts
  type Bookmark (line 5) | interface Bookmark {
  type TScene (line 12) | interface TScene {
  type TrackType (line 22) | type TrackType = "video" | "text" | "audio" | "sticker" | "effect";
  type BaseTrack (line 24) | interface BaseTrack {
  type VideoTrack (line 29) | interface VideoTrack extends BaseTrack {
  type TextTrack (line 37) | interface TextTrack extends BaseTrack {
  type AudioTrack (line 43) | interface AudioTrack extends BaseTrack {
  type StickerTrack (line 49) | interface StickerTrack extends BaseTrack {
  type EffectTrack (line 55) | interface EffectTrack extends BaseTrack {
  type TimelineTrack (line 61) | type TimelineTrack =
  type BaseAudioElement (line 70) | interface BaseAudioElement extends BaseTimelineElement {
  type UploadAudioElement (line 77) | interface UploadAudioElement extends BaseAudioElement {
  type LibraryAudioElement (line 82) | interface LibraryAudioElement extends BaseAudioElement {
  type AudioElement (line 87) | type AudioElement = UploadAudioElement | LibraryAudioElement;
  type BaseTimelineElement (line 89) | interface BaseTimelineElement {
  type VideoElement (line 100) | interface VideoElement extends BaseTimelineElement {
  type ImageElement (line 111) | interface ImageElement extends BaseTimelineElement {
  type TextBackground (line 121) | interface TextBackground {
  type TextElement (line 131) | interface TextElement extends BaseTimelineElement {
  type StickerElement (line 151) | interface StickerElement extends BaseTimelineElement {
  type EffectElement (line 161) | interface EffectElement extends BaseTimelineElement {
  type VisualElement (line 167) | type VisualElement =
  type ElementUpdatePatch (line 173) | type ElementUpdatePatch =
  type TimelineElement (line 178) | type TimelineElement =
  type ElementType (line 186) | type ElementType = TimelineElement["type"];
  type CreateUploadAudioElement (line 188) | type CreateUploadAudioElement = Omit<UploadAudioElement, "id">;
  type CreateLibraryAudioElement (line 189) | type CreateLibraryAudioElement = Omit<LibraryAudioElement, "id">;
  type CreateAudioElement (line 190) | type CreateAudioElement =
  type CreateVideoElement (line 193) | type CreateVideoElement = Omit<VideoElement, "id">;
  type CreateImageElement (line 194) | type CreateImageElement = Omit<ImageElement, "id">;
  type CreateTextElement (line 195) | type CreateTextElement = Omit<TextElement, "id">;
  type CreateStickerElement (line 196) | type CreateStickerElement = Omit<StickerElement, "id">;
  type CreateEffectElement (line 197) | type CreateEffectElement = Omit<EffectElement, "id">;
  type CreateTimelineElement (line 198) | type CreateTimelineElement =
  type ElementDragState (line 206) | interface ElementDragState {
  type DropTarget (line 218) | interface DropTarget {
  type ComputeDropTargetParams (line 226) | interface ComputeDropTargetParams {
  type ClipboardItem (line 242) | interface ClipboardItem {

FILE: apps/web/src/types/transcription.ts
  type TranscriptionLanguage (line 3) | type TranscriptionLanguage = LanguageCode | "auto";
  type TranscriptionSegment (line 5) | interface TranscriptionSegment {
  type TranscriptionResult (line 11) | interface TranscriptionResult {
  type TranscriptionStatus (line 17) | type TranscriptionStatus =
  type TranscriptionProgress (line 24) | interface TranscriptionProgress {
  type TranscriptionModelId (line 30) | type TranscriptionModelId =
  type TranscriptionModel (line 36) | interface TranscriptionModel {
  type CaptionChunk (line 43) | interface CaptionChunk {

FILE: apps/web/src/utils/browser.ts
  function downloadBlob (line 1) | function downloadBlob({
  function isTypableDOMElement (line 18) | function isTypableDOMElement({

FILE: apps/web/src/utils/color.ts
  type ColorFormat (line 3) | type ColorFormat = "hex" | "rgb" | "hsl" | "hsv";
  function hexToHsv (line 9) | function hexToHsv({ hex }: { hex: string }): [number, number, number] {
  function hsvToHex (line 15) | function hsvToHex({
  function parseHexAlpha (line 28) | function parseHexAlpha({ hex }: { hex: string }): {
  function appendAlpha (line 42) | function appendAlpha({
  function stripCssNoise (line 55) | function stripCssNoise({ text }: { text: string }): string {
  function colorToHexWithAlpha (line 71) | function colorToHexWithAlpha({ color }: { color: Rgb }): string {
  function extractColorFromText (line 80) | function extractColorFromText({
  function formatColorValue (line 107) | function formatColorValue({
  function parseColorInput (line 135) | function parseColorInput({

FILE: apps/web/src/utils/date.ts
  function formatDate (line 1) | function formatDate({ date }: { date: Date }): string {

FILE: apps/web/src/utils/geometry.ts
  function dimensionToAspectRatio (line 1) | function dimensionToAspectRatio({

FILE: apps/web/src/utils/id.ts
  function generateUUID (line 1) | function generateUUID(): string {

FILE: apps/web/src/utils/math.ts
  function clamp (line 1) | function clamp({
  function isNearlyEqual (line 13) | function isNearlyEqual({
  function evaluateMathExpression (line 25) | function evaluateMathExpression({

FILE: apps/web/src/utils/platform.ts
  function getPlatformSpecialKey (line 1) | function getPlatformSpecialKey(): string {
  function getPlatformAlternateKey (line 5) | function getPlatformAlternateKey(): string {
  function isAppleDevice (line 9) | function isAppleDevice(): boolean {

FILE: apps/web/src/utils/string.ts
  function capitalizeFirstLetter (line 1) | function capitalizeFirstLetter({ string }: { string: string }) {
  function uppercase (line 5) | function uppercase({ string }: { string: string }) {

FILE: apps/web/src/utils/ui.ts
  function cn (line 4) | function cn(...inputs: ClassValue[]): string {

FILE: packages/env/src/tools.ts
  type ToolsEnv (line 27) | type ToolsEnv = z.infer<typeof toolsEnvSchema>;

FILE: packages/env/src/web.ts
  type WebEnv (line 32) | type WebEnv = z.infer<typeof webEnvSchema>;

FILE: packages/ui/src/icons/brand.tsx
  function OcVercelIcon (line 1) | function OcVercelIcon({ className }: { className?: string }) {
  function OcMarbleIcon (line 17) | function OcMarbleIcon({
  function OcDataBuddyIcon (line 43) | function OcDataBuddyIcon({

FILE: packages/ui/src/icons/ui.tsx
  function OcVideoIcon (line 1) | function OcVideoIcon({
  function OcCheckerboardIcon (line 34) | function OcCheckerboardIcon({
  function OcSlidersVerticalIcon (line 68) | function OcSlidersVerticalIcon({
  function OcSocialIcon (line 113) | function OcSocialIcon({
  function OcTextWidthIcon (line 162) | function OcTextWidthIcon({
  function OcTextHeightIcon (line 190) | function OcTextHeightIcon({
  function OcFontIcon (line 218) | function OcFontIcon({
Condensed preview — 510 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (3,076K chars).
[
  {
    "path": ".cursor/commands/review.md",
    "chars": 7651,
    "preview": "# Code Review Checklist\n\nReview every point below carefully to ensure files follow consistent code style and best practi"
  },
  {
    "path": ".cursor/rules/codebase-index.mdc",
    "chars": 53527,
    "preview": "---\nalwaysApply: false\n---\n\n# video-editor-oss Codebase Index\n\n**This file provides an index of exported functions, type"
  },
  {
    "path": ".cursor/rules/comments.mdc",
    "chars": 1001,
    "preview": "---\nalwaysApply: false\n---\n# Comment Guidelines\n\n## Good Comments (Human-style)\n- Explain WHY, not WHAT\n- Document non-o"
  },
  {
    "path": ".cursor/rules/handling-uncertainty.mdc",
    "chars": 558,
    "preview": "---\nalwaysApply: false\n---\n# Handling Uncertainty\n\n## Principle\nIf you can't confidently respond due to missing context,"
  },
  {
    "path": ".cursor/rules/readability.mdc",
    "chars": 328,
    "preview": "---\nalwaysApply: false\n---\n# Readability First\n\nOptimize code for AI agents to understand and modify.\n\nNever abbreviate."
  },
  {
    "path": ".cursor/rules/separation-of-concerns.mdc",
    "chars": 1331,
    "preview": "---\nalwaysApply: false\n---\n# Separation of Concerns\n\n## Core Principle\n\nEach file should have one single purpose/respons"
  },
  {
    "path": ".cursor/rules/ultracite.mdc",
    "chars": 3102,
    "preview": "---\nalwaysApply: false\n---\n# Project Context\n\nUltracite enforces strict type safety, accessibility standards, and consis"
  },
  {
    "path": ".cursor/rules/writing-scannable-code.mdc",
    "chars": 1524,
    "preview": "---\nalwaysApply: false\n---\n# Scannable Code Guidelines/Separating Concerns.\n\n## Core Principle\n\nCode should be scannable"
  },
  {
    "path": ".cursor/settings.json",
    "chars": 57,
    "preview": "{\n\t\"plugins\": {\n\t\t\"figma\": {\n\t\t\t\"enabled\": true\n\t\t}\n\t}\n}\n"
  },
  {
    "path": ".cursor/skills/design/SKILL.md",
    "chars": 4278,
    "preview": "---\nname: frontend-design\ndescription: Create distinctive, production-grade frontend interfaces with high design quality"
  },
  {
    "path": ".dockerignore",
    "chars": 141,
    "preview": "node_modules\n.next\n.git\n.gitignore\n*.md\n.env*\n!.env.example\n.cursor\n.vscode\ndiffs\ndocs\nagent-transcripts\nmcps\nterminals\n"
  },
  {
    "path": ".github/CODE_OF_CONDUCT.md",
    "chars": 3678,
    "preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participa"
  },
  {
    "path": ".github/CONTRIBUTING.md",
    "chars": 5452,
    "preview": "# Contributing to OpenCut\n\n⚠️ We are currently NOT accepting feature PRs while we build out the core editor.\n\nIf you wan"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.yml",
    "chars": 1955,
    "preview": "name: Bug report\ndescription: Create a report to help us improve\ntitle: \"[BUG] \"\nlabels: bug\nbody:\n  - type: input\n    i"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.yml",
    "chars": 1435,
    "preview": "name: Feature request\ndescription: Suggest an idea for OpenCut\ntitle: \"[FEATURE] \"\nlabels: enhancement\nbody:\n  - type: m"
  },
  {
    "path": ".github/SECURITY.md",
    "chars": 805,
    "preview": "# Security Policy\n\n## Supported Versions\n\n| Version | Supported          |\n| ------- | ------------------ |\n| 1.x.x   | "
  },
  {
    "path": ".github/SUPPORT.md",
    "chars": 761,
    "preview": "# Getting Help\n\nThanks for using OpenCut! If you need help, here are your options:\n\n## Documentation\n\n- Check our [READM"
  },
  {
    "path": ".github/copilot-instructions.md",
    "chars": 16761,
    "preview": "---\napplyTo: \"**/*.{ts,tsx,js,jsx}\"\n---\n\n# Project Context\n\nUltracite enforces strict type safety, accessibility standar"
  },
  {
    "path": ".github/pull_request_template.md",
    "chars": 273,
    "preview": "⚠️ READ BEFORE SUBMITTING ⚠️\n\nWe are not currently accepting PRs except for critical bugs.\n\nIf this is a bug fix:\n- [ ] "
  },
  {
    "path": ".github/workflows/bun-ci.yml",
    "chars": 1812,
    "preview": "name: Bun CI\n\nconcurrency:\n  group: bun-ci-${{ github.ref }}\n  cancel-in-progress: true\n\non:\n  push:\n    branches: [main"
  },
  {
    "path": ".gitignore",
    "chars": 292,
    "preview": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# asdf version management\n.tool-v"
  },
  {
    "path": ".npmrc",
    "chars": 46,
    "preview": "install-strategy=\"nested\"\nnode-linker=isolated"
  },
  {
    "path": ".vscode/settings.json",
    "chars": 621,
    "preview": "{\n\t\"editor.defaultFormatter\": \"esbenp.prettier-vscode\",\n\t\"[javascript][typescript][javascriptreact][typescriptreact][jso"
  },
  {
    "path": "AGENTS.md",
    "chars": 2852,
    "preview": "# AGENTS.md\n\n## Overview\n\nPrivacy-first video editor, with a focus on simplicity and ease of use.\n\n## Lib vs Utils\n\n- `l"
  },
  {
    "path": "LICENSE",
    "chars": 1052,
    "preview": "Copyright 2025 OpenCut\r\n\r\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software "
  },
  {
    "path": "README.md",
    "chars": 4045,
    "preview": "<table width=\"100%\">\n  <tr>\n    <td align=\"left\" width=\"120\">\n      <img src=\"apps/web/public/logos/opencut/1k/logo-whit"
  },
  {
    "path": "apps/web/.env.example",
    "chars": 799,
    "preview": "# Environment variables example\n# Copy this file to .env.local and update the values as needed\n\n# Node\nNODE_ENV=developm"
  },
  {
    "path": "apps/web/.gitignore",
    "chars": 72,
    "preview": "# Turborepo\n.turbo\n\n# Env vars\n.env*\n!.env.example\n\n.next/\n\n.font-cache/"
  },
  {
    "path": "apps/web/Dockerfile",
    "chars": 1844,
    "preview": "FROM oven/bun:alpine AS base\n\nFROM base AS builder\n\nWORKDIR /app\n\nARG FREESOUND_CLIENT_ID\nARG FREESOUND_API_KEY\n\nCOPY pa"
  },
  {
    "path": "apps/web/components.json",
    "chars": 402,
    "preview": "{\n\t\"$schema\": \"https://ui.shadcn.com/schema.json\",\n\t\"style\": \"new-york\",\n\t\"rsc\": true,\n\t\"tsx\": true,\n\t\"tailwind\": {\n\t\t\"c"
  },
  {
    "path": "apps/web/content/changelog/0.1.0.md",
    "chars": 1727,
    "preview": "---\nversion: \"0.1.0\"\ndate: \"2026-02-23\"\ntitle: \"Editor foundation\"\ndescription: \"This first release focuses on making ed"
  },
  {
    "path": "apps/web/content/changelog/0.2.0.md",
    "chars": 2646,
    "preview": "---\nversion: \"0.2.0\"\ndate: \"2026-03-01\"\ntitle: \"Motion & effects\"\ndescription: \"This release adds the foundation for mot"
  },
  {
    "path": "apps/web/content-collections.ts",
    "chars": 814,
    "preview": "import { defineCollection, defineConfig } from \"@content-collections/core\";\nimport { z } from \"zod\";\n\nconst changelog = "
  },
  {
    "path": "apps/web/drizzle.config.ts",
    "chars": 560,
    "preview": "import type { Config } from \"drizzle-kit\";\nimport * as dotenv from \"dotenv\";\nimport { webEnv } from \"@opencut/env/web\";\n"
  },
  {
    "path": "apps/web/migrations/0000_brainy_saracen.sql",
    "chars": 2194,
    "preview": "CREATE TABLE \"accounts\" (\n\t\"id\" text PRIMARY KEY NOT NULL,\n\t\"account_id\" text NOT NULL,\n\t\"provider_id\" text NOT NULL,\n\t\""
  },
  {
    "path": "apps/web/migrations/meta/0000_snapshot.json",
    "chars": 7036,
    "preview": "{\n\t\"id\": \"33a6742f-89da-4ac5-958f-421aa1cf9bd6\",\n\t\"prevId\": \"00000000-0000-0000-0000-000000000000\",\n\t\"version\": \"7\",\n\t\"d"
  },
  {
    "path": "apps/web/migrations/meta/_journal.json",
    "chars": 186,
    "preview": "{\n\t\"version\": \"7\",\n\t\"dialect\": \"postgresql\",\n\t\"entries\": [\n\t\t{\n\t\t\t\"idx\": 0,\n\t\t\t\"version\": \"7\",\n\t\t\t\"when\": 1750753385927,"
  },
  {
    "path": "apps/web/next-env.d.ts",
    "chars": 251,
    "preview": "/// <reference types=\"next\" />\n/// <reference types=\"next/image-types/global\" />\nimport \"./.next/dev/types/routes.d.ts\";"
  },
  {
    "path": "apps/web/next.config.ts",
    "chars": 1143,
    "preview": "import type { NextConfig } from \"next\";\nimport { withBotId } from \"botid/next/config\";\nimport { withContentCollections }"
  },
  {
    "path": "apps/web/package.json",
    "chars": 3413,
    "preview": "{\n  \"name\": \"@opencut/web\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"packageManager\": \"bun@1.2.18\",\n  \"scripts\": {\n  "
  },
  {
    "path": "apps/web/postcss.config.mjs",
    "chars": 142,
    "preview": "/** @type {import('postcss-load-config').Config} */\nconst config = {\n\tplugins: {\n\t\t\"@tailwindcss/postcss\": {},\n\t},\n};\n\ne"
  },
  {
    "path": "apps/web/public/browserconfig.xml",
    "chars": 380,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<browserconfig>\n    <msapplication>\n        <tile>\n            <square70x70logo s"
  },
  {
    "path": "apps/web/public/countries.json",
    "chars": 39418,
    "preview": "[\n\t{\n\t\t\"name\": \"Andorra\",\n\t\t\"code\": \"AD\",\n\t\t\"languages\": [\"catalan\"],\n\t\t\"flag_colors\": [\"blue\", \"yellow\", \"red\"],\n\t\t\"reg"
  },
  {
    "path": "apps/web/public/ffmpeg/ffmpeg-core.js",
    "chars": 112117,
    "preview": "var createFFmpegCore = (() => {\n  var _scriptDir =\n    typeof document !== \"undefined\" && document.currentScript\n      ?"
  },
  {
    "path": "apps/web/public/fonts/font-atlas.json",
    "chars": 215766,
    "preview": "{\n\t\"fonts\": {\n\t\t\"42dot Sans\": {\n\t\t\t\"x\": 0,\n\t\t\t\"y\": 0,\n\t\t\t\"w\": 131,\n\t\t\t\"ch\": 0,\n\t\t\t\"s\": [\"300\", \"400\", \"500\", \"600\", \"700"
  },
  {
    "path": "apps/web/public/manifest.json",
    "chars": 895,
    "preview": "{\n\t\"name\": \"OpenCut\",\n\t\"description\": \"A simple but powerful video editor that gets the job done. In your browser.\",\n\t\"d"
  },
  {
    "path": "apps/web/scripts/generate-font-sprites.ts",
    "chars": 7231,
    "preview": "/**\n * Generates font sprite atlas for the font picker.\n *\n * Downloads Google Fonts from Fontsource, renders each font "
  },
  {
    "path": "apps/web/src/app/api/auth/[...all]/route.ts",
    "chars": 150,
    "preview": "import { auth } from \"@/lib/auth/server\";\nimport { toNextJsHandler } from \"better-auth/next-js\";\n\nexport const { POST, G"
  },
  {
    "path": "apps/web/src/app/api/health/route.ts",
    "chars": 77,
    "preview": "export async function GET() {\n\treturn new Response(\"OK\", { status: 200 });\n}\n"
  },
  {
    "path": "apps/web/src/app/api/sounds/search/route.ts",
    "chars": 7435,
    "preview": "import { webEnv } from \"@opencut/env/web\";\nimport { type NextRequest, NextResponse } from \"next/server\";\nimport { z } fr"
  },
  {
    "path": "apps/web/src/app/base-page.tsx",
    "chars": 1240,
    "preview": "import { Header } from \"@/components/header\";\nimport { Footer } from \"@/components/footer\";\nimport { cn } from \"@/utils/"
  },
  {
    "path": "apps/web/src/app/blog/[slug]/page.tsx",
    "chars": 3255,
    "preview": "import type { Metadata } from \"next\";\nimport Image from \"next/image\";\nimport { notFound } from \"next/navigation\";\nimport"
  },
  {
    "path": "apps/web/src/app/blog/page.tsx",
    "chars": 1509,
    "preview": "import type { Metadata } from \"next\";\nimport Link from \"next/link\";\nimport { BasePage } from \"@/app/base-page\";\nimport {"
  },
  {
    "path": "apps/web/src/app/brand/page.tsx",
    "chars": 6982,
    "preview": "\"use client\";\n\nimport type { CSSProperties } from \"react\";\nimport Image from \"next/image\";\nimport Link from \"next/link\";"
  },
  {
    "path": "apps/web/src/app/changelog/[version]/page.tsx",
    "chars": 3301,
    "preview": "import type { Metadata } from \"next\";\nimport Link from \"next/link\";\nimport { notFound } from \"next/navigation\";\nimport {"
  },
  {
    "path": "apps/web/src/app/changelog/components/copy-markdown-button.tsx",
    "chars": 1509,
    "preview": "\"use client\";\n\nimport { useState } from \"react\";\nimport { CheckIcon, ClipboardIcon } from \"lucide-react\";\nimport { getSe"
  },
  {
    "path": "apps/web/src/app/changelog/components/release.tsx",
    "chars": 2414,
    "preview": "import type { ReactNode } from \"react\";\nimport Link from \"next/link\";\nimport { cn } from \"@/utils/ui\";\nimport { getSecti"
  },
  {
    "path": "apps/web/src/app/changelog/page.tsx",
    "chars": 2122,
    "preview": "import type { Metadata } from \"next\";\nimport { BasePage } from \"@/app/base-page\";\nimport { Separator } from \"@/component"
  },
  {
    "path": "apps/web/src/app/changelog/utils.ts",
    "chars": 1194,
    "preview": "import { allChangelogs } from \"content-collections\";\n\nexport type Change = { type: string; text: string };\nexport type R"
  },
  {
    "path": "apps/web/src/app/contributors/page.tsx",
    "chars": 7068,
    "preview": "import type { Metadata } from \"next\";\nimport Link from \"next/link\";\nimport { GitHubContributeSection } from \"@/component"
  },
  {
    "path": "apps/web/src/app/editor/[project_id]/page.tsx",
    "chars": 2992,
    "preview": "\"use client\";\n\nimport { useParams } from \"next/navigation\";\nimport {\n\tResizablePanelGroup,\n\tResizablePanel,\n\tResizableHa"
  },
  {
    "path": "apps/web/src/app/globals.css",
    "chars": 7379,
    "preview": "@import \"tailwindcss\";\n\n/* Custom variant for dark mode */\n@custom-variant dark (&:where(.dark, .dark *));\n\n/* Plugins *"
  },
  {
    "path": "apps/web/src/app/layout.tsx",
    "chars": 1678,
    "preview": "import { ThemeProvider } from \"next-themes\";\nimport Script from \"next/script\";\nimport \"./globals.css\";\nimport { Toaster "
  },
  {
    "path": "apps/web/src/app/metadata.ts",
    "chars": 2075,
    "preview": "import type { Metadata } from \"next\";\nimport { SITE_INFO, SITE_URL } from \"@/constants/site-constants\";\n\nexport const ba"
  },
  {
    "path": "apps/web/src/app/page.tsx",
    "chars": 430,
    "preview": "import { Hero } from \"@/components/landing/hero\";\nimport { Header } from \"@/components/header\";\nimport { Footer } from \""
  },
  {
    "path": "apps/web/src/app/privacy/page.tsx",
    "chars": 8879,
    "preview": "import type { Metadata } from \"next\";\nimport { BasePage } from \"@/app/base-page\";\nimport {\n\tAccordion,\n\tAccordionContent"
  },
  {
    "path": "apps/web/src/app/projects/page.tsx",
    "chars": 26462,
    "preview": "\"use client\";\n\nimport Image from \"next/image\";\nimport Link from \"next/link\";\nimport { useRouter } from \"next/navigation\""
  },
  {
    "path": "apps/web/src/app/projects/store.ts",
    "chars": 3873,
    "preview": "import { create } from \"zustand\";\nimport { persist } from \"zustand/middleware\";\nimport type { TProjectSortKey } from \"@/"
  },
  {
    "path": "apps/web/src/app/roadmap/page.tsx",
    "chars": 4098,
    "preview": "import type { Metadata } from \"next\";\nimport { BasePage } from \"@/app/base-page\";\nimport { GitHubContributeSection } fro"
  },
  {
    "path": "apps/web/src/app/robots.ts",
    "chars": 312,
    "preview": "import type { MetadataRoute } from \"next\";\nimport { SITE_URL } from \"@/constants/site-constants\";\n\nexport default functi"
  },
  {
    "path": "apps/web/src/app/rss.xml/route.ts",
    "chars": 1248,
    "preview": "import { Feed } from \"feed\";\nimport { getPosts } from \"@/lib/blog/query\";\nimport { SITE_INFO, SITE_URL } from \"@/constan"
  },
  {
    "path": "apps/web/src/app/sitemap.ts",
    "chars": 1311,
    "preview": "import { SITE_URL } from \"@/constants/site-constants\";\nimport { getPosts } from \"@/lib/blog/query\";\nimport type { Metada"
  },
  {
    "path": "apps/web/src/app/sponsors/page.tsx",
    "chars": 2362,
    "preview": "import type { Metadata } from \"next\";\nimport Image from \"next/image\";\nimport Link from \"next/link\";\nimport { BasePage } "
  },
  {
    "path": "apps/web/src/app/terms/page.tsx",
    "chars": 9868,
    "preview": "import type { Metadata } from \"next\";\nimport { BasePage } from \"@/app/base-page\";\nimport {\n\tAccordion,\n\tAccordionContent"
  },
  {
    "path": "apps/web/src/components/editable-timecode.tsx",
    "chars": 3507,
    "preview": "\"use client\";\n\nimport { useEffect, useRef, useState } from \"react\";\nimport { formatTimeCode, parseTimeCode } from \"@/lib"
  },
  {
    "path": "apps/web/src/components/editor/dialogs/delete-project-dialog.tsx",
    "chars": 2042,
    "preview": "import { Button } from \"@/components/ui/button\";\nimport {\n\tDialog,\n\tDialogBody,\n\tDialogContent,\n\tDialogFooter,\n\tDialogHe"
  },
  {
    "path": "apps/web/src/components/editor/dialogs/migration-dialog.tsx",
    "chars": 1262,
    "preview": "\"use client\";\n\nimport {\n\tDialog,\n\tDialogContent,\n\tDialogDescription,\n\tDialogHeader,\n\tDialogTitle,\n} from \"@/components/u"
  },
  {
    "path": "apps/web/src/components/editor/dialogs/project-info-dialog.tsx",
    "chars": 1994,
    "preview": "import {\n\tDialog,\n\tDialogBody,\n\tDialogContent,\n\tDialogFooter,\n\tDialogHeader,\n\tDialogTitle,\n} from \"@/components/ui/dialo"
  },
  {
    "path": "apps/web/src/components/editor/dialogs/rename-project-dialog.tsx",
    "chars": 1520,
    "preview": "import { Button } from \"@/components/ui/button\";\nimport {\n\tDialog,\n\tDialogBody,\n\tDialogContent,\n\tDialogFooter,\n\tDialogHe"
  },
  {
    "path": "apps/web/src/components/editor/dialogs/shortcuts-dialog.tsx",
    "chars": 5606,
    "preview": "\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport { toast } from \"sonner\";\nimport {\n\ttype KeyboardShort"
  },
  {
    "path": "apps/web/src/components/editor/editor-header.tsx",
    "chars": 6494,
    "preview": "\"use client\";\n\nimport { Button } from \"../ui/button\";\nimport { useRef, useState } from \"react\";\nimport {\n\tDropdownMenu,\n"
  },
  {
    "path": "apps/web/src/components/editor/export-button.tsx",
    "chars": 9716,
    "preview": "\"use client\";\n\nimport { useState } from \"react\";\nimport { TransitionTopIcon } from \"@hugeicons/core-free-icons\";\nimport "
  },
  {
    "path": "apps/web/src/components/editor/mobile-gate.tsx",
    "chars": 2174,
    "preview": "\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport Link from \"next/link\";\nimport { Button } from \"../ui/"
  },
  {
    "path": "apps/web/src/components/editor/onboarding.tsx",
    "chars": 3574,
    "preview": "\"use client\";\n\nimport { ArrowRightIcon } from \"lucide-react\";\nimport { useState } from \"react\";\nimport ReactMarkdown fro"
  },
  {
    "path": "apps/web/src/components/editor/panels/assets/drag-overlay.tsx",
    "chars": 1512,
    "preview": "import { HugeiconsIcon } from \"@hugeicons/react\";\nimport { UploadIcon } from \"@hugeicons/core-free-icons\";\n\ninterface Me"
  },
  {
    "path": "apps/web/src/components/editor/panels/assets/draggable-item.tsx",
    "chars": 6106,
    "preview": "\"use client\";\n\nimport { Plus } from \"lucide-react\";\nimport { type ReactNode, useEffect, useRef, useState } from \"react\";"
  },
  {
    "path": "apps/web/src/components/editor/panels/assets/index.tsx",
    "chars": 1392,
    "preview": "\"use client\";\n\nimport { Separator } from \"@/components/ui/separator\";\nimport { type Tab, useAssetsPanelStore } from \"@/s"
  },
  {
    "path": "apps/web/src/components/editor/panels/assets/tabbar.tsx",
    "chars": 2875,
    "preview": "\"use client\";\n\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport {\n\tTooltip,\n\tTooltipContent,\n\tTo"
  },
  {
    "path": "apps/web/src/components/editor/panels/assets/views/assets.tsx",
    "chars": 13758,
    "preview": "\"use client\";\n\nimport Image from \"next/image\";\nimport { useMemo, useState } from \"react\";\nimport { toast } from \"sonner\""
  },
  {
    "path": "apps/web/src/components/editor/panels/assets/views/base-view.tsx",
    "chars": 1147,
    "preview": "import { cn } from \"@/utils/ui\";\n\ninterface PanelViewProps extends React.HTMLAttributes<HTMLDivElement> {\n\ttitle?: strin"
  },
  {
    "path": "apps/web/src/components/editor/panels/assets/views/captions.tsx",
    "chars": 4576,
    "preview": "import { Button } from \"@/components/ui/button\";\nimport { PanelView } from \"@/components/editor/panels/assets/views/base"
  },
  {
    "path": "apps/web/src/components/editor/panels/assets/views/effects.tsx",
    "chars": 2388,
    "preview": "\"use client\";\n\nimport { useEffect, useRef, useCallback } from \"react\";\nimport { PanelView } from \"@/components/editor/pa"
  },
  {
    "path": "apps/web/src/components/editor/panels/assets/views/settings-legacy.tsx",
    "chars": 9272,
    "preview": "// @ts-nocheck\n\n\"use client\";\n\nimport Image from \"next/image\";\nimport { memo, useCallback, useMemo } from \"react\";\nimpor"
  },
  {
    "path": "apps/web/src/components/editor/panels/assets/views/settings.tsx",
    "chars": 4603,
    "preview": "\"use client\";\n\nimport { PanelView } from \"@/components/editor/panels/assets/views/base-view\";\nimport {\n\tSelect,\n\tSelectC"
  },
  {
    "path": "apps/web/src/components/editor/panels/assets/views/sounds.tsx",
    "chars": 14496,
    "preview": "\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport { Button } from \"@/components/ui/button\";\nimport {\n\tD"
  },
  {
    "path": "apps/web/src/components/editor/panels/assets/views/stickers.tsx",
    "chars": 8530,
    "preview": "\"use client\";\n\nimport Image from \"next/image\";\nimport type { CSSProperties } from \"react\";\nimport { useEffect, useMemo, "
  },
  {
    "path": "apps/web/src/components/editor/panels/assets/views/text.tsx",
    "chars": 1295,
    "preview": "import { DraggableItem } from \"@/components/editor/panels/assets/draggable-item\";\nimport { PanelView } from \"@/component"
  },
  {
    "path": "apps/web/src/components/editor/panels/preview/bookmark-note-overlay.tsx",
    "chars": 1434,
    "preview": "\"use client\";\n\nimport { useEditor } from \"@/hooks/use-editor\";\nimport { findCurrentScene } from \"@/lib/scenes\";\nimport {"
  },
  {
    "path": "apps/web/src/components/editor/panels/preview/context-menu.tsx",
    "chars": 1100,
    "preview": "\"use client\";\n\nimport {\n\tContextMenuCheckboxItem,\n\tContextMenuContent,\n\tContextMenuItem,\n} from \"@/components/ui/context"
  },
  {
    "path": "apps/web/src/components/editor/panels/preview/index.tsx",
    "chars": 6273,
    "preview": "\"use client\";\n\nimport { useCallback, useMemo, useRef } from \"react\";\nimport useDeepCompareEffect from \"use-deep-compare-"
  },
  {
    "path": "apps/web/src/components/editor/panels/preview/layout-guide-overlay.tsx",
    "chars": 611,
    "preview": "\"use client\";\n\nimport { usePreviewStore } from \"@/stores/preview-store\";\nimport Image from \"next/image\";\n\nfunction TikTo"
  },
  {
    "path": "apps/web/src/components/editor/panels/preview/preview-interaction-overlay.tsx",
    "chars": 1438,
    "preview": "import { usePreviewInteraction } from \"@/hooks/use-preview-interaction\";\nimport { TransformHandles } from \"./transform-h"
  },
  {
    "path": "apps/web/src/components/editor/panels/preview/snap-guides.tsx",
    "chars": 1580,
    "preview": "\"use client\";\n\nimport { useEditor } from \"@/hooks/use-editor\";\nimport type { SnapLine } from \"@/lib/preview/preview-snap"
  },
  {
    "path": "apps/web/src/components/editor/panels/preview/text-edit-overlay.tsx",
    "chars": 4055,
    "preview": "\"use client\";\n\nimport { useCallback, useEffect, useRef } from \"react\";\nimport { useEditor } from \"@/hooks/use-editor\";\ni"
  },
  {
    "path": "apps/web/src/components/editor/panels/preview/toolbar.tsx",
    "chars": 2280,
    "preview": "\"use client\";\n\nimport { useEditor } from \"@/hooks/use-editor\";\nimport { formatTimeCode } from \"@/lib/time\";\nimport { inv"
  },
  {
    "path": "apps/web/src/components/editor/panels/preview/transform-handles.tsx",
    "chars": 7546,
    "preview": "\"use client\";\n\nimport { useTransformHandles } from \"@/hooks/use-transform-handles\";\nimport { useEditor } from \"@/hooks/u"
  },
  {
    "path": "apps/web/src/components/editor/panels/properties/audio-properties.tsx",
    "chars": 101,
    "preview": "export function AudioProperties() {\n\treturn <div className=\"space-y-4 p-5\">Audio properties</div>;\n}\n"
  },
  {
    "path": "apps/web/src/components/editor/panels/properties/clip-effects-properties.tsx",
    "chars": 5434,
    "preview": "\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport type { Effect } from \"@/types/effects\";\nimport type {"
  },
  {
    "path": "apps/web/src/components/editor/panels/properties/effect-param-field.tsx",
    "chars": 3151,
    "preview": "\"use client\";\n\nimport type { EffectParamDefinition, NumberEffectParamDefinition } from \"@/types/effects\";\nimport { clamp"
  },
  {
    "path": "apps/web/src/components/editor/panels/properties/effect-properties.tsx",
    "chars": 1348,
    "preview": "\"use client\";\n\nimport type { EffectElement } from \"@/types/timeline\";\nimport { getEffect } from \"@/lib/effects/registry\""
  },
  {
    "path": "apps/web/src/components/editor/panels/properties/empty-view.tsx",
    "chars": 635,
    "preview": "import { HugeiconsIcon } from \"@hugeicons/react\";\nimport { Settings05Icon } from \"@hugeicons/core-free-icons\";\n\nexport f"
  },
  {
    "path": "apps/web/src/components/editor/panels/properties/hooks/use-element-playhead.ts",
    "chars": 717,
    "preview": "import { useEditor } from \"@/hooks/use-editor\";\nimport { getElementLocalTime } from \"@/lib/animation\";\nimport { TIME_EPS"
  },
  {
    "path": "apps/web/src/components/editor/panels/properties/hooks/use-keyframed-color-property.ts",
    "chars": 2306,
    "preview": "import { useEditor } from \"@/hooks/use-editor\";\nimport {\n\tgetKeyframeAtTime,\n\thasKeyframesForPath,\n\tupsertElementKeyfram"
  },
  {
    "path": "apps/web/src/components/editor/panels/properties/hooks/use-keyframed-number-property.ts",
    "chars": 3100,
    "preview": "import { useEditor } from \"@/hooks/use-editor\";\nimport {\n\tgetKeyframeAtTime,\n\thasKeyframesForPath,\n\tupsertElementKeyfram"
  },
  {
    "path": "apps/web/src/components/editor/panels/properties/hooks/use-property-draft.ts",
    "chars": 1771,
    "preview": "import { useReducer, useRef } from \"react\";\nimport { evaluateMathExpression } from \"@/utils/math\";\n\nfunction looksLikeEx"
  },
  {
    "path": "apps/web/src/components/editor/panels/properties/index.tsx",
    "chars": 2514,
    "preview": "\"use client\";\n\nimport { ScrollArea } from \"@/components/ui/scroll-area\";\nimport { AudioProperties } from \"./audio-proper"
  },
  {
    "path": "apps/web/src/components/editor/panels/properties/keyframe-toggle.tsx",
    "chars": 666,
    "preview": "import { Button } from \"@/components/ui/button\";\nimport { HugeiconsIcon } from \"@hugeicons/react\";\nimport { KeyframeIcon"
  },
  {
    "path": "apps/web/src/components/editor/panels/properties/section.tsx",
    "chars": 5742,
    "preview": "import { createContext, useContext, useEffect, useState } from \"react\";\nimport { cn } from \"@/utils/ui\";\nimport { Hugeic"
  },
  {
    "path": "apps/web/src/components/editor/panels/properties/sections/blending.tsx",
    "chars": 6402,
    "preview": "import { useEditor } from \"@/hooks/use-editor\";\nimport { clamp } from \"@/utils/math\";\nimport { NumberField } from \"@/com"
  },
  {
    "path": "apps/web/src/components/editor/panels/properties/sections/index.tsx",
    "chars": 57,
    "preview": "export * from \"./transform\";\nexport * from \"./blending\";\n"
  },
  {
    "path": "apps/web/src/components/editor/panels/properties/sections/transform.tsx",
    "chars": 10804,
    "preview": "import { NumberField } from \"@/components/ui/number-field\";\nimport { useEditor } from \"@/hooks/use-editor\";\nimport { cla"
  },
  {
    "path": "apps/web/src/components/editor/panels/properties/text-properties.tsx",
    "chars": 20868,
    "preview": "import { Textarea } from \"@/components/ui/textarea\";\nimport { FontPicker } from \"@/components/ui/font-picker\";\nimport ty"
  },
  {
    "path": "apps/web/src/components/editor/panels/properties/video-properties.tsx",
    "chars": 513,
    "preview": "import type {\n\tImageElement,\n\tStickerElement,\n\tVideoElement,\n} from \"@/types/timeline\";\nimport { BlendingSection, Transf"
  },
  {
    "path": "apps/web/src/components/editor/panels/timeline/audio-waveform.tsx",
    "chars": 3729,
    "preview": "import { useEffect, useRef, useState } from \"react\";\nimport WaveSurfer from \"wavesurfer.js\";\n\ninterface AudioWaveformPro"
  },
  {
    "path": "apps/web/src/components/editor/panels/timeline/bookmarks.tsx",
    "chars": 11670,
    "preview": "\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport type { EditorCore } from \"@/core\";\nimport { useEditor"
  },
  {
    "path": "apps/web/src/components/editor/panels/timeline/drag-line.tsx",
    "chars": 628,
    "preview": "import { getDropLineY } from \"@/lib/timeline/drop-utils\";\nimport type { TimelineTrack, DropTarget } from \"@/types/timeli"
  },
  {
    "path": "apps/web/src/components/editor/panels/timeline/index.tsx",
    "chars": 17341,
    "preview": "\"use client\";\n\nimport { ScrollArea } from \"@/components/ui/scroll-area\";\nimport {\n\tDelete02Icon,\n\tTaskAdd02Icon,\n\tViewIc"
  },
  {
    "path": "apps/web/src/components/editor/panels/timeline/snap-indicator.tsx",
    "chars": 1314,
    "preview": "\"use client\";\n\nimport { useSnapIndicatorPosition } from \"@/hooks/timeline/use-snap-indicator-position\";\nimport type { Sn"
  },
  {
    "path": "apps/web/src/components/editor/panels/timeline/timeline-element.tsx",
    "chars": 21711,
    "preview": "\"use client\";\n\nimport { useEditor } from \"@/hooks/use-editor\";\nimport { useAssetsPanelStore } from \"@/stores/assets-pane"
  },
  {
    "path": "apps/web/src/components/editor/panels/timeline/timeline-playhead.tsx",
    "chars": 2934,
    "preview": "\"use client\";\n\nimport { useRef } from \"react\";\nimport {\n\tgetCenteredLineLeft,\n\tTIMELINE_INDICATOR_LINE_WIDTH_PX,\n\ttimeli"
  },
  {
    "path": "apps/web/src/components/editor/panels/timeline/timeline-ruler.tsx",
    "chars": 3611,
    "preview": "import { type JSX, useLayoutEffect, useRef } from \"react\";\nimport { TIMELINE_CONSTANTS } from \"@/constants/timeline-cons"
  },
  {
    "path": "apps/web/src/components/editor/panels/timeline/timeline-tick.tsx",
    "chars": 832,
    "preview": "\"use client\";\n\nimport { timelineTimeToSnappedPixels } from \"@/lib/timeline\";\nimport { formatRulerLabel } from \"@/lib/tim"
  },
  {
    "path": "apps/web/src/components/editor/panels/timeline/timeline-toolbar.tsx",
    "chars": 7000,
    "preview": "import { useEditor } from \"@/hooks/use-editor\";\nimport {\n\tTooltipProvider,\n\tTooltip,\n\tTooltipTrigger,\n\tTooltipContent,\n}"
  },
  {
    "path": "apps/web/src/components/editor/panels/timeline/timeline-track.tsx",
    "chars": 3390,
    "preview": "\"use client\";\n\nimport { useElementSelection } from \"@/hooks/timeline/element/use-element-selection\";\nimport { TimelineEl"
  },
  {
    "path": "apps/web/src/components/editor/scenes-view.tsx",
    "chars": 5437,
    "preview": "\"use client\";\n\nimport {\n\tSheet,\n\tSheetContent,\n\tSheetDescription,\n\tSheetHeader,\n\tSheetTitle,\n\tSheetTrigger,\n} from \"@/co"
  },
  {
    "path": "apps/web/src/components/editor/selection-box.tsx",
    "chars": 1290,
    "preview": "\"use client\";\n\nimport { useMemo } from \"react\";\n\ninterface SelectionBoxProps {\n\tstartPos: { x: number; y: number } | nul"
  },
  {
    "path": "apps/web/src/components/footer.tsx",
    "chars": 3775,
    "preview": "import Link from \"next/link\";\nimport { RiDiscordFill, RiTwitterXLine } from \"react-icons/ri\";\nimport { FaGithub } from \""
  },
  {
    "path": "apps/web/src/components/gitHub-contribute-section.tsx",
    "chars": 1218,
    "preview": "import { Button } from \"./ui/button\";\nimport Link from \"next/link\";\nimport { SOCIAL_LINKS } from \"@/constants/site-const"
  },
  {
    "path": "apps/web/src/components/header.tsx",
    "chars": 5159,
    "preview": "\"use client\";\n\nimport { useState } from \"react\";\nimport Link from \"next/link\";\nimport { motion } from \"motion/react\";\nim"
  },
  {
    "path": "apps/web/src/components/landing/handlebars.tsx",
    "chars": 5393,
    "preview": "\"use client\";\n\nimport { type PropsWithChildren, useEffect, useRef, useState } from \"react\";\n\ntype HandlebarsProps = Prop"
  },
  {
    "path": "apps/web/src/components/landing/hero.tsx",
    "chars": 1328,
    "preview": "\"use client\";\n\nimport { Button } from \"../ui/button\";\nimport { ArrowRight } from \"lucide-react\";\nimport Image from \"next"
  },
  {
    "path": "apps/web/src/components/providers/editor-provider.tsx",
    "chars": 3550,
    "preview": "\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport { useRouter } from \"next/navigation\";\nimport { Loader"
  },
  {
    "path": "apps/web/src/components/storage-provider.tsx",
    "chars": 1947,
    "preview": "\"use client\";\n\nimport { createContext, useContext, useEffect, useRef, useState } from \"react\";\nimport { toast } from \"so"
  },
  {
    "path": "apps/web/src/components/theme-toggle.tsx",
    "chars": 890,
    "preview": "\"use client\";\n\nimport { Button } from \"./ui/button\";\nimport { useTheme } from \"next-themes\";\nimport { cn } from \"@/utils"
  },
  {
    "path": "apps/web/src/components/ui/accordion.tsx",
    "chars": 1929,
    "preview": "\"use client\";\n\nimport * as React from \"react\";\nimport { Accordion as AccordionPrimitive } from \"radix-ui\";\nimport { Chev"
  },
  {
    "path": "apps/web/src/components/ui/alert-dialog.tsx",
    "chars": 4299,
    "preview": "\"use client\";\n\nimport * as React from \"react\";\nimport { AlertDialog as AlertDialogPrimitive } from \"radix-ui\";\n\nimport {"
  },
  {
    "path": "apps/web/src/components/ui/alert.tsx",
    "chars": 1856,
    "preview": "import * as React from \"react\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { cn } from \"@"
  },
  {
    "path": "apps/web/src/components/ui/aspect-ratio.tsx",
    "chars": 152,
    "preview": "\"use client\";\n\nimport { AspectRatio as AspectRatioPrimitive } from \"radix-ui\";\n\nconst AspectRatio = AspectRatioPrimitive"
  },
  {
    "path": "apps/web/src/components/ui/avatar.tsx",
    "chars": 1370,
    "preview": "\"use client\";\n\nimport * as React from \"react\";\nimport { Avatar as AvatarPrimitive } from \"radix-ui\";\n\nimport { cn } from"
  },
  {
    "path": "apps/web/src/components/ui/badge.tsx",
    "chars": 1083,
    "preview": "import type * as React from \"react\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\n\nimport { cn } f"
  },
  {
    "path": "apps/web/src/components/ui/breadcrumb.tsx",
    "chars": 2140,
    "preview": "import { ChevronRight, MoreHorizontal } from \"lucide-react\";\nimport { Slot } from \"radix-ui\";\n\nimport { cn } from \"@/uti"
  },
  {
    "path": "apps/web/src/components/ui/button.tsx",
    "chars": 2133,
    "preview": "import * as React from \"react\";\nimport { Slot as SlotPrimitive } from \"radix-ui\";\nimport { cva, type VariantProps } from"
  },
  {
    "path": "apps/web/src/components/ui/calendar.tsx",
    "chars": 2596,
    "preview": "\"use client\";\n\nimport type * as React from \"react\";\nimport { ChevronLeft, ChevronRight } from \"lucide-react\";\nimport { D"
  },
  {
    "path": "apps/web/src/components/ui/card.tsx",
    "chars": 1772,
    "preview": "import * as React from \"react\";\n\nimport { cn } from \"@/utils/ui\";\n\nconst Card = React.forwardRef<\n\tHTMLDivElement,\n\tReac"
  },
  {
    "path": "apps/web/src/components/ui/checkbox.tsx",
    "chars": 1051,
    "preview": "\"use client\";\n\nimport * as React from \"react\";\nimport { Checkbox as CheckboxPrimitive } from \"radix-ui\";\nimport { Check "
  },
  {
    "path": "apps/web/src/components/ui/collapsible.tsx",
    "chars": 330,
    "preview": "\"use client\";\n\nimport { Collapsible as CollapsiblePrimitive } from \"radix-ui\";\n\nconst Collapsible = CollapsiblePrimitive"
  },
  {
    "path": "apps/web/src/components/ui/color-picker.tsx",
    "chars": 13444,
    "preview": "import { forwardRef, useEffect, useRef, useState } from \"react\";\nimport { cn } from \"@/utils/ui\";\nimport { Input } from "
  },
  {
    "path": "apps/web/src/components/ui/context-menu.tsx",
    "chars": 8224,
    "preview": "\"use client\";\n\nimport * as React from \"react\";\nimport { ContextMenu as ContextMenuPrimitive } from \"radix-ui\";\nimport { "
  },
  {
    "path": "apps/web/src/components/ui/dialog.tsx",
    "chars": 3586,
    "preview": "\"use client\";\n\nimport * as React from \"react\";\nimport { Dialog as DialogPrimitive } from \"radix-ui\";\nimport { X } from \""
  },
  {
    "path": "apps/web/src/components/ui/dropdown-menu.tsx",
    "chars": 7766,
    "preview": "\"use client\";\n\nimport * as React from \"react\";\nimport { DropdownMenu as DropdownMenuPrimitive } from \"radix-ui\";\nimport "
  },
  {
    "path": "apps/web/src/components/ui/font-picker.tsx",
    "chars": 8079,
    "preview": "\"use client\";\n\nimport {\n\tuseState,\n\tuseMemo,\n\tuseRef,\n\tuseEffect,\n\tuseCallback,\n\ttype CSSProperties,\n} from \"react\";\nimp"
  },
  {
    "path": "apps/web/src/components/ui/form.tsx",
    "chars": 3993,
    "preview": "\"use client\";\n\nimport * as React from \"react\";\nimport { type Label as LabelPrimitive, Slot as SlotPrimitive } from \"radi"
  },
  {
    "path": "apps/web/src/components/ui/hover-card.tsx",
    "chars": 1181,
    "preview": "\"use client\";\n\nimport * as React from \"react\";\nimport { HoverCard as HoverCardPrimitive } from \"radix-ui\";\n\nimport { cn "
  },
  {
    "path": "apps/web/src/components/ui/input-with-back.tsx",
    "chars": 2222,
    "preview": "\"use client\";\n\nimport { Button } from \"@/components/ui/button\";\nimport { Input } from \"@/components/ui/input\";\nimport { "
  },
  {
    "path": "apps/web/src/components/ui/input.tsx",
    "chars": 3798,
    "preview": "\"use client\";\n\nimport { Eye, EyeOff, X } from \"lucide-react\";\nimport { cva, type VariantProps } from \"class-variance-aut"
  },
  {
    "path": "apps/web/src/components/ui/label.tsx",
    "chars": 737,
    "preview": "\"use client\";\n\nimport * as React from \"react\";\nimport { Label as LabelPrimitive } from \"radix-ui\";\nimport { cva, type Va"
  },
  {
    "path": "apps/web/src/components/ui/menubar.tsx",
    "chars": 7717,
    "preview": "\"use client\";\n\nimport * as React from \"react\";\nimport { Menubar as MenubarPrimitive } from \"radix-ui\";\nimport { Check, C"
  },
  {
    "path": "apps/web/src/components/ui/navigation-menu.tsx",
    "chars": 4886,
    "preview": "import * as React from \"react\";\nimport { NavigationMenu as NavigationMenuPrimitive } from \"radix-ui\";\nimport { cva } fro"
  },
  {
    "path": "apps/web/src/components/ui/number-field.tsx",
    "chars": 5158,
    "preview": "\"use client\";\n\nimport { cn } from \"@/utils/ui\";\nimport { useRef, useState, type ComponentProps } from \"react\";\nimport { "
  },
  {
    "path": "apps/web/src/components/ui/popover.tsx",
    "chars": 1026,
    "preview": "\"use client\";\n\nimport * as React from \"react\";\nimport { Popover as PopoverPrimitive } from \"radix-ui\";\nimport { cn } fro"
  },
  {
    "path": "apps/web/src/components/ui/progress.tsx",
    "chars": 742,
    "preview": "\"use client\";\n\nimport * as React from \"react\";\nimport { Progress as ProgressPrimitive } from \"radix-ui\";\n\nimport { cn } "
  },
  {
    "path": "apps/web/src/components/ui/prose.tsx",
    "chars": 2391,
    "preview": "import { cn } from \"@/utils/ui\";\nimport rehypeParse from \"rehype-parse\";\nimport { unified } from \"unified\";\nimport { cre"
  },
  {
    "path": "apps/web/src/components/ui/radio-group.tsx",
    "chars": 1374,
    "preview": "\"use client\";\n\nimport * as React from \"react\";\nimport { RadioGroup as RadioGroupPrimitive } from \"radix-ui\";\nimport { Ci"
  },
  {
    "path": "apps/web/src/components/ui/react-markdown-wrapper.tsx",
    "chars": 611,
    "preview": "import ReactMarkdown from \"react-markdown\";\nimport { cn } from \"@/utils/ui\";\n\nexport function ReactMarkdownWrapper({ chi"
  },
  {
    "path": "apps/web/src/components/ui/resizable.tsx",
    "chars": 1428,
    "preview": "\"use client\";\n\nimport * as ResizablePrimitive from \"react-resizable-panels\";\n\nimport { cn } from \"@/utils/ui\";\n\nconst Re"
  },
  {
    "path": "apps/web/src/components/ui/scroll-area.tsx",
    "chars": 388,
    "preview": "import * as React from \"react\";\nimport { cn } from \"@/utils/ui\";\n\nconst ScrollArea = React.forwardRef<\n\tHTMLDivElement,\n"
  },
  {
    "path": "apps/web/src/components/ui/select.tsx",
    "chars": 6361,
    "preview": "\"use client\";\n\nimport * as React from \"react\";\nimport { Select as SelectPrimitive } from \"radix-ui\";\nimport { Check } fr"
  },
  {
    "path": "apps/web/src/components/ui/separator.tsx",
    "chars": 730,
    "preview": "\"use client\";\n\nimport * as React from \"react\";\nimport * as SeparatorPrimitive from \"@radix-ui/react-separator\";\n\nimport "
  },
  {
    "path": "apps/web/src/components/ui/sheet.tsx",
    "chars": 4229,
    "preview": "\"use client\";\n\nimport * as React from \"react\";\nimport { Dialog as SheetPrimitive } from \"radix-ui\";\nimport { cva, type V"
  },
  {
    "path": "apps/web/src/components/ui/skeleton.tsx",
    "chars": 254,
    "preview": "import { cn } from \"@/utils/ui\";\n\nfunction Skeleton({\n\tclassName,\n\t...props\n}: React.HTMLAttributes<HTMLDivElement>) {\n\t"
  },
  {
    "path": "apps/web/src/components/ui/slider.tsx",
    "chars": 1036,
    "preview": "\"use client\";\n\nimport * as React from \"react\";\nimport { Slider as SliderPrimitive } from \"radix-ui\";\n\nimport { cn } from"
  },
  {
    "path": "apps/web/src/components/ui/sonner.tsx",
    "chars": 906,
    "preview": "\"use client\";\n\nimport { useTheme } from \"next-themes\";\nimport { Toaster as Sonner } from \"sonner\";\n\ntype ToasterProps = "
  },
  {
    "path": "apps/web/src/components/ui/spinner.tsx",
    "chars": 437,
    "preview": "import { HugeiconsIcon, type HugeiconsIconProps } from \"@hugeicons/react\";\nimport { Loading03Icon } from \"@hugeicons/cor"
  },
  {
    "path": "apps/web/src/components/ui/split-button.tsx",
    "chars": 2389,
    "preview": "import { Button, type ButtonProps } from \"@/components/ui/button\";\nimport { Separator } from \"@/components/ui/separator\""
  },
  {
    "path": "apps/web/src/components/ui/switch.tsx",
    "chars": 1094,
    "preview": "\"use client\";\n\nimport * as React from \"react\";\nimport { Switch as SwitchPrimitives } from \"radix-ui\";\n\nimport { cn } fro"
  },
  {
    "path": "apps/web/src/components/ui/table.tsx",
    "chars": 2743,
    "preview": "import * as React from \"react\";\n\nimport { cn } from \"@/utils/ui\";\n\nconst Table = React.forwardRef<\n\tHTMLTableElement,\n\tR"
  },
  {
    "path": "apps/web/src/components/ui/tabs.tsx",
    "chars": 1834,
    "preview": "\"use client\";\n\nimport * as React from \"react\";\nimport { Tabs as TabsPrimitive } from \"radix-ui\";\n\nimport { cn } from \"@/"
  },
  {
    "path": "apps/web/src/components/ui/textarea.tsx",
    "chars": 894,
    "preview": "import * as React from \"react\";\n\nimport { cn } from \"@/utils/ui\";\n\nconst Textarea = React.forwardRef<\n\tHTMLTextAreaEleme"
  },
  {
    "path": "apps/web/src/components/ui/toast.tsx",
    "chars": 4701,
    "preview": "\"use client\";\n\nimport * as React from \"react\";\nimport { Toast as ToastPrimitives } from \"radix-ui\";\nimport { cva, type V"
  },
  {
    "path": "apps/web/src/components/ui/toggle-group.tsx",
    "chars": 1674,
    "preview": "\"use client\";\n\nimport * as React from \"react\";\nimport { ToggleGroup as ToggleGroupPrimitive } from \"radix-ui\";\nimport ty"
  },
  {
    "path": "apps/web/src/components/ui/toggle.tsx",
    "chars": 1422,
    "preview": "\"use client\";\n\nimport * as React from \"react\";\nimport { Toggle as TogglePrimitive } from \"radix-ui\";\nimport { cva, type "
  },
  {
    "path": "apps/web/src/components/ui/tooltip.tsx",
    "chars": 2737,
    "preview": "import { cva, type VariantProps } from \"class-variance-authority\";\nimport { Tooltip as TooltipPrimitive } from \"radix-ui"
  },
  {
    "path": "apps/web/src/constants/animation-constants.ts",
    "chars": 87,
    "preview": "export const TIME_EPSILON_SECONDS = 1 / 1000;\nexport const MIN_TRANSFORM_SCALE = 0.01;\n"
  },
  {
    "path": "apps/web/src/constants/editor-constants.ts",
    "chars": 129,
    "preview": "export const PANEL_CONFIG = {\n\tpanels: {\n\t\ttools: 25,\n\t\tpreview: 50,\n\t\tproperties: 25,\n\t\tmainContent: 50,\n\t\ttimeline: 50"
  },
  {
    "path": "apps/web/src/constants/export-constants.ts",
    "chars": 264,
    "preview": "import type { ExportOptions } from \"@/types/export\";\n\nexport const DEFAULT_EXPORT_OPTIONS = {\n\tformat: \"mp4\",\n\tquality: "
  },
  {
    "path": "apps/web/src/constants/font-constants.ts",
    "chars": 203,
    "preview": "export const DEFAULT_FONT = \"Arial\";\n\nexport const SYSTEM_FONTS = new Set([\n\t\"Arial\",\n\t\"Helvetica\",\n\t\"Times New Roman\",\n"
  },
  {
    "path": "apps/web/src/constants/language-constants.ts",
    "chars": 347,
    "preview": "export const LANGUAGES = [\n\t{ code: \"en\", name: \"English\" },\n\t{ code: \"es\", name: \"Spanish\" },\n\t{ code: \"it\", name: \"Ita"
  },
  {
    "path": "apps/web/src/constants/project-constants.ts",
    "chars": 834,
    "preview": "import type { TCanvasSize } from \"@/types/project\";\n\nexport const DEFAULT_CANVAS_PRESETS: TCanvasSize[] = [\n\t{ width: 19"
  },
  {
    "path": "apps/web/src/constants/site-constants.ts",
    "chars": 1699,
    "preview": "import { OcDataBuddyIcon, OcMarbleIcon } from \"@opencut/ui/icons\";\n\nexport const SITE_URL = \"https://opencut.app\";\n\nexpo"
  },
  {
    "path": "apps/web/src/constants/sticker-constants.ts",
    "chars": 139,
    "preview": "export const STICKER_CATEGORIES = {\n\tall: \"All\",\n\tlogos: \"Logos\",\n\ticons: \"Icons\",\n\temoji: \"Emoji\",\n\tflags: \"Flags\",\n\tsh"
  }
]

// ... and 310 more files (download for full content)

About this extraction

This page contains the full source code of the OpenCut-app/OpenCut GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 510 files (33.3 MB), approximately 709.5k tokens, and a symbol index with 1793 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!