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 ` ``` ## 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 ; } // ✅ correct function Parent() { return ; } 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 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 export const TRACK_HEIGHTS: Record 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 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; }) 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; }) 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; }) 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 use-sound-search.ts export function useSoundSearch({ query, commercialOnly, }: { query: string; commercialOnly: boolean; }) use-transform-handles.ts export function useTransformHandles({ canvasRef, }: { canvasRef: React.RefObject; }) ## apps/web/src/hooks/actions use-action-handler.ts export function useActionHandler( action: A, handler: TActionFunc, isActive: TActionHandlerOptions, ) use-editor-actions.ts export function useEditorActions() ## apps/web/src/hooks/storage use-local-storage.ts export function useLocalStorage({ key, defaultValue, }: { key: string; defaultValue: T; }): [ T, ({ value }: { value: T | ((previousValue: T) => T) }) => void, boolean, ] ## apps/web/src/hooks/timeline use-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; }): 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 } export interface CollectionInfo { prefix: string total: number title?: string uncategorized?: string[] categories?: Record hidden?: string[] aliases?: Record } export function getCollections( category?: string, ): Promise> export function getCollection( prefix: string, ): Promise export function searchIcons( query: string, limit: number = 64, prefixes?: string[], category?: string, ): Promise export function buildIconSvgUrl( host: string, iconName: string, params?: { color?: string; width?: number; height?: number; flip?: "horizontal" | "vertical" | "horizontal,vertical"; rotate?: number | string; }, ): string export function getIconSvgUrl( iconName: string, params?: Parameters[2], ): string export function downloadSvgAsText( iconName: string, params?: Parameters[1], ): Promise export function svgToFile(svgText: string, fileName: string): File export const POPULAR_COLLECTIONS export function getCategoriesFromCollections( collections: Record, ): string[] rate-limit.ts export const baseRateLimit export function checkRateLimit({ request }: { request: Request }) scenes.ts export function getMainScene({ scenes }: { scenes: TScene[] }): TScene | null export function ensureMainScene({ scenes }: { scenes: TScene[] }): TScene[] export function buildDefaultScene({ name, isMain, }: { name: string; isMain: boolean; }): TScene export function canDeleteScene({ scene }: { scene: TScene }): { canDelete: boolean; reason?: string; } export function getFallbackSceneAfterDelete({ scenes, deletedSceneId, currentSceneId, }: { scenes: TScene[]; deletedSceneId: string; currentSceneId: string | null; }): TScene | null export function findCurrentScene({ scenes, currentSceneId, }: { scenes: TScene[]; currentSceneId: string; }): TScene | null export function getProjectDurationFromScenes({ scenes, }: { scenes: TScene[]; }): number export function updateSceneInArray({ scenes, sceneId, updates, }: { scenes: TScene[]; sceneId: string; updates: Partial; }): TScene[] time.ts export function roundToFrame({ time, fps, }: { time: number; fps: number; }): number export function formatTimeCode({ timeInSeconds, format = "HH:MM:SS:CS", fps, }: { timeInSeconds: number; format?: TTimeCode; fps?: number; }): string export function parseTimeCode({ timeCode, format = "HH:MM:SS:CS", fps, }: { timeCode: string; format?: TTimeCode; fps: number; }): number | null export function guessTimeCodeFormat({ timeCode, }: { timeCode: string; }): TTimeCode | null export function timeToFrame({ time, fps, }: { time: number; fps: number; }): number export function frameToTime({ frame, fps, }: { frame: number; fps: number; }): number export function snapTimeToFrame({ time, fps, }: { time: number; fps: number; }): number export function getSnappedSeekTime({ rawTime, duration, fps, }: { rawTime: number; duration: number; fps: number; }): number export function getLastFrameTime({ duration, fps, }: { duration: number; fps: number; }): number ## apps/web/src/lib/actions definitions.ts export type TActionCategory = | "playback" | "navigation" | "editing" | "selection" | "history" | "timeline" | "controls" export interface TActionDefinition { description: string category: TActionCategory defaultShortcuts?: ShortcutKey[] args?: Record } export const ACTIONS export type TAction = keyof typeof ACTIONS export function getActionDefinition(action: TAction): TActionDefinition export function getDefaultShortcuts(): Record registry.ts export function bindAction( action: A, handler: TActionFunc, ) export function unbindAction( action: A, handler: TActionFunc, ) export const invokeAction = ( action: A, args?: TArgOfAction, trigger?: TInvocationTrigger, ) => ... types.ts export type TActionArgsMap = { "seek-forward": { seconds: number } | undefined; "seek-backward": { seconds: number } | undef... export type TActionWithArgs = keyof TActionArgsMap export type TActionWithOptionalArgs = | TActionWithNoArgs | TKeysWithValueUndefined export type TActionWithNoArgs = Exclude export type TArgOfAction = A extends TActionWithArgs ? TActionArgsMap[A] : undefined export type TActionFunc = A extends TActionWithArgs ? (arg: TArgOfAction, trigger?: TInvocationTrigger) => void : (_?:... export type TInvocationTrigger = "keypress" | "mouseclick" export type TBoundActionList = { [A in TAction]?: Array>; } export type TActionHandlerOptions = | MutableRefObject | boolean | undefined ## apps/web/src/lib/auth server.ts export const auth export type Auth = typeof auth ## apps/web/src/lib/blog query.ts export function getPosts() export function getTags() export function getSinglePost({ slug }: { slug: string }) export function getCategories() export function getAuthors() export function processHtmlContent({ html, }: { html: string; }): Promise ## apps/web/src/lib/db index.ts export const db schema.ts export const users export const sessions export const accounts export const verifications ## apps/web/src/lib/fonts google-fonts.ts export function getCachedFontAtlas(): FontAtlas | null export function clearFontAtlasCache(): void export function prefetchFontAtlas(): Promise export function loadFullFont({ family, weights = [400, 700], }: { family: string; weights?: number[]; }): Promise export function loadFonts({ families, }: { families: string[]; }): Promise ## apps/web/src/lib/gradients canvas.ts export function drawCssBackground({ ctx, width, height, css, }: { ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D; width: number; height: number; css: string; }): void parser.ts export type GradientOrientation = LinearOrientation | Array export type Color = | { type: "hex"; value: string } | { type: "literal"; value: string } | { type: "rgb"; value: A... export type ColorStop = Color & { length?: Distance } export type GradientAst = { type: GradientType; orientation: GradientOrientation | undefined; colorStops: Array => ... export const GradientParser ## apps/web/src/lib/media audio.ts export type CollectedAudioElement = Omit< AudioElement, "type" | "mediaId" | "volume" | "id" | "name" | "sourceType" | "sourceUrl" ... export function createAudioContext(): AudioContext export interface DecodedAudio { samples: Float32Array sampleRate: number } export function decodeAudioToFloat32({ audioBlob, }: { audioBlob: Blob; }): Promise export function collectAudioElements({ tracks, mediaAssets, audioContext, }: { tracks: TimelineTrack[]; mediaAssets: MediaAsset[]; audioContext: AudioContext; }): Promise export interface AudioClipSource { id: string sourceKey: string file: File startTime: number duration: number trimStart: number trimEnd: number muted: boolean } export function collectAudioMixSources({ tracks, mediaAssets, }: { tracks: TimelineTrack[]; mediaAssets: MediaAsset[]; }): Promise export function collectAudioClips({ tracks, mediaAssets, }: { tracks: TimelineTrack[]; mediaAssets: MediaAsset[]; }): Promise export function createTimelineAudioBuffer({ tracks, mediaAssets, duration, sampleRate = 44100, audioContext, }: { tracks: TimelineTrack[]; mediaAssets: MediaAsset[]; duration: number; sampleRate?: number; audioContext?: AudioContext; }): Promise media-utils.ts export const SUPPORTS_AUDIO: readonly MediaType[] export function mediaSupportsAudio({ media, }: { media: MediaAsset | null | undefined; }): boolean export const getMediaTypeFromFile = ({ file, }: { file: File; }): MediaType | null => ... mediabunny.ts export function getVideoInfo({ videoFile, }: { videoFile: File; }): Promise<{ duration: number; width: number; height: number; fps: number; }> export const extractTimelineAudio = ({ tracks, mediaAssets, totalDuration, onProgress, }: { tracks: TimelineTrack[]; mediaAssets: MediaAsset[]; totalDuration: number; onProgress?: (progress: number) => void; }): Promise => ... processing.ts export interface ProcessedMediaAsset extends Omit export function generateThumbnail({ videoFile, timeInSeconds, }: { videoFile: File; timeInSeconds: number; }): Promise export function generateImageThumbnail({ imageFile, }: { imageFile: File; }): Promise export function processMediaAssets({ files, onProgress, }: { files: FileList | File[]; onProgress?: ({ progress }: { progress: number }) => void; }): Promise ## apps/web/src/lib/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 export function browseStickers({ category, page = 1, limit = DEFAULT_SEARCH_LIMIT, }: { category: StickerCategory; page?: number; limit?: number; }): Promise 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>; }): 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>; 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 { renderer: CanvasRenderer format: ExportFormat quality: ExportQuality shouldIncludeAudio: boolean audioBuffer: AudioBuffer isCancelled constructor({ width, height, fps, format, quality, shouldIncludeAudio, audioBuffer, }: ExportParams) cancel(): void async export({ rootNode, }: { rootNode: RootNode; }): Promise } ## apps/web/src/services/storage indexeddb-adapter.ts export class IndexedDBAdapter implements StorageAdapter { dbName: string storeName: string version: number constructor(dbName: string, storeName: string, version = 1) async get(key: string): Promise async set(key: string, value: T): Promise async remove(key: string): Promise async list(): Promise async getAll(): Promise async clear(): Promise } export function deleteDatabase({ dbName, }: { dbName: string; }): Promise opfs-adapter.ts export class OPFSAdapter implements StorageAdapter { directoryName: string constructor(directoryName = "media") async get(key: string): Promise async set(key: string, file: File): Promise async remove(key: string): Promise async list(): Promise async clear(): Promise static isSupported(): boolean } service.ts export const storageService types.ts export interface StorageAdapter { get(key: string): Promise set(key: string, value: T): Promise remove(key: string): Promise list(): Promise clear(): Promise } export interface MediaAssetData { id: string name: string type: MediaType size: number lastModified: number width?: number height?: number duration?: number fps?: number ephemeral?: boolean thumbnailUrl?: string } export type SerializedScene = Omit & { createdAt: string; updatedAt: string; } export type SerializedProjectMetadata = Omit< TProjectMetadata, "createdAt" | "updatedAt" > & { createdAt: string; updatedAt: string; } export type SerializedProject = Omit & { metadata: SerializedProjectMetadata; scenes: Serializ... export interface StorageConfig { projectsDb: string mediaDb: string savedSoundsDb: string version: number } ## apps/web/src/services/transcription 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 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 { 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 } 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 } 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 browse({ options, }: { options?: StickerProviderBrowseOptions; }): Promise 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 export type CreateLibraryAudioElement = Omit export type CreateAudioElement = | CreateUploadAudioElement | CreateLibraryAudioElement export type CreateVideoElement = Omit export type CreateImageElement = Omit export type CreateTextElement = Omit export type CreateStickerElement = Omit export type CreateTimelineElement = | CreateAudioElement | CreateVideoElement | CreateImageElement | CreateTextElement | CreateSt... export interface ElementDragState { isDragging: boolean elementId: string | null trackId: string | null startMouseX: number startMouseY: number startElementTime: number clickOffsetTime: number currentTime: number currentMouseY: number } export interface DropTarget { trackIndex: number isNewTrack: boolean insertPosition: "above" | "below" | null xPosition: number } export interface ComputeDropTargetParams { elementType: ElementType mouseX: number mouseY: number tracks: TimelineTrack[] playheadTime: number isExternalDrop: boolean elementDuration: number pixelsPerSecond: number zoomLevel: number verticalDragDirection?: "up" | "down" | null startTimeOverride?: number excludeElementId?: string } export interface ClipboardItem { trackId: string trackType: TrackType element: CreateTimelineElement } transcription.ts export type TranscriptionLanguage = LanguageCode | "auto" export interface TranscriptionSegment { text: string start: number end: number } export interface TranscriptionResult { text: string segments: TranscriptionSegment[] language: string } export type TranscriptionStatus = | "idle" | "loading-model" | "transcribing" | "complete" | "error" export interface TranscriptionProgress { status: TranscriptionStatus progress: number message?: string } export type TranscriptionModelId = | "whisper-tiny" | "whisper-small" | "whisper-medium" | "whisper-large-v3-turbo" export interface TranscriptionModel { id: TranscriptionModelId name: string huggingFaceId: string description: string } export interface CaptionChunk { text: string startTime: number duration: number } ## apps/web/src/utils browser.ts export function isTypableDOMElement({ element, }: { element: HTMLElement; }): boolean 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 `...`. ### 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` 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 `` elements in Next.js projects. - Don't use `` 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 `` or ``. - Only use the `scope` prop on `` 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 `...`. - 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` 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 `` elements in Next.js projects. - Don't use `` 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
{tracks.length} tracks
; } ``` 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 ================================================
OpenCut Logo

OpenCut

A free, open-source video editor for web, desktop, and mobile.

## 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. Vercel OSS Program Powered by fal.ai ## 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 ================================================ /// /// 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 ================================================ #ffffff ================================================ 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{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>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)}resolvedPath=PATH.normalizeArray(resolvedPath.split("/").filter(p=>!!p),!resolvedAbsolute).join("/");return(resolvedAbsolute?"/":"")+resolvedPath||"."},relative:(from,to)=>{from=PATH_FS.resolve(from).substr(1);to=PATH_FS.resolve(to).substr(1);function trim(arr){var start=0;for(;start=0;end--){if(arr[end]!=="")break}if(start>end)return[];return arr.slice(start,end-start+1)}var fromParts=trim(from.split("/"));var toParts=trim(to.split("/"));var length=Math.min(fromParts.length,toParts.length);var samePartsLength=length;for(var i=0;i=55296&&c<=57343){len+=4;++i}else{len+=3}}return len}function stringToUTF8Array(str,heap,outIdx,maxBytesToWrite){if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i=55296&&u<=57343){var u1=str.charCodeAt(++i);u=65536+((u&1023)<<10)|u1&1023}if(u<=127){if(outIdx>=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}}heap[outIdx]=0;return outIdx-startIdx}function intArrayFromString(stringy,dontAddNull,length){var len=length>0?length:lengthBytesUTF8(stringy)+1;var u8array=new Array(len);var numBytesWritten=stringToUTF8Array(stringy,u8array,0,u8array.length);if(dontAddNull)u8array.length=numBytesWritten;return u8array}var TTY={ttys:[],init:function(){},shutdown:function(){},register:function(dev,ops){TTY.ttys[dev]={input:[],output:[],ops:ops};FS.registerDevice(dev,TTY.stream_ops)},stream_ops:{open:function(stream){var tty=TTY.ttys[stream.node.rdev];if(!tty){throw new FS.ErrnoError(43)}stream.tty=tty;stream.seekable=false},close:function(stream){stream.tty.ops.fsync(stream.tty)},fsync:function(stream){stream.tty.ops.fsync(stream.tty)},read:function(stream,buffer,offset,length,pos){if(!stream.tty||!stream.tty.ops.get_char){throw new FS.ErrnoError(60)}var bytesRead=0;for(var i=0;i0){out(UTF8ArrayToString(tty.output,0));tty.output=[]}}},default_tty1_ops:{put_char:function(tty,val){if(val===null||val===10){err(UTF8ArrayToString(tty.output,0));tty.output=[]}else{if(val!=0)tty.output.push(val)}},fsync:function(tty){if(tty.output&&tty.output.length>0){err(UTF8ArrayToString(tty.output,0));tty.output=[]}}}};function zeroMemory(address,size){HEAPU8.fill(0,address,address+size);return address}function alignMemory(size,alignment){return Math.ceil(size/alignment)*alignment}function mmapAlloc(size){size=alignMemory(size,65536);var ptr=_emscripten_builtin_memalign(65536,size);if(!ptr)return 0;return zeroMemory(ptr,size)}var MEMFS={ops_table:null,mount:function(mount){return MEMFS.createNode(null,"/",16384|511,0)},createNode:function(parent,name,mode,dev){if(FS.isBlkdev(mode)||FS.isFIFO(mode)){throw new FS.ErrnoError(63)}if(!MEMFS.ops_table){MEMFS.ops_table={dir:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr,lookup:MEMFS.node_ops.lookup,mknod:MEMFS.node_ops.mknod,rename:MEMFS.node_ops.rename,unlink:MEMFS.node_ops.unlink,rmdir:MEMFS.node_ops.rmdir,readdir:MEMFS.node_ops.readdir,symlink:MEMFS.node_ops.symlink},stream:{llseek:MEMFS.stream_ops.llseek}},file:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr},stream:{llseek:MEMFS.stream_ops.llseek,read:MEMFS.stream_ops.read,write:MEMFS.stream_ops.write,allocate:MEMFS.stream_ops.allocate,mmap:MEMFS.stream_ops.mmap,msync:MEMFS.stream_ops.msync}},link:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr,readlink:MEMFS.node_ops.readlink},stream:{}},chrdev:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr},stream:FS.chrdev_stream_ops}}}var node=FS.createNode(parent,name,mode,dev);if(FS.isDir(node.mode)){node.node_ops=MEMFS.ops_table.dir.node;node.stream_ops=MEMFS.ops_table.dir.stream;node.contents={}}else if(FS.isFile(node.mode)){node.node_ops=MEMFS.ops_table.file.node;node.stream_ops=MEMFS.ops_table.file.stream;node.usedBytes=0;node.contents=null}else if(FS.isLink(node.mode)){node.node_ops=MEMFS.ops_table.link.node;node.stream_ops=MEMFS.ops_table.link.stream}else if(FS.isChrdev(node.mode)){node.node_ops=MEMFS.ops_table.chrdev.node;node.stream_ops=MEMFS.ops_table.chrdev.stream}node.timestamp=Date.now();if(parent){parent.contents[name]=node;parent.timestamp=node.timestamp}return node},getFileDataAsTypedArray:function(node){if(!node.contents)return new Uint8Array(0);if(node.contents.subarray)return node.contents.subarray(0,node.usedBytes);return new Uint8Array(node.contents)},expandFileStorage:function(node,newCapacity){var prevCapacity=node.contents?node.contents.length:0;if(prevCapacity>=newCapacity)return;var CAPACITY_DOUBLING_MAX=1024*1024;newCapacity=Math.max(newCapacity,prevCapacity*(prevCapacity>>0);if(prevCapacity!=0)newCapacity=Math.max(newCapacity,256);var oldContents=node.contents;node.contents=new Uint8Array(newCapacity);if(node.usedBytes>0)node.contents.set(oldContents.subarray(0,node.usedBytes),0)},resizeFileStorage:function(node,newSize){if(node.usedBytes==newSize)return;if(newSize==0){node.contents=null;node.usedBytes=0}else{var oldContents=node.contents;node.contents=new Uint8Array(newSize);if(oldContents){node.contents.set(oldContents.subarray(0,Math.min(newSize,node.usedBytes)))}node.usedBytes=newSize}},node_ops:{getattr:function(node){var attr={};attr.dev=FS.isChrdev(node.mode)?node.id:1;attr.ino=node.id;attr.mode=node.mode;attr.nlink=1;attr.uid=0;attr.gid=0;attr.rdev=node.rdev;if(FS.isDir(node.mode)){attr.size=4096}else if(FS.isFile(node.mode)){attr.size=node.usedBytes}else if(FS.isLink(node.mode)){attr.size=node.link.length}else{attr.size=0}attr.atime=new Date(node.timestamp);attr.mtime=new Date(node.timestamp);attr.ctime=new Date(node.timestamp);attr.blksize=4096;attr.blocks=Math.ceil(attr.size/attr.blksize);return attr},setattr:function(node,attr){if(attr.mode!==undefined){node.mode=attr.mode}if(attr.timestamp!==undefined){node.timestamp=attr.timestamp}if(attr.size!==undefined){MEMFS.resizeFileStorage(node,attr.size)}},lookup:function(parent,name){throw FS.genericErrors[44]},mknod:function(parent,name,mode,dev){return MEMFS.createNode(parent,name,mode,dev)},rename:function(old_node,new_dir,new_name){if(FS.isDir(old_node.mode)){var new_node;try{new_node=FS.lookupNode(new_dir,new_name)}catch(e){}if(new_node){for(var i in new_node.contents){throw new FS.ErrnoError(55)}}}delete old_node.parent.contents[old_node.name];old_node.parent.timestamp=Date.now();old_node.name=new_name;new_dir.contents[new_name]=old_node;new_dir.timestamp=old_node.parent.timestamp;old_node.parent=new_dir},unlink:function(parent,name){delete parent.contents[name];parent.timestamp=Date.now()},rmdir:function(parent,name){var node=FS.lookupNode(parent,name);for(var i in node.contents){throw new FS.ErrnoError(55)}delete parent.contents[name];parent.timestamp=Date.now()},readdir:function(node){var entries=[".",".."];for(var key in node.contents){if(!node.contents.hasOwnProperty(key)){continue}entries.push(key)}return entries},symlink:function(parent,newname,oldpath){var node=MEMFS.createNode(parent,newname,511|40960,0);node.link=oldpath;return node},readlink:function(node){if(!FS.isLink(node.mode)){throw new FS.ErrnoError(28)}return node.link}},stream_ops:{read:function(stream,buffer,offset,length,position){var contents=stream.node.contents;if(position>=stream.node.usedBytes)return 0;var size=Math.min(stream.node.usedBytes-position,length);if(size>8&&contents.subarray){buffer.set(contents.subarray(position,position+size),offset)}else{for(var i=0;i0||position+length{assert(arrayBuffer,`Loading data file "${url}" failed (no arrayBuffer).`);onload(new Uint8Array(arrayBuffer));if(dep)removeRunDependency(dep)},event=>{if(onerror){onerror()}else{throw`Loading data file "${url}" failed.`}});if(dep)addRunDependency(dep)}var preloadPlugins=Module["preloadPlugins"]||[];function FS_handledByPreloadPlugin(byteArray,fullname,finish,onerror){if(typeof Browser!="undefined")Browser.init();var handled=false;preloadPlugins.forEach(function(plugin){if(handled)return;if(plugin["canHandle"](fullname)){plugin["handle"](byteArray,fullname,finish,onerror);handled=true}});return handled}function FS_createPreloadedFile(parent,name,url,canRead,canWrite,onload,onerror,dontCreateFile,canOwn,preFinish){var fullname=name?PATH_FS.resolve(PATH.join2(parent,name)):parent;var dep=getUniqueRunDependency(`cp ${fullname}`);function processData(byteArray){function finish(byteArray){if(preFinish)preFinish();if(!dontCreateFile){FS.createDataFile(parent,name,byteArray,canRead,canWrite,canOwn)}if(onload)onload();removeRunDependency(dep)}if(FS_handledByPreloadPlugin(byteArray,fullname,finish,()=>{if(onerror)onerror();removeRunDependency(dep)})){return}finish(byteArray)}addRunDependency(dep);if(typeof url=="string"){asyncLoad(url,byteArray=>processData(byteArray),onerror)}else{processData(url)}}function FS_modeStringToFlags(str){var flagModes={"r":0,"r+":2,"w":512|64|1,"w+":512|64|2,"a":1024|64|1,"a+":1024|64|2};var flags=flagModes[str];if(typeof flags=="undefined"){throw new Error(`Unknown file open mode: ${str}`)}return flags}function FS_getMode(canRead,canWrite){var mode=0;if(canRead)mode|=292|73;if(canWrite)mode|=146;return mode}var WORKERFS={DIR_MODE:16895,FILE_MODE:33279,reader:null,mount:function(mount){assert(ENVIRONMENT_IS_WORKER);if(!WORKERFS.reader)WORKERFS.reader=new FileReaderSync;var root=WORKERFS.createNode(null,"/",WORKERFS.DIR_MODE,0);var createdParents={};function ensureParent(path){var parts=path.split("/");var parent=root;for(var i=0;i=stream.node.size)return 0;var chunk=stream.node.contents.slice(position,position+length);var ab=WORKERFS.reader.readAsArrayBuffer(chunk);buffer.set(new Uint8Array(ab),offset);return chunk.size},write:function(stream,buffer,offset,length,position){throw new FS.ErrnoError(29)},llseek:function(stream,offset,whence){var position=offset;if(whence===1){position+=stream.position}else if(whence===2){if(FS.isFile(stream.node.mode)){position+=stream.node.size}}if(position<0){throw new FS.ErrnoError(28)}return position}}};var FS={root:null,mounts:[],devices:{},streams:[],nextInode:1,nameTable:null,currentPath:"/",initialized:false,ignorePermissions:true,ErrnoError:null,genericErrors:{},filesystems:null,syncFSRequests:0,lookupPath:(path,opts={})=>{path=PATH_FS.resolve(path);if(!path)return{path:"",node:null};var defaults={follow_mount:true,recurse_count:0};opts=Object.assign(defaults,opts);if(opts.recurse_count>8){throw new FS.ErrnoError(32)}var parts=path.split("/").filter(p=>!!p);var current=FS.root;var current_path="/";for(var i=0;i40){throw new FS.ErrnoError(32)}}}}return{path:current_path,node:current}},getPath:node=>{var path;while(true){if(FS.isRoot(node)){var mount=node.mount.mountpoint;if(!path)return mount;return mount[mount.length-1]!=="/"?`${mount}/${path}`:mount+path}path=path?`${node.name}/${path}`:node.name;node=node.parent}},hashName:(parentid,name)=>{var hash=0;for(var i=0;i>>0)%FS.nameTable.length},hashAddNode:node=>{var hash=FS.hashName(node.parent.id,node.name);node.name_next=FS.nameTable[hash];FS.nameTable[hash]=node},hashRemoveNode:node=>{var hash=FS.hashName(node.parent.id,node.name);if(FS.nameTable[hash]===node){FS.nameTable[hash]=node.name_next}else{var current=FS.nameTable[hash];while(current){if(current.name_next===node){current.name_next=node.name_next;break}current=current.name_next}}},lookupNode:(parent,name)=>{var errCode=FS.mayLookup(parent);if(errCode){throw new FS.ErrnoError(errCode,parent)}var hash=FS.hashName(parent.id,name);for(var node=FS.nameTable[hash];node;node=node.name_next){var nodeName=node.name;if(node.parent.id===parent.id&&nodeName===name){return node}}return FS.lookup(parent,name)},createNode:(parent,name,mode,rdev)=>{var node=new FS.FSNode(parent,name,mode,rdev);FS.hashAddNode(node);return node},destroyNode:node=>{FS.hashRemoveNode(node)},isRoot:node=>{return node===node.parent},isMountpoint:node=>{return!!node.mounted},isFile:mode=>{return(mode&61440)===32768},isDir:mode=>{return(mode&61440)===16384},isLink:mode=>{return(mode&61440)===40960},isChrdev:mode=>{return(mode&61440)===8192},isBlkdev:mode=>{return(mode&61440)===24576},isFIFO:mode=>{return(mode&61440)===4096},isSocket:mode=>{return(mode&49152)===49152},flagsToPermissionString:flag=>{var perms=["r","w","rw"][flag&3];if(flag&512){perms+="w"}return perms},nodePermissions:(node,perms)=>{if(FS.ignorePermissions){return 0}if(perms.includes("r")&&!(node.mode&292)){return 2}else if(perms.includes("w")&&!(node.mode&146)){return 2}else if(perms.includes("x")&&!(node.mode&73)){return 2}return 0},mayLookup:dir=>{var errCode=FS.nodePermissions(dir,"x");if(errCode)return errCode;if(!dir.node_ops.lookup)return 2;return 0},mayCreate:(dir,name)=>{try{var node=FS.lookupNode(dir,name);return 20}catch(e){}return FS.nodePermissions(dir,"wx")},mayDelete:(dir,name,isdir)=>{var node;try{node=FS.lookupNode(dir,name)}catch(e){return e.errno}var errCode=FS.nodePermissions(dir,"wx");if(errCode){return errCode}if(isdir){if(!FS.isDir(node.mode)){return 54}if(FS.isRoot(node)||FS.getPath(node)===FS.cwd()){return 10}}else{if(FS.isDir(node.mode)){return 31}}return 0},mayOpen:(node,flags)=>{if(!node){return 44}if(FS.isLink(node.mode)){return 32}else if(FS.isDir(node.mode)){if(FS.flagsToPermissionString(flags)!=="r"||flags&512){return 31}}return FS.nodePermissions(node,FS.flagsToPermissionString(flags))},MAX_OPEN_FDS:4096,nextfd:()=>{for(var fd=0;fd<=FS.MAX_OPEN_FDS;fd++){if(!FS.streams[fd]){return fd}}throw new FS.ErrnoError(33)},getStream:fd=>FS.streams[fd],createStream:(stream,fd=-1)=>{if(!FS.FSStream){FS.FSStream=function(){this.shared={}};FS.FSStream.prototype={};Object.defineProperties(FS.FSStream.prototype,{object:{get:function(){return this.node},set:function(val){this.node=val}},isRead:{get:function(){return(this.flags&2097155)!==1}},isWrite:{get:function(){return(this.flags&2097155)!==0}},isAppend:{get:function(){return this.flags&1024}},flags:{get:function(){return this.shared.flags},set:function(val){this.shared.flags=val}},position:{get:function(){return this.shared.position},set:function(val){this.shared.position=val}}})}stream=Object.assign(new FS.FSStream,stream);if(fd==-1){fd=FS.nextfd()}stream.fd=fd;FS.streams[fd]=stream;return stream},closeStream:fd=>{FS.streams[fd]=null},chrdev_stream_ops:{open:stream=>{var device=FS.getDevice(stream.node.rdev);stream.stream_ops=device.stream_ops;if(stream.stream_ops.open){stream.stream_ops.open(stream)}},llseek:()=>{throw new FS.ErrnoError(70)}},major:dev=>dev>>8,minor:dev=>dev&255,makedev:(ma,mi)=>ma<<8|mi,registerDevice:(dev,ops)=>{FS.devices[dev]={stream_ops:ops}},getDevice:dev=>FS.devices[dev],getMounts:mount=>{var mounts=[];var check=[mount];while(check.length){var m=check.pop();mounts.push(m);check.push.apply(check,m.mounts)}return mounts},syncfs:(populate,callback)=>{if(typeof populate=="function"){callback=populate;populate=false}FS.syncFSRequests++;if(FS.syncFSRequests>1){err(`warning: ${FS.syncFSRequests} FS.syncfs operations in flight at once, probably just doing extra work`)}var mounts=FS.getMounts(FS.root.mount);var completed=0;function doCallback(errCode){FS.syncFSRequests--;return callback(errCode)}function done(errCode){if(errCode){if(!done.errored){done.errored=true;return doCallback(errCode)}return}if(++completed>=mounts.length){doCallback(null)}}mounts.forEach(mount=>{if(!mount.type.syncfs){return done(null)}mount.type.syncfs(mount,populate,done)})},mount:(type,opts,mountpoint)=>{var root=mountpoint==="/";var pseudo=!mountpoint;var node;if(root&&FS.root){throw new FS.ErrnoError(10)}else if(!root&&!pseudo){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});mountpoint=lookup.path;node=lookup.node;if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}if(!FS.isDir(node.mode)){throw new FS.ErrnoError(54)}}var mount={type:type,opts:opts,mountpoint:mountpoint,mounts:[]};var mountRoot=type.mount(mount);mountRoot.mount=mount;mount.root=mountRoot;if(root){FS.root=mountRoot}else if(node){node.mounted=mount;if(node.mount){node.mount.mounts.push(mount)}}return mountRoot},unmount:mountpoint=>{var lookup=FS.lookupPath(mountpoint,{follow_mount:false});if(!FS.isMountpoint(lookup.node)){throw new FS.ErrnoError(28)}var node=lookup.node;var mount=node.mounted;var mounts=FS.getMounts(mount);Object.keys(FS.nameTable).forEach(hash=>{var current=FS.nameTable[hash];while(current){var next=current.name_next;if(mounts.includes(current.mount)){FS.destroyNode(current)}current=next}});node.mounted=null;var idx=node.mount.mounts.indexOf(mount);node.mount.mounts.splice(idx,1)},lookup:(parent,name)=>{return parent.node_ops.lookup(parent,name)},mknod:(path,mode,dev)=>{var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;var name=PATH.basename(path);if(!name||name==="."||name===".."){throw new FS.ErrnoError(28)}var errCode=FS.mayCreate(parent,name);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.mknod){throw new FS.ErrnoError(63)}return parent.node_ops.mknod(parent,name,mode,dev)},create:(path,mode)=>{mode=mode!==undefined?mode:438;mode&=4095;mode|=32768;return FS.mknod(path,mode,0)},mkdir:(path,mode)=>{mode=mode!==undefined?mode:511;mode&=511|512;mode|=16384;return FS.mknod(path,mode,0)},mkdirTree:(path,mode)=>{var dirs=path.split("/");var d="";for(var i=0;i{if(typeof dev=="undefined"){dev=mode;mode=438}mode|=8192;return FS.mknod(path,mode,dev)},symlink:(oldpath,newpath)=>{if(!PATH_FS.resolve(oldpath)){throw new FS.ErrnoError(44)}var lookup=FS.lookupPath(newpath,{parent:true});var parent=lookup.node;if(!parent){throw new FS.ErrnoError(44)}var newname=PATH.basename(newpath);var errCode=FS.mayCreate(parent,newname);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.symlink){throw new FS.ErrnoError(63)}return parent.node_ops.symlink(parent,newname,oldpath)},rename:(old_path,new_path)=>{var old_dirname=PATH.dirname(old_path);var new_dirname=PATH.dirname(new_path);var old_name=PATH.basename(old_path);var new_name=PATH.basename(new_path);var lookup,old_dir,new_dir;lookup=FS.lookupPath(old_path,{parent:true});old_dir=lookup.node;lookup=FS.lookupPath(new_path,{parent:true});new_dir=lookup.node;if(!old_dir||!new_dir)throw new FS.ErrnoError(44);if(old_dir.mount!==new_dir.mount){throw new FS.ErrnoError(75)}var old_node=FS.lookupNode(old_dir,old_name);var relative=PATH_FS.relative(old_path,new_dirname);if(relative.charAt(0)!=="."){throw new FS.ErrnoError(28)}relative=PATH_FS.relative(new_path,old_dirname);if(relative.charAt(0)!=="."){throw new FS.ErrnoError(55)}var new_node;try{new_node=FS.lookupNode(new_dir,new_name)}catch(e){}if(old_node===new_node){return}var isdir=FS.isDir(old_node.mode);var errCode=FS.mayDelete(old_dir,old_name,isdir);if(errCode){throw new FS.ErrnoError(errCode)}errCode=new_node?FS.mayDelete(new_dir,new_name,isdir):FS.mayCreate(new_dir,new_name);if(errCode){throw new FS.ErrnoError(errCode)}if(!old_dir.node_ops.rename){throw new FS.ErrnoError(63)}if(FS.isMountpoint(old_node)||new_node&&FS.isMountpoint(new_node)){throw new FS.ErrnoError(10)}if(new_dir!==old_dir){errCode=FS.nodePermissions(old_dir,"w");if(errCode){throw new FS.ErrnoError(errCode)}}FS.hashRemoveNode(old_node);try{old_dir.node_ops.rename(old_node,new_dir,new_name)}catch(e){throw e}finally{FS.hashAddNode(old_node)}},rmdir:path=>{var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;var name=PATH.basename(path);var node=FS.lookupNode(parent,name);var errCode=FS.mayDelete(parent,name,true);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.rmdir){throw new FS.ErrnoError(63)}if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}parent.node_ops.rmdir(parent,name);FS.destroyNode(node)},readdir:path=>{var lookup=FS.lookupPath(path,{follow:true});var node=lookup.node;if(!node.node_ops.readdir){throw new FS.ErrnoError(54)}return node.node_ops.readdir(node)},unlink:path=>{var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;if(!parent){throw new FS.ErrnoError(44)}var name=PATH.basename(path);var node=FS.lookupNode(parent,name);var errCode=FS.mayDelete(parent,name,false);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.unlink){throw new FS.ErrnoError(63)}if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}parent.node_ops.unlink(parent,name);FS.destroyNode(node)},readlink:path=>{var lookup=FS.lookupPath(path);var link=lookup.node;if(!link){throw new FS.ErrnoError(44)}if(!link.node_ops.readlink){throw new FS.ErrnoError(28)}return PATH_FS.resolve(FS.getPath(link.parent),link.node_ops.readlink(link))},stat:(path,dontFollow)=>{var lookup=FS.lookupPath(path,{follow:!dontFollow});var node=lookup.node;if(!node){throw new FS.ErrnoError(44)}if(!node.node_ops.getattr){throw new FS.ErrnoError(63)}return node.node_ops.getattr(node)},lstat:path=>{return FS.stat(path,true)},chmod:(path,mode,dontFollow)=>{var node;if(typeof path=="string"){var lookup=FS.lookupPath(path,{follow:!dontFollow});node=lookup.node}else{node=path}if(!node.node_ops.setattr){throw new FS.ErrnoError(63)}node.node_ops.setattr(node,{mode:mode&4095|node.mode&~4095,timestamp:Date.now()})},lchmod:(path,mode)=>{FS.chmod(path,mode,true)},fchmod:(fd,mode)=>{var stream=FS.getStream(fd);if(!stream){throw new FS.ErrnoError(8)}FS.chmod(stream.node,mode)},chown:(path,uid,gid,dontFollow)=>{var node;if(typeof path=="string"){var lookup=FS.lookupPath(path,{follow:!dontFollow});node=lookup.node}else{node=path}if(!node.node_ops.setattr){throw new FS.ErrnoError(63)}node.node_ops.setattr(node,{timestamp:Date.now()})},lchown:(path,uid,gid)=>{FS.chown(path,uid,gid,true)},fchown:(fd,uid,gid)=>{var stream=FS.getStream(fd);if(!stream){throw new FS.ErrnoError(8)}FS.chown(stream.node,uid,gid)},truncate:(path,len)=>{if(len<0){throw new FS.ErrnoError(28)}var node;if(typeof path=="string"){var lookup=FS.lookupPath(path,{follow:true});node=lookup.node}else{node=path}if(!node.node_ops.setattr){throw new FS.ErrnoError(63)}if(FS.isDir(node.mode)){throw new FS.ErrnoError(31)}if(!FS.isFile(node.mode)){throw new FS.ErrnoError(28)}var errCode=FS.nodePermissions(node,"w");if(errCode){throw new FS.ErrnoError(errCode)}node.node_ops.setattr(node,{size:len,timestamp:Date.now()})},ftruncate:(fd,len)=>{var stream=FS.getStream(fd);if(!stream){throw new FS.ErrnoError(8)}if((stream.flags&2097155)===0){throw new FS.ErrnoError(28)}FS.truncate(stream.node,len)},utime:(path,atime,mtime)=>{var lookup=FS.lookupPath(path,{follow:true});var node=lookup.node;node.node_ops.setattr(node,{timestamp:Math.max(atime,mtime)})},open:(path,flags,mode)=>{if(path===""){throw new FS.ErrnoError(44)}flags=typeof flags=="string"?FS_modeStringToFlags(flags):flags;mode=typeof mode=="undefined"?438:mode;if(flags&64){mode=mode&4095|32768}else{mode=0}var node;if(typeof path=="object"){node=path}else{path=PATH.normalize(path);try{var lookup=FS.lookupPath(path,{follow:!(flags&131072)});node=lookup.node}catch(e){}}var created=false;if(flags&64){if(node){if(flags&128){throw new FS.ErrnoError(20)}}else{node=FS.mknod(path,mode,0);created=true}}if(!node){throw new FS.ErrnoError(44)}if(FS.isChrdev(node.mode)){flags&=~512}if(flags&65536&&!FS.isDir(node.mode)){throw new FS.ErrnoError(54)}if(!created){var errCode=FS.mayOpen(node,flags);if(errCode){throw new FS.ErrnoError(errCode)}}if(flags&512&&!created){FS.truncate(node,0)}flags&=~(128|512|131072);var stream=FS.createStream({node:node,path:FS.getPath(node),flags:flags,seekable:true,position:0,stream_ops:node.stream_ops,ungotten:[],error:false});if(stream.stream_ops.open){stream.stream_ops.open(stream)}if(Module["logReadFiles"]&&!(flags&1)){if(!FS.readFiles)FS.readFiles={};if(!(path in FS.readFiles)){FS.readFiles[path]=1}}return stream},close:stream=>{if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if(stream.getdents)stream.getdents=null;try{if(stream.stream_ops.close){stream.stream_ops.close(stream)}}catch(e){throw e}finally{FS.closeStream(stream.fd)}stream.fd=null},isClosed:stream=>{return stream.fd===null},llseek:(stream,offset,whence)=>{if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if(!stream.seekable||!stream.stream_ops.llseek){throw new FS.ErrnoError(70)}if(whence!=0&&whence!=1&&whence!=2){throw new FS.ErrnoError(28)}stream.position=stream.stream_ops.llseek(stream,offset,whence);stream.ungotten=[];return stream.position},read:(stream,buffer,offset,length,position)=>{if(length<0||position<0){throw new FS.ErrnoError(28)}if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if((stream.flags&2097155)===1){throw new FS.ErrnoError(8)}if(FS.isDir(stream.node.mode)){throw new FS.ErrnoError(31)}if(!stream.stream_ops.read){throw new FS.ErrnoError(28)}var seeking=typeof position!="undefined";if(!seeking){position=stream.position}else if(!stream.seekable){throw new FS.ErrnoError(70)}var bytesRead=stream.stream_ops.read(stream,buffer,offset,length,position);if(!seeking)stream.position+=bytesRead;return bytesRead},write:(stream,buffer,offset,length,position,canOwn)=>{if(length<0||position<0){throw new FS.ErrnoError(28)}if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if((stream.flags&2097155)===0){throw new FS.ErrnoError(8)}if(FS.isDir(stream.node.mode)){throw new FS.ErrnoError(31)}if(!stream.stream_ops.write){throw new FS.ErrnoError(28)}if(stream.seekable&&stream.flags&1024){FS.llseek(stream,0,2)}var seeking=typeof position!="undefined";if(!seeking){position=stream.position}else if(!stream.seekable){throw new FS.ErrnoError(70)}var bytesWritten=stream.stream_ops.write(stream,buffer,offset,length,position,canOwn);if(!seeking)stream.position+=bytesWritten;return bytesWritten},allocate:(stream,offset,length)=>{if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if(offset<0||length<=0){throw new FS.ErrnoError(28)}if((stream.flags&2097155)===0){throw new FS.ErrnoError(8)}if(!FS.isFile(stream.node.mode)&&!FS.isDir(stream.node.mode)){throw new FS.ErrnoError(43)}if(!stream.stream_ops.allocate){throw new FS.ErrnoError(138)}stream.stream_ops.allocate(stream,offset,length)},mmap:(stream,length,position,prot,flags)=>{if((prot&2)!==0&&(flags&2)===0&&(stream.flags&2097155)!==2){throw new FS.ErrnoError(2)}if((stream.flags&2097155)===1){throw new FS.ErrnoError(2)}if(!stream.stream_ops.mmap){throw new FS.ErrnoError(43)}return stream.stream_ops.mmap(stream,length,position,prot,flags)},msync:(stream,buffer,offset,length,mmapFlags)=>{if(!stream.stream_ops.msync){return 0}return stream.stream_ops.msync(stream,buffer,offset,length,mmapFlags)},munmap:stream=>0,ioctl:(stream,cmd,arg)=>{if(!stream.stream_ops.ioctl){throw new FS.ErrnoError(59)}return stream.stream_ops.ioctl(stream,cmd,arg)},readFile:(path,opts={})=>{opts.flags=opts.flags||0;opts.encoding=opts.encoding||"binary";if(opts.encoding!=="utf8"&&opts.encoding!=="binary"){throw new Error(`Invalid encoding type "${opts.encoding}"`)}var ret;var stream=FS.open(path,opts.flags);var stat=FS.stat(path);var length=stat.size;var buf=new Uint8Array(length);FS.read(stream,buf,0,length,0);if(opts.encoding==="utf8"){ret=UTF8ArrayToString(buf,0)}else if(opts.encoding==="binary"){ret=buf}FS.close(stream);return ret},writeFile:(path,data,opts={})=>{opts.flags=opts.flags||577;var stream=FS.open(path,opts.flags,opts.mode);if(typeof data=="string"){var buf=new Uint8Array(lengthBytesUTF8(data)+1);var actualNumBytes=stringToUTF8Array(data,buf,0,buf.length);FS.write(stream,buf,0,actualNumBytes,undefined,opts.canOwn)}else if(ArrayBuffer.isView(data)){FS.write(stream,data,0,data.byteLength,undefined,opts.canOwn)}else{throw new Error("Unsupported data type")}FS.close(stream)},cwd:()=>FS.currentPath,chdir:path=>{var lookup=FS.lookupPath(path,{follow:true});if(lookup.node===null){throw new FS.ErrnoError(44)}if(!FS.isDir(lookup.node.mode)){throw new FS.ErrnoError(54)}var errCode=FS.nodePermissions(lookup.node,"x");if(errCode){throw new FS.ErrnoError(errCode)}FS.currentPath=lookup.path},createDefaultDirectories:()=>{FS.mkdir("/tmp");FS.mkdir("/home");FS.mkdir("/home/web_user")},createDefaultDevices:()=>{FS.mkdir("/dev");FS.registerDevice(FS.makedev(1,3),{read:()=>0,write:(stream,buffer,offset,length,pos)=>length});FS.mkdev("/dev/null",FS.makedev(1,3));TTY.register(FS.makedev(5,0),TTY.default_tty_ops);TTY.register(FS.makedev(6,0),TTY.default_tty1_ops);FS.mkdev("/dev/tty",FS.makedev(5,0));FS.mkdev("/dev/tty1",FS.makedev(6,0));var randomBuffer=new Uint8Array(1024),randomLeft=0;var randomByte=()=>{if(randomLeft===0){randomLeft=randomFill(randomBuffer).byteLength}return randomBuffer[--randomLeft]};FS.createDevice("/dev","random",randomByte);FS.createDevice("/dev","urandom",randomByte);FS.mkdir("/dev/shm");FS.mkdir("/dev/shm/tmp")},createSpecialDirectories:()=>{FS.mkdir("/proc");var proc_self=FS.mkdir("/proc/self");FS.mkdir("/proc/self/fd");FS.mount({mount:()=>{var node=FS.createNode(proc_self,"fd",16384|511,73);node.node_ops={lookup:(parent,name)=>{var fd=+name;var stream=FS.getStream(fd);if(!stream)throw new FS.ErrnoError(8);var ret={parent:null,mount:{mountpoint:"fake"},node_ops:{readlink:()=>stream.path}};ret.parent=ret;return ret}};return node}},{},"/proc/self/fd")},createStandardStreams:()=>{if(Module["stdin"]){FS.createDevice("/dev","stdin",Module["stdin"])}else{FS.symlink("/dev/tty","/dev/stdin")}if(Module["stdout"]){FS.createDevice("/dev","stdout",null,Module["stdout"])}else{FS.symlink("/dev/tty","/dev/stdout")}if(Module["stderr"]){FS.createDevice("/dev","stderr",null,Module["stderr"])}else{FS.symlink("/dev/tty1","/dev/stderr")}var stdin=FS.open("/dev/stdin",0);var stdout=FS.open("/dev/stdout",1);var stderr=FS.open("/dev/stderr",1)},ensureErrnoError:()=>{if(FS.ErrnoError)return;FS.ErrnoError=function ErrnoError(errno,node){this.name="ErrnoError";this.node=node;this.setErrno=function(errno){this.errno=errno};this.setErrno(errno);this.message="FS error"};FS.ErrnoError.prototype=new Error;FS.ErrnoError.prototype.constructor=FS.ErrnoError;[44].forEach(code=>{FS.genericErrors[code]=new FS.ErrnoError(code);FS.genericErrors[code].stack=""})},staticInit:()=>{FS.ensureErrnoError();FS.nameTable=new Array(4096);FS.mount(MEMFS,{},"/");FS.createDefaultDirectories();FS.createDefaultDevices();FS.createSpecialDirectories();FS.filesystems={"MEMFS":MEMFS,"WORKERFS":WORKERFS}},init:(input,output,error)=>{FS.init.initialized=true;FS.ensureErrnoError();Module["stdin"]=input||Module["stdin"];Module["stdout"]=output||Module["stdout"];Module["stderr"]=error||Module["stderr"];FS.createStandardStreams()},quit:()=>{FS.init.initialized=false;for(var i=0;i{var ret=FS.analyzePath(path,dontResolveLastLink);if(!ret.exists){return null}return ret.object},analyzePath:(path,dontResolveLastLink)=>{try{var lookup=FS.lookupPath(path,{follow:!dontResolveLastLink});path=lookup.path}catch(e){}var ret={isRoot:false,exists:false,error:0,name:null,path:null,object:null,parentExists:false,parentPath:null,parentObject:null};try{var lookup=FS.lookupPath(path,{parent:true});ret.parentExists=true;ret.parentPath=lookup.path;ret.parentObject=lookup.node;ret.name=PATH.basename(path);lookup=FS.lookupPath(path,{follow:!dontResolveLastLink});ret.exists=true;ret.path=lookup.path;ret.object=lookup.node;ret.name=lookup.node.name;ret.isRoot=lookup.path==="/"}catch(e){ret.error=e.errno}return ret},createPath:(parent,path,canRead,canWrite)=>{parent=typeof parent=="string"?parent:FS.getPath(parent);var parts=path.split("/").reverse();while(parts.length){var part=parts.pop();if(!part)continue;var current=PATH.join2(parent,part);try{FS.mkdir(current)}catch(e){}parent=current}return current},createFile:(parent,name,properties,canRead,canWrite)=>{var path=PATH.join2(typeof parent=="string"?parent:FS.getPath(parent),name);var mode=FS_getMode(canRead,canWrite);return FS.create(path,mode)},createDataFile:(parent,name,data,canRead,canWrite,canOwn)=>{var path=name;if(parent){parent=typeof parent=="string"?parent:FS.getPath(parent);path=name?PATH.join2(parent,name):parent}var mode=FS_getMode(canRead,canWrite);var node=FS.create(path,mode);if(data){if(typeof data=="string"){var arr=new Array(data.length);for(var i=0,len=data.length;i{var path=PATH.join2(typeof parent=="string"?parent:FS.getPath(parent),name);var mode=FS_getMode(!!input,!!output);if(!FS.createDevice.major)FS.createDevice.major=64;var dev=FS.makedev(FS.createDevice.major++,0);FS.registerDevice(dev,{open:stream=>{stream.seekable=false},close:stream=>{if(output&&output.buffer&&output.buffer.length){output(10)}},read:(stream,buffer,offset,length,pos)=>{var bytesRead=0;for(var i=0;i{for(var i=0;i{if(obj.isDevice||obj.isFolder||obj.link||obj.contents)return true;if(typeof XMLHttpRequest!="undefined"){throw new Error("Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread.")}else if(read_){try{obj.contents=intArrayFromString(read_(obj.url),true);obj.usedBytes=obj.contents.length}catch(e){throw new FS.ErrnoError(29)}}else{throw new Error("Cannot load without read() or XMLHttpRequest.")}},createLazyFile:(parent,name,url,canRead,canWrite)=>{function LazyUint8Array(){this.lengthKnown=false;this.chunks=[]}LazyUint8Array.prototype.get=function LazyUint8Array_get(idx){if(idx>this.length-1||idx<0){return undefined}var chunkOffset=idx%this.chunkSize;var chunkNum=idx/this.chunkSize|0;return this.getter(chunkNum)[chunkOffset]};LazyUint8Array.prototype.setDataGetter=function LazyUint8Array_setDataGetter(getter){this.getter=getter};LazyUint8Array.prototype.cacheLength=function LazyUint8Array_cacheLength(){var xhr=new XMLHttpRequest;xhr.open("HEAD",url,false);xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))throw new Error("Couldn't load "+url+". Status: "+xhr.status);var datalength=Number(xhr.getResponseHeader("Content-length"));var header;var hasByteServing=(header=xhr.getResponseHeader("Accept-Ranges"))&&header==="bytes";var usesGzip=(header=xhr.getResponseHeader("Content-Encoding"))&&header==="gzip";var chunkSize=1024*1024;if(!hasByteServing)chunkSize=datalength;var doXHR=(from,to)=>{if(from>to)throw new Error("invalid range ("+from+", "+to+") or no bytes requested!");if(to>datalength-1)throw new Error("only "+datalength+" bytes available! programmer error!");var xhr=new XMLHttpRequest;xhr.open("GET",url,false);if(datalength!==chunkSize)xhr.setRequestHeader("Range","bytes="+from+"-"+to);xhr.responseType="arraybuffer";if(xhr.overrideMimeType){xhr.overrideMimeType("text/plain; charset=x-user-defined")}xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))throw new Error("Couldn't load "+url+". Status: "+xhr.status);if(xhr.response!==undefined){return new Uint8Array(xhr.response||[])}return intArrayFromString(xhr.responseText||"",true)};var lazyArray=this;lazyArray.setDataGetter(chunkNum=>{var start=chunkNum*chunkSize;var end=(chunkNum+1)*chunkSize-1;end=Math.min(end,datalength-1);if(typeof lazyArray.chunks[chunkNum]=="undefined"){lazyArray.chunks[chunkNum]=doXHR(start,end)}if(typeof lazyArray.chunks[chunkNum]=="undefined")throw new Error("doXHR failed!");return lazyArray.chunks[chunkNum]});if(usesGzip||!datalength){chunkSize=datalength=1;datalength=this.getter(0).length;chunkSize=datalength;out("LazyFiles on gzip forces download of the whole file when length is accessed")}this._length=datalength;this._chunkSize=chunkSize;this.lengthKnown=true};if(typeof XMLHttpRequest!="undefined"){if(!ENVIRONMENT_IS_WORKER)throw"Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc";var lazyArray=new LazyUint8Array;Object.defineProperties(lazyArray,{length:{get:function(){if(!this.lengthKnown){this.cacheLength()}return this._length}},chunkSize:{get:function(){if(!this.lengthKnown){this.cacheLength()}return this._chunkSize}}});var properties={isDevice:false,contents:lazyArray}}else{var properties={isDevice:false,url:url}}var node=FS.createFile(parent,name,properties,canRead,canWrite);if(properties.contents){node.contents=properties.contents}else if(properties.url){node.contents=null;node.url=properties.url}Object.defineProperties(node,{usedBytes:{get:function(){return this.contents.length}}});var stream_ops={};var keys=Object.keys(node.stream_ops);keys.forEach(key=>{var fn=node.stream_ops[key];stream_ops[key]=function forceLoadLazyFile(){FS.forceLoadFile(node);return fn.apply(null,arguments)}});function writeChunks(stream,buffer,offset,length,position){var contents=stream.node.contents;if(position>=contents.length)return 0;var size=Math.min(contents.length-position,length);if(contents.slice){for(var i=0;i{FS.forceLoadFile(node);return writeChunks(stream,buffer,offset,length,position)};stream_ops.mmap=(stream,length,position,prot,flags)=>{FS.forceLoadFile(node);var ptr=mmapAlloc(length);if(!ptr){throw new FS.ErrnoError(48)}writeChunks(stream,HEAP8,ptr,length,position);return{ptr:ptr,allocated:true}};node.stream_ops=stream_ops;return node}};var SYSCALLS={DEFAULT_POLLMASK:5,calculateAt:function(dirfd,path,allowEmpty){if(PATH.isAbs(path)){return path}var dir;if(dirfd===-100){dir=FS.cwd()}else{var dirstream=SYSCALLS.getStreamFromFD(dirfd);dir=dirstream.path}if(path.length==0){if(!allowEmpty){throw new FS.ErrnoError(44)}return dir}return PATH.join2(dir,path)},doStat:function(func,path,buf){try{var stat=func(path)}catch(e){if(e&&e.node&&PATH.normalize(path)!==PATH.normalize(FS.getPath(e.node))){return-54}throw e}HEAP32[buf>>2]=stat.dev;HEAP32[buf+8>>2]=stat.ino;HEAP32[buf+12>>2]=stat.mode;HEAPU32[buf+16>>2]=stat.nlink;HEAP32[buf+20>>2]=stat.uid;HEAP32[buf+24>>2]=stat.gid;HEAP32[buf+28>>2]=stat.rdev;HEAP64[buf+40>>3]=BigInt(stat.size);HEAP32[buf+48>>2]=4096;HEAP32[buf+52>>2]=stat.blocks;var atime=stat.atime.getTime();var mtime=stat.mtime.getTime();var ctime=stat.ctime.getTime();HEAP64[buf+56>>3]=BigInt(Math.floor(atime/1e3));HEAPU32[buf+64>>2]=atime%1e3*1e3;HEAP64[buf+72>>3]=BigInt(Math.floor(mtime/1e3));HEAPU32[buf+80>>2]=mtime%1e3*1e3;HEAP64[buf+88>>3]=BigInt(Math.floor(ctime/1e3));HEAPU32[buf+96>>2]=ctime%1e3*1e3;HEAP64[buf+104>>3]=BigInt(stat.ino);return 0},doMsync:function(addr,stream,len,flags,offset){if(!FS.isFile(stream.node.mode)){throw new FS.ErrnoError(43)}if(flags&2){return 0}var buffer=HEAPU8.slice(addr,addr+len);FS.msync(stream,buffer,offset,len,flags)},varargs:undefined,get:function(){SYSCALLS.varargs+=4;var ret=HEAP32[SYSCALLS.varargs-4>>2];return ret},getStr:function(ptr){var ret=UTF8ToString(ptr);return ret},getStreamFromFD:function(fd){var stream=FS.getStream(fd);if(!stream)throw new FS.ErrnoError(8);return stream}};function ___syscall__newselect(nfds,readfds,writefds,exceptfds,timeout){try{var total=0;var srcReadLow=readfds?HEAP32[readfds>>2]:0,srcReadHigh=readfds?HEAP32[readfds+4>>2]:0;var srcWriteLow=writefds?HEAP32[writefds>>2]:0,srcWriteHigh=writefds?HEAP32[writefds+4>>2]:0;var srcExceptLow=exceptfds?HEAP32[exceptfds>>2]:0,srcExceptHigh=exceptfds?HEAP32[exceptfds+4>>2]:0;var dstReadLow=0,dstReadHigh=0;var dstWriteLow=0,dstWriteHigh=0;var dstExceptLow=0,dstExceptHigh=0;var allLow=(readfds?HEAP32[readfds>>2]:0)|(writefds?HEAP32[writefds>>2]:0)|(exceptfds?HEAP32[exceptfds>>2]:0);var allHigh=(readfds?HEAP32[readfds+4>>2]:0)|(writefds?HEAP32[writefds+4>>2]:0)|(exceptfds?HEAP32[exceptfds+4>>2]:0);var check=function(fd,low,high,val){return fd<32?low&val:high&val};for(var fd=0;fd>2]=dstReadLow;HEAP32[readfds+4>>2]=dstReadHigh}if(writefds){HEAP32[writefds>>2]=dstWriteLow;HEAP32[writefds+4>>2]=dstWriteHigh}if(exceptfds){HEAP32[exceptfds>>2]=dstExceptLow;HEAP32[exceptfds+4>>2]=dstExceptHigh}return total}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}var SOCKFS={mount:function(mount){Module["websocket"]=Module["websocket"]&&"object"===typeof Module["websocket"]?Module["websocket"]:{};Module["websocket"]._callbacks={};Module["websocket"]["on"]=function(event,callback){if("function"===typeof callback){this._callbacks[event]=callback}return this};Module["websocket"].emit=function(event,param){if("function"===typeof this._callbacks[event]){this._callbacks[event].call(this,param)}};return FS.createNode(null,"/",16384|511,0)},createSocket:function(family,type,protocol){type&=~526336;var streaming=type==1;if(streaming&&protocol&&protocol!=6){throw new FS.ErrnoError(66)}var sock={family:family,type:type,protocol:protocol,server:null,error:null,peers:{},pending:[],recv_queue:[],sock_ops:SOCKFS.websocket_sock_ops};var name=SOCKFS.nextname();var node=FS.createNode(SOCKFS.root,name,49152,0);node.sock=sock;var stream=FS.createStream({path:name,node:node,flags:2,seekable:false,stream_ops:SOCKFS.stream_ops});sock.stream=stream;return sock},getSocket:function(fd){var stream=FS.getStream(fd);if(!stream||!FS.isSocket(stream.node.mode)){return null}return stream.node.sock},stream_ops:{poll:function(stream){var sock=stream.node.sock;return sock.sock_ops.poll(sock)},ioctl:function(stream,request,varargs){var sock=stream.node.sock;return sock.sock_ops.ioctl(sock,request,varargs)},read:function(stream,buffer,offset,length,position){var sock=stream.node.sock;var msg=sock.sock_ops.recvmsg(sock,length);if(!msg){return 0}buffer.set(msg.buffer,offset);return msg.buffer.length},write:function(stream,buffer,offset,length,position){var sock=stream.node.sock;return sock.sock_ops.sendmsg(sock,buffer,offset,length)},close:function(stream){var sock=stream.node.sock;sock.sock_ops.close(sock)}},nextname:function(){if(!SOCKFS.nextname.current){SOCKFS.nextname.current=0}return"socket["+SOCKFS.nextname.current+++"]"},websocket_sock_ops:{createPeer:function(sock,addr,port){var ws;if(typeof addr=="object"){ws=addr;addr=null;port=null}if(ws){if(ws._socket){addr=ws._socket.remoteAddress;port=ws._socket.remotePort}else{var result=/ws[s]?:\/\/([^:]+):(\d+)/.exec(ws.url);if(!result){throw new Error("WebSocket URL must be in the format ws(s)://address:port")}addr=result[1];port=parseInt(result[2],10)}}else{try{var runtimeConfig=Module["websocket"]&&"object"===typeof Module["websocket"];var url="ws:#".replace("#","//");if(runtimeConfig){if("string"===typeof Module["websocket"]["url"]){url=Module["websocket"]["url"]}}if(url==="ws://"||url==="wss://"){var parts=addr.split("/");url=url+parts[0]+":"+port+"/"+parts.slice(1).join("/")}var subProtocols="binary";if(runtimeConfig){if("string"===typeof Module["websocket"]["subprotocol"]){subProtocols=Module["websocket"]["subprotocol"]}}var opts=undefined;if(subProtocols!=="null"){subProtocols=subProtocols.replace(/^ +| +$/g,"").split(/ *, */);opts=subProtocols}if(runtimeConfig&&null===Module["websocket"]["subprotocol"]){subProtocols="null";opts=undefined}var WebSocketConstructor;{WebSocketConstructor=WebSocket}ws=new WebSocketConstructor(url,opts);ws.binaryType="arraybuffer"}catch(e){throw new FS.ErrnoError(23)}}var peer={addr:addr,port:port,socket:ws,dgram_send_queue:[]};SOCKFS.websocket_sock_ops.addPeer(sock,peer);SOCKFS.websocket_sock_ops.handlePeerEvents(sock,peer);if(sock.type===2&&typeof sock.sport!="undefined"){peer.dgram_send_queue.push(new Uint8Array([255,255,255,255,"p".charCodeAt(0),"o".charCodeAt(0),"r".charCodeAt(0),"t".charCodeAt(0),(sock.sport&65280)>>8,sock.sport&255]))}return peer},getPeer:function(sock,addr,port){return sock.peers[addr+":"+port]},addPeer:function(sock,peer){sock.peers[peer.addr+":"+peer.port]=peer},removePeer:function(sock,peer){delete sock.peers[peer.addr+":"+peer.port]},handlePeerEvents:function(sock,peer){var first=true;var handleOpen=function(){Module["websocket"].emit("open",sock.stream.fd);try{var queued=peer.dgram_send_queue.shift();while(queued){peer.socket.send(queued);queued=peer.dgram_send_queue.shift()}}catch(e){peer.socket.close()}};function handleMessage(data){if(typeof data=="string"){var encoder=new TextEncoder;data=encoder.encode(data)}else{assert(data.byteLength!==undefined);if(data.byteLength==0){return}data=new Uint8Array(data)}var wasfirst=first;first=false;if(wasfirst&&data.length===10&&data[0]===255&&data[1]===255&&data[2]===255&&data[3]===255&&data[4]==="p".charCodeAt(0)&&data[5]==="o".charCodeAt(0)&&data[6]==="r".charCodeAt(0)&&data[7]==="t".charCodeAt(0)){var newport=data[8]<<8|data[9];SOCKFS.websocket_sock_ops.removePeer(sock,peer);peer.port=newport;SOCKFS.websocket_sock_ops.addPeer(sock,peer);return}sock.recv_queue.push({addr:peer.addr,port:peer.port,data:data});Module["websocket"].emit("message",sock.stream.fd)}if(ENVIRONMENT_IS_NODE){peer.socket.on("open",handleOpen);peer.socket.on("message",function(data,isBinary){if(!isBinary){return}handleMessage(new Uint8Array(data).buffer)});peer.socket.on("close",function(){Module["websocket"].emit("close",sock.stream.fd)});peer.socket.on("error",function(error){sock.error=14;Module["websocket"].emit("error",[sock.stream.fd,sock.error,"ECONNREFUSED: Connection refused"])})}else{peer.socket.onopen=handleOpen;peer.socket.onclose=function(){Module["websocket"].emit("close",sock.stream.fd)};peer.socket.onmessage=function peer_socket_onmessage(event){handleMessage(event.data)};peer.socket.onerror=function(error){sock.error=14;Module["websocket"].emit("error",[sock.stream.fd,sock.error,"ECONNREFUSED: Connection refused"])}}},poll:function(sock){if(sock.type===1&&sock.server){return sock.pending.length?64|1:0}var mask=0;var dest=sock.type===1?SOCKFS.websocket_sock_ops.getPeer(sock,sock.daddr,sock.dport):null;if(sock.recv_queue.length||!dest||dest&&dest.socket.readyState===dest.socket.CLOSING||dest&&dest.socket.readyState===dest.socket.CLOSED){mask|=64|1}if(!dest||dest&&dest.socket.readyState===dest.socket.OPEN){mask|=4}if(dest&&dest.socket.readyState===dest.socket.CLOSING||dest&&dest.socket.readyState===dest.socket.CLOSED){mask|=16}return mask},ioctl:function(sock,request,arg){switch(request){case 21531:var bytes=0;if(sock.recv_queue.length){bytes=sock.recv_queue[0].data.length}HEAP32[arg>>2]=bytes;return 0;default:return 28}},close:function(sock){if(sock.server){try{sock.server.close()}catch(e){}sock.server=null}var peers=Object.keys(sock.peers);for(var i=0;i>2]=value;return value}function inetPton4(str){var b=str.split(".");for(var i=0;i<4;i++){var tmp=Number(b[i]);if(isNaN(tmp))return null;b[i]=tmp}return(b[0]|b[1]<<8|b[2]<<16|b[3]<<24)>>>0}function jstoi_q(str){return parseInt(str)}function inetPton6(str){var words;var w,offset,z;var valid6regx=/^((?=.*::)(?!.*::.+::)(::)?([\dA-F]{1,4}:(:|\b)|){5}|([\dA-F]{1,4}:){6})((([\dA-F]{1,4}((?!\3)::|:\b|$))|(?!\2\3)){2}|(((2[0-4]|1\d|[1-9])?\d|25[0-5])\.?\b){4})$/i;var parts=[];if(!valid6regx.test(str)){return null}if(str==="::"){return[0,0,0,0,0,0,0,0]}if(str.startsWith("::")){str=str.replace("::","Z:")}else{str=str.replace("::",":Z:")}if(str.indexOf(".")>0){str=str.replace(new RegExp("[.]","g"),":");words=str.split(":");words[words.length-4]=jstoi_q(words[words.length-4])+jstoi_q(words[words.length-3])*256;words[words.length-3]=jstoi_q(words[words.length-2])+jstoi_q(words[words.length-1])*256;words=words.slice(0,words.length-2)}else{words=str.split(":")}offset=0;z=0;for(w=0;w>2]=16}HEAP16[sa>>1]=family;HEAP32[sa+4>>2]=addr;HEAP16[sa+2>>1]=_htons(port);break;case 10:addr=inetPton6(addr);zeroMemory(sa,28);if(addrlen){HEAP32[addrlen>>2]=28}HEAP32[sa>>2]=family;HEAP32[sa+8>>2]=addr[0];HEAP32[sa+12>>2]=addr[1];HEAP32[sa+16>>2]=addr[2];HEAP32[sa+20>>2]=addr[3];HEAP16[sa+2>>1]=_htons(port);break;default:return 5}return 0}var DNS={address_map:{id:1,addrs:{},names:{}},lookup_name:function(name){var res=inetPton4(name);if(res!==null){return name}res=inetPton6(name);if(res!==null){return name}var addr;if(DNS.address_map.addrs[name]){addr=DNS.address_map.addrs[name]}else{var id=DNS.address_map.id++;assert(id<65535,"exceeded max address mappings of 65535");addr="172.29."+(id&255)+"."+(id&65280);DNS.address_map.names[addr]=name;DNS.address_map.addrs[name]=addr}return addr},lookup_addr:function(addr){if(DNS.address_map.names[addr]){return DNS.address_map.names[addr]}return null}};function ___syscall_accept4(fd,addr,addrlen,flags,d1,d2){try{var sock=getSocketFromFD(fd);var newsock=sock.sock_ops.accept(sock);if(addr){var errno=writeSockaddr(addr,newsock.family,DNS.lookup_name(newsock.daddr),newsock.dport,addrlen)}return newsock.stream.fd}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function inetNtop4(addr){return(addr&255)+"."+(addr>>8&255)+"."+(addr>>16&255)+"."+(addr>>24&255)}function inetNtop6(ints){var str="";var word=0;var longest=0;var lastzero=0;var zstart=0;var len=0;var i=0;var parts=[ints[0]&65535,ints[0]>>16,ints[1]&65535,ints[1]>>16,ints[2]&65535,ints[2]>>16,ints[3]&65535,ints[3]>>16];var hasipv4=true;var v4part="";for(i=0;i<5;i++){if(parts[i]!==0){hasipv4=false;break}}if(hasipv4){v4part=inetNtop4(parts[6]|parts[7]<<16);if(parts[5]===-1){str="::ffff:";str+=v4part;return str}if(parts[5]===0){str="::";if(v4part==="0.0.0.0")v4part="";if(v4part==="0.0.0.1")v4part="1";str+=v4part;return str}}for(word=0;word<8;word++){if(parts[word]===0){if(word-lastzero>1){len=0}lastzero=word;len++}if(len>longest){longest=len;zstart=word-longest+1}}for(word=0;word<8;word++){if(longest>1){if(parts[word]===0&&word>=zstart&&word>1];var port=_ntohs(HEAPU16[sa+2>>1]);var addr;switch(family){case 2:if(salen!==16){return{errno:28}}addr=HEAP32[sa+4>>2];addr=inetNtop4(addr);break;case 10:if(salen!==28){return{errno:28}}addr=[HEAP32[sa+8>>2],HEAP32[sa+12>>2],HEAP32[sa+16>>2],HEAP32[sa+20>>2]];addr=inetNtop6(addr);break;default:return{errno:5}}return{family:family,addr:addr,port:port}}function getSocketAddress(addrp,addrlen,allowNull){if(allowNull&&addrp===0)return null;var info=readSockaddr(addrp,addrlen);if(info.errno)throw new FS.ErrnoError(info.errno);info.addr=DNS.lookup_addr(info.addr)||info.addr;return info}function ___syscall_bind(fd,addr,addrlen,d1,d2,d3){try{var sock=getSocketFromFD(fd);var info=getSocketAddress(addr,addrlen);sock.sock_ops.bind(sock,info.addr,info.port);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_connect(fd,addr,addrlen,d1,d2,d3){try{var sock=getSocketFromFD(fd);var info=getSocketAddress(addr,addrlen);sock.sock_ops.connect(sock,info.addr,info.port);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_faccessat(dirfd,path,amode,flags){try{path=SYSCALLS.getStr(path);path=SYSCALLS.calculateAt(dirfd,path);if(amode&~7){return-28}var lookup=FS.lookupPath(path,{follow:true});var node=lookup.node;if(!node){return-44}var perms="";if(amode&4)perms+="r";if(amode&2)perms+="w";if(amode&1)perms+="x";if(perms&&FS.nodePermissions(node,perms)){return-2}return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_fcntl64(fd,cmd,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(cmd){case 0:{var arg=SYSCALLS.get();if(arg<0){return-28}var newStream;newStream=FS.createStream(stream,arg);return newStream.fd}case 1:case 2:return 0;case 3:return stream.flags;case 4:{var arg=SYSCALLS.get();stream.flags|=arg;return 0}case 5:{var arg=SYSCALLS.get();var offset=0;HEAP16[arg+offset>>1]=2;return 0}case 6:case 7:return 0;case 16:case 8:return-28;case 9:setErrNo(28);return-1;default:{return-28}}}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_fstat64(fd,buf){try{var stream=SYSCALLS.getStreamFromFD(fd);return SYSCALLS.doStat(FS.stat,stream.path,buf)}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function stringToUTF8(str,outPtr,maxBytesToWrite){return stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite)}function ___syscall_getdents64(fd,dirp,count){try{var stream=SYSCALLS.getStreamFromFD(fd);if(!stream.getdents){stream.getdents=FS.readdir(stream.path)}var struct_size=280;var pos=0;var off=FS.llseek(stream,0,1);var idx=Math.floor(off/struct_size);while(idx>3]=BigInt(id);HEAP64[dirp+pos+8>>3]=BigInt((idx+1)*struct_size);HEAP16[dirp+pos+16>>1]=280;HEAP8[dirp+pos+18>>0]=type;stringToUTF8(name,dirp+pos+19,256);pos+=struct_size;idx+=1}FS.llseek(stream,idx*struct_size,0);return pos}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_getpeername(fd,addr,addrlen,d1,d2,d3){try{var sock=getSocketFromFD(fd);if(!sock.daddr){return-53}var errno=writeSockaddr(addr,sock.family,DNS.lookup_name(sock.daddr),sock.dport,addrlen);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_getsockname(fd,addr,addrlen,d1,d2,d3){try{var sock=getSocketFromFD(fd);var errno=writeSockaddr(addr,sock.family,DNS.lookup_name(sock.saddr||"0.0.0.0"),sock.sport,addrlen);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_getsockopt(fd,level,optname,optval,optlen,d1){try{var sock=getSocketFromFD(fd);if(level===1){if(optname===4){HEAP32[optval>>2]=sock.error;HEAP32[optlen>>2]=4;sock.error=null;return 0}}return-50}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_ioctl(fd,op,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(op){case 21509:case 21505:{if(!stream.tty)return-59;return 0}case 21510:case 21511:case 21512:case 21506:case 21507:case 21508:{if(!stream.tty)return-59;return 0}case 21519:{if(!stream.tty)return-59;var argp=SYSCALLS.get();HEAP32[argp>>2]=0;return 0}case 21520:{if(!stream.tty)return-59;return-28}case 21531:{var argp=SYSCALLS.get();return FS.ioctl(stream,op,argp)}case 21523:{if(!stream.tty)return-59;return 0}case 21524:{if(!stream.tty)return-59;return 0}default:return-28}}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_listen(fd,backlog){try{var sock=getSocketFromFD(fd);sock.sock_ops.listen(sock,backlog);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_lstat64(path,buf){try{path=SYSCALLS.getStr(path);return SYSCALLS.doStat(FS.lstat,path,buf)}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_mkdirat(dirfd,path,mode){try{path=SYSCALLS.getStr(path);path=SYSCALLS.calculateAt(dirfd,path);path=PATH.normalize(path);if(path[path.length-1]==="/")path=path.substr(0,path.length-1);FS.mkdir(path,mode,0);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_newfstatat(dirfd,path,buf,flags){try{path=SYSCALLS.getStr(path);var nofollow=flags&256;var allowEmpty=flags&4096;flags=flags&~6400;path=SYSCALLS.calculateAt(dirfd,path,allowEmpty);return SYSCALLS.doStat(nofollow?FS.lstat:FS.stat,path,buf)}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_openat(dirfd,path,flags,varargs){SYSCALLS.varargs=varargs;try{path=SYSCALLS.getStr(path);path=SYSCALLS.calculateAt(dirfd,path);var mode=varargs?SYSCALLS.get():0;return FS.open(path,flags,mode).fd}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_poll(fds,nfds,timeout){try{var nonzero=0;for(var i=0;i>2];var events=HEAP16[pollfd+4>>1];var mask=32;var stream=FS.getStream(fd);if(stream){mask=SYSCALLS.DEFAULT_POLLMASK;if(stream.stream_ops.poll){mask=stream.stream_ops.poll(stream)}}mask&=events|8|16;if(mask)nonzero++;HEAP16[pollfd+6>>1]=mask}return nonzero}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_recvfrom(fd,buf,len,flags,addr,addrlen){try{var sock=getSocketFromFD(fd);var msg=sock.sock_ops.recvmsg(sock,len);if(!msg)return 0;if(addr){var errno=writeSockaddr(addr,sock.family,DNS.lookup_name(msg.addr),msg.port,addrlen)}HEAPU8.set(msg.buffer,buf);return msg.buffer.byteLength}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_renameat(olddirfd,oldpath,newdirfd,newpath){try{oldpath=SYSCALLS.getStr(oldpath);newpath=SYSCALLS.getStr(newpath);oldpath=SYSCALLS.calculateAt(olddirfd,oldpath);newpath=SYSCALLS.calculateAt(newdirfd,newpath);FS.rename(oldpath,newpath);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_rmdir(path){try{path=SYSCALLS.getStr(path);FS.rmdir(path);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_sendto(fd,message,length,flags,addr,addr_len){try{var sock=getSocketFromFD(fd);var dest=getSocketAddress(addr,addr_len,true);if(!dest){return FS.write(sock.stream,HEAP8,message,length)}return sock.sock_ops.sendmsg(sock,HEAP8,message,length,dest.addr,dest.port)}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_socket(domain,type,protocol){try{var sock=SOCKFS.createSocket(domain,type,protocol);return sock.stream.fd}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_stat64(path,buf){try{path=SYSCALLS.getStr(path);return SYSCALLS.doStat(FS.stat,path,buf)}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_unlinkat(dirfd,path,flags){try{path=SYSCALLS.getStr(path);path=SYSCALLS.calculateAt(dirfd,path);if(flags===0){FS.unlink(path)}else if(flags===512){FS.rmdir(path)}else{abort("Invalid flags passed to unlinkat")}return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}var nowIsMonotonic=true;function __emscripten_get_now_is_monotonic(){return nowIsMonotonic}function __emscripten_throw_longjmp(){throw Infinity}function readI53FromI64(ptr){return HEAPU32[ptr>>2]+HEAP32[ptr+4>>2]*4294967296}function __gmtime_js(time,tmPtr){var date=new Date(readI53FromI64(time)*1e3);HEAP32[tmPtr>>2]=date.getUTCSeconds();HEAP32[tmPtr+4>>2]=date.getUTCMinutes();HEAP32[tmPtr+8>>2]=date.getUTCHours();HEAP32[tmPtr+12>>2]=date.getUTCDate();HEAP32[tmPtr+16>>2]=date.getUTCMonth();HEAP32[tmPtr+20>>2]=date.getUTCFullYear()-1900;HEAP32[tmPtr+24>>2]=date.getUTCDay();var start=Date.UTC(date.getUTCFullYear(),0,1,0,0,0,0);var yday=(date.getTime()-start)/(1e3*60*60*24)|0;HEAP32[tmPtr+28>>2]=yday}function isLeapYear(year){return year%4===0&&(year%100!==0||year%400===0)}var MONTH_DAYS_LEAP_CUMULATIVE=[0,31,60,91,121,152,182,213,244,274,305,335];var MONTH_DAYS_REGULAR_CUMULATIVE=[0,31,59,90,120,151,181,212,243,273,304,334];function ydayFromDate(date){var leap=isLeapYear(date.getFullYear());var monthDaysCumulative=leap?MONTH_DAYS_LEAP_CUMULATIVE:MONTH_DAYS_REGULAR_CUMULATIVE;var yday=monthDaysCumulative[date.getMonth()]+date.getDate()-1;return yday}function __localtime_js(time,tmPtr){var date=new Date(readI53FromI64(time)*1e3);HEAP32[tmPtr>>2]=date.getSeconds();HEAP32[tmPtr+4>>2]=date.getMinutes();HEAP32[tmPtr+8>>2]=date.getHours();HEAP32[tmPtr+12>>2]=date.getDate();HEAP32[tmPtr+16>>2]=date.getMonth();HEAP32[tmPtr+20>>2]=date.getFullYear()-1900;HEAP32[tmPtr+24>>2]=date.getDay();var yday=ydayFromDate(date)|0;HEAP32[tmPtr+28>>2]=yday;HEAP32[tmPtr+36>>2]=-(date.getTimezoneOffset()*60);var start=new Date(date.getFullYear(),0,1);var summerOffset=new Date(date.getFullYear(),6,1).getTimezoneOffset();var winterOffset=start.getTimezoneOffset();var dst=(summerOffset!=winterOffset&&date.getTimezoneOffset()==Math.min(winterOffset,summerOffset))|0;HEAP32[tmPtr+32>>2]=dst}function __mktime_js(tmPtr){var date=new Date(HEAP32[tmPtr+20>>2]+1900,HEAP32[tmPtr+16>>2],HEAP32[tmPtr+12>>2],HEAP32[tmPtr+8>>2],HEAP32[tmPtr+4>>2],HEAP32[tmPtr>>2],0);var dst=HEAP32[tmPtr+32>>2];var guessedOffset=date.getTimezoneOffset();var start=new Date(date.getFullYear(),0,1);var summerOffset=new Date(date.getFullYear(),6,1).getTimezoneOffset();var winterOffset=start.getTimezoneOffset();var dstOffset=Math.min(winterOffset,summerOffset);if(dst<0){HEAP32[tmPtr+32>>2]=Number(summerOffset!=winterOffset&&dstOffset==guessedOffset)}else if(dst>0!=(dstOffset==guessedOffset)){var nonDstOffset=Math.max(winterOffset,summerOffset);var trueOffset=dst>0?dstOffset:nonDstOffset;date.setTime(date.getTime()+(trueOffset-guessedOffset)*6e4)}HEAP32[tmPtr+24>>2]=date.getDay();var yday=ydayFromDate(date)|0;HEAP32[tmPtr+28>>2]=yday;HEAP32[tmPtr>>2]=date.getSeconds();HEAP32[tmPtr+4>>2]=date.getMinutes();HEAP32[tmPtr+8>>2]=date.getHours();HEAP32[tmPtr+12>>2]=date.getDate();HEAP32[tmPtr+16>>2]=date.getMonth();HEAP32[tmPtr+20>>2]=date.getYear();return date.getTime()/1e3|0}function __mmap_js(len,prot,flags,fd,off,allocated,addr){try{var stream=SYSCALLS.getStreamFromFD(fd);var res=FS.mmap(stream,len,off,prot,flags);var ptr=res.ptr;HEAP32[allocated>>2]=res.allocated;HEAPU32[addr>>2]=ptr;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function __munmap_js(addr,len,prot,flags,fd,offset){try{var stream=SYSCALLS.getStreamFromFD(fd);if(prot&2){SYSCALLS.doMsync(addr,stream,len,flags,offset)}FS.munmap(stream)}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function stringToNewUTF8(str){var size=lengthBytesUTF8(str)+1;var ret=_malloc(size);if(ret)stringToUTF8(str,ret,size);return ret}function __tzset_js(timezone,daylight,tzname){var currentYear=(new Date).getFullYear();var winter=new Date(currentYear,0,1);var summer=new Date(currentYear,6,1);var winterOffset=winter.getTimezoneOffset();var summerOffset=summer.getTimezoneOffset();var stdTimezoneOffset=Math.max(winterOffset,summerOffset);HEAPU32[timezone>>2]=stdTimezoneOffset*60;HEAP32[daylight>>2]=Number(winterOffset!=summerOffset);function extractZone(date){var match=date.toTimeString().match(/\(([A-Za-z ]+)\)$/);return match?match[1]:"GMT"}var winterName=extractZone(winter);var summerName=extractZone(summer);var winterNamePtr=stringToNewUTF8(winterName);var summerNamePtr=stringToNewUTF8(summerName);if(summerOffset>2]=winterNamePtr;HEAPU32[tzname+4>>2]=summerNamePtr}else{HEAPU32[tzname>>2]=summerNamePtr;HEAPU32[tzname+4>>2]=winterNamePtr}}function _abort(){abort("")}Module["_abort"]=_abort;function _dlopen(handle){abort(dlopenMissingError)}var readEmAsmArgsArray=[];function readEmAsmArgs(sigPtr,buf){readEmAsmArgsArray.length=0;var ch;buf>>=2;while(ch=HEAPU8[sigPtr++]){buf+=ch!=105&buf;readEmAsmArgsArray.push(ch==105?HEAP32[buf]:(ch==106?HEAP64:HEAPF64)[buf++>>1]);++buf}return readEmAsmArgsArray}function runEmAsmFunction(code,sigPtr,argbuf){var args=readEmAsmArgs(sigPtr,argbuf);return ASM_CONSTS[code].apply(null,args)}function _emscripten_asm_const_int(code,sigPtr,argbuf){return runEmAsmFunction(code,sigPtr,argbuf)}function _emscripten_date_now(){return Date.now()}function getHeapMax(){return 2147483648}function _emscripten_get_heap_max(){return getHeapMax()}var _emscripten_get_now;_emscripten_get_now=()=>performance.now();function _emscripten_memcpy_big(dest,src,num){HEAPU8.copyWithin(dest,src,src+num)}function emscripten_realloc_buffer(size){var b=wasmMemory.buffer;try{wasmMemory.grow(size-b.byteLength+65535>>>16);updateMemoryViews();return 1}catch(e){}}function _emscripten_resize_heap(requestedSize){var oldSize=HEAPU8.length;requestedSize=requestedSize>>>0;var maxHeapSize=getHeapMax();if(requestedSize>maxHeapSize){return false}var alignUp=(x,multiple)=>x+(multiple-x%multiple)%multiple;for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignUp(Math.max(requestedSize,overGrownHeapSize),65536));var replacement=emscripten_realloc_buffer(newSize);if(replacement){return true}}return false}var ENV={};function getExecutableName(){return thisProgram||"./this.program"}function getEnvStrings(){if(!getEnvStrings.strings){var lang=(typeof navigator=="object"&&navigator.languages&&navigator.languages[0]||"C").replace("-","_")+".UTF-8";var env={"USER":"web_user","LOGNAME":"web_user","PATH":"/","PWD":"/","HOME":"/home/web_user","LANG":lang,"_":getExecutableName()};for(var x in ENV){if(ENV[x]===undefined)delete env[x];else env[x]=ENV[x]}var strings=[];for(var x in env){strings.push(`${x}=${env[x]}`)}getEnvStrings.strings=strings}return getEnvStrings.strings}function stringToAscii(str,buffer){for(var i=0;i>0]=str.charCodeAt(i)}HEAP8[buffer>>0]=0}function _environ_get(__environ,environ_buf){var bufSize=0;getEnvStrings().forEach(function(string,i){var ptr=environ_buf+bufSize;HEAPU32[__environ+i*4>>2]=ptr;stringToAscii(string,ptr);bufSize+=string.length+1});return 0}function _environ_sizes_get(penviron_count,penviron_buf_size){var strings=getEnvStrings();HEAPU32[penviron_count>>2]=strings.length;var bufSize=0;strings.forEach(function(string){bufSize+=string.length+1});HEAPU32[penviron_buf_size>>2]=bufSize;return 0}function _proc_exit(code){EXITSTATUS=code;if(!keepRuntimeAlive()){if(Module["onExit"])Module["onExit"](code);ABORT=true}quit_(code,new ExitStatus(code))}function exitJS(status,implicit){EXITSTATUS=status;_proc_exit(status)}var _exit=exitJS;function _fd_close(fd){try{var stream=SYSCALLS.getStreamFromFD(fd);FS.close(stream);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}function _fd_fdstat_get(fd,pbuf){try{var rightsBase=0;var rightsInheriting=0;var flags=0;{var stream=SYSCALLS.getStreamFromFD(fd);var type=stream.tty?2:FS.isDir(stream.mode)?3:FS.isLink(stream.mode)?7:4}HEAP8[pbuf>>0]=type;HEAP16[pbuf+2>>1]=flags;HEAP64[pbuf+8>>3]=BigInt(rightsBase);HEAP64[pbuf+16>>3]=BigInt(rightsInheriting);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}function doReadv(stream,iov,iovcnt,offset){var ret=0;for(var i=0;i>2];var len=HEAPU32[iov+4>>2];iov+=8;var curr=FS.read(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr;if(curr>2]=num;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}var MAX_INT53=9007199254740992;var MIN_INT53=-9007199254740992;function bigintToI53Checked(num){return numMAX_INT53?NaN:Number(num)}function _fd_seek(fd,offset,whence,newOffset){try{offset=bigintToI53Checked(offset);if(isNaN(offset))return 61;var stream=SYSCALLS.getStreamFromFD(fd);FS.llseek(stream,offset,whence);HEAP64[newOffset>>3]=BigInt(stream.position);if(stream.getdents&&offset===0&&whence===0)stream.getdents=null;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}function doWritev(stream,iov,iovcnt,offset){var ret=0;for(var i=0;i>2];var len=HEAPU32[iov+4>>2];iov+=8;var curr=FS.write(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr;if(typeof offset!=="undefined"){offset+=curr}}return ret}function _fd_write(fd,iov,iovcnt,pnum){try{var stream=SYSCALLS.getStreamFromFD(fd);var num=doWritev(stream,iov,iovcnt);HEAPU32[pnum>>2]=num;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}function _getaddrinfo(node,service,hint,out){var addr=0;var port=0;var flags=0;var family=0;var type=0;var proto=0;var ai;function allocaddrinfo(family,type,proto,canon,addr,port){var sa,salen,ai;var errno;salen=family===10?28:16;addr=family===10?inetNtop6(addr):inetNtop4(addr);sa=_malloc(salen);errno=writeSockaddr(sa,family,addr,port);assert(!errno);ai=_malloc(32);HEAP32[ai+4>>2]=family;HEAP32[ai+8>>2]=type;HEAP32[ai+12>>2]=proto;HEAPU32[ai+24>>2]=canon;HEAPU32[ai+20>>2]=sa;if(family===10){HEAP32[ai+16>>2]=28}else{HEAP32[ai+16>>2]=16}HEAP32[ai+28>>2]=0;return ai}if(hint){flags=HEAP32[hint>>2];family=HEAP32[hint+4>>2];type=HEAP32[hint+8>>2];proto=HEAP32[hint+12>>2]}if(type&&!proto){proto=type===2?17:6}if(!type&&proto){type=proto===17?2:1}if(proto===0){proto=6}if(type===0){type=1}if(!node&&!service){return-2}if(flags&~(1|2|4|1024|8|16|32)){return-1}if(hint!==0&&HEAP32[hint>>2]&2&&!node){return-1}if(flags&32){return-2}if(type!==0&&type!==1&&type!==2){return-7}if(family!==0&&family!==2&&family!==10){return-6}if(service){service=UTF8ToString(service);port=parseInt(service,10);if(isNaN(port)){if(flags&1024){return-2}return-8}}if(!node){if(family===0){family=2}if((flags&1)===0){if(family===2){addr=_htonl(2130706433)}else{addr=[0,0,0,1]}}ai=allocaddrinfo(family,type,proto,null,addr,port);HEAPU32[out>>2]=ai;return 0}node=UTF8ToString(node);addr=inetPton4(node);if(addr!==null){if(family===0||family===2){family=2}else if(family===10&&flags&8){addr=[0,0,_htonl(65535),addr];family=10}else{return-2}}else{addr=inetPton6(node);if(addr!==null){if(family===0||family===10){family=10}else{return-2}}}if(addr!=null){ai=allocaddrinfo(family,type,proto,node,addr,port);HEAPU32[out>>2]=ai;return 0}if(flags&4){return-2}node=DNS.lookup_name(node);addr=inetPton4(node);if(family===0){family=2}else if(family===10){addr=[0,0,_htonl(65535),addr]}ai=allocaddrinfo(family,type,proto,null,addr,port);HEAPU32[out>>2]=ai;return 0}function _getnameinfo(sa,salen,node,nodelen,serv,servlen,flags){var info=readSockaddr(sa,salen);if(info.errno){return-6}var port=info.port;var addr=info.addr;var overflowed=false;if(node&&nodelen){var lookup;if(flags&1||!(lookup=DNS.lookup_addr(addr))){if(flags&8){return-2}}else{addr=lookup}var numBytesWrittenExclNull=stringToUTF8(addr,node,nodelen);if(numBytesWrittenExclNull+1>=nodelen){overflowed=true}}if(serv&&servlen){port=""+port;var numBytesWrittenExclNull=stringToUTF8(port,serv,servlen);if(numBytesWrittenExclNull+1>=servlen){overflowed=true}}if(overflowed){return-12}return 0}function arraySum(array,index){var sum=0;for(var i=0;i<=index;sum+=array[i++]){}return sum}var MONTH_DAYS_LEAP=[31,29,31,30,31,30,31,31,30,31,30,31];var MONTH_DAYS_REGULAR=[31,28,31,30,31,30,31,31,30,31,30,31];function addDays(date,days){var newDate=new Date(date.getTime());while(days>0){var leap=isLeapYear(newDate.getFullYear());var currentMonth=newDate.getMonth();var daysInCurrentMonth=(leap?MONTH_DAYS_LEAP:MONTH_DAYS_REGULAR)[currentMonth];if(days>daysInCurrentMonth-newDate.getDate()){days-=daysInCurrentMonth-newDate.getDate()+1;newDate.setDate(1);if(currentMonth<11){newDate.setMonth(currentMonth+1)}else{newDate.setMonth(0);newDate.setFullYear(newDate.getFullYear()+1)}}else{newDate.setDate(newDate.getDate()+days);return newDate}}return newDate}function writeArrayToMemory(array,buffer){HEAP8.set(array,buffer)}function _strftime(s,maxsize,format,tm){var tm_zone=HEAP32[tm+40>>2];var date={tm_sec:HEAP32[tm>>2],tm_min:HEAP32[tm+4>>2],tm_hour:HEAP32[tm+8>>2],tm_mday:HEAP32[tm+12>>2],tm_mon:HEAP32[tm+16>>2],tm_year:HEAP32[tm+20>>2],tm_wday:HEAP32[tm+24>>2],tm_yday:HEAP32[tm+28>>2],tm_isdst:HEAP32[tm+32>>2],tm_gmtoff:HEAP32[tm+36>>2],tm_zone:tm_zone?UTF8ToString(tm_zone):""};var pattern=UTF8ToString(format);var EXPANSION_RULES_1={"%c":"%a %b %d %H:%M:%S %Y","%D":"%m/%d/%y","%F":"%Y-%m-%d","%h":"%b","%r":"%I:%M:%S %p","%R":"%H:%M","%T":"%H:%M:%S","%x":"%m/%d/%y","%X":"%H:%M:%S","%Ec":"%c","%EC":"%C","%Ex":"%m/%d/%y","%EX":"%H:%M:%S","%Ey":"%y","%EY":"%Y","%Od":"%d","%Oe":"%e","%OH":"%H","%OI":"%I","%Om":"%m","%OM":"%M","%OS":"%S","%Ou":"%u","%OU":"%U","%OV":"%V","%Ow":"%w","%OW":"%W","%Oy":"%y"};for(var rule in EXPANSION_RULES_1){pattern=pattern.replace(new RegExp(rule,"g"),EXPANSION_RULES_1[rule])}var WEEKDAYS=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];var MONTHS=["January","February","March","April","May","June","July","August","September","October","November","December"];function leadingSomething(value,digits,character){var str=typeof value=="number"?value.toString():value||"";while(str.length0?1:0}var compare;if((compare=sgn(date1.getFullYear()-date2.getFullYear()))===0){if((compare=sgn(date1.getMonth()-date2.getMonth()))===0){compare=sgn(date1.getDate()-date2.getDate())}}return compare}function getFirstWeekStartDate(janFourth){switch(janFourth.getDay()){case 0:return new Date(janFourth.getFullYear()-1,11,29);case 1:return janFourth;case 2:return new Date(janFourth.getFullYear(),0,3);case 3:return new Date(janFourth.getFullYear(),0,2);case 4:return new Date(janFourth.getFullYear(),0,1);case 5:return new Date(janFourth.getFullYear()-1,11,31);case 6:return new Date(janFourth.getFullYear()-1,11,30)}}function getWeekBasedYear(date){var thisDate=addDays(new Date(date.tm_year+1900,0,1),date.tm_yday);var janFourthThisYear=new Date(thisDate.getFullYear(),0,4);var janFourthNextYear=new Date(thisDate.getFullYear()+1,0,4);var firstWeekStartThisYear=getFirstWeekStartDate(janFourthThisYear);var firstWeekStartNextYear=getFirstWeekStartDate(janFourthNextYear);if(compareByDay(firstWeekStartThisYear,thisDate)<=0){if(compareByDay(firstWeekStartNextYear,thisDate)<=0){return thisDate.getFullYear()+1}return thisDate.getFullYear()}return thisDate.getFullYear()-1}var EXPANSION_RULES_2={"%a":function(date){return WEEKDAYS[date.tm_wday].substring(0,3)},"%A":function(date){return WEEKDAYS[date.tm_wday]},"%b":function(date){return MONTHS[date.tm_mon].substring(0,3)},"%B":function(date){return MONTHS[date.tm_mon]},"%C":function(date){var year=date.tm_year+1900;return leadingNulls(year/100|0,2)},"%d":function(date){return leadingNulls(date.tm_mday,2)},"%e":function(date){return leadingSomething(date.tm_mday,2," ")},"%g":function(date){return getWeekBasedYear(date).toString().substring(2)},"%G":function(date){return getWeekBasedYear(date)},"%H":function(date){return leadingNulls(date.tm_hour,2)},"%I":function(date){var twelveHour=date.tm_hour;if(twelveHour==0)twelveHour=12;else if(twelveHour>12)twelveHour-=12;return leadingNulls(twelveHour,2)},"%j":function(date){return leadingNulls(date.tm_mday+arraySum(isLeapYear(date.tm_year+1900)?MONTH_DAYS_LEAP:MONTH_DAYS_REGULAR,date.tm_mon-1),3)},"%m":function(date){return leadingNulls(date.tm_mon+1,2)},"%M":function(date){return leadingNulls(date.tm_min,2)},"%n":function(){return"\n"},"%p":function(date){if(date.tm_hour>=0&&date.tm_hour<12){return"AM"}return"PM"},"%S":function(date){return leadingNulls(date.tm_sec,2)},"%t":function(){return"\t"},"%u":function(date){return date.tm_wday||7},"%U":function(date){var days=date.tm_yday+7-date.tm_wday;return leadingNulls(Math.floor(days/7),2)},"%V":function(date){var val=Math.floor((date.tm_yday+7-(date.tm_wday+6)%7)/7);if((date.tm_wday+371-date.tm_yday-2)%7<=2){val++}if(!val){val=52;var dec31=(date.tm_wday+7-date.tm_yday-1)%7;if(dec31==4||dec31==5&&isLeapYear(date.tm_year%400-1)){val++}}else if(val==53){var jan1=(date.tm_wday+371-date.tm_yday)%7;if(jan1!=4&&(jan1!=3||!isLeapYear(date.tm_year)))val=1}return leadingNulls(val,2)},"%w":function(date){return date.tm_wday},"%W":function(date){var days=date.tm_yday+7-(date.tm_wday+6)%7;return leadingNulls(Math.floor(days/7),2)},"%y":function(date){return(date.tm_year+1900).toString().substring(2)},"%Y":function(date){return date.tm_year+1900},"%z":function(date){var off=date.tm_gmtoff;var ahead=off>=0;off=Math.abs(off)/60;off=off/60*100+off%60;return(ahead?"+":"-")+String("0000"+off).slice(-4)},"%Z":function(date){return date.tm_zone},"%%":function(){return"%"}};pattern=pattern.replace(/%%/g,"\0\0");for(var rule in EXPANSION_RULES_2){if(pattern.includes(rule)){pattern=pattern.replace(new RegExp(rule,"g"),EXPANSION_RULES_2[rule](date))}}pattern=pattern.replace(/\0\0/g,"%");var bytes=intArrayFromString(pattern,false);if(bytes.length>maxsize){return 0}writeArrayToMemory(bytes,s);return bytes.length-1}var FSNode=function(parent,name,mode,rdev){if(!parent){parent=this}this.parent=parent;this.mount=parent.mount;this.mounted=null;this.id=FS.nextInode++;this.name=name;this.mode=mode;this.node_ops={};this.stream_ops={};this.rdev=rdev};var readMode=292|73;var writeMode=146;Object.defineProperties(FSNode.prototype,{read:{get:function(){return(this.mode&readMode)===readMode},set:function(val){val?this.mode|=readMode:this.mode&=~readMode}},write:{get:function(){return(this.mode&writeMode)===writeMode},set:function(val){val?this.mode|=writeMode:this.mode&=~writeMode}},isFolder:{get:function(){return FS.isDir(this.mode)}},isDevice:{get:function(){return FS.isChrdev(this.mode)}}});FS.FSNode=FSNode;FS.createPreloadedFile=FS_createPreloadedFile;FS.staticInit();var wasmImports={"b":___assert_fail,"f":___cxa_throw,"ka":___dlsym,"R":___syscall__newselect,"L":___syscall_accept4,"K":___syscall_bind,"J":___syscall_connect,"la":___syscall_faccessat,"g":___syscall_fcntl64,"ha":___syscall_fstat64,"U":___syscall_getdents64,"I":___syscall_getpeername,"H":___syscall_getsockname,"G":___syscall_getsockopt,"y":___syscall_ioctl,"F":___syscall_listen,"ea":___syscall_lstat64,"$":___syscall_mkdirat,"fa":___syscall_newfstatat,"w":___syscall_openat,"V":___syscall_poll,"E":___syscall_recvfrom,"T":___syscall_renameat,"S":___syscall_rmdir,"D":___syscall_sendto,"v":___syscall_socket,"ga":___syscall_stat64,"O":___syscall_unlinkat,"ia":__emscripten_get_now_is_monotonic,"M":__emscripten_throw_longjmp,"Y":__gmtime_js,"Z":__localtime_js,"_":__mktime_js,"W":__mmap_js,"X":__munmap_js,"P":__tzset_js,"a":_abort,"t":_dlopen,"oa":_emscripten_asm_const_int,"m":_emscripten_date_now,"Q":_emscripten_get_heap_max,"p":_emscripten_get_now,"ja":_emscripten_memcpy_big,"N":_emscripten_resize_heap,"ca":_environ_get,"da":_environ_sizes_get,"l":_exit,"n":_fd_close,"ba":_fd_fdstat_get,"x":_fd_read,"aa":_fd_seek,"q":_fd_write,"k":_getaddrinfo,"i":_getnameinfo,"pa":invoke_i,"na":invoke_ii,"c":invoke_iii,"o":invoke_iiii,"s":invoke_iiiii,"z":invoke_iiiiii,"r":invoke_iiiiiiiii,"B":invoke_iiiijj,"qa":invoke_iij,"h":invoke_vi,"j":invoke_vii,"d":invoke_viiii,"ma":invoke_viiiiii,"A":invoke_viiiiiiii,"C":is_timeout,"u":send_progress,"e":_strftime};var asm=createWasm();var ___wasm_call_ctors=function(){return(___wasm_call_ctors=Module["asm"]["sa"]).apply(null,arguments)};var _malloc=Module["_malloc"]=function(){return(_malloc=Module["_malloc"]=Module["asm"]["ta"]).apply(null,arguments)};var ___errno_location=function(){return(___errno_location=Module["asm"]["va"]).apply(null,arguments)};var _ntohs=function(){return(_ntohs=Module["asm"]["wa"]).apply(null,arguments)};var _htons=function(){return(_htons=Module["asm"]["xa"]).apply(null,arguments)};var _ffmpeg=Module["_ffmpeg"]=function(){return(_ffmpeg=Module["_ffmpeg"]=Module["asm"]["ya"]).apply(null,arguments)};var _ffprobe=Module["_ffprobe"]=function(){return(_ffprobe=Module["_ffprobe"]=Module["asm"]["za"]).apply(null,arguments)};var _htonl=function(){return(_htonl=Module["asm"]["Aa"]).apply(null,arguments)};var _emscripten_builtin_memalign=function(){return(_emscripten_builtin_memalign=Module["asm"]["Ba"]).apply(null,arguments)};var _setThrew=function(){return(_setThrew=Module["asm"]["Ca"]).apply(null,arguments)};var stackSave=function(){return(stackSave=Module["asm"]["Da"]).apply(null,arguments)};var stackRestore=function(){return(stackRestore=Module["asm"]["Ea"]).apply(null,arguments)};var ___cxa_is_pointer_type=function(){return(___cxa_is_pointer_type=Module["asm"]["Fa"]).apply(null,arguments)};var _ff_h264_cabac_tables=Module["_ff_h264_cabac_tables"]=1546732;var ___start_em_js=Module["___start_em_js"]=6077485;var ___stop_em_js=Module["___stop_em_js"]=6077662;function invoke_iiiii(index,a1,a2,a3,a4){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_vii(index,a1,a2){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iii(index,a1,a2){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiijj(index,a1,a2,a3,a4,a5){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_vi(index,a1){var sp=stackSave();try{getWasmTableEntry(index)(a1)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiii(index,a1,a2,a3,a4){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiii(index,a1,a2,a3){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iij(index,a1,a2){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_i(index){var sp=stackSave();try{return getWasmTableEntry(index)()}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_ii(index,a1){var sp=stackSave();try{return getWasmTableEntry(index)(a1)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiiii(index,a1,a2,a3,a4,a5){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiiii(index,a1,a2,a3,a4,a5,a6){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}Module["setValue"]=setValue;Module["getValue"]=getValue;Module["UTF8ToString"]=UTF8ToString;Module["stringToUTF8"]=stringToUTF8;Module["lengthBytesUTF8"]=lengthBytesUTF8;Module["FS"]=FS;var calledRun;dependenciesFulfilled=function runCaller(){if(!calledRun)run();if(!calledRun)dependenciesFulfilled=runCaller};function run(){if(runDependencies>0){return}preRun();if(runDependencies>0){return}function doRun(){if(calledRun)return;calledRun=true;Module["calledRun"]=true;if(ABORT)return;initRuntime();readyPromiseResolve(Module);if(Module["onRuntimeInitialized"])Module["onRuntimeInitialized"]();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(function(){setTimeout(function(){Module["setStatus"]("")},1);doRun()},1)}else{doRun()}}if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}run(); return createFFmpegCore.ready; }; })(); if (typeof exports === "object" && typeof module === "object") module.exports = createFFmpegCore; else if (typeof define === "function" && define["amd"]) define([], () => createFFmpegCore); else if (typeof exports === "object") exports["createFFmpegCore"] = createFFmpegCore; ================================================ FILE: apps/web/public/ffmpeg/ffmpeg-core.wasm ================================================ [File too large to display: 30.7 MB] ================================================ FILE: apps/web/public/fonts/font-atlas.json ================================================ { "fonts": { "42dot Sans": { "x": 0, "y": 0, "w": 131, "ch": 0, "s": ["300", "400", "500", "600", "700", "800"] }, "ABeeZee": { "x": 145, "y": 0, "w": 106, "ch": 0, "s": ["400", "400i"] }, "Abel": { "x": 265, "y": 0, "w": 47, "ch": 0, "s": ["400"] }, "Abhaya Libre": { "x": 326, "y": 0, "w": 139, "ch": 0, "s": ["400", "500", "600", "700", "800"] }, "Aboreto": { "x": 479, "y": 0, "w": 124, "ch": 0, "s": ["400"] }, "Abril Fatface": { "x": 617, "y": 0, "w": 152, "ch": 0, "s": ["400"] }, "Abyssinica SIL": { "x": 783, "y": 0, "w": 165, "ch": 0, "s": ["400"] }, "Aclonica": { "x": 962, "y": 0, "w": 122, "ch": 0, "s": ["400"] }, "Acme": { "x": 1098, "y": 0, "w": 63, "ch": 0, "s": ["400"] }, "Actor": { "x": 0, "y": 40, "w": 64, "ch": 0, "s": ["400"] }, "Adamina": { "x": 78, "y": 40, "w": 113, "ch": 0, "s": ["400"] }, "ADLaM Display": { "x": 205, "y": 40, "w": 183, "ch": 0, "s": ["400"] }, "Advent Pro": { "x": 402, "y": 40, "w": 107, "ch": 0, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Afacad": { "x": 523, "y": 40, "w": 77, "ch": 0, "s": ["400", "400i", "500", "500i", "600", "600i", "700", "700i"] }, "Afacad Flux": { "x": 614, "y": 40, "w": 118, "ch": 0, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Agbalumo": { "x": 746, "y": 40, "w": 119, "ch": 0, "s": ["400"] }, "Agdasima": { "x": 879, "y": 40, "w": 81, "ch": 0, "s": ["400", "700"] }, "Agu Display": { "x": 974, "y": 40, "w": 140, "ch": 0, "s": ["400"] }, "Aguafina Script": { "x": 0, "y": 80, "w": 120, "ch": 0, "s": ["400"] }, "Akatab": { "x": 134, "y": 80, "w": 81, "ch": 0, "s": ["400", "500", "600", "700", "800", "900"] }, "Akaya Kanadaka": { "x": 229, "y": 80, "w": 176, "ch": 0, "s": ["400"] }, "Akaya Telivigala": { "x": 419, "y": 80, "w": 165, "ch": 0, "s": ["400"] }, "Akronim": { "x": 598, "y": 80, "w": 79, "ch": 0, "s": ["400"] }, "Akshar": { "x": 691, "y": 80, "w": 71, "ch": 0, "s": ["300", "400", "500", "600", "700"] }, "Aladin": { "x": 776, "y": 80, "w": 61, "ch": 0, "s": ["400"] }, "Alan Sans": { "x": 851, "y": 80, "w": 116, "ch": 0, "s": ["300", "400", "500", "600", "700", "800", "900"] }, "Alata": { "x": 981, "y": 80, "w": 68, "ch": 0, "s": ["400"] }, "Alatsi": { "x": 1063, "y": 80, "w": 66, "ch": 0, "s": ["400"] }, "Albert Sans": { "x": 0, "y": 120, "w": 133, "ch": 0, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Aldrich": { "x": 147, "y": 120, "w": 94, "ch": 0, "s": ["400"] }, "Alef": { "x": 255, "y": 120, "w": 54, "ch": 0, "s": ["400", "700"] }, "Alegreya": { "x": 323, "y": 120, "w": 92, "ch": 0, "s": [ "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Alegreya Sans": { "x": 429, "y": 120, "w": 137, "ch": 0, "s": [ "100", "100i", "300", "300i", "400", "400i", "500", "500i", "700", "700i", "800", "800i", "900", "900i" ] }, "Alegreya Sans SC": { "x": 580, "y": 120, "w": 179, "ch": 0, "s": [ "100", "100i", "300", "300i", "400", "400i", "500", "500i", "700", "700i", "800", "800i", "900", "900i" ] }, "Alegreya SC": { "x": 773, "y": 120, "w": 137, "ch": 0, "s": [ "400", "400i", "500", "500i", "700", "700i", "800", "800i", "900", "900i" ] }, "Aleo": { "x": 924, "y": 120, "w": 59, "ch": 0, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Alex Brush": { "x": 997, "y": 120, "w": 117, "ch": 0, "s": ["400"] }, "Alexandria": { "x": 0, "y": 160, "w": 136, "ch": 0, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Alfa Slab One": { "x": 150, "y": 160, "w": 180, "ch": 0, "s": ["400"] }, "Alice": { "x": 344, "y": 160, "w": 60, "ch": 0, "s": ["400"] }, "Alike": { "x": 418, "y": 160, "w": 62, "ch": 0, "s": ["400"] }, "Alike Angular": { "x": 494, "y": 160, "w": 155, "ch": 0, "s": ["400"] }, "Alkalami": { "x": 663, "y": 160, "w": 109, "ch": 0, "s": ["400"] }, "Alkatra": { "x": 786, "y": 160, "w": 84, "ch": 0, "s": ["400", "500", "600", "700"] }, "Allan": { "x": 884, "y": 160, "w": 48, "ch": 0, "s": ["400", "700"] }, "Allerta": { "x": 946, "y": 160, "w": 93, "ch": 0, "s": ["400"] }, "Allerta Stencil": { "x": 0, "y": 200, "w": 182, "ch": 0, "s": ["400"] }, "Allison": { "x": 196, "y": 200, "w": 56, "ch": 0, "s": ["400"] }, "Allura": { "x": 266, "y": 200, "w": 69, "ch": 0, "s": ["400"] }, "Almarai": { "x": 349, "y": 200, "w": 88, "ch": 0, "s": ["300", "400", "700", "800"] }, "Almendra": { "x": 451, "y": 200, "w": 107, "ch": 0, "s": ["400", "400i", "700", "700i"] }, "Almendra Display": { "x": 572, "y": 200, "w": 192, "ch": 0, "s": ["400"] }, "Almendra SC": { "x": 778, "y": 200, "w": 142, "ch": 0, "s": ["400"] }, "Alumni Sans": { "x": 934, "y": 200, "w": 96, "ch": 0, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Alumni Sans Collegiate One": { "x": 0, "y": 240, "w": 201, "ch": 0, "s": ["400", "400i"] }, "Alumni Sans Inline One": { "x": 215, "y": 240, "w": 180, "ch": 0, "s": ["400", "400i"] }, "Alumni Sans Pinstripe": { "x": 409, "y": 240, "w": 159, "ch": 0, "s": ["400", "400i"] }, "Alumni Sans SC": { "x": 582, "y": 240, "w": 125, "ch": 0, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Amarante": { "x": 721, "y": 240, "w": 108, "ch": 0, "s": ["400"] }, "Amaranth": { "x": 843, "y": 240, "w": 110, "ch": 0, "s": ["400", "400i", "700", "700i"] }, "Amarna": { "x": 967, "y": 240, "w": 92, "ch": 0, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i" ] }, "Amatic SC": { "x": 1073, "y": 240, "w": 77, "ch": 0, "s": ["400", "700"] }, "Amethysta": { "x": 0, "y": 280, "w": 133, "ch": 0, "s": ["400"] }, "Amiko": { "x": 147, "y": 280, "w": 84, "ch": 0, "s": ["400", "600", "700"] }, "Amiri": { "x": 245, "y": 280, "w": 63, "ch": 0, "s": ["400", "400i", "700", "700i"] }, "Amiri Quran": { "x": 322, "y": 280, "w": 131, "ch": 0, "s": ["400"] }, "Amita": { "x": 467, "y": 280, "w": 73, "ch": 0, "s": ["400", "700"] }, "Anaheim": { "x": 554, "y": 280, "w": 93, "ch": 0, "s": ["400", "500", "600", "700", "800"] }, "Ancizar Sans": { "x": 661, "y": 280, "w": 126, "ch": 0, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Ancizar Serif": { "x": 801, "y": 280, "w": 136, "ch": 0, "s": [ "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Andada Pro": { "x": 951, "y": 280, "w": 137, "ch": 0, "s": [ "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i" ] }, "Andika": { "x": 1102, "y": 280, "w": 85, "ch": 0, "s": ["400", "400i", "700", "700i"] }, "Anek Bangla": { "x": 0, "y": 320, "w": 131, "ch": 0, "s": ["100", "200", "300", "400", "500", "600", "700", "800"] }, "Anek Devanagari": { "x": 145, "y": 320, "w": 176, "ch": 0, "s": ["100", "200", "300", "400", "500", "600", "700", "800"] }, "Anek Gujarati": { "x": 335, "y": 320, "w": 143, "ch": 0, "s": ["100", "200", "300", "400", "500", "600", "700", "800"] }, "Anek Gurmukhi": { "x": 492, "y": 320, "w": 164, "ch": 0, "s": ["100", "200", "300", "400", "500", "600", "700", "800"] }, "Anek Kannada": { "x": 670, "y": 320, "w": 152, "ch": 0, "s": ["100", "200", "300", "400", "500", "600", "700", "800"] }, "Anek Latin": { "x": 836, "y": 320, "w": 115, "ch": 0, "s": ["100", "200", "300", "400", "500", "600", "700", "800"] }, "Anek Malayalam": { "x": 965, "y": 320, "w": 171, "ch": 0, "s": ["100", "200", "300", "400", "500", "600", "700", "800"] }, "Anek Odia": { "x": 0, "y": 360, "w": 108, "ch": 0, "s": ["100", "200", "300", "400", "500", "600", "700", "800"] }, "Anek Tamil": { "x": 122, "y": 360, "w": 119, "ch": 0, "s": ["100", "200", "300", "400", "500", "600", "700", "800"] }, "Anek Telugu": { "x": 255, "y": 360, "w": 131, "ch": 0, "s": ["100", "200", "300", "400", "500", "600", "700", "800"] }, "Angkor": { "x": 400, "y": 360, "w": 111, "ch": 0, "s": ["400"] }, "Annapurna SIL": { "x": 525, "y": 360, "w": 151, "ch": 0, "s": ["400", "700"] }, "Annie Use Your Telescope": { "x": 690, "y": 360, "w": 215, "ch": 0, "s": ["400"] }, "Anonymous Pro": { "x": 919, "y": 360, "w": 179, "ch": 0, "s": ["400", "400i", "700", "700i"] }, "Anta": { "x": 1112, "y": 360, "w": 62, "ch": 0, "s": ["400"] }, "Antic": { "x": 0, "y": 400, "w": 60, "ch": 0, "s": ["400"] }, "Antic Didone": { "x": 74, "y": 400, "w": 145, "ch": 0, "s": ["400"] }, "Antic Slab": { "x": 233, "y": 400, "w": 114, "ch": 0, "s": ["400"] }, "Anton": { "x": 361, "y": 400, "w": 63, "ch": 0, "s": ["400"] }, "Anton SC": { "x": 438, "y": 400, "w": 93, "ch": 0, "s": ["400"] }, "Antonio": { "x": 545, "y": 400, "w": 76, "ch": 0, "s": ["100", "200", "300", "400", "500", "600", "700"] }, "Anuphan": { "x": 635, "y": 400, "w": 105, "ch": 0, "s": ["100", "200", "300", "400", "500", "600", "700"] }, "Anybody": { "x": 754, "y": 400, "w": 107, "ch": 0, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Aoboshi One": { "x": 875, "y": 400, "w": 158, "ch": 0, "s": ["400"] }, "AR One Sans": { "x": 1047, "y": 400, "w": 152, "ch": 0, "s": ["400", "500", "600", "700"] }, "Arapey": { "x": 0, "y": 440, "w": 75, "ch": 0, "s": ["400", "400i"] }, "Arbutus": { "x": 89, "y": 440, "w": 128, "ch": 0, "s": ["400"] }, "Arbutus Slab": { "x": 231, "y": 440, "w": 159, "ch": 0, "s": ["400"] }, "Architects Daughter": { "x": 404, "y": 440, "w": 237, "ch": 0, "s": ["400"] }, "Archivo": { "x": 655, "y": 440, "w": 90, "ch": 0, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Archivo Black": { "x": 759, "y": 440, "w": 190, "ch": 0, "s": ["400"] }, "Archivo Narrow": { "x": 963, "y": 440, "w": 144, "ch": 0, "s": ["400", "400i", "500", "500i", "600", "600i", "700", "700i"] }, "Are You Serious": { "x": 0, "y": 480, "w": 142, "ch": 0, "s": ["400"] }, "Aref Ruqaa": { "x": 156, "y": 480, "w": 130, "ch": 0, "s": ["400", "700"] }, "Aref Ruqaa Ink": { "x": 300, "y": 480, "w": 172, "ch": 0, "s": ["400", "700"] }, "Arima": { "x": 486, "y": 480, "w": 73, "ch": 0, "s": ["100", "200", "300", "400", "500", "600", "700"] }, "Arima Madurai": { "x": 573, "y": 480, "w": 170, "ch": 0, "s": ["100", "200", "300", "400", "500", "700", "800", "900"] }, "Arimo": { "x": 757, "y": 480, "w": 71, "ch": 0, "s": ["400", "400i", "500", "500i", "600", "600i", "700", "700i"] }, "Arizonia": { "x": 842, "y": 480, "w": 95, "ch": 0, "s": ["400"] }, "Armata": { "x": 951, "y": 480, "w": 100, "ch": 0, "s": ["400"] }, "Arsenal": { "x": 1065, "y": 480, "w": 80, "ch": 0, "s": ["400", "400i", "700", "700i"] }, "Arsenal SC": { "x": 0, "y": 520, "w": 118, "ch": 0, "s": ["400", "400i", "700", "700i"] }, "Artifika": { "x": 132, "y": 520, "w": 105, "ch": 0, "s": ["400"] }, "Arvo": { "x": 251, "y": 520, "w": 65, "ch": 0, "s": ["400", "400i", "700", "700i"] }, "Arya": { "x": 330, "y": 520, "w": 53, "ch": 0, "s": ["400", "700"] }, "Asap": { "x": 397, "y": 520, "w": 60, "ch": 0, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Asap Condensed": { "x": 471, "y": 520, "w": 153, "ch": 0, "s": [ "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Asar": { "x": 638, "y": 520, "w": 53, "ch": 0, "s": ["400"] }, "Asimovian": { "x": 705, "y": 520, "w": 117, "ch": 0, "s": ["400"] }, "Asset": { "x": 836, "y": 520, "w": 154, "ch": 0, "s": ["400"] }, "Assistant": { "x": 1004, "y": 520, "w": 98, "ch": 0, "s": ["200", "300", "400", "500", "600", "700", "800"] }, "Asta Sans": { "x": 0, "y": 560, "w": 115, "ch": 0, "s": ["300", "400", "500", "600", "700", "800"] }, "Astloch": { "x": 129, "y": 560, "w": 72, "ch": 0, "s": ["400", "700"] }, "Asul": { "x": 215, "y": 560, "w": 54, "ch": 0, "s": ["400", "700"] }, "Athiti": { "x": 283, "y": 560, "w": 62, "ch": 0, "s": ["200", "300", "400", "500", "600", "700"] }, "Atkinson Hyperlegible": { "x": 359, "y": 560, "w": 239, "ch": 0, "s": ["400", "400i", "700", "700i"] }, "Atkinson Hyperlegible Mono": { "x": 612, "y": 560, "w": 403, "ch": 0, "s": [ "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i" ] }, "Atkinson Hyperlegible Next": { "x": 0, "y": 600, "w": 296, "ch": 0, "s": [ "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i" ] }, "Atma": { "x": 310, "y": 600, "w": 59, "ch": 0, "s": ["300", "400", "500", "600", "700"] }, "Atomic Age": { "x": 383, "y": 600, "w": 144, "ch": 0, "s": ["400"] }, "Aubrey": { "x": 541, "y": 600, "w": 67, "ch": 0, "s": ["400"] }, "Audiowide": { "x": 622, "y": 600, "w": 145, "ch": 0, "s": ["400"] }, "Autour One": { "x": 781, "y": 600, "w": 168, "ch": 0, "s": ["400"] }, "Average": { "x": 963, "y": 600, "w": 88, "ch": 0, "s": ["400"] }, "Average Sans": { "x": 0, "y": 640, "w": 138, "ch": 0, "s": ["400"] }, "Averia Gruesa Libre": { "x": 152, "y": 640, "w": 217, "ch": 0, "s": ["400"] }, "Averia Libre": { "x": 383, "y": 640, "w": 138, "ch": 0, "s": ["300", "300i", "400", "400i", "700", "700i"] }, "Averia Sans Libre": { "x": 535, "y": 640, "w": 190, "ch": 0, "s": ["300", "300i", "400", "400i", "700", "700i"] }, "Averia Serif Libre": { "x": 739, "y": 640, "w": 202, "ch": 0, "s": ["300", "300i", "400", "400i", "700", "700i"] }, "Azeret Mono": { "x": 955, "y": 640, "w": 180, "ch": 0, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "B612": { "x": 0, "y": 680, "w": 71, "ch": 0, "s": ["400", "400i", "700", "700i"] }, "B612 Mono": { "x": 85, "y": 680, "w": 149, "ch": 0, "s": ["400", "400i", "700", "700i"] }, "Babylonica": { "x": 248, "y": 680, "w": 90, "ch": 0, "s": ["400"] }, "Bacasime Antique": { "x": 352, "y": 680, "w": 182, "ch": 0, "s": ["400"] }, "Bad Script": { "x": 548, "y": 680, "w": 103, "ch": 0, "s": ["400"] }, "Badeen Display": { "x": 665, "y": 680, "w": 194, "ch": 0, "s": ["400"] }, "Bagel Fat One": { "x": 873, "y": 680, "w": 158, "ch": 0, "s": ["400"] }, "Bahiana": { "x": 1045, "y": 680, "w": 58, "ch": 0, "s": ["400"] }, "Bahianita": { "x": 1117, "y": 680, "w": 63, "ch": 0, "s": ["400"] }, "Bai Jamjuree": { "x": 0, "y": 720, "w": 152, "ch": 0, "s": [ "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i" ] }, "Bakbak One": { "x": 166, "y": 720, "w": 141, "ch": 0, "s": ["400"] }, "Ballet": { "x": 321, "y": 720, "w": 65, "ch": 0, "s": ["400"] }, "Baloo 2": { "x": 400, "y": 720, "w": 84, "ch": 0, "s": ["400", "500", "600", "700", "800"] }, "Baloo Bhai 2": { "x": 498, "y": 720, "w": 134, "ch": 0, "s": ["400", "500", "600", "700", "800"] }, "Baloo Bhaijaan 2": { "x": 646, "y": 720, "w": 177, "ch": 0, "s": ["400", "500", "600", "700", "800"] }, "Baloo Bhaina 2": { "x": 837, "y": 720, "w": 160, "ch": 0, "s": ["400", "500", "600", "700", "800"] }, "Baloo Chettan 2": { "x": 1011, "y": 720, "w": 172, "ch": 0, "s": ["400", "500", "600", "700", "800"] }, "Baloo Da 2": { "x": 0, "y": 760, "w": 117, "ch": 0, "s": ["400", "500", "600", "700", "800"] }, "Baloo Paaji 2": { "x": 131, "y": 760, "w": 138, "ch": 0, "s": ["400", "500", "600", "700", "800"] }, "Baloo Tamma 2": { "x": 283, "y": 760, "w": 169, "ch": 0, "s": ["400", "500", "600", "700", "800"] }, "Baloo Tammudu 2": { "x": 466, "y": 760, "w": 191, "ch": 0, "s": ["400", "500", "600", "700", "800"] }, "Baloo Thambi 2": { "x": 671, "y": 760, "w": 165, "ch": 0, "s": ["400", "500", "600", "700", "800"] }, "Balsamiq Sans": { "x": 850, "y": 760, "w": 168, "ch": 0, "s": ["400", "400i", "700", "700i"] }, "Balthazar": { "x": 1032, "y": 760, "w": 96, "ch": 0, "s": ["400"] }, "Bangers": { "x": 0, "y": 0, "w": 81, "ch": 1, "s": ["400"] }, "Barlow": { "x": 95, "y": 0, "w": 80, "ch": 1, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Barlow Condensed": { "x": 189, "y": 0, "w": 158, "ch": 1, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Barlow Semi Condensed": { "x": 361, "y": 0, "w": 231, "ch": 1, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Barriecito": { "x": 606, "y": 0, "w": 106, "ch": 1, "s": ["400"] }, "Barrio": { "x": 726, "y": 0, "w": 80, "ch": 1, "s": ["400"] }, "Basic": { "x": 820, "y": 0, "w": 61, "ch": 1, "s": ["400"] }, "Baskervville": { "x": 895, "y": 0, "w": 136, "ch": 1, "s": ["400", "400i", "500", "500i", "600", "600i", "700", "700i"] }, "Baskervville SC": { "x": 0, "y": 40, "w": 187, "ch": 1, "s": ["400", "500", "600", "700"] }, "Battambang": { "x": 201, "y": 40, "w": 147, "ch": 1, "s": ["100", "300", "400", "700", "900"] }, "Baumans": { "x": 362, "y": 40, "w": 101, "ch": 1, "s": ["400"] }, "Bayon": { "x": 477, "y": 40, "w": 61, "ch": 1, "s": ["400"] }, "BBH Bartle": { "x": 552, "y": 40, "w": 298, "ch": 1, "s": ["400"] }, "BBH Bogle": { "x": 864, "y": 40, "w": 98, "ch": 1, "s": ["400"] }, "BBH Hegarty": { "x": 976, "y": 40, "w": 182, "ch": 1, "s": ["400"] }, "BBH Sans Bartle": { "x": 0, "y": 80, "w": 427, "ch": 1, "s": ["400"] }, "BBH Sans Bogle": { "x": 441, "y": 80, "w": 142, "ch": 1, "s": ["400"] }, "BBH Sans Hegarty": { "x": 597, "y": 80, "w": 254, "ch": 1, "s": ["400"] }, "Be Vietnam Pro": { "x": 865, "y": 80, "w": 188, "ch": 1, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Beau Rivage": { "x": 1067, "y": 80, "w": 108, "ch": 1, "s": ["400"] }, "Bebas Neue": { "x": 0, "y": 120, "w": 96, "ch": 1, "s": ["400"] }, "Beiruti": { "x": 110, "y": 120, "w": 66, "ch": 1, "s": ["200", "300", "400", "500", "600", "700", "800", "900"] }, "Belanosima": { "x": 190, "y": 120, "w": 124, "ch": 1, "s": ["400", "600", "700"] }, "Belgrano": { "x": 328, "y": 120, "w": 115, "ch": 1, "s": ["400"] }, "Bellefair": { "x": 457, "y": 120, "w": 81, "ch": 1, "s": ["400"] }, "Belleza": { "x": 552, "y": 120, "w": 79, "ch": 1, "s": ["400"] }, "Bellota": { "x": 645, "y": 120, "w": 84, "ch": 1, "s": ["300", "300i", "400", "400i", "700", "700i"] }, "Bellota Text": { "x": 743, "y": 120, "w": 129, "ch": 1, "s": ["300", "300i", "400", "400i", "700", "700i"] }, "BenchNine": { "x": 886, "y": 120, "w": 82, "ch": 1, "s": ["300", "400", "700"] }, "Benne": { "x": 982, "y": 120, "w": 66, "ch": 1, "s": ["400"] }, "Bentham": { "x": 1062, "y": 120, "w": 98, "ch": 1, "s": ["400"] }, "Berkshire Swash": { "x": 0, "y": 160, "w": 178, "ch": 1, "s": ["400"] }, "Besley": { "x": 192, "y": 160, "w": 86, "ch": 1, "s": [ "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Beth Ellen": { "x": 292, "y": 160, "w": 138, "ch": 1, "s": ["400"] }, "Bevan": { "x": 444, "y": 160, "w": 94, "ch": 1, "s": ["400", "400i"] }, "BhuTuka Expanded One": { "x": 552, "y": 160, "w": 412, "ch": 1, "s": ["400"] }, "Big Shoulders": { "x": 978, "y": 160, "w": 117, "ch": 1, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Big Shoulders Display": { "x": 0, "y": 200, "w": 158, "ch": 1, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Big Shoulders Inline": { "x": 172, "y": 200, "w": 167, "ch": 1, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Big Shoulders Inline Display": { "x": 353, "y": 200, "w": 203, "ch": 1, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Big Shoulders Inline Text": { "x": 570, "y": 200, "w": 201, "ch": 1, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Big Shoulders Stencil": { "x": 785, "y": 200, "w": 177, "ch": 1, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Big Shoulders Stencil Display": { "x": 976, "y": 200, "w": 211, "ch": 1, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Big Shoulders Stencil Text": { "x": 0, "y": 240, "w": 210, "ch": 1, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Big Shoulders Text": { "x": 224, "y": 240, "w": 150, "ch": 1, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Bigelow Rules": { "x": 388, "y": 240, "w": 86, "ch": 1, "s": ["400"] }, "Bigshot One": { "x": 488, "y": 240, "w": 138, "ch": 1, "s": ["400"] }, "Bilbo": { "x": 640, "y": 240, "w": 45, "ch": 1, "s": ["400"] }, "Bilbo Swash Caps": { "x": 699, "y": 240, "w": 144, "ch": 1, "s": ["400"] }, "BioRhyme": { "x": 857, "y": 240, "w": 139, "ch": 1, "s": ["200", "300", "400", "500", "600", "700", "800"] }, "BioRhyme Expanded": { "x": 0, "y": 280, "w": 455, "ch": 1, "s": ["200", "300", "400", "700", "800"] }, "Birthstone": { "x": 469, "y": 280, "w": 79, "ch": 1, "s": ["400"] }, "Birthstone Bounce": { "x": 562, "y": 280, "w": 167, "ch": 1, "s": ["400", "500"] }, "Biryani": { "x": 743, "y": 280, "w": 90, "ch": 1, "s": ["200", "300", "400", "600", "700", "800", "900"] }, "Bitcount": { "x": 847, "y": 280, "w": 124, "ch": 1, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Bitcount Grid Double": { "x": 0, "y": 320, "w": 296, "ch": 1, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Bitcount Grid Double Ink": { "x": 310, "y": 320, "w": 354, "ch": 1, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Bitcount Grid Single": { "x": 678, "y": 320, "w": 296, "ch": 1, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Bitcount Grid Single Ink": { "x": 0, "y": 360, "w": 354, "ch": 1, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Bitcount Ink": { "x": 368, "y": 360, "w": 181, "ch": 1, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Bitcount Prop Double": { "x": 563, "y": 360, "w": 265, "ch": 1, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Bitcount Prop Double Ink": { "x": 842, "y": 360, "w": 308, "ch": 1, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Bitcount Prop Single": { "x": 0, "y": 400, "w": 244, "ch": 1, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Bitcount Prop Single Ink": { "x": 258, "y": 400, "w": 282, "ch": 1, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Bitcount Single": { "x": 554, "y": 400, "w": 224, "ch": 1, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Bitcount Single Ink": { "x": 792, "y": 400, "w": 282, "ch": 1, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Bitter": { "x": 1088, "y": 400, "w": 71, "ch": 1, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "BIZ UDGothic": { "x": 0, "y": 440, "w": 152, "ch": 1, "s": ["400", "700"] }, "BIZ UDMincho": { "x": 166, "y": 440, "w": 152, "ch": 1, "s": ["400", "700"] }, "BIZ UDPGothic": { "x": 332, "y": 440, "w": 209, "ch": 1, "s": ["400", "700"] }, "BIZ UDPMincho": { "x": 555, "y": 440, "w": 208, "ch": 1, "s": ["400", "700"] }, "Black And White Picture": { "x": 777, "y": 440, "w": 218, "ch": 1, "s": ["400"] }, "Black Han Sans": { "x": 0, "y": 480, "w": 212, "ch": 1, "s": ["400"] }, "Black Ops One": { "x": 226, "y": 480, "w": 191, "ch": 1, "s": ["400"] }, "Blaka": { "x": 431, "y": 480, "w": 57, "ch": 1, "s": ["400"] }, "Blaka Hollow": { "x": 502, "y": 480, "w": 119, "ch": 1, "s": ["400"] }, "Blaka Ink": { "x": 635, "y": 480, "w": 87, "ch": 1, "s": ["400"] }, "Blinker": { "x": 736, "y": 480, "w": 77, "ch": 1, "s": ["100", "200", "300", "400", "600", "700", "800", "900"] }, "Bodoni Moda": { "x": 827, "y": 480, "w": 155, "ch": 1, "s": [ "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Bodoni Moda SC": { "x": 996, "y": 480, "w": 200, "ch": 1, "s": [ "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Bokor": { "x": 0, "y": 520, "w": 58, "ch": 1, "s": ["400"] }, "Boldonse": { "x": 72, "y": 520, "w": 147, "ch": 1, "s": ["400"] }, "Bona Nova": { "x": 233, "y": 520, "w": 124, "ch": 1, "s": ["400", "400i", "700", "700i"] }, "Bona Nova SC": { "x": 371, "y": 520, "w": 170, "ch": 1, "s": ["400", "400i", "700", "700i"] }, "Bonbon": { "x": 555, "y": 520, "w": 104, "ch": 1, "s": ["400"] }, "Bonheur Royale": { "x": 673, "y": 520, "w": 124, "ch": 1, "s": ["400"] }, "Boogaloo": { "x": 811, "y": 520, "w": 81, "ch": 1, "s": ["400"] }, "Borel": { "x": 906, "y": 520, "w": 69, "ch": 1, "s": ["400"] }, "Bowlby One": { "x": 989, "y": 520, "w": 174, "ch": 1, "s": ["400"] }, "Bowlby One SC": { "x": 0, "y": 560, "w": 213, "ch": 1, "s": ["400"] }, "Braah One": { "x": 227, "y": 560, "w": 124, "ch": 1, "s": ["400"] }, "Brawler": { "x": 365, "y": 560, "w": 95, "ch": 1, "s": ["400", "700"] }, "Bree Serif": { "x": 474, "y": 560, "w": 113, "ch": 1, "s": ["400"] }, "Bricolage Grotesque": { "x": 601, "y": 560, "w": 238, "ch": 1, "s": ["200", "300", "400", "500", "600", "700", "800"] }, "Briem Hand": { "x": 853, "y": 560, "w": 146, "ch": 1, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Bruno Ace": { "x": 1013, "y": 560, "w": 168, "ch": 1, "s": ["400"] }, "Bruno Ace SC": { "x": 0, "y": 600, "w": 219, "ch": 1, "s": ["400"] }, "Brygada 1918": { "x": 233, "y": 600, "w": 157, "ch": 1, "s": ["400", "400i", "500", "500i", "600", "600i", "700", "700i"] }, "Bubblegum Sans": { "x": 404, "y": 600, "w": 155, "ch": 1, "s": ["400"] }, "Bubbler One": { "x": 573, "y": 600, "w": 117, "ch": 1, "s": ["400"] }, "Buenard": { "x": 704, "y": 600, "w": 93, "ch": 1, "s": ["400", "500", "600", "700"] }, "Bungee": { "x": 811, "y": 600, "w": 110, "ch": 1, "s": ["400"] }, "Bungee Hairline": { "x": 935, "y": 600, "w": 249, "ch": 1, "s": ["400"] }, "Bungee Inline": { "x": 0, "y": 640, "w": 213, "ch": 1, "s": ["400"] }, "Bungee Outline": { "x": 227, "y": 640, "w": 232, "ch": 1, "s": ["400"] }, "Bungee Shade": { "x": 473, "y": 640, "w": 229, "ch": 1, "s": ["400"] }, "Bungee Spice": { "x": 716, "y": 640, "w": 193, "ch": 1, "s": ["400"] }, "Bungee Tint": { "x": 923, "y": 640, "w": 179, "ch": 1, "s": ["400"] }, "Butcherman": { "x": 0, "y": 680, "w": 161, "ch": 1, "s": ["400"] }, "Butterfly Kids": { "x": 175, "y": 680, "w": 116, "ch": 1, "s": ["400"] }, "Bytesized": { "x": 305, "y": 680, "w": 116, "ch": 1, "s": ["400"] }, "Cabin": { "x": 435, "y": 680, "w": 67, "ch": 1, "s": ["400", "400i", "500", "500i", "600", "600i", "700", "700i"] }, "Cabin Condensed": { "x": 516, "y": 680, "w": 163, "ch": 1, "s": ["400", "500", "600", "700"] }, "Cabin Sketch": { "x": 693, "y": 680, "w": 141, "ch": 1, "s": ["400", "700"] }, "Cactus Classical Serif": { "x": 848, "y": 680, "w": 235, "ch": 1, "s": ["400"] }, "Caesar Dressing": { "x": 0, "y": 720, "w": 164, "ch": 1, "s": ["400"] }, "Cagliostro": { "x": 178, "y": 720, "w": 115, "ch": 1, "s": ["400"] }, "Cairo": { "x": 307, "y": 720, "w": 60, "ch": 1, "s": ["200", "300", "400", "500", "600", "700", "800", "900"] }, "Cairo Play": { "x": 381, "y": 720, "w": 108, "ch": 1, "s": ["200", "300", "400", "500", "600", "700", "800", "900"] }, "Cal Sans": { "x": 503, "y": 720, "w": 99, "ch": 1, "s": ["400"] }, "Caladea": { "x": 616, "y": 720, "w": 84, "ch": 1, "s": ["400", "400i", "700", "700i"] }, "Calistoga": { "x": 714, "y": 720, "w": 109, "ch": 1, "s": ["400"] }, "Calligraffitti": { "x": 837, "y": 720, "w": 124, "ch": 1, "s": ["400"] }, "Cambay": { "x": 975, "y": 720, "w": 92, "ch": 1, "s": ["400", "400i", "700", "700i"] }, "Cambo": { "x": 1081, "y": 720, "w": 82, "ch": 1, "s": ["400"] }, "Candal": { "x": 0, "y": 760, "w": 102, "ch": 1, "s": ["400"] }, "Cantarell": { "x": 116, "y": 760, "w": 109, "ch": 1, "s": ["400", "400i", "700", "700i"] }, "Cantata One": { "x": 239, "y": 760, "w": 163, "ch": 1, "s": ["400"] }, "Cantora One": { "x": 416, "y": 760, "w": 134, "ch": 1, "s": ["400"] }, "Caprasimo": { "x": 564, "y": 760, "w": 139, "ch": 1, "s": ["400"] }, "Capriola": { "x": 717, "y": 760, "w": 110, "ch": 1, "s": ["400"] }, "Caramel": { "x": 841, "y": 760, "w": 66, "ch": 1, "s": ["400"] }, "Carattere": { "x": 921, "y": 760, "w": 79, "ch": 1, "s": ["400"] }, "Cardo": { "x": 1014, "y": 760, "w": 72, "ch": 1, "s": ["400", "400i", "700", "700i"] }, "Carlito": { "x": 1100, "y": 760, "w": 73, "ch": 1, "s": ["400", "400i", "700", "700i"] }, "Carme": { "x": 0, "y": 0, "w": 79, "ch": 2, "s": ["400"] }, "Carrois Gothic": { "x": 93, "y": 0, "w": 155, "ch": 2, "s": ["400"] }, "Carrois Gothic SC": { "x": 262, "y": 0, "w": 211, "ch": 2, "s": ["400"] }, "Carter One": { "x": 487, "y": 0, "w": 136, "ch": 2, "s": ["400"] }, "Cascadia Code": { "x": 637, "y": 0, "w": 191, "ch": 2, "s": [ "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i" ] }, "Cascadia Mono": { "x": 842, "y": 0, "w": 191, "ch": 2, "s": [ "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i" ] }, "Castoro": { "x": 1047, "y": 0, "w": 93, "ch": 2, "s": ["400", "400i"] }, "Castoro Titling": { "x": 0, "y": 40, "w": 236, "ch": 2, "s": ["400"] }, "Catamaran": { "x": 250, "y": 40, "w": 116, "ch": 2, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Caudex": { "x": 380, "y": 40, "w": 92, "ch": 2, "s": ["400", "400i", "700", "700i"] }, "Cause": { "x": 486, "y": 40, "w": 72, "ch": 2, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Caveat": { "x": 572, "y": 40, "w": 63, "ch": 2, "s": ["400", "500", "600", "700"] }, "Caveat Brush": { "x": 649, "y": 40, "w": 120, "ch": 2, "s": ["400"] }, "Cedarville Cursive": { "x": 783, "y": 40, "w": 203, "ch": 2, "s": ["400"] }, "Ceviche One": { "x": 1000, "y": 40, "w": 111, "ch": 2, "s": ["400"] }, "Chakra Petch": { "x": 0, "y": 80, "w": 154, "ch": 2, "s": [ "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i" ] }, "Changa": { "x": 168, "y": 80, "w": 86, "ch": 2, "s": ["200", "300", "400", "500", "600", "700", "800"] }, "Changa One": { "x": 268, "y": 80, "w": 136, "ch": 2, "s": ["400", "400i"] }, "Chango": { "x": 418, "y": 80, "w": 128, "ch": 2, "s": ["400"] }, "Charis SIL": { "x": 560, "y": 80, "w": 116, "ch": 2, "s": ["400", "400i", "700", "700i"] }, "Charm": { "x": 690, "y": 80, "w": 69, "ch": 2, "s": ["400", "700"] }, "Charmonman": { "x": 773, "y": 80, "w": 136, "ch": 2, "s": ["400", "700"] }, "Chathura": { "x": 923, "y": 80, "w": 55, "ch": 2, "s": ["100", "300", "400", "700", "800"] }, "Chau Philomene One": { "x": 992, "y": 80, "w": 200, "ch": 2, "s": ["400", "400i"] }, "Chela One": { "x": 0, "y": 120, "w": 97, "ch": 2, "s": ["400"] }, "Chelsea Market": { "x": 111, "y": 120, "w": 200, "ch": 2, "s": ["400"] }, "Cherish": { "x": 325, "y": 120, "w": 60, "ch": 2, "s": ["400"] }, "Cherry Bomb One": { "x": 399, "y": 120, "w": 200, "ch": 2, "s": ["400"] }, "Cherry Cream Soda": { "x": 613, "y": 120, "w": 274, "ch": 2, "s": ["400"] }, "Cherry Swash": { "x": 901, "y": 120, "w": 159, "ch": 2, "s": ["400", "700"] }, "Chewy": { "x": 1074, "y": 120, "w": 73, "ch": 2, "s": ["400"] }, "Chicle": { "x": 0, "y": 160, "w": 56, "ch": 2, "s": ["400"] }, "Chilanka": { "x": 70, "y": 160, "w": 99, "ch": 2, "s": ["400"] }, "Chiron GoRound TC": { "x": 183, "y": 160, "w": 225, "ch": 2, "s": ["200", "300", "400", "500", "600", "700", "800", "900"] }, "Chiron Hei HK": { "x": 422, "y": 160, "w": 163, "ch": 2, "s": [ "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Chivo": { "x": 599, "y": 160, "w": 72, "ch": 2, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Chivo Mono": { "x": 685, "y": 160, "w": 152, "ch": 2, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Chocolate Classical Sans": { "x": 851, "y": 160, "w": 283, "ch": 2, "s": ["400"] }, "Chokokutai": { "x": 0, "y": 200, "w": 148, "ch": 2, "s": ["400"] }, "Chonburi": { "x": 162, "y": 200, "w": 138, "ch": 2, "s": ["400"] }, "Cinzel": { "x": 314, "y": 200, "w": 94, "ch": 2, "s": ["400", "500", "600", "700", "800", "900"] }, "Cinzel Decorative": { "x": 422, "y": 200, "w": 262, "ch": 2, "s": ["400", "700", "900"] }, "Clicker Script": { "x": 698, "y": 200, "w": 117, "ch": 2, "s": ["400"] }, "Climate Crisis": { "x": 829, "y": 200, "w": 247, "ch": 2, "s": ["400"] }, "Coda": { "x": 1090, "y": 200, "w": 65, "ch": 2, "s": ["400", "800"] }, "Codystar": { "x": 0, "y": 240, "w": 137, "ch": 2, "s": ["300", "400"] }, "Coiny": { "x": 151, "y": 240, "w": 79, "ch": 2, "s": ["400"] }, "Combo": { "x": 244, "y": 240, "w": 69, "ch": 2, "s": ["400"] }, "Comfortaa": { "x": 327, "y": 240, "w": 143, "ch": 2, "s": ["300", "400", "500", "600", "700"] }, "Comforter": { "x": 484, "y": 240, "w": 99, "ch": 2, "s": ["400"] }, "Comforter Brush": { "x": 597, "y": 240, "w": 147, "ch": 2, "s": ["400"] }, "Comic Neue": { "x": 758, "y": 240, "w": 133, "ch": 2, "s": ["300", "300i", "400", "400i", "700", "700i"] }, "Comic Relief": { "x": 905, "y": 240, "w": 147, "ch": 2, "s": ["400", "700"] }, "Coming Soon": { "x": 0, "y": 280, "w": 152, "ch": 2, "s": ["400"] }, "Comme": { "x": 166, "y": 280, "w": 96, "ch": 2, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Commissioner": { "x": 276, "y": 280, "w": 166, "ch": 2, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Concert One": { "x": 456, "y": 280, "w": 136, "ch": 2, "s": ["400"] }, "Condiment": { "x": 606, "y": 280, "w": 96, "ch": 2, "s": ["400"] }, "Contrail One": { "x": 716, "y": 280, "w": 128, "ch": 2, "s": ["400"] }, "Convergence": { "x": 858, "y": 280, "w": 155, "ch": 2, "s": ["400"] }, "Cookie": { "x": 1027, "y": 280, "w": 55, "ch": 2, "s": ["400"] }, "Copse": { "x": 1096, "y": 280, "w": 73, "ch": 2, "s": ["400"] }, "Coral Pixels": { "x": 0, "y": 320, "w": 140, "ch": 2, "s": ["400"] }, "Corben": { "x": 154, "y": 320, "w": 91, "ch": 2, "s": ["400", "700"] }, "Corinthia": { "x": 259, "y": 320, "w": 69, "ch": 2, "s": ["400", "700"] }, "Cormorant": { "x": 342, "y": 320, "w": 113, "ch": 2, "s": [ "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i" ] }, "Cormorant Garamond": { "x": 469, "y": 320, "w": 220, "ch": 2, "s": [ "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i" ] }, "Cormorant Infant": { "x": 703, "y": 320, "w": 181, "ch": 2, "s": [ "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i" ] }, "Cormorant SC": { "x": 898, "y": 320, "w": 167, "ch": 2, "s": ["300", "400", "500", "600", "700"] }, "Cormorant Unicase": { "x": 0, "y": 360, "w": 222, "ch": 2, "s": ["300", "400", "500", "600", "700"] }, "Cormorant Upright": { "x": 236, "y": 360, "w": 188, "ch": 2, "s": ["300", "400", "500", "600", "700"] }, "Cossette Texte": { "x": 438, "y": 360, "w": 164, "ch": 2, "s": ["400", "700"] }, "Cossette Titre": { "x": 616, "y": 360, "w": 116, "ch": 2, "s": ["400", "700"] }, "Courgette": { "x": 746, "y": 360, "w": 109, "ch": 2, "s": ["400"] }, "Courier Prime": { "x": 869, "y": 360, "w": 196, "ch": 2, "s": ["400", "400i", "700", "700i"] }, "Cousine": { "x": 1079, "y": 360, "w": 109, "ch": 2, "s": ["400", "400i", "700", "700i"] }, "Coustard": { "x": 0, "y": 400, "w": 115, "ch": 2, "s": ["400", "900"] }, "Covered By Your Grace": { "x": 129, "y": 400, "w": 206, "ch": 2, "s": ["400"] }, "Crafty Girls": { "x": 349, "y": 400, "w": 153, "ch": 2, "s": ["400"] }, "Creepster": { "x": 516, "y": 400, "w": 101, "ch": 2, "s": ["400"] }, "Crete Round": { "x": 631, "y": 400, "w": 142, "ch": 2, "s": ["400", "400i"] }, "Crimson Pro": { "x": 787, "y": 400, "w": 127, "ch": 2, "s": [ "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Crimson Text": { "x": 928, "y": 400, "w": 139, "ch": 2, "s": ["400", "400i", "600", "600i", "700", "700i"] }, "Croissant One": { "x": 0, "y": 440, "w": 186, "ch": 2, "s": ["400"] }, "Crushed": { "x": 200, "y": 440, "w": 79, "ch": 2, "s": ["400"] }, "Cuprum": { "x": 293, "y": 440, "w": 77, "ch": 2, "s": ["400", "400i", "500", "500i", "600", "600i", "700", "700i"] }, "Cute Font": { "x": 384, "y": 440, "w": 78, "ch": 2, "s": ["400"] }, "Cutive": { "x": 476, "y": 440, "w": 98, "ch": 2, "s": ["400"] }, "Cutive Mono": { "x": 588, "y": 440, "w": 168, "ch": 2, "s": ["400"] }, "Dai Banna SIL": { "x": 770, "y": 440, "w": 137, "ch": 2, "s": [ "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i" ] }, "Damion": { "x": 921, "y": 440, "w": 78, "ch": 2, "s": ["400"] }, "Dancing Script": { "x": 1013, "y": 440, "w": 137, "ch": 2, "s": ["400", "500", "600", "700"] }, "Danfo": { "x": 0, "y": 480, "w": 86, "ch": 2, "s": ["400"] }, "Dangrek": { "x": 100, "y": 480, "w": 91, "ch": 2, "s": ["400"] }, "Darker Grotesque": { "x": 205, "y": 480, "w": 163, "ch": 2, "s": ["300", "400", "500", "600", "700", "800", "900"] }, "Darumadrop One": { "x": 382, "y": 480, "w": 184, "ch": 2, "s": ["400"] }, "David Libre": { "x": 580, "y": 480, "w": 120, "ch": 2, "s": ["400", "500", "700"] }, "Dawning of a New Day": { "x": 714, "y": 480, "w": 208, "ch": 2, "s": ["400"] }, "Days One": { "x": 936, "y": 480, "w": 133, "ch": 2, "s": ["400"] }, "Dekko": { "x": 1083, "y": 480, "w": 62, "ch": 2, "s": ["400"] }, "Dela Gothic One": { "x": 0, "y": 520, "w": 234, "ch": 2, "s": ["400"] }, "Delicious Handrawn": { "x": 248, "y": 520, "w": 161, "ch": 2, "s": ["400"] }, "Delius": { "x": 423, "y": 520, "w": 74, "ch": 2, "s": ["400"] }, "Delius Swash Caps": { "x": 511, "y": 520, "w": 214, "ch": 2, "s": ["400"] }, "Delius Unicase": { "x": 739, "y": 520, "w": 229, "ch": 2, "s": ["400", "700"] }, "Della Respira": { "x": 982, "y": 520, "w": 149, "ch": 2, "s": ["400"] }, "Denk One": { "x": 0, "y": 560, "w": 105, "ch": 2, "s": ["400"] }, "Devonshire": { "x": 119, "y": 560, "w": 87, "ch": 2, "s": ["400"] }, "Dhurjati": { "x": 220, "y": 560, "w": 75, "ch": 2, "s": ["400"] }, "Didact Gothic": { "x": 309, "y": 560, "w": 145, "ch": 2, "s": ["400"] }, "Diphylleia": { "x": 468, "y": 560, "w": 112, "ch": 2, "s": ["400"] }, "Diplomata": { "x": 594, "y": 560, "w": 252, "ch": 2, "s": ["400"] }, "Diplomata SC": { "x": 860, "y": 560, "w": 334, "ch": 2, "s": ["400"] }, "DM Mono": { "x": 0, "y": 600, "w": 109, "ch": 2, "s": ["300", "300i", "400", "400i", "500", "500i"] }, "DM Sans": { "x": 123, "y": 600, "w": 104, "ch": 2, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "DM Serif Display": { "x": 241, "y": 600, "w": 181, "ch": 2, "s": ["400", "400i"] }, "DM Serif Text": { "x": 436, "y": 600, "w": 150, "ch": 2, "s": ["400", "400i"] }, "Do Hyeon": { "x": 600, "y": 600, "w": 101, "ch": 2, "s": ["400"] }, "Dokdo": { "x": 715, "y": 600, "w": 68, "ch": 2, "s": ["400"] }, "Domine": { "x": 797, "y": 600, "w": 101, "ch": 2, "s": ["400", "500", "600", "700"] }, "Donegal One": { "x": 912, "y": 600, "w": 161, "ch": 2, "s": ["400"] }, "Dongle": { "x": 1087, "y": 600, "w": 56, "ch": 2, "s": ["300", "400", "700"] }, "Doppio One": { "x": 0, "y": 640, "w": 138, "ch": 2, "s": ["400"] }, "Dorsa": { "x": 152, "y": 640, "w": 32, "ch": 2, "s": ["400"] }, "Dosis": { "x": 198, "y": 640, "w": 56, "ch": 2, "s": ["200", "300", "400", "500", "600", "700", "800"] }, "DotGothic16": { "x": 268, "y": 640, "w": 140, "ch": 2, "s": ["400"] }, "Doto": { "x": 422, "y": 640, "w": 66, "ch": 2, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Dr Sugiyama": { "x": 502, "y": 640, "w": 116, "ch": 2, "s": ["400"] }, "Duru Sans": { "x": 632, "y": 640, "w": 133, "ch": 2, "s": ["400"] }, "Dynalight": { "x": 779, "y": 640, "w": 84, "ch": 2, "s": ["400"] }, "DynaPuff": { "x": 877, "y": 640, "w": 121, "ch": 2, "s": ["400", "500", "600", "700"] }, "Eagle Lake": { "x": 1012, "y": 640, "w": 153, "ch": 2, "s": ["400"] }, "East Sea Dokdo": { "x": 0, "y": 680, "w": 118, "ch": 2, "s": ["400"] }, "Eater": { "x": 132, "y": 680, "w": 90, "ch": 2, "s": ["400"] }, "EB Garamond": { "x": 236, "y": 680, "w": 141, "ch": 2, "s": [ "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i" ] }, "Economica": { "x": 391, "y": 680, "w": 88, "ch": 2, "s": ["400", "400i", "700", "700i"] }, "Eczar": { "x": 493, "y": 680, "w": 65, "ch": 2, "s": ["400", "500", "600", "700", "800"] }, "Edu AU VIC WA NT Arrows": { "x": 572, "y": 680, "w": 302, "ch": 2, "s": ["400", "500", "600", "700"] }, "Edu AU VIC WA NT Dots": { "x": 888, "y": 680, "w": 261, "ch": 2, "s": ["400", "500", "600", "700"] }, "Edu AU VIC WA NT Guides": { "x": 0, "y": 720, "w": 285, "ch": 2, "s": ["400", "500", "600", "700"] }, "Edu AU VIC WA NT Hand": { "x": 299, "y": 720, "w": 274, "ch": 2, "s": ["400", "500", "600", "700"] }, "Edu AU VIC WA NT Pre": { "x": 587, "y": 720, "w": 257, "ch": 2, "s": ["400", "500", "600", "700"] }, "Edu NSW ACT Cursive": { "x": 858, "y": 720, "w": 276, "ch": 2, "s": ["400", "500", "600", "700"] }, "Edu NSW ACT Foundation": { "x": 0, "y": 760, "w": 239, "ch": 2, "s": ["400", "500", "600", "700"] }, "Edu NSW ACT Hand Pre": { "x": 253, "y": 760, "w": 294, "ch": 2, "s": ["400", "500", "600", "700"] }, "Edu QLD Beginner": { "x": 561, "y": 760, "w": 177, "ch": 2, "s": ["400", "500", "600", "700"] }, "Edu QLD Hand": { "x": 752, "y": 760, "w": 175, "ch": 2, "s": ["400", "500", "600", "700"] }, "Edu SA Beginner": { "x": 941, "y": 760, "w": 155, "ch": 2, "s": ["400", "500", "600", "700"] }, "Edu SA Hand": { "x": 0, "y": 0, "w": 159, "ch": 3, "s": ["400", "500", "600", "700"] }, "Edu TAS Beginner": { "x": 173, "y": 0, "w": 175, "ch": 3, "s": ["400", "500", "600", "700"] }, "Edu VIC WA NT Beginner": { "x": 362, "y": 0, "w": 243, "ch": 3, "s": ["400", "500", "600", "700"] }, "Edu VIC WA NT Hand": { "x": 619, "y": 0, "w": 230, "ch": 3, "s": ["400", "500", "600", "700"] }, "Edu VIC WA NT Hand Pre": { "x": 863, "y": 0, "w": 282, "ch": 3, "s": ["400", "500", "600", "700"] }, "El Messiri": { "x": 0, "y": 40, "w": 109, "ch": 3, "s": ["400", "500", "600", "700"] }, "Electrolize": { "x": 123, "y": 40, "w": 123, "ch": 3, "s": ["400"] }, "Elms Sans": { "x": 260, "y": 40, "w": 123, "ch": 3, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Elsie": { "x": 397, "y": 40, "w": 61, "ch": 3, "s": ["400", "900"] }, "Elsie Swash Caps": { "x": 472, "y": 40, "w": 190, "ch": 3, "s": ["400", "900"] }, "Emblema One": { "x": 676, "y": 40, "w": 198, "ch": 3, "s": ["400"] }, "Emilys Candy": { "x": 888, "y": 40, "w": 148, "ch": 3, "s": ["400"] }, "Encode Sans": { "x": 1050, "y": 40, "w": 141, "ch": 3, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Encode Sans Condensed": { "x": 0, "y": 80, "w": 227, "ch": 3, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Encode Sans Expanded": { "x": 241, "y": 80, "w": 287, "ch": 3, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Encode Sans SC": { "x": 542, "y": 80, "w": 186, "ch": 3, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Encode Sans Semi Condensed": { "x": 742, "y": 80, "w": 299, "ch": 3, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Encode Sans Semi Expanded": { "x": 0, "y": 120, "w": 330, "ch": 3, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Engagement": { "x": 344, "y": 120, "w": 80, "ch": 3, "s": ["400"] }, "Englebert": { "x": 438, "y": 120, "w": 97, "ch": 3, "s": ["400"] }, "Enriqueta": { "x": 549, "y": 120, "w": 115, "ch": 3, "s": ["400", "500", "600", "700"] }, "Ephesis": { "x": 678, "y": 120, "w": 72, "ch": 3, "s": ["400"] }, "Epilogue": { "x": 764, "y": 120, "w": 109, "ch": 3, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Epunda Sans": { "x": 887, "y": 120, "w": 133, "ch": 3, "s": [ "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Epunda Slab": { "x": 1034, "y": 120, "w": 132, "ch": 3, "s": [ "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Erica One": { "x": 0, "y": 160, "w": 131, "ch": 3, "s": ["400"] }, "Esteban": { "x": 145, "y": 160, "w": 93, "ch": 3, "s": ["400"] }, "Estonia": { "x": 252, "y": 160, "w": 47, "ch": 3, "s": ["400"] }, "Euphoria Script": { "x": 313, "y": 160, "w": 127, "ch": 3, "s": ["400"] }, "Ewert": { "x": 454, "y": 160, "w": 97, "ch": 3, "s": ["400"] }, "Exile": { "x": 565, "y": 160, "w": 81, "ch": 3, "s": ["400"] }, "Exo": { "x": 660, "y": 160, "w": 48, "ch": 3, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Exo 2": { "x": 722, "y": 160, "w": 67, "ch": 3, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Expletus Sans": { "x": 803, "y": 160, "w": 159, "ch": 3, "s": ["400", "400i", "500", "500i", "600", "600i", "700", "700i"] }, "Explora": { "x": 976, "y": 160, "w": 87, "ch": 3, "s": ["400"] }, "Faculty Glyphic": { "x": 0, "y": 200, "w": 182, "ch": 3, "s": ["400"] }, "Fahkwang": { "x": 196, "y": 200, "w": 133, "ch": 3, "s": [ "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i" ] }, "Familjen Grotesk": { "x": 343, "y": 200, "w": 178, "ch": 3, "s": ["400", "400i", "500", "500i", "600", "600i", "700", "700i"] }, "Fanwood Text": { "x": 535, "y": 200, "w": 140, "ch": 3, "s": ["400", "400i"] }, "Farro": { "x": 689, "y": 200, "w": 72, "ch": 3, "s": ["300", "400", "500", "700"] }, "Farsan": { "x": 775, "y": 200, "w": 61, "ch": 3, "s": ["400"] }, "Fascinate": { "x": 850, "y": 200, "w": 135, "ch": 3, "s": ["400"] }, "Fascinate Inline": { "x": 0, "y": 240, "w": 220, "ch": 3, "s": ["400"] }, "Faster One": { "x": 234, "y": 240, "w": 165, "ch": 3, "s": ["400"] }, "Fasthand": { "x": 413, "y": 240, "w": 84, "ch": 3, "s": ["400"] }, "Fauna One": { "x": 511, "y": 240, "w": 141, "ch": 3, "s": ["400"] }, "Faustina": { "x": 666, "y": 240, "w": 97, "ch": 3, "s": [ "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i" ] }, "Federant": { "x": 777, "y": 240, "w": 105, "ch": 3, "s": ["400"] }, "Federo": { "x": 896, "y": 240, "w": 78, "ch": 3, "s": ["400"] }, "Felipa": { "x": 988, "y": 240, "w": 60, "ch": 3, "s": ["400"] }, "Fenix": { "x": 1062, "y": 240, "w": 63, "ch": 3, "s": ["400"] }, "Festive": { "x": 0, "y": 280, "w": 67, "ch": 3, "s": ["400"] }, "Figtree": { "x": 81, "y": 280, "w": 85, "ch": 3, "s": [ "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Finger Paint": { "x": 180, "y": 280, "w": 158, "ch": 3, "s": ["400"] }, "Finlandica": { "x": 352, "y": 280, "w": 114, "ch": 3, "s": ["400", "400i", "500", "500i", "600", "600i", "700", "700i"] }, "Fira Code": { "x": 480, "y": 280, "w": 138, "ch": 3, "s": ["300", "400", "500", "600", "700"] }, "Fira Mono": { "x": 632, "y": 280, "w": 138, "ch": 3, "s": ["400", "500", "700"] }, "Fira Sans": { "x": 784, "y": 280, "w": 105, "ch": 3, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Fira Sans Condensed": { "x": 903, "y": 280, "w": 211, "ch": 3, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Fira Sans Extra Condensed": { "x": 0, "y": 320, "w": 244, "ch": 3, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Fjalla One": { "x": 258, "y": 320, "w": 98, "ch": 3, "s": ["400"] }, "Fjord One": { "x": 370, "y": 320, "w": 115, "ch": 3, "s": ["400"] }, "Flamenco": { "x": 499, "y": 320, "w": 99, "ch": 3, "s": ["300", "400"] }, "Flavors": { "x": 612, "y": 320, "w": 82, "ch": 3, "s": ["400"] }, "Fleur De Leah": { "x": 708, "y": 320, "w": 133, "ch": 3, "s": ["400"] }, "Flow Block": { "x": 855, "y": 320, "w": 126, "ch": 3, "s": ["400"] }, "Flow Circular": { "x": 995, "y": 320, "w": 145, "ch": 3, "s": ["400"] }, "Flow Rounded": { "x": 0, "y": 360, "w": 160, "ch": 3, "s": ["400"] }, "Foldit": { "x": 174, "y": 360, "w": 54, "ch": 3, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Fondamento": { "x": 242, "y": 360, "w": 133, "ch": 3, "s": ["400", "400i"] }, "Fontdiner Swanky": { "x": 389, "y": 360, "w": 239, "ch": 3, "s": ["400"] }, "Forum": { "x": 642, "y": 360, "w": 70, "ch": 3, "s": ["400"] }, "Fragment Mono": { "x": 726, "y": 360, "w": 201, "ch": 3, "s": ["400", "400i"] }, "Francois One": { "x": 941, "y": 360, "w": 136, "ch": 3, "s": ["400"] }, "Frank Ruhl Libre": { "x": 0, "y": 400, "w": 184, "ch": 3, "s": ["300", "400", "500", "600", "700", "800", "900"] }, "Fraunces": { "x": 198, "y": 400, "w": 112, "ch": 3, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Freckle Face": { "x": 324, "y": 400, "w": 141, "ch": 3, "s": ["400"] }, "Fredericka the Great": { "x": 479, "y": 400, "w": 243, "ch": 3, "s": ["400"] }, "Fredoka": { "x": 736, "y": 400, "w": 96, "ch": 3, "s": ["300", "400", "500", "600", "700"] }, "Fredoka One": { "x": 846, "y": 400, "w": 155, "ch": 3, "s": ["400"] }, "Freehand": { "x": 1015, "y": 400, "w": 85, "ch": 3, "s": ["400"] }, "Freeman": { "x": 0, "y": 440, "w": 91, "ch": 3, "s": ["400"] }, "Fresca": { "x": 105, "y": 440, "w": 67, "ch": 3, "s": ["400"] }, "Frijole": { "x": 186, "y": 440, "w": 132, "ch": 3, "s": ["400"] }, "Fruktur": { "x": 332, "y": 440, "w": 94, "ch": 3, "s": ["400", "400i"] }, "Fugaz One": { "x": 440, "y": 440, "w": 127, "ch": 3, "s": ["400"] }, "Fuggles": { "x": 581, "y": 440, "w": 58, "ch": 3, "s": ["400"] }, "Funnel Display": { "x": 653, "y": 440, "w": 174, "ch": 3, "s": ["300", "400", "500", "600", "700", "800"] }, "Funnel Sans": { "x": 841, "y": 440, "w": 140, "ch": 3, "s": [ "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i" ] }, "Fustat": { "x": 995, "y": 440, "w": 75, "ch": 3, "s": ["200", "300", "400", "500", "600", "700", "800"] }, "Fuzzy Bubbles": { "x": 0, "y": 480, "w": 179, "ch": 3, "s": ["400", "700"] }, "Ga Maamli": { "x": 193, "y": 480, "w": 111, "ch": 3, "s": ["400"] }, "Gabarito": { "x": 318, "y": 480, "w": 99, "ch": 3, "s": ["400", "500", "600", "700", "800", "900"] }, "Gabriela": { "x": 431, "y": 480, "w": 106, "ch": 3, "s": ["400"] }, "Gaegu": { "x": 551, "y": 480, "w": 70, "ch": 3, "s": ["300", "400", "700"] }, "Gafata": { "x": 635, "y": 480, "w": 72, "ch": 3, "s": ["400"] }, "Gajraj One": { "x": 721, "y": 480, "w": 148, "ch": 3, "s": ["400"] }, "Galada": { "x": 883, "y": 480, "w": 78, "ch": 3, "s": ["400"] }, "Galdeano": { "x": 975, "y": 480, "w": 98, "ch": 3, "s": ["400"] }, "Galindo": { "x": 1087, "y": 480, "w": 101, "ch": 3, "s": ["400"] }, "Gamja Flower": { "x": 0, "y": 520, "w": 130, "ch": 3, "s": ["400"] }, "Gantari": { "x": 144, "y": 520, "w": 89, "ch": 3, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Gasoek One": { "x": 247, "y": 520, "w": 163, "ch": 3, "s": ["400"] }, "Gayathri": { "x": 424, "y": 520, "w": 99, "ch": 3, "s": ["100", "400", "700"] }, "Geist": { "x": 537, "y": 520, "w": 66, "ch": 3, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Geist Mono": { "x": 617, "y": 520, "w": 152, "ch": 3, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Gelasio": { "x": 783, "y": 520, "w": 87, "ch": 3, "s": ["400", "400i", "500", "500i", "600", "600i", "700", "700i"] }, "Gemunu Libre": { "x": 884, "y": 520, "w": 126, "ch": 3, "s": ["200", "300", "400", "500", "600", "700", "800"] }, "Genos": { "x": 1024, "y": 520, "w": 67, "ch": 3, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Gentium Book Basic": { "x": 0, "y": 560, "w": 209, "ch": 3, "s": ["400", "400i", "700", "700i"] }, "Gentium Book Plus": { "x": 223, "y": 560, "w": 198, "ch": 3, "s": ["400", "400i", "700", "700i"] }, "Gentium Plus": { "x": 435, "y": 560, "w": 140, "ch": 3, "s": ["400", "400i", "700", "700i"] }, "Geo": { "x": 589, "y": 560, "w": 42, "ch": 3, "s": ["400", "400i"] }, "Geologica": { "x": 645, "y": 560, "w": 122, "ch": 3, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Geom": { "x": 781, "y": 560, "w": 76, "ch": 3, "s": [ "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Georama": { "x": 871, "y": 560, "w": 102, "ch": 3, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Geostar": { "x": 987, "y": 560, "w": 133, "ch": 3, "s": ["400"] }, "Geostar Fill": { "x": 0, "y": 600, "w": 192, "ch": 3, "s": ["400"] }, "Germania One": { "x": 206, "y": 600, "w": 137, "ch": 3, "s": ["400"] }, "GFS Didot": { "x": 357, "y": 600, "w": 121, "ch": 3, "s": ["400"] }, "GFS Neohellenic": { "x": 492, "y": 600, "w": 155, "ch": 3, "s": ["400", "400i", "700", "700i"] }, "Gideon Roman": { "x": 661, "y": 600, "w": 172, "ch": 3, "s": ["400"] }, "Gidole": { "x": 847, "y": 600, "w": 73, "ch": 3, "s": ["400"] }, "Gidugu": { "x": 934, "y": 600, "w": 58, "ch": 3, "s": ["400"] }, "Gilda Display": { "x": 1006, "y": 600, "w": 156, "ch": 3, "s": ["400"] }, "Girassol": { "x": 0, "y": 640, "w": 91, "ch": 3, "s": ["400"] }, "Give You Glory": { "x": 105, "y": 640, "w": 170, "ch": 3, "s": ["400"] }, "Glass Antiqua": { "x": 289, "y": 640, "w": 130, "ch": 3, "s": ["400"] }, "Glegoo": { "x": 433, "y": 640, "w": 90, "ch": 3, "s": ["400", "700"] }, "Gloock": { "x": 537, "y": 640, "w": 89, "ch": 3, "s": ["400"] }, "Gloria Hallelujah": { "x": 640, "y": 640, "w": 190, "ch": 3, "s": ["400"] }, "Glory": { "x": 844, "y": 640, "w": 56, "ch": 3, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i" ] }, "Gluten": { "x": 914, "y": 640, "w": 90, "ch": 3, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Goblin One": { "x": 0, "y": 680, "w": 202, "ch": 3, "s": ["400"] }, "Gochi Hand": { "x": 216, "y": 680, "w": 121, "ch": 3, "s": ["400"] }, "Goldman": { "x": 351, "y": 680, "w": 118, "ch": 3, "s": ["400", "700"] }, "Golos Text": { "x": 483, "y": 680, "w": 130, "ch": 3, "s": ["400", "500", "600", "700", "800", "900"] }, "Google Sans": { "x": 627, "y": 680, "w": 146, "ch": 3, "s": ["400", "400i", "500", "500i", "600", "600i", "700", "700i"] }, "Google Sans Code": { "x": 787, "y": 680, "w": 239, "ch": 3, "s": [ "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i" ] }, "Google Sans Flex": { "x": 0, "y": 720, "w": 193, "ch": 3, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Gorditas": { "x": 207, "y": 720, "w": 111, "ch": 3, "s": ["400", "700"] }, "Gothic A1": { "x": 332, "y": 720, "w": 114, "ch": 3, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Gotu": { "x": 460, "y": 720, "w": 67, "ch": 3, "s": ["400"] }, "Goudy Bookletter 1911": { "x": 541, "y": 720, "w": 218, "ch": 3, "s": ["400"] }, "Gowun Batang": { "x": 773, "y": 720, "w": 162, "ch": 3, "s": ["400", "700"] }, "Gowun Dodum": { "x": 949, "y": 720, "w": 161, "ch": 3, "s": ["400"] }, "Graduate": { "x": 0, "y": 760, "w": 134, "ch": 3, "s": ["400"] }, "Grand Hotel": { "x": 148, "y": 760, "w": 101, "ch": 3, "s": ["400"] }, "Grandiflora One": { "x": 263, "y": 760, "w": 174, "ch": 3, "s": ["400"] }, "Grandstander": { "x": 451, "y": 760, "w": 173, "ch": 3, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Grape Nuts": { "x": 638, "y": 760, "w": 111, "ch": 3, "s": ["400"] }, "Gravitas One": { "x": 763, "y": 760, "w": 230, "ch": 3, "s": ["400"] }, "Great Vibes": { "x": 1007, "y": 760, "w": 104, "ch": 3, "s": ["400"] }, "Grechen Fuemen": { "x": 0, "y": 0, "w": 179, "ch": 4, "s": ["400"] }, "Grenze": { "x": 193, "y": 0, "w": 69, "ch": 4, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Grenze Gotisch": { "x": 276, "y": 0, "w": 135, "ch": 4, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Grey Qo": { "x": 425, "y": 0, "w": 64, "ch": 4, "s": ["400"] }, "Griffy": { "x": 503, "y": 0, "w": 71, "ch": 4, "s": ["400"] }, "Gruppo": { "x": 588, "y": 0, "w": 95, "ch": 4, "s": ["400"] }, "Gudea": { "x": 697, "y": 0, "w": 74, "ch": 4, "s": ["400", "400i", "700", "700i"] }, "Gugi": { "x": 785, "y": 0, "w": 60, "ch": 4, "s": ["400"] }, "Gulzar": { "x": 859, "y": 0, "w": 77, "ch": 4, "s": ["400"] }, "Gupter": { "x": 950, "y": 0, "w": 72, "ch": 4, "s": ["400", "500", "700"] }, "Gurajada": { "x": 1036, "y": 0, "w": 71, "ch": 4, "s": ["400"] }, "Gwendolyn": { "x": 0, "y": 40, "w": 89, "ch": 4, "s": ["400", "700"] }, "Habibi": { "x": 103, "y": 40, "w": 81, "ch": 4, "s": ["400"] }, "Hachi Maru Pop": { "x": 198, "y": 40, "w": 246, "ch": 4, "s": ["400"] }, "Hahmlet": { "x": 458, "y": 40, "w": 112, "ch": 4, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Halant": { "x": 584, "y": 40, "w": 75, "ch": 4, "s": ["300", "400", "500", "600", "700"] }, "Hammersmith One": { "x": 673, "y": 40, "w": 217, "ch": 4, "s": ["400"] }, "Hanalei": { "x": 904, "y": 40, "w": 91, "ch": 4, "s": ["400"] }, "Hanalei Fill": { "x": 1009, "y": 40, "w": 136, "ch": 4, "s": ["400"] }, "Handjet": { "x": 0, "y": 80, "w": 68, "ch": 4, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Handlee": { "x": 82, "y": 80, "w": 88, "ch": 4, "s": ["400"] }, "Hanken Grotesk": { "x": 184, "y": 80, "w": 181, "ch": 4, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Hanuman": { "x": 379, "y": 80, "w": 123, "ch": 4, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Happy Monkey": { "x": 516, "y": 80, "w": 189, "ch": 4, "s": ["400"] }, "Harmattan": { "x": 719, "y": 80, "w": 99, "ch": 4, "s": ["400", "500", "600", "700"] }, "Headland One": { "x": 832, "y": 80, "w": 185, "ch": 4, "s": ["400"] }, "Hedvig Letters Sans": { "x": 0, "y": 120, "w": 224, "ch": 4, "s": ["400"] }, "Hedvig Letters Serif": { "x": 238, "y": 120, "w": 226, "ch": 4, "s": ["400"] }, "Heebo": { "x": 478, "y": 120, "w": 78, "ch": 4, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Henny Penny": { "x": 570, "y": 120, "w": 148, "ch": 4, "s": ["400"] }, "Hepta Slab": { "x": 732, "y": 120, "w": 150, "ch": 4, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Herr Von Muellerhoff": { "x": 896, "y": 120, "w": 145, "ch": 4, "s": ["400"] }, "Hi Melody": { "x": 1055, "y": 120, "w": 84, "ch": 4, "s": ["400"] }, "Hina Mincho": { "x": 0, "y": 160, "w": 128, "ch": 4, "s": ["400"] }, "Hind": { "x": 142, "y": 160, "w": 56, "ch": 4, "s": ["300", "400", "500", "600", "700"] }, "Hind Guntur": { "x": 212, "y": 160, "w": 134, "ch": 4, "s": ["300", "400", "500", "600", "700"] }, "Hind Madurai": { "x": 360, "y": 160, "w": 146, "ch": 4, "s": ["300", "400", "500", "600", "700"] }, "Hind Mysuru": { "x": 520, "y": 160, "w": 138, "ch": 4, "s": ["300", "400", "500", "600", "700"] }, "Hind Siliguri": { "x": 672, "y": 160, "w": 130, "ch": 4, "s": ["300", "400", "500", "600", "700"] }, "Hind Vadodara": { "x": 816, "y": 160, "w": 161, "ch": 4, "s": ["300", "400", "500", "600", "700"] }, "Holtwood One SC": { "x": 0, "y": 200, "w": 292, "ch": 4, "s": ["400"] }, "Homemade Apple": { "x": 306, "y": 200, "w": 239, "ch": 4, "s": ["400"] }, "Homenaje": { "x": 559, "y": 200, "w": 85, "ch": 4, "s": ["400"] }, "Honk": { "x": 658, "y": 200, "w": 59, "ch": 4, "s": ["400"] }, "Host Grotesk": { "x": 731, "y": 200, "w": 150, "ch": 4, "s": [ "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i" ] }, "Hubballi": { "x": 895, "y": 200, "w": 87, "ch": 4, "s": ["400"] }, "Hubot Sans": { "x": 996, "y": 200, "w": 137, "ch": 4, "s": [ "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Huninn": { "x": 0, "y": 240, "w": 89, "ch": 4, "s": ["400"] }, "Hurricane": { "x": 103, "y": 240, "w": 76, "ch": 4, "s": ["400"] }, "Iansui": { "x": 193, "y": 240, "w": 78, "ch": 4, "s": ["400"] }, "Ibarra Real Nova": { "x": 285, "y": 240, "w": 182, "ch": 4, "s": ["400", "400i", "500", "500i", "600", "600i", "700", "700i"] }, "IBM Plex Mono": { "x": 481, "y": 240, "w": 196, "ch": 4, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i" ] }, "IBM Plex Sans": { "x": 691, "y": 240, "w": 163, "ch": 4, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i" ] }, "IBM Plex Sans Arabic": { "x": 868, "y": 240, "w": 237, "ch": 4, "s": ["100", "200", "300", "400", "500", "600", "700"] }, "IBM Plex Sans Condensed": { "x": 0, "y": 280, "w": 263, "ch": 4, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i" ] }, "IBM Plex Sans Devanagari": { "x": 277, "y": 280, "w": 289, "ch": 4, "s": ["100", "200", "300", "400", "500", "600", "700"] }, "IBM Plex Sans Hebrew": { "x": 580, "y": 280, "w": 253, "ch": 4, "s": ["100", "200", "300", "400", "500", "600", "700"] }, "IBM Plex Sans JP": { "x": 847, "y": 280, "w": 204, "ch": 4, "s": ["100", "200", "300", "400", "500", "600", "700"] }, "IBM Plex Sans KR": { "x": 0, "y": 320, "w": 199, "ch": 4, "s": ["100", "200", "300", "400", "500", "600", "700"] }, "IBM Plex Sans Thai": { "x": 213, "y": 320, "w": 215, "ch": 4, "s": ["100", "200", "300", "400", "500", "600", "700"] }, "IBM Plex Sans Thai Looped": { "x": 442, "y": 320, "w": 300, "ch": 4, "s": ["100", "200", "300", "400", "500", "600", "700"] }, "IBM Plex Serif": { "x": 756, "y": 320, "w": 167, "ch": 4, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i" ] }, "Iceberg": { "x": 937, "y": 320, "w": 84, "ch": 4, "s": ["400"] }, "Iceland": { "x": 1035, "y": 320, "w": 75, "ch": 4, "s": ["400"] }, "IM Fell Double Pica": { "x": 0, "y": 360, "w": 204, "ch": 4, "s": ["400", "400i"] }, "IM Fell Double Pica SC": { "x": 218, "y": 360, "w": 261, "ch": 4, "s": ["400"] }, "IM Fell DW Pica": { "x": 493, "y": 360, "w": 175, "ch": 4, "s": ["400", "400i"] }, "IM Fell DW Pica SC": { "x": 682, "y": 360, "w": 220, "ch": 4, "s": ["400"] }, "IM Fell English": { "x": 916, "y": 360, "w": 168, "ch": 4, "s": ["400", "400i"] }, "IM Fell English SC": { "x": 0, "y": 400, "w": 215, "ch": 4, "s": ["400"] }, "IM Fell French Canon": { "x": 229, "y": 400, "w": 233, "ch": 4, "s": ["400", "400i"] }, "IM Fell French Canon SC": { "x": 476, "y": 400, "w": 279, "ch": 4, "s": ["400"] }, "IM Fell Great Primer": { "x": 769, "y": 400, "w": 220, "ch": 4, "s": ["400", "400i"] }, "IM Fell Great Primer SC": { "x": 0, "y": 440, "w": 289, "ch": 4, "s": ["400"] }, "Imbue": { "x": 303, "y": 440, "w": 52, "ch": 4, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Imperial Script": { "x": 369, "y": 440, "w": 119, "ch": 4, "s": ["400"] }, "Imprima": { "x": 502, "y": 440, "w": 98, "ch": 4, "s": ["400"] }, "Inclusive Sans": { "x": 614, "y": 440, "w": 172, "ch": 4, "s": [ "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i" ] }, "Inconsolata": { "x": 800, "y": 440, "w": 140, "ch": 4, "s": ["200", "300", "400", "500", "600", "700", "800", "900"] }, "Inder": { "x": 954, "y": 440, "w": 64, "ch": 4, "s": ["400"] }, "Indie Flower": { "x": 1032, "y": 440, "w": 125, "ch": 4, "s": ["400"] }, "Ingrid Darling": { "x": 0, "y": 480, "w": 114, "ch": 4, "s": ["400"] }, "Inika": { "x": 128, "y": 480, "w": 67, "ch": 4, "s": ["400", "700"] }, "Inknut Antiqua": { "x": 209, "y": 480, "w": 211, "ch": 4, "s": ["300", "400", "500", "600", "700", "800", "900"] }, "Inria Sans": { "x": 434, "y": 480, "w": 110, "ch": 4, "s": ["300", "300i", "400", "400i", "700", "700i"] }, "Inria Serif": { "x": 558, "y": 480, "w": 120, "ch": 4, "s": ["300", "300i", "400", "400i", "700", "700i"] }, "Inspiration": { "x": 692, "y": 480, "w": 78, "ch": 4, "s": ["400"] }, "Instrument Sans": { "x": 784, "y": 480, "w": 189, "ch": 4, "s": ["400", "400i", "500", "500i", "600", "600i", "700", "700i"] }, "Instrument Serif": { "x": 987, "y": 480, "w": 143, "ch": 4, "s": ["400", "400i"] }, "Intel One Mono": { "x": 0, "y": 520, "w": 215, "ch": 4, "s": [ "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i" ] }, "Inter": { "x": 229, "y": 520, "w": 60, "ch": 4, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Inter Tight": { "x": 303, "y": 520, "w": 113, "ch": 4, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Irish Grover": { "x": 430, "y": 520, "w": 139, "ch": 4, "s": ["400"] }, "Island Moments": { "x": 583, "y": 520, "w": 125, "ch": 4, "s": ["400"] }, "Istok Web": { "x": 722, "y": 520, "w": 116, "ch": 4, "s": ["400", "400i", "700", "700i"] }, "Italiana": { "x": 852, "y": 520, "w": 83, "ch": 4, "s": ["400"] }, "Italianno": { "x": 949, "y": 520, "w": 68, "ch": 4, "s": ["400"] }, "Itim": { "x": 1031, "y": 520, "w": 52, "ch": 4, "s": ["400"] }, "Jacquard 12": { "x": 0, "y": 560, "w": 110, "ch": 4, "s": ["400"] }, "Jacquard 12 Charted": { "x": 124, "y": 560, "w": 184, "ch": 4, "s": ["400"] }, "Jacquard 24": { "x": 322, "y": 560, "w": 108, "ch": 4, "s": ["400"] }, "Jacquard 24 Charted": { "x": 444, "y": 560, "w": 179, "ch": 4, "s": ["400"] }, "Jacquarda Bastarda 9": { "x": 637, "y": 560, "w": 261, "ch": 4, "s": ["400"] }, "Jacquarda Bastarda 9 Charted": { "x": 0, "y": 600, "w": 357, "ch": 4, "s": ["400"] }, "Jacques Francois": { "x": 371, "y": 600, "w": 197, "ch": 4, "s": ["400"] }, "Jacques Francois Shadow": { "x": 582, "y": 600, "w": 315, "ch": 4, "s": ["400"] }, "Jaini": { "x": 911, "y": 600, "w": 50, "ch": 4, "s": ["400"] }, "Jaini Purva": { "x": 975, "y": 600, "w": 102, "ch": 4, "s": ["400"] }, "Jaldi": { "x": 1091, "y": 600, "w": 52, "ch": 4, "s": ["400", "700"] }, "Jaro": { "x": 0, "y": 640, "w": 50, "ch": 4, "s": ["400"] }, "Jersey 10": { "x": 64, "y": 640, "w": 84, "ch": 4, "s": ["400"] }, "Jersey 10 Charted": { "x": 162, "y": 640, "w": 158, "ch": 4, "s": ["400"] }, "Jersey 15": { "x": 334, "y": 640, "w": 91, "ch": 4, "s": ["400"] }, "Jersey 15 Charted": { "x": 439, "y": 640, "w": 168, "ch": 4, "s": ["400"] }, "Jersey 20": { "x": 621, "y": 640, "w": 98, "ch": 4, "s": ["400"] }, "Jersey 20 Charted": { "x": 733, "y": 640, "w": 176, "ch": 4, "s": ["400"] }, "Jersey 25": { "x": 923, "y": 640, "w": 102, "ch": 4, "s": ["400"] }, "Jersey 25 Charted": { "x": 0, "y": 680, "w": 180, "ch": 4, "s": ["400"] }, "JetBrains Mono": { "x": 194, "y": 680, "w": 210, "ch": 4, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i" ] }, "Jim Nightshade": { "x": 418, "y": 680, "w": 119, "ch": 4, "s": ["400"] }, "Joan": { "x": 551, "y": 680, "w": 54, "ch": 4, "s": ["400"] }, "Jockey One": { "x": 619, "y": 680, "w": 108, "ch": 4, "s": ["400"] }, "Jolly Lodger": { "x": 741, "y": 680, "w": 90, "ch": 4, "s": ["400"] }, "Jomhuria": { "x": 845, "y": 680, "w": 64, "ch": 4, "s": ["400"] }, "Jomolhari": { "x": 923, "y": 680, "w": 116, "ch": 4, "s": ["400"] }, "Josefin Sans": { "x": 1053, "y": 680, "w": 138, "ch": 4, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i" ] }, "Josefin Slab": { "x": 0, "y": 720, "w": 123, "ch": 4, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i" ] }, "Jost": { "x": 137, "y": 720, "w": 43, "ch": 4, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Joti One": { "x": 194, "y": 720, "w": 100, "ch": 4, "s": ["400"] }, "Jua": { "x": 308, "y": 720, "w": 48, "ch": 4, "s": ["400"] }, "Judson": { "x": 370, "y": 720, "w": 80, "ch": 4, "s": ["400", "400i", "700", "700i"] }, "Julee": { "x": 464, "y": 720, "w": 57, "ch": 4, "s": ["400"] }, "Julius Sans One": { "x": 535, "y": 720, "w": 210, "ch": 4, "s": ["400"] }, "Junge": { "x": 759, "y": 720, "w": 73, "ch": 4, "s": ["400"] }, "Jura": { "x": 846, "y": 720, "w": 59, "ch": 4, "s": ["300", "400", "500", "600", "700"] }, "Just Another Hand": { "x": 919, "y": 720, "w": 112, "ch": 4, "s": ["400"] }, "Just Me Again Down Here": { "x": 0, "y": 760, "w": 200, "ch": 4, "s": ["400"] }, "K2D": { "x": 214, "y": 760, "w": 56, "ch": 4, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i" ] }, "Kablammo": { "x": 284, "y": 760, "w": 128, "ch": 4, "s": ["400"] }, "Kadwa": { "x": 426, "y": 760, "w": 87, "ch": 4, "s": ["400", "700"] }, "Kaisei Decol": { "x": 527, "y": 760, "w": 154, "ch": 4, "s": ["400", "500", "700"] }, "Kaisei HarunoUmi": { "x": 695, "y": 760, "w": 223, "ch": 4, "s": ["400", "500", "700"] }, "Kaisei Opti": { "x": 932, "y": 760, "w": 137, "ch": 4, "s": ["400", "500", "700"] }, "Kaisei Tokumin": { "x": 0, "y": 0, "w": 191, "ch": 5, "s": ["400", "500", "700", "800"] }, "Kalam": { "x": 205, "y": 0, "w": 70, "ch": 5, "s": ["300", "400", "700"] }, "Kalnia": { "x": 289, "y": 0, "w": 87, "ch": 5, "s": ["100", "200", "300", "400", "500", "600", "700"] }, "Kalnia Glaze": { "x": 390, "y": 0, "w": 162, "ch": 5, "s": ["100", "200", "300", "400", "500", "600", "700"] }, "Kameron": { "x": 566, "y": 0, "w": 107, "ch": 5, "s": ["400", "500", "600", "700"] }, "Kanchenjunga": { "x": 687, "y": 0, "w": 165, "ch": 5, "s": ["400", "500", "600", "700"] }, "Kanit": { "x": 866, "y": 0, "w": 65, "ch": 5, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Kantumruy Pro": { "x": 945, "y": 0, "w": 175, "ch": 5, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i" ] }, "Kapakana": { "x": 0, "y": 40, "w": 83, "ch": 5, "s": ["300", "400"] }, "Karantina": { "x": 97, "y": 40, "w": 66, "ch": 5, "s": ["300", "400", "700"] }, "Karla": { "x": 177, "y": 40, "w": 65, "ch": 5, "s": [ "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i" ] }, "Karma": { "x": 256, "y": 40, "w": 79, "ch": 5, "s": ["300", "400", "500", "600", "700"] }, "Katibeh": { "x": 349, "y": 40, "w": 67, "ch": 5, "s": ["400"] }, "Kaushan Script": { "x": 430, "y": 40, "w": 159, "ch": 5, "s": ["400"] }, "Kavivanar": { "x": 603, "y": 40, "w": 107, "ch": 5, "s": ["400"] }, "Kavoon": { "x": 724, "y": 40, "w": 95, "ch": 5, "s": ["400"] }, "Kay Pho Du": { "x": 833, "y": 40, "w": 132, "ch": 5, "s": ["400", "500", "600", "700"] }, "Kdam Thmor Pro": { "x": 979, "y": 40, "w": 189, "ch": 5, "s": ["400"] }, "Keania One": { "x": 0, "y": 80, "w": 126, "ch": 5, "s": ["400"] }, "Kedebideri": { "x": 140, "y": 80, "w": 120, "ch": 5, "s": ["400", "500", "600", "700", "800", "900"] }, "Kelly Slab": { "x": 274, "y": 80, "w": 115, "ch": 5, "s": ["400"] }, "Kenia": { "x": 403, "y": 80, "w": 57, "ch": 5, "s": ["400"] }, "Khand": { "x": 474, "y": 80, "w": 61, "ch": 5, "s": ["300", "400", "500", "600", "700"] }, "Khula": { "x": 549, "y": 80, "w": 68, "ch": 5, "s": ["300", "400", "600", "700", "800"] }, "Kings": { "x": 631, "y": 80, "w": 63, "ch": 5, "s": ["400"] }, "Kirang Haerang": { "x": 708, "y": 80, "w": 145, "ch": 5, "s": ["400"] }, "Kite One": { "x": 867, "y": 80, "w": 97, "ch": 5, "s": ["400"] }, "Kiwi Maru": { "x": 978, "y": 80, "w": 135, "ch": 5, "s": ["300", "400", "500"] }, "Klee One": { "x": 0, "y": 120, "w": 109, "ch": 5, "s": ["400", "600"] }, "Knewave": { "x": 123, "y": 120, "w": 103, "ch": 5, "s": ["400"] }, "Kodchasan": { "x": 240, "y": 120, "w": 139, "ch": 5, "s": [ "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i" ] }, "Kode Mono": { "x": 393, "y": 120, "w": 138, "ch": 5, "s": ["400", "500", "600", "700"] }, "Koh Santepheap": { "x": 545, "y": 120, "w": 195, "ch": 5, "s": ["100", "300", "400", "700", "900"] }, "KoHo": { "x": 754, "y": 120, "w": 66, "ch": 5, "s": [ "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i" ] }, "Kolker Brush": { "x": 834, "y": 120, "w": 84, "ch": 5, "s": ["400"] }, "Konkhmer Sleokchher": { "x": 0, "y": 160, "w": 269, "ch": 5, "s": ["400"] }, "Kosugi": { "x": 283, "y": 160, "w": 80, "ch": 5, "s": ["400"] }, "Kosugi Maru": { "x": 377, "y": 160, "w": 140, "ch": 5, "s": ["400"] }, "Kotta One": { "x": 531, "y": 160, "w": 110, "ch": 5, "s": ["400"] }, "Koulen": { "x": 655, "y": 160, "w": 72, "ch": 5, "s": ["400"] }, "Kranky": { "x": 741, "y": 160, "w": 87, "ch": 5, "s": ["400"] }, "Kreon": { "x": 842, "y": 160, "w": 68, "ch": 5, "s": ["300", "400", "500", "600", "700"] }, "Kristi": { "x": 924, "y": 160, "w": 46, "ch": 5, "s": ["400"] }, "Krona One": { "x": 984, "y": 160, "w": 173, "ch": 5, "s": ["400"] }, "Krub": { "x": 0, "y": 200, "w": 62, "ch": 5, "s": [ "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i" ] }, "Kufam": { "x": 76, "y": 200, "w": 84, "ch": 5, "s": [ "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Kulim Park": { "x": 174, "y": 200, "w": 116, "ch": 5, "s": [ "200", "200i", "300", "300i", "400", "400i", "600", "600i", "700", "700i" ] }, "Kumar One": { "x": 304, "y": 200, "w": 167, "ch": 5, "s": ["400"] }, "Kumar One Outline": { "x": 485, "y": 200, "w": 277, "ch": 5, "s": ["400"] }, "Kumbh Sans": { "x": 776, "y": 200, "w": 147, "ch": 5, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Kurale": { "x": 937, "y": 200, "w": 77, "ch": 5, "s": ["400"] }, "La Belle Aurore": { "x": 1028, "y": 200, "w": 170, "ch": 5, "s": ["400"] }, "Labrada": { "x": 0, "y": 240, "w": 91, "ch": 5, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Lacquer": { "x": 105, "y": 240, "w": 102, "ch": 5, "s": ["400"] }, "Laila": { "x": 221, "y": 240, "w": 60, "ch": 5, "s": ["300", "400", "500", "600", "700"] }, "Lakki Reddy": { "x": 295, "y": 240, "w": 138, "ch": 5, "s": ["400"] }, "Lalezar": { "x": 447, "y": 240, "w": 82, "ch": 5, "s": ["400"] }, "Lancelot": { "x": 543, "y": 240, "w": 80, "ch": 5, "s": ["400"] }, "Langar": { "x": 637, "y": 240, "w": 83, "ch": 5, "s": ["400"] }, "Lateef": { "x": 734, "y": 240, "w": 57, "ch": 5, "s": ["200", "300", "400", "500", "600", "700", "800"] }, "Lato": { "x": 805, "y": 240, "w": 55, "ch": 5, "s": [ "100", "100i", "300", "300i", "400", "400i", "700", "700i", "900", "900i" ] }, "Lavishly Yours": { "x": 874, "y": 240, "w": 135, "ch": 5, "s": ["400"] }, "League Gothic": { "x": 1023, "y": 240, "w": 97, "ch": 5, "s": ["400"] }, "League Script": { "x": 0, "y": 280, "w": 156, "ch": 5, "s": ["400"] }, "League Spartan": { "x": 170, "y": 280, "w": 166, "ch": 5, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Leckerli One": { "x": 350, "y": 280, "w": 144, "ch": 5, "s": ["400"] }, "Ledger": { "x": 508, "y": 280, "w": 91, "ch": 5, "s": ["400"] }, "Lekton": { "x": 613, "y": 280, "w": 80, "ch": 5, "s": ["400", "400i", "700", "700i"] }, "Lemon": { "x": 707, "y": 280, "w": 100, "ch": 5, "s": ["400"] }, "Lemonada": { "x": 821, "y": 280, "w": 153, "ch": 5, "s": ["300", "400", "500", "600", "700"] }, "Lexend": { "x": 988, "y": 280, "w": 93, "ch": 5, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Lexend Deca": { "x": 0, "y": 320, "w": 159, "ch": 5, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Lexend Exa": { "x": 173, "y": 320, "w": 176, "ch": 5, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Lexend Giga": { "x": 363, "y": 320, "w": 205, "ch": 5, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Lexend Mega": { "x": 582, "y": 320, "w": 223, "ch": 5, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Lexend Peta": { "x": 819, "y": 320, "w": 218, "ch": 5, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Lexend Tera": { "x": 0, "y": 360, "w": 221, "ch": 5, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Lexend Zetta": { "x": 235, "y": 360, "w": 260, "ch": 5, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Libertinus Keyboard": { "x": 509, "y": 360, "w": 490, "ch": 5, "s": ["400"] }, "Libertinus Math": { "x": 1013, "y": 360, "w": 166, "ch": 5, "s": ["400"] }, "Libertinus Mono": { "x": 0, "y": 400, "w": 239, "ch": 5, "s": ["400"] }, "Libertinus Sans": { "x": 253, "y": 400, "w": 161, "ch": 5, "s": ["400", "400i", "700", "700i"] }, "Libertinus Serif": { "x": 428, "y": 400, "w": 160, "ch": 5, "s": ["400", "400i", "600", "600i", "700", "700i"] }, "Libertinus Serif Display": { "x": 602, "y": 400, "w": 234, "ch": 5, "s": ["400"] }, "Libre Barcode 128": { "x": 850, "y": 400, "w": 143, "ch": 5, "s": ["400"] }, "Libre Barcode 128 Text": { "x": 1007, "y": 400, "w": 183, "ch": 5, "s": ["400"] }, "Libre Barcode 39": { "x": 0, "y": 440, "w": 193, "ch": 5, "s": ["400"] }, "Libre Barcode 39 Extended": { "x": 207, "y": 440, "w": 492, "ch": 5, "s": ["400"] }, "Libre Barcode 39 Extended Text": { "x": 0, "y": 480, "w": 584, "ch": 5, "s": ["400"] }, "Libre Barcode 39 Text": { "x": 598, "y": 480, "w": 250, "ch": 5, "s": ["400"] }, "Libre Barcode EAN13 Text": { "x": 862, "y": 480, "w": 87, "ch": 5, "s": ["400"] }, "Libre Baskerville": { "x": 963, "y": 480, "w": 216, "ch": 5, "s": ["400", "400i", "500", "500i", "600", "600i", "700", "700i"] }, "Libre Bodoni": { "x": 0, "y": 520, "w": 153, "ch": 5, "s": ["400", "400i", "500", "500i", "600", "600i", "700", "700i"] }, "Libre Caslon Display": { "x": 167, "y": 520, "w": 197, "ch": 5, "s": ["400"] }, "Libre Caslon Text": { "x": 378, "y": 520, "w": 210, "ch": 5, "s": ["400", "400i", "700", "700i"] }, "Libre Franklin": { "x": 602, "y": 520, "w": 159, "ch": 5, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Licorice": { "x": 775, "y": 520, "w": 60, "ch": 5, "s": ["400"] }, "Life Savers": { "x": 849, "y": 520, "w": 119, "ch": 5, "s": ["400", "700", "800"] }, "Lilex": { "x": 982, "y": 520, "w": 80, "ch": 5, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i" ] }, "Lilita One": { "x": 1076, "y": 520, "w": 106, "ch": 5, "s": ["400"] }, "Lily Script One": { "x": 0, "y": 560, "w": 156, "ch": 5, "s": ["400"] }, "Limelight": { "x": 170, "y": 560, "w": 132, "ch": 5, "s": ["400"] }, "Linden Hill": { "x": 316, "y": 560, "w": 113, "ch": 5, "s": ["400", "400i"] }, "LINE Seed JP": { "x": 443, "y": 560, "w": 169, "ch": 5, "s": ["100", "400", "700", "800"] }, "Linefont": { "x": 626, "y": 560, "w": 28, "ch": 5, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Lisu Bosa": { "x": 668, "y": 560, "w": 106, "ch": 5, "s": [ "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Liter": { "x": 788, "y": 560, "w": 56, "ch": 5, "s": ["400"] }, "Literata": { "x": 858, "y": 560, "w": 99, "ch": 5, "s": [ "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Liu Jian Mao Cao": { "x": 971, "y": 560, "w": 164, "ch": 5, "s": ["400"] }, "Livvic": { "x": 0, "y": 600, "w": 66, "ch": 5, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "900", "900i" ] }, "Lobster": { "x": 80, "y": 600, "w": 72, "ch": 5, "s": ["400"] }, "Lobster Two": { "x": 166, "y": 600, "w": 114, "ch": 5, "s": ["400", "400i", "700", "700i"] }, "Londrina Outline": { "x": 294, "y": 600, "w": 156, "ch": 5, "s": ["400"] }, "Londrina Shadow": { "x": 464, "y": 600, "w": 165, "ch": 5, "s": ["400"] }, "Londrina Sketch": { "x": 643, "y": 600, "w": 156, "ch": 5, "s": ["400"] }, "Londrina Solid": { "x": 813, "y": 600, "w": 136, "ch": 5, "s": ["100", "300", "400", "900"] }, "Long Cang": { "x": 963, "y": 600, "w": 98, "ch": 5, "s": ["400"] }, "Lora": { "x": 1075, "y": 600, "w": 59, "ch": 5, "s": ["400", "400i", "500", "500i", "600", "600i", "700", "700i"] }, "Love Light": { "x": 0, "y": 640, "w": 88, "ch": 5, "s": ["400"] }, "Love Ya Like A Sister": { "x": 102, "y": 640, "w": 235, "ch": 5, "s": ["400"] }, "Loved by the King": { "x": 351, "y": 640, "w": 145, "ch": 5, "s": ["400"] }, "Lovers Quarrel": { "x": 510, "y": 640, "w": 92, "ch": 5, "s": ["400"] }, "Luckiest Guy": { "x": 616, "y": 640, "w": 156, "ch": 5, "s": ["400"] }, "Lugrasimo": { "x": 786, "y": 640, "w": 144, "ch": 5, "s": ["400"] }, "Lumanosimo": { "x": 944, "y": 640, "w": 185, "ch": 5, "s": ["400"] }, "Lunasima": { "x": 0, "y": 680, "w": 119, "ch": 5, "s": ["400", "700"] }, "Lusitana": { "x": 133, "y": 680, "w": 96, "ch": 5, "s": ["400", "700"] }, "Lustria": { "x": 243, "y": 680, "w": 86, "ch": 5, "s": ["400"] }, "Luxurious Roman": { "x": 343, "y": 680, "w": 201, "ch": 5, "s": ["400"] }, "Luxurious Script": { "x": 558, "y": 680, "w": 128, "ch": 5, "s": ["400"] }, "LXGW Marker Gothic": { "x": 700, "y": 680, "w": 233, "ch": 5, "s": ["400"] }, "LXGW WenKai Mono TC": { "x": 947, "y": 680, "w": 236, "ch": 5, "s": ["300", "400", "700"] }, "LXGW WenKai TC": { "x": 0, "y": 720, "w": 206, "ch": 5, "s": ["300", "400", "700"] }, "M PLUS 1": { "x": 220, "y": 720, "w": 120, "ch": 5, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "M PLUS 1 Code": { "x": 354, "y": 720, "w": 164, "ch": 5, "s": ["100", "200", "300", "400", "500", "600", "700"] }, "M PLUS 1p": { "x": 532, "y": 720, "w": 129, "ch": 5, "s": ["100", "300", "400", "500", "700", "800", "900"] }, "M PLUS 2": { "x": 675, "y": 720, "w": 120, "ch": 5, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "M PLUS Code Latin": { "x": 809, "y": 720, "w": 212, "ch": 5, "s": ["100", "200", "300", "400", "500", "600", "700"] }, "M PLUS Rounded 1c": { "x": 0, "y": 760, "w": 225, "ch": 5, "s": ["100", "300", "400", "500", "700", "800", "900"] }, "Ma Shan Zheng": { "x": 239, "y": 760, "w": 126, "ch": 5, "s": ["400"] }, "Macondo": { "x": 379, "y": 760, "w": 100, "ch": 5, "s": ["400"] }, "Macondo Swash Caps": { "x": 493, "y": 760, "w": 228, "ch": 5, "s": ["400"] }, "Mada": { "x": 735, "y": 760, "w": 63, "ch": 5, "s": ["200", "300", "400", "500", "600", "700", "800", "900"] }, "Madimi One": { "x": 812, "y": 760, "w": 133, "ch": 5, "s": ["400"] }, "Magra": { "x": 959, "y": 760, "w": 70, "ch": 5, "s": ["400", "700"] }, "Maiden Orange": { "x": 1043, "y": 760, "w": 127, "ch": 5, "s": ["400"] }, "Maitree": { "x": 0, "y": 0, "w": 96, "ch": 6, "s": ["200", "300", "400", "500", "600", "700"] }, "Major Mono Display": { "x": 110, "y": 0, "w": 328, "ch": 6, "s": ["400"] }, "Mako": { "x": 452, "y": 0, "w": 67, "ch": 6, "s": ["400"] }, "Mali": { "x": 533, "y": 0, "w": 56, "ch": 6, "s": [ "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i" ] }, "Mallanna": { "x": 603, "y": 0, "w": 96, "ch": 6, "s": ["400"] }, "Maname": { "x": 713, "y": 0, "w": 100, "ch": 6, "s": ["400"] }, "Mandali": { "x": 827, "y": 0, "w": 94, "ch": 6, "s": ["400"] }, "Manjari": { "x": 935, "y": 0, "w": 87, "ch": 6, "s": ["100", "400", "700"] }, "Manrope": { "x": 1036, "y": 0, "w": 107, "ch": 6, "s": ["200", "300", "400", "500", "600", "700", "800"] }, "Mansalva": { "x": 0, "y": 40, "w": 111, "ch": 6, "s": ["400"] }, "Manuale": { "x": 125, "y": 40, "w": 101, "ch": 6, "s": [ "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i" ] }, "Manufacturing Consent": { "x": 240, "y": 40, "w": 214, "ch": 6, "s": ["400"] }, "Marcellus": { "x": 468, "y": 40, "w": 114, "ch": 6, "s": ["400"] }, "Marcellus SC": { "x": 596, "y": 40, "w": 157, "ch": 6, "s": ["400"] }, "Marck Script": { "x": 767, "y": 40, "w": 137, "ch": 6, "s": ["400"] }, "Margarine": { "x": 918, "y": 40, "w": 119, "ch": 6, "s": ["400"] }, "Marhey": { "x": 1051, "y": 40, "w": 107, "ch": 6, "s": ["300", "400", "500", "600", "700"] }, "Markazi Text": { "x": 0, "y": 80, "w": 113, "ch": 6, "s": ["400", "500", "600", "700"] }, "Marko One": { "x": 127, "y": 80, "w": 152, "ch": 6, "s": ["400"] }, "Marmelad": { "x": 293, "y": 80, "w": 118, "ch": 6, "s": ["400"] }, "Martel": { "x": 425, "y": 80, "w": 88, "ch": 6, "s": ["200", "300", "400", "600", "700", "800", "900"] }, "Martel Sans": { "x": 527, "y": 80, "w": 143, "ch": 6, "s": ["200", "300", "400", "600", "700", "800", "900"] }, "Martian Mono": { "x": 684, "y": 80, "w": 210, "ch": 6, "s": ["100", "200", "300", "400", "500", "600", "700", "800"] }, "Marvel": { "x": 908, "y": 80, "w": 64, "ch": 6, "s": ["400", "400i", "700", "700i"] }, "Matangi": { "x": 986, "y": 80, "w": 99, "ch": 6, "s": ["300", "400", "500", "600", "700", "800", "900"] }, "Mate": { "x": 1099, "y": 80, "w": 61, "ch": 6, "s": ["400", "400i"] }, "Mate SC": { "x": 0, "y": 120, "w": 99, "ch": 6, "s": ["400"] }, "Matemasie": { "x": 113, "y": 120, "w": 145, "ch": 6, "s": ["400"] }, "Material Icons": { "x": 272, "y": 120, "w": 344, "ch": 6, "s": ["400"] }, "Material Icons Outlined": { "x": 630, "y": 120, "w": 560, "ch": 6, "s": ["400"] }, "Material Icons Round": { "x": 0, "y": 160, "w": 488, "ch": 6, "s": ["400"] }, "Material Icons Sharp": { "x": 502, "y": 160, "w": 488, "ch": 6, "s": ["400"] }, "Material Icons Two Tone": { "x": 0, "y": 200, "w": 560, "ch": 6, "s": ["400"] }, "Material Symbols": { "x": 574, "y": 200, "w": 392, "ch": 6, "s": ["100", "200", "300", "400", "500", "600", "700"] }, "Material Symbols Outlined": { "x": 0, "y": 240, "w": 608, "ch": 6, "s": ["100", "200", "300", "400", "500", "600", "700"] }, "Material Symbols Rounded": { "x": 0, "y": 280, "w": 584, "ch": 6, "s": ["100", "200", "300", "400", "500", "600", "700"] }, "Material Symbols Sharp": { "x": 598, "y": 280, "w": 536, "ch": 6, "s": ["100", "200", "300", "400", "500", "600", "700"] }, "Maven Pro": { "x": 0, "y": 320, "w": 123, "ch": 6, "s": ["400", "500", "600", "700", "800", "900"] }, "McLaren": { "x": 137, "y": 320, "w": 118, "ch": 6, "s": ["400"] }, "Mea Culpa": { "x": 269, "y": 320, "w": 102, "ch": 6, "s": ["400"] }, "Meddon": { "x": 385, "y": 320, "w": 129, "ch": 6, "s": ["400"] }, "MedievalSharp": { "x": 528, "y": 320, "w": 170, "ch": 6, "s": ["400"] }, "Medula One": { "x": 712, "y": 320, "w": 78, "ch": 6, "s": ["400"] }, "Meera Inimai": { "x": 804, "y": 320, "w": 138, "ch": 6, "s": ["400"] }, "Megrim": { "x": 956, "y": 320, "w": 87, "ch": 6, "s": ["400"] }, "Meie Script": { "x": 1057, "y": 320, "w": 133, "ch": 6, "s": ["400"] }, "Menbere": { "x": 0, "y": 360, "w": 112, "ch": 6, "s": ["100", "200", "300", "400", "500", "600", "700"] }, "Meow Script": { "x": 126, "y": 360, "w": 119, "ch": 6, "s": ["400"] }, "Merienda": { "x": 259, "y": 360, "w": 121, "ch": 6, "s": ["300", "400", "500", "600", "700", "800", "900"] }, "Merienda One": { "x": 394, "y": 360, "w": 177, "ch": 6, "s": ["400"] }, "Merriweather": { "x": 585, "y": 360, "w": 168, "ch": 6, "s": [ "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Merriweather Sans": { "x": 767, "y": 360, "w": 223, "ch": 6, "s": [ "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i" ] }, "Metal": { "x": 1004, "y": 360, "w": 58, "ch": 6, "s": ["400"] }, "Metal Mania": { "x": 1076, "y": 360, "w": 121, "ch": 6, "s": ["400"] }, "Metamorphous": { "x": 0, "y": 400, "w": 210, "ch": 6, "s": ["400"] }, "Metrophobic": { "x": 224, "y": 400, "w": 149, "ch": 6, "s": ["400"] }, "Michroma": { "x": 387, "y": 400, "w": 161, "ch": 6, "s": ["400"] }, "Micro 5": { "x": 562, "y": 400, "w": 65, "ch": 6, "s": ["400"] }, "Micro 5 Charted": { "x": 641, "y": 400, "w": 126, "ch": 6, "s": ["400"] }, "Milonga": { "x": 781, "y": 400, "w": 99, "ch": 6, "s": ["400"] }, "Miltonian": { "x": 894, "y": 400, "w": 130, "ch": 6, "s": ["400"] }, "Miltonian Tattoo": { "x": 0, "y": 440, "w": 214, "ch": 6, "s": ["400"] }, "Mina": { "x": 228, "y": 440, "w": 58, "ch": 6, "s": ["400", "700"] }, "Mingzat": { "x": 300, "y": 440, "w": 99, "ch": 6, "s": ["400"] }, "Miniver": { "x": 413, "y": 440, "w": 89, "ch": 6, "s": ["400"] }, "Miriam Libre": { "x": 516, "y": 440, "w": 161, "ch": 6, "s": ["400", "500", "600", "700"] }, "Mirza": { "x": 691, "y": 440, "w": 60, "ch": 6, "s": ["400", "500", "600", "700"] }, "Miss Fajardose": { "x": 765, "y": 440, "w": 108, "ch": 6, "s": ["400"] }, "Mitr": { "x": 887, "y": 440, "w": 55, "ch": 6, "s": ["200", "300", "400", "500", "600", "700"] }, "Mochiy Pop One": { "x": 956, "y": 440, "w": 223, "ch": 6, "s": ["400"] }, "Mochiy Pop P One": { "x": 0, "y": 480, "w": 248, "ch": 6, "s": ["400"] }, "Modak": { "x": 262, "y": 480, "w": 79, "ch": 6, "s": ["400"] }, "Modern Antiqua": { "x": 355, "y": 480, "w": 196, "ch": 6, "s": ["400"] }, "Moderustic": { "x": 565, "y": 480, "w": 135, "ch": 6, "s": ["300", "400", "500", "600", "700", "800"] }, "Mogra": { "x": 714, "y": 480, "w": 80, "ch": 6, "s": ["400"] }, "Mohave": { "x": 808, "y": 480, "w": 77, "ch": 6, "s": [ "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i" ] }, "Moirai One": { "x": 899, "y": 480, "w": 146, "ch": 6, "s": ["400"] }, "Molengo": { "x": 1059, "y": 480, "w": 96, "ch": 6, "s": ["400"] }, "Momo Signature": { "x": 0, "y": 520, "w": 227, "ch": 6, "s": ["400"] }, "Momo Trust Display": { "x": 241, "y": 520, "w": 249, "ch": 6, "s": ["400"] }, "Momo Trust Sans": { "x": 504, "y": 520, "w": 207, "ch": 6, "s": ["200", "300", "400", "500", "600", "700", "800"] }, "Mona Sans": { "x": 725, "y": 520, "w": 136, "ch": 6, "s": [ "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Monda": { "x": 875, "y": 520, "w": 85, "ch": 6, "s": ["400", "500", "600", "700"] }, "Monofett": { "x": 974, "y": 520, "w": 119, "ch": 6, "s": ["400"] }, "Monomakh": { "x": 0, "y": 560, "w": 138, "ch": 6, "s": ["400"] }, "Monomaniac One": { "x": 152, "y": 560, "w": 156, "ch": 6, "s": ["400"] }, "Monoton": { "x": 322, "y": 560, "w": 143, "ch": 6, "s": ["400"] }, "Monsieur La Doulaise": { "x": 479, "y": 560, "w": 215, "ch": 6, "s": ["400"] }, "Montaga": { "x": 708, "y": 560, "w": 102, "ch": 6, "s": ["400"] }, "Montagu Slab": { "x": 824, "y": 560, "w": 173, "ch": 6, "s": ["100", "200", "300", "400", "500", "600", "700"] }, "MonteCarlo": { "x": 1011, "y": 560, "w": 107, "ch": 6, "s": ["400"] }, "Montez": { "x": 1132, "y": 560, "w": 66, "ch": 6, "s": ["400"] }, "Montserrat": { "x": 0, "y": 600, "w": 141, "ch": 6, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Montserrat Alternates": { "x": 155, "y": 600, "w": 282, "ch": 6, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Montserrat Subrayada": { "x": 451, "y": 600, "w": 347, "ch": 6, "s": ["400", "700"] }, "Montserrat Underline": { "x": 812, "y": 600, "w": 266, "ch": 6, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Moo Lah Lah": { "x": 0, "y": 640, "w": 137, "ch": 6, "s": ["400"] }, "Mooli": { "x": 151, "y": 640, "w": 77, "ch": 6, "s": ["400"] }, "Moon Dance": { "x": 242, "y": 640, "w": 106, "ch": 6, "s": ["400"] }, "Moul": { "x": 362, "y": 640, "w": 82, "ch": 6, "s": ["400"] }, "Moulpali": { "x": 458, "y": 640, "w": 90, "ch": 6, "s": ["400"] }, "Mountains of Christmas": { "x": 562, "y": 640, "w": 219, "ch": 6, "s": ["400", "700"] }, "Mouse Memoirs": { "x": 795, "y": 640, "w": 116, "ch": 6, "s": ["400"] }, "Mozilla Headline": { "x": 925, "y": 640, "w": 192, "ch": 6, "s": ["200", "300", "400", "500", "600", "700"] }, "Mozilla Text": { "x": 0, "y": 680, "w": 148, "ch": 6, "s": ["200", "300", "400", "500", "600", "700"] }, "Mr Bedfort": { "x": 162, "y": 680, "w": 124, "ch": 6, "s": ["400"] }, "Mr Dafoe": { "x": 300, "y": 680, "w": 92, "ch": 6, "s": ["400"] }, "Mr De Haviland": { "x": 406, "y": 680, "w": 115, "ch": 6, "s": ["400"] }, "Mrs Saint Delafield": { "x": 535, "y": 680, "w": 159, "ch": 6, "s": ["400"] }, "Mrs Sheppards": { "x": 708, "y": 680, "w": 139, "ch": 6, "s": ["400"] }, "Ms Madi": { "x": 861, "y": 680, "w": 89, "ch": 6, "s": ["400"] }, "Mukta": { "x": 964, "y": 680, "w": 73, "ch": 6, "s": ["200", "300", "400", "500", "600", "700", "800"] }, "Mukta Mahee": { "x": 1051, "y": 680, "w": 146, "ch": 6, "s": ["200", "300", "400", "500", "600", "700", "800"] }, "Mukta Malar": { "x": 0, "y": 720, "w": 137, "ch": 6, "s": ["200", "300", "400", "500", "600", "700", "800"] }, "Mukta Vaani": { "x": 151, "y": 720, "w": 133, "ch": 6, "s": ["200", "300", "400", "500", "600", "700", "800"] }, "Mulish": { "x": 298, "y": 720, "w": 80, "ch": 6, "s": [ "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Murecho": { "x": 392, "y": 720, "w": 102, "ch": 6, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "MuseoModerno": { "x": 508, "y": 720, "w": 199, "ch": 6, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "My Soul": { "x": 721, "y": 720, "w": 107, "ch": 6, "s": ["400"] }, "Mynerve": { "x": 842, "y": 720, "w": 104, "ch": 6, "s": ["400"] }, "Mystery Quest": { "x": 960, "y": 720, "w": 152, "ch": 6, "s": ["400"] }, "Nabla": { "x": 1126, "y": 720, "w": 72, "ch": 6, "s": ["400"] }, "Namdhinggo": { "x": 0, "y": 760, "w": 130, "ch": 6, "s": ["400", "500", "600", "700", "800"] }, "Nanum Brush Script": { "x": 144, "y": 760, "w": 168, "ch": 6, "s": ["400"] }, "Nanum Gothic": { "x": 326, "y": 760, "w": 171, "ch": 6, "s": ["400", "700", "800"] }, "Nanum Gothic Coding": { "x": 511, "y": 760, "w": 236, "ch": 6, "s": ["400", "700"] }, "Nanum Myeongjo": { "x": 761, "y": 760, "w": 201, "ch": 6, "s": ["400", "700", "800"] }, "Nanum Pen Script": { "x": 976, "y": 760, "w": 157, "ch": 6, "s": ["400"] }, "Narnoor": { "x": 0, "y": 0, "w": 90, "ch": 7, "s": ["400", "500", "600", "700", "800"] }, "Nata Sans": { "x": 104, "y": 0, "w": 123, "ch": 7, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "National Park": { "x": 241, "y": 0, "w": 149, "ch": 7, "s": ["200", "300", "400", "500", "600", "700", "800"] }, "Neonderthaw": { "x": 404, "y": 0, "w": 139, "ch": 7, "s": ["400"] }, "Nerko One": { "x": 557, "y": 0, "w": 107, "ch": 7, "s": ["400"] }, "Neucha": { "x": 678, "y": 0, "w": 72, "ch": 7, "s": ["400"] }, "Neuton": { "x": 764, "y": 0, "w": 76, "ch": 7, "s": [ "200", "200i", "300", "300i", "400", "400i", "700", "700i", "800", "800i" ] }, "New Amsterdam": { "x": 854, "y": 0, "w": 139, "ch": 7, "s": ["400"] }, "New Rocker": { "x": 1007, "y": 0, "w": 133, "ch": 7, "s": ["400"] }, "New Tegomin": { "x": 0, "y": 40, "w": 159, "ch": 7, "s": ["400"] }, "News Cycle": { "x": 173, "y": 40, "w": 116, "ch": 7, "s": ["400", "700"] }, "Newsreader": { "x": 303, "y": 40, "w": 129, "ch": 7, "s": [ "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i" ] }, "Niconne": { "x": 446, "y": 40, "w": 79, "ch": 7, "s": ["400"] }, "Niramit": { "x": 539, "y": 40, "w": 84, "ch": 7, "s": [ "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i" ] }, "Nixie One": { "x": 637, "y": 40, "w": 123, "ch": 7, "s": ["400"] }, "Nobile": { "x": 774, "y": 40, "w": 82, "ch": 7, "s": ["400", "400i", "500", "500i", "700", "700i"] }, "Nokora": { "x": 870, "y": 40, "w": 91, "ch": 7, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Norican": { "x": 975, "y": 40, "w": 78, "ch": 7, "s": ["400"] }, "Nosifer": { "x": 0, "y": 80, "w": 154, "ch": 7, "s": ["400"] }, "Notable": { "x": 168, "y": 80, "w": 144, "ch": 7, "s": ["400"] }, "Nothing You Could Do": { "x": 326, "y": 80, "w": 248, "ch": 7, "s": ["400"] }, "Noticia Text": { "x": 588, "y": 80, "w": 142, "ch": 7, "s": ["400", "400i", "700", "700i"] }, "Noto Kufi Arabic": { "x": 744, "y": 80, "w": 196, "ch": 7, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Music": { "x": 954, "y": 80, "w": 136, "ch": 7, "s": ["400"] }, "Noto Naskh Arabic": { "x": 0, "y": 120, "w": 221, "ch": 7, "s": ["400", "500", "600", "700"] }, "Noto Nastaliq Urdu": { "x": 235, "y": 120, "w": 222, "ch": 7, "s": ["400", "500", "600", "700"] }, "Noto Rashi Hebrew": { "x": 471, "y": 120, "w": 230, "ch": 7, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Sans": { "x": 715, "y": 120, "w": 124, "ch": 7, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Noto Sans Adlam": { "x": 853, "y": 120, "w": 202, "ch": 7, "s": ["400", "500", "600", "700"] }, "Noto Sans Adlam Unjoined": { "x": 0, "y": 160, "w": 310, "ch": 7, "s": ["400", "500", "600", "700"] }, "Noto Sans Anatolian Hieroglyphs": { "x": 324, "y": 160, "w": 380, "ch": 7, "s": ["400"] }, "Noto Sans Arabic": { "x": 718, "y": 160, "w": 201, "ch": 7, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Sans Armenian": { "x": 933, "y": 160, "w": 240, "ch": 7, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Sans Avestan": { "x": 0, "y": 200, "w": 219, "ch": 7, "s": ["400"] }, "Noto Sans Balinese": { "x": 233, "y": 200, "w": 225, "ch": 7, "s": ["400", "500", "600", "700"] }, "Noto Sans Bamum": { "x": 472, "y": 200, "w": 219, "ch": 7, "s": ["400", "500", "600", "700"] }, "Noto Sans Bassa Vah": { "x": 705, "y": 200, "w": 250, "ch": 7, "s": ["400", "500", "600", "700"] }, "Noto Sans Batak": { "x": 969, "y": 200, "w": 194, "ch": 7, "s": ["400"] }, "Noto Sans Bengali": { "x": 0, "y": 240, "w": 214, "ch": 7, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Sans Bhaiksuki": { "x": 228, "y": 240, "w": 236, "ch": 7, "s": ["400"] }, "Noto Sans Brahmi": { "x": 478, "y": 240, "w": 212, "ch": 7, "s": ["400"] }, "Noto Sans Buginese": { "x": 704, "y": 240, "w": 235, "ch": 7, "s": ["400"] }, "Noto Sans Buhid": { "x": 953, "y": 240, "w": 196, "ch": 7, "s": ["400"] }, "Noto Sans Canadian Aboriginal": { "x": 0, "y": 280, "w": 361, "ch": 7, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Sans Carian": { "x": 375, "y": 280, "w": 203, "ch": 7, "s": ["400"] }, "Noto Sans Caucasian Albanian": { "x": 592, "y": 280, "w": 353, "ch": 7, "s": ["400"] }, "Noto Sans Chakma": { "x": 959, "y": 280, "w": 222, "ch": 7, "s": ["400"] }, "Noto Sans Cham": { "x": 0, "y": 320, "w": 196, "ch": 7, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Sans Cherokee": { "x": 210, "y": 320, "w": 238, "ch": 7, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Sans Chorasmian": { "x": 462, "y": 320, "w": 268, "ch": 7, "s": ["400"] }, "Noto Sans Coptic": { "x": 744, "y": 320, "w": 201, "ch": 7, "s": ["400"] }, "Noto Sans Cuneiform": { "x": 0, "y": 360, "w": 250, "ch": 7, "s": ["400"] }, "Noto Sans Cypriot": { "x": 264, "y": 360, "w": 211, "ch": 7, "s": ["400"] }, "Noto Sans Cypro Minoan": { "x": 489, "y": 360, "w": 288, "ch": 7, "s": ["400"] }, "Noto Sans Deseret": { "x": 791, "y": 360, "w": 218, "ch": 7, "s": ["400"] }, "Noto Sans Devanagari": { "x": 0, "y": 400, "w": 259, "ch": 7, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Sans Display": { "x": 273, "y": 400, "w": 200, "ch": 7, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Noto Sans Duployan": { "x": 487, "y": 400, "w": 238, "ch": 7, "s": ["400", "700"] }, "Noto Sans Egyptian Hieroglyphs": { "x": 739, "y": 400, "w": 369, "ch": 7, "s": ["400"] }, "Noto Sans Elbasan": { "x": 0, "y": 440, "w": 217, "ch": 7, "s": ["400"] }, "Noto Sans Elymaic": { "x": 231, "y": 440, "w": 222, "ch": 7, "s": ["400"] }, "Noto Sans Ethiopic": { "x": 467, "y": 440, "w": 220, "ch": 7, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Sans Georgian": { "x": 701, "y": 440, "w": 234, "ch": 7, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Sans Glagolitic": { "x": 949, "y": 440, "w": 235, "ch": 7, "s": ["400"] }, "Noto Sans Gothic": { "x": 0, "y": 480, "w": 203, "ch": 7, "s": ["400"] }, "Noto Sans Grantha": { "x": 217, "y": 480, "w": 222, "ch": 7, "s": ["400"] }, "Noto Sans Gujarati": { "x": 453, "y": 480, "w": 220, "ch": 7, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Sans Gunjala Gondi": { "x": 687, "y": 480, "w": 291, "ch": 7, "s": ["400", "500", "600", "700"] }, "Noto Sans Gurmukhi": { "x": 0, "y": 520, "w": 243, "ch": 7, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Sans Hanifi Rohingya": { "x": 257, "y": 520, "w": 309, "ch": 7, "s": ["400", "500", "600", "700"] }, "Noto Sans Hanunoo": { "x": 580, "y": 520, "w": 235, "ch": 7, "s": ["400"] }, "Noto Sans Hatran": { "x": 829, "y": 520, "w": 208, "ch": 7, "s": ["400"] }, "Noto Sans Hebrew": { "x": 0, "y": 560, "w": 217, "ch": 7, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Sans HK": { "x": 231, "y": 560, "w": 160, "ch": 7, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Sans Imperial Aramaic": { "x": 405, "y": 560, "w": 323, "ch": 7, "s": ["400"] }, "Noto Sans Indic Siyaq Numbers": { "x": 742, "y": 560, "w": 351, "ch": 7, "s": ["400"] }, "Noto Sans Inscriptional Pahlavi": { "x": 0, "y": 600, "w": 358, "ch": 7, "s": ["400"] }, "Noto Sans Inscriptional Parthian": { "x": 372, "y": 600, "w": 373, "ch": 7, "s": ["400"] }, "Noto Sans Javanese": { "x": 759, "y": 600, "w": 229, "ch": 7, "s": ["400", "500", "600", "700"] }, "Noto Sans JP": { "x": 1002, "y": 600, "w": 155, "ch": 7, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Sans Kaithi": { "x": 0, "y": 640, "w": 195, "ch": 7, "s": ["400"] }, "Noto Sans Kannada": { "x": 209, "y": 640, "w": 231, "ch": 7, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Sans Kawi": { "x": 454, "y": 640, "w": 180, "ch": 7, "s": ["400", "500", "600", "700"] }, "Noto Sans Kayah Li": { "x": 648, "y": 640, "w": 224, "ch": 7, "s": ["400", "500", "600", "700"] }, "Noto Sans Kharoshthi": { "x": 886, "y": 640, "w": 253, "ch": 7, "s": ["400"] }, "Noto Sans Khmer": { "x": 0, "y": 680, "w": 205, "ch": 7, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Sans Khojki": { "x": 219, "y": 680, "w": 199, "ch": 7, "s": ["400"] }, "Noto Sans Khudawadi": { "x": 432, "y": 680, "w": 256, "ch": 7, "s": ["400"] }, "Noto Sans KR": { "x": 702, "y": 680, "w": 158, "ch": 7, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Sans Lao": { "x": 874, "y": 680, "w": 170, "ch": 7, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Sans Lao Looped": { "x": 0, "y": 720, "w": 261, "ch": 7, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Sans Lepcha": { "x": 275, "y": 720, "w": 211, "ch": 7, "s": ["400"] }, "Noto Sans Limbu": { "x": 500, "y": 720, "w": 201, "ch": 7, "s": ["400"] }, "Noto Sans Linear A": { "x": 715, "y": 720, "w": 222, "ch": 7, "s": ["400"] }, "Noto Sans Linear B": { "x": 951, "y": 720, "w": 222, "ch": 7, "s": ["400"] }, "Noto Sans Lisu": { "x": 0, "y": 760, "w": 175, "ch": 7, "s": ["400", "500", "600", "700"] }, "Noto Sans Lydian": { "x": 189, "y": 760, "w": 204, "ch": 7, "s": ["400"] }, "Noto Sans Mahajani": { "x": 407, "y": 760, "w": 234, "ch": 7, "s": ["400"] }, "Noto Sans Malayalam": { "x": 655, "y": 760, "w": 255, "ch": 7, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Sans Mandaic": { "x": 924, "y": 760, "w": 226, "ch": 7, "s": ["400"] }, "Noto Sans Manichaean": { "x": 0, "y": 0, "w": 268, "ch": 8, "s": ["400"] }, "Noto Sans Marchen": { "x": 282, "y": 0, "w": 227, "ch": 8, "s": ["400"] }, "Noto Sans Masaram Gondi": { "x": 523, "y": 0, "w": 318, "ch": 8, "s": ["400"] }, "Noto Sans Math": { "x": 855, "y": 0, "w": 189, "ch": 8, "s": ["400"] }, "Noto Sans Mayan Numerals": { "x": 0, "y": 40, "w": 322, "ch": 8, "s": ["400"] }, "Noto Sans Medefaidrin": { "x": 336, "y": 40, "w": 267, "ch": 8, "s": ["400", "500", "600", "700"] }, "Noto Sans Meetei Mayek": { "x": 617, "y": 40, "w": 289, "ch": 8, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Sans Mende Kikakui": { "x": 0, "y": 80, "w": 296, "ch": 8, "s": ["400"] }, "Noto Sans Meroitic": { "x": 310, "y": 80, "w": 222, "ch": 8, "s": ["400"] }, "Noto Sans Miao": { "x": 546, "y": 80, "w": 186, "ch": 8, "s": ["400"] }, "Noto Sans Modi": { "x": 746, "y": 80, "w": 187, "ch": 8, "s": ["400"] }, "Noto Sans Mongolian": { "x": 947, "y": 80, "w": 251, "ch": 8, "s": ["400"] }, "Noto Sans Mono": { "x": 0, "y": 120, "w": 210, "ch": 8, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Sans Mro": { "x": 224, "y": 120, "w": 176, "ch": 8, "s": ["400"] }, "Noto Sans Multani": { "x": 414, "y": 120, "w": 216, "ch": 8, "s": ["400"] }, "Noto Sans Myanmar": { "x": 644, "y": 120, "w": 238, "ch": 8, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Sans Nabataean": { "x": 896, "y": 120, "w": 254, "ch": 8, "s": ["400"] }, "Noto Sans Nag Mundari": { "x": 0, "y": 160, "w": 274, "ch": 8, "s": ["400", "500", "600", "700"] }, "Noto Sans Nandinagari": { "x": 288, "y": 160, "w": 272, "ch": 8, "s": ["400"] }, "Noto Sans New Tai Lue": { "x": 574, "y": 160, "w": 265, "ch": 8, "s": ["400", "500", "600", "700"] }, "Noto Sans Newa": { "x": 853, "y": 160, "w": 194, "ch": 8, "s": ["400"] }, "Noto Sans NKo": { "x": 0, "y": 200, "w": 178, "ch": 8, "s": ["400"] }, "Noto Sans NKo Unjoined": { "x": 192, "y": 200, "w": 286, "ch": 8, "s": ["400", "500", "600", "700"] }, "Noto Sans Nushu": { "x": 492, "y": 200, "w": 204, "ch": 8, "s": ["400"] }, "Noto Sans Ogham": { "x": 710, "y": 200, "w": 214, "ch": 8, "s": ["400"] }, "Noto Sans Ol Chiki": { "x": 938, "y": 200, "w": 216, "ch": 8, "s": ["400", "500", "600", "700"] }, "Noto Sans Old Hungarian": { "x": 0, "y": 240, "w": 296, "ch": 8, "s": ["400"] }, "Noto Sans Old Italic": { "x": 310, "y": 240, "w": 230, "ch": 8, "s": ["400"] }, "Noto Sans Old North Arabian": { "x": 554, "y": 240, "w": 336, "ch": 8, "s": ["400"] }, "Noto Sans Old Permic": { "x": 904, "y": 240, "w": 253, "ch": 8, "s": ["400"] }, "Noto Sans Old Persian": { "x": 0, "y": 280, "w": 260, "ch": 8, "s": ["400"] }, "Noto Sans Old Sogdian": { "x": 274, "y": 280, "w": 260, "ch": 8, "s": ["400"] }, "Noto Sans Old South Arabian": { "x": 548, "y": 280, "w": 336, "ch": 8, "s": ["400"] }, "Noto Sans Old Turkic": { "x": 898, "y": 280, "w": 243, "ch": 8, "s": ["400"] }, "Noto Sans Oriya": { "x": 0, "y": 320, "w": 190, "ch": 8, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Sans Osage": { "x": 204, "y": 320, "w": 202, "ch": 8, "s": ["400"] }, "Noto Sans Osmanya": { "x": 420, "y": 320, "w": 238, "ch": 8, "s": ["400"] }, "Noto Sans Pahawh Hmong": { "x": 672, "y": 320, "w": 310, "ch": 8, "s": ["400"] }, "Noto Sans Palmyrene": { "x": 0, "y": 360, "w": 251, "ch": 8, "s": ["400"] }, "Noto Sans Pau Cin Hau": { "x": 265, "y": 360, "w": 269, "ch": 8, "s": ["400"] }, "Noto Sans PhagsPa": { "x": 548, "y": 360, "w": 227, "ch": 8, "s": ["400"] }, "Noto Sans Phoenician": { "x": 789, "y": 360, "w": 254, "ch": 8, "s": ["400"] }, "Noto Sans Psalter Pahlavi": { "x": 0, "y": 400, "w": 295, "ch": 8, "s": ["400"] }, "Noto Sans Rejang": { "x": 309, "y": 400, "w": 208, "ch": 8, "s": ["400"] }, "Noto Sans Runic": { "x": 531, "y": 400, "w": 192, "ch": 8, "s": ["400"] }, "Noto Sans Samaritan": { "x": 737, "y": 400, "w": 245, "ch": 8, "s": ["400"] }, "Noto Sans Saurashtra": { "x": 0, "y": 440, "w": 254, "ch": 8, "s": ["400"] }, "Noto Sans SC": { "x": 268, "y": 440, "w": 157, "ch": 8, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Sans Sharada": { "x": 439, "y": 440, "w": 222, "ch": 8, "s": ["400"] }, "Noto Sans Shavian": { "x": 675, "y": 440, "w": 218, "ch": 8, "s": ["400"] }, "Noto Sans Siddham": { "x": 907, "y": 440, "w": 227, "ch": 8, "s": ["400"] }, "Noto Sans SignWriting": { "x": 0, "y": 480, "w": 297, "ch": 8, "s": ["400"] }, "Noto Sans Sinhala": { "x": 311, "y": 480, "w": 224, "ch": 8, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Sans Sogdian": { "x": 549, "y": 480, "w": 216, "ch": 8, "s": ["400"] }, "Noto Sans Sora Sompeng": { "x": 779, "y": 480, "w": 295, "ch": 8, "s": ["400", "500", "600", "700"] }, "Noto Sans Soyombo": { "x": 0, "y": 520, "w": 236, "ch": 8, "s": ["400"] }, "Noto Sans Sundanese": { "x": 250, "y": 520, "w": 254, "ch": 8, "s": ["400", "500", "600", "700"] }, "Noto Sans Sunuwar": { "x": 518, "y": 520, "w": 237, "ch": 8, "s": ["400"] }, "Noto Sans Syloti Nagri": { "x": 769, "y": 520, "w": 260, "ch": 8, "s": ["400"] }, "Noto Sans Symbols": { "x": 0, "y": 560, "w": 225, "ch": 8, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Sans Symbols 2": { "x": 239, "y": 560, "w": 257, "ch": 8, "s": ["400"] }, "Noto Sans Syriac": { "x": 510, "y": 560, "w": 196, "ch": 8, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Sans Syriac Eastern": { "x": 720, "y": 560, "w": 288, "ch": 8, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Sans Syriac Western": { "x": 0, "y": 600, "w": 296, "ch": 8, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Sans Tagalog": { "x": 310, "y": 600, "w": 218, "ch": 8, "s": ["400"] }, "Noto Sans Tagbanwa": { "x": 542, "y": 600, "w": 247, "ch": 8, "s": ["400"] }, "Noto Sans Tai Le": { "x": 803, "y": 600, "w": 193, "ch": 8, "s": ["400"] }, "Noto Sans Tai Tham": { "x": 0, "y": 640, "w": 243, "ch": 8, "s": ["400", "500", "600", "700"] }, "Noto Sans Tai Viet": { "x": 257, "y": 640, "w": 210, "ch": 8, "s": ["400"] }, "Noto Sans Takri": { "x": 481, "y": 640, "w": 184, "ch": 8, "s": ["400"] }, "Noto Sans Tamil": { "x": 679, "y": 640, "w": 190, "ch": 8, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Sans Tamil Supplement": { "x": 0, "y": 680, "w": 334, "ch": 8, "s": ["400"] }, "Noto Sans Tangsa": { "x": 348, "y": 680, "w": 209, "ch": 8, "s": ["400", "500", "600", "700"] }, "Noto Sans TC": { "x": 571, "y": 680, "w": 156, "ch": 8, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Sans Telugu": { "x": 741, "y": 680, "w": 207, "ch": 8, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Sans Thaana": { "x": 962, "y": 680, "w": 213, "ch": 8, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Sans Thai": { "x": 0, "y": 720, "w": 178, "ch": 8, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Sans Thai Looped": { "x": 192, "y": 720, "w": 269, "ch": 8, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Sans Tifinagh": { "x": 475, "y": 720, "w": 222, "ch": 8, "s": ["400"] }, "Noto Sans Tirhuta": { "x": 711, "y": 720, "w": 211, "ch": 8, "s": ["400"] }, "Noto Sans Ugaritic": { "x": 936, "y": 720, "w": 218, "ch": 8, "s": ["400"] }, "Noto Sans Vai": { "x": 0, "y": 760, "w": 164, "ch": 8, "s": ["400"] }, "Noto Sans Vithkuqi": { "x": 178, "y": 760, "w": 223, "ch": 8, "s": ["400", "500", "600", "700"] }, "Noto Sans Wancho": { "x": 415, "y": 760, "w": 228, "ch": 8, "s": ["400"] }, "Noto Sans Warang Citi": { "x": 657, "y": 760, "w": 264, "ch": 8, "s": ["400"] }, "Noto Sans Yi": { "x": 935, "y": 760, "w": 149, "ch": 8, "s": ["400"] }, "Noto Sans Zanabazar Square": { "x": 0, "y": 0, "w": 334, "ch": 9, "s": ["400"] }, "Noto Serif": { "x": 348, "y": 0, "w": 123, "ch": 9, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Noto Serif Ahom": { "x": 485, "y": 0, "w": 196, "ch": 9, "s": ["400"] }, "Noto Serif Armenian": { "x": 695, "y": 0, "w": 245, "ch": 9, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Serif Balinese": { "x": 954, "y": 0, "w": 225, "ch": 9, "s": ["400"] }, "Noto Serif Bengali": { "x": 0, "y": 40, "w": 215, "ch": 9, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Serif Devanagari": { "x": 229, "y": 40, "w": 261, "ch": 9, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Serif Display": { "x": 504, "y": 40, "w": 212, "ch": 9, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Noto Serif Dives Akuru": { "x": 730, "y": 40, "w": 274, "ch": 9, "s": ["400"] }, "Noto Serif Dogra": { "x": 0, "y": 80, "w": 197, "ch": 9, "s": ["400"] }, "Noto Serif Ethiopic": { "x": 211, "y": 80, "w": 224, "ch": 9, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Serif Georgian": { "x": 449, "y": 80, "w": 234, "ch": 9, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Serif Grantha": { "x": 697, "y": 80, "w": 223, "ch": 9, "s": ["400"] }, "Noto Serif Gujarati": { "x": 934, "y": 80, "w": 225, "ch": 9, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Serif Gurmukhi": { "x": 0, "y": 120, "w": 248, "ch": 9, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Serif Hebrew": { "x": 262, "y": 120, "w": 221, "ch": 9, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Serif Hentaigana": { "x": 497, "y": 120, "w": 269, "ch": 9, "s": ["200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Serif HK": { "x": 780, "y": 120, "w": 170, "ch": 9, "s": ["200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Serif JP": { "x": 964, "y": 120, "w": 157, "ch": 9, "s": ["200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Serif Kannada": { "x": 0, "y": 160, "w": 231, "ch": 9, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Serif Khitan Small Script": { "x": 245, "y": 160, "w": 383, "ch": 9, "s": ["400"] }, "Noto Serif Khmer": { "x": 642, "y": 160, "w": 208, "ch": 9, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Serif Khojki": { "x": 864, "y": 160, "w": 204, "ch": 9, "s": ["400", "500", "600", "700"] }, "Noto Serif KR": { "x": 0, "y": 200, "w": 167, "ch": 9, "s": ["200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Serif Lao": { "x": 181, "y": 200, "w": 171, "ch": 9, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Serif Makasar": { "x": 366, "y": 200, "w": 226, "ch": 9, "s": ["400"] }, "Noto Serif Malayalam": { "x": 606, "y": 200, "w": 259, "ch": 9, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Serif NP Hmong": { "x": 879, "y": 200, "w": 252, "ch": 9, "s": ["400", "500", "600", "700"] }, "Noto Serif Old Uyghur": { "x": 0, "y": 240, "w": 257, "ch": 9, "s": ["400"] }, "Noto Serif Oriya": { "x": 271, "y": 240, "w": 200, "ch": 9, "s": ["400", "500", "600", "700"] }, "Noto Serif Ottoman Siyaq": { "x": 485, "y": 240, "w": 298, "ch": 9, "s": ["400"] }, "Noto Serif SC": { "x": 797, "y": 240, "w": 162, "ch": 9, "s": ["200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Serif Sinhala": { "x": 973, "y": 240, "w": 227, "ch": 9, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Serif Tamil": { "x": 0, "y": 280, "w": 194, "ch": 9, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Noto Serif Tangut": { "x": 208, "y": 280, "w": 220, "ch": 9, "s": ["400"] }, "Noto Serif TC": { "x": 442, "y": 280, "w": 164, "ch": 9, "s": ["200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Serif Telugu": { "x": 620, "y": 280, "w": 207, "ch": 9, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Serif Thai": { "x": 841, "y": 280, "w": 180, "ch": 9, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Serif Tibetan": { "x": 0, "y": 320, "w": 217, "ch": 9, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Noto Serif Todhri": { "x": 231, "y": 320, "w": 201, "ch": 9, "s": ["400"] }, "Noto Serif Toto": { "x": 446, "y": 320, "w": 179, "ch": 9, "s": ["400", "500", "600", "700"] }, "Noto Serif Vithkuqi": { "x": 639, "y": 320, "w": 228, "ch": 9, "s": ["400", "500", "600", "700"] }, "Noto Serif Yezidi": { "x": 881, "y": 320, "w": 198, "ch": 9, "s": ["400", "500", "600", "700"] }, "Noto Traditional Nushu": { "x": 0, "y": 360, "w": 289, "ch": 9, "s": ["300", "400", "500", "600", "700"] }, "Noto Znamenny Musical Notation": { "x": 303, "y": 360, "w": 388, "ch": 9, "s": ["400"] }, "Nova Cut": { "x": 705, "y": 360, "w": 108, "ch": 9, "s": ["400"] }, "Nova Flat": { "x": 827, "y": 360, "w": 112, "ch": 9, "s": ["400"] }, "Nova Mono": { "x": 953, "y": 360, "w": 130, "ch": 9, "s": ["400"] }, "Nova Oval": { "x": 0, "y": 400, "w": 123, "ch": 9, "s": ["400"] }, "Nova Round": { "x": 137, "y": 400, "w": 140, "ch": 9, "s": ["400"] }, "Nova Script": { "x": 291, "y": 400, "w": 140, "ch": 9, "s": ["400"] }, "Nova Slim": { "x": 445, "y": 400, "w": 121, "ch": 9, "s": ["400"] }, "Nova Square": { "x": 580, "y": 400, "w": 148, "ch": 9, "s": ["400"] }, "NTR": { "x": 742, "y": 400, "w": 49, "ch": 9, "s": ["400"] }, "Numans": { "x": 805, "y": 400, "w": 113, "ch": 9, "s": ["400"] }, "Nunito": { "x": 932, "y": 400, "w": 81, "ch": 9, "s": [ "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Nunito Sans": { "x": 1027, "y": 400, "w": 139, "ch": 9, "s": [ "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Nuosu SIL": { "x": 0, "y": 440, "w": 117, "ch": 9, "s": ["400"] }, "Odibee Sans": { "x": 131, "y": 440, "w": 95, "ch": 9, "s": ["400"] }, "Odor Mean Chey": { "x": 240, "y": 440, "w": 184, "ch": 9, "s": ["400"] }, "Offside": { "x": 438, "y": 440, "w": 96, "ch": 9, "s": ["400"] }, "Oi": { "x": 548, "y": 440, "w": 47, "ch": 9, "s": ["400"] }, "Ojuju": { "x": 609, "y": 440, "w": 62, "ch": 9, "s": ["200", "300", "400", "500", "600", "700", "800"] }, "Old Standard TT": { "x": 685, "y": 440, "w": 187, "ch": 9, "s": ["400", "400i", "700", "700i"] }, "Oldenburg": { "x": 886, "y": 440, "w": 136, "ch": 9, "s": ["400"] }, "Ole": { "x": 1036, "y": 440, "w": 33, "ch": 9, "s": ["400"] }, "Oleo Script": { "x": 1083, "y": 440, "w": 109, "ch": 9, "s": ["400", "700"] }, "Oleo Script Swash Caps": { "x": 0, "y": 480, "w": 227, "ch": 9, "s": ["400", "700"] }, "Onest": { "x": 241, "y": 480, "w": 76, "ch": 9, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Oooh Baby": { "x": 331, "y": 480, "w": 109, "ch": 9, "s": ["400"] }, "Open Sans": { "x": 454, "y": 480, "w": 129, "ch": 9, "s": [ "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i" ] }, "Oranienbaum": { "x": 597, "y": 480, "w": 137, "ch": 9, "s": ["400"] }, "Orbit": { "x": 748, "y": 480, "w": 80, "ch": 9, "s": ["400"] }, "Orbitron": { "x": 842, "y": 480, "w": 117, "ch": 9, "s": ["400", "500", "600", "700", "800", "900"] }, "Oregano": { "x": 973, "y": 480, "w": 78, "ch": 9, "s": ["400", "400i"] }, "Orelega One": { "x": 0, "y": 520, "w": 138, "ch": 9, "s": ["400"] }, "Orienta": { "x": 152, "y": 520, "w": 94, "ch": 9, "s": ["400"] }, "Original Surfer": { "x": 260, "y": 520, "w": 175, "ch": 9, "s": ["400"] }, "Oswald": { "x": 449, "y": 520, "w": 70, "ch": 9, "s": ["200", "300", "400", "500", "600", "700"] }, "Outfit": { "x": 533, "y": 520, "w": 71, "ch": 9, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Over the Rainbow": { "x": 618, "y": 520, "w": 189, "ch": 9, "s": ["400"] }, "Overlock": { "x": 821, "y": 520, "w": 95, "ch": 9, "s": ["400", "400i", "700", "700i", "900", "900i"] }, "Overlock SC": { "x": 930, "y": 520, "w": 139, "ch": 9, "s": ["400"] }, "Overpass": { "x": 1083, "y": 520, "w": 109, "ch": 9, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Overpass Mono": { "x": 0, "y": 560, "w": 201, "ch": 9, "s": ["300", "400", "500", "600", "700"] }, "Ovo": { "x": 215, "y": 560, "w": 52, "ch": 9, "s": ["400"] }, "Oxanium": { "x": 281, "y": 560, "w": 106, "ch": 9, "s": ["200", "300", "400", "500", "600", "700", "800"] }, "Oxygen": { "x": 401, "y": 560, "w": 92, "ch": 9, "s": ["300", "400", "700"] }, "Oxygen Mono": { "x": 507, "y": 560, "w": 167, "ch": 9, "s": ["400"] }, "Pacifico": { "x": 688, "y": 560, "w": 87, "ch": 9, "s": ["400"] }, "Padauk": { "x": 789, "y": 560, "w": 79, "ch": 9, "s": ["400", "700"] }, "Padyakke Expanded One": { "x": 0, "y": 600, "w": 415, "ch": 9, "s": ["400"] }, "Palanquin": { "x": 429, "y": 600, "w": 111, "ch": 9, "s": ["100", "200", "300", "400", "500", "600", "700"] }, "Palanquin Dark": { "x": 554, "y": 600, "w": 172, "ch": 9, "s": ["400", "500", "600", "700"] }, "Palette Mosaic": { "x": 740, "y": 600, "w": 212, "ch": 9, "s": ["400"] }, "Pangolin": { "x": 966, "y": 600, "w": 93, "ch": 9, "s": ["400"] }, "Paprika": { "x": 1073, "y": 600, "w": 105, "ch": 9, "s": ["400"] }, "Parastoo": { "x": 0, "y": 640, "w": 82, "ch": 9, "s": ["400", "500", "600", "700"] }, "Parisienne": { "x": 96, "y": 640, "w": 103, "ch": 9, "s": ["400"] }, "Parkinsans": { "x": 213, "y": 640, "w": 139, "ch": 9, "s": ["300", "400", "500", "600", "700", "800"] }, "Passero One": { "x": 366, "y": 640, "w": 125, "ch": 9, "s": ["400"] }, "Passion One": { "x": 505, "y": 640, "w": 114, "ch": 9, "s": ["400", "700", "900"] }, "Passions Conflict": { "x": 633, "y": 640, "w": 109, "ch": 9, "s": ["400"] }, "Pathway Extreme": { "x": 756, "y": 640, "w": 214, "ch": 9, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Pathway Gothic One": { "x": 984, "y": 640, "w": 154, "ch": 9, "s": ["400"] }, "Patrick Hand": { "x": 0, "y": 680, "w": 118, "ch": 9, "s": ["400"] }, "Patrick Hand SC": { "x": 132, "y": 680, "w": 154, "ch": 9, "s": ["400"] }, "Pattaya": { "x": 300, "y": 680, "w": 85, "ch": 9, "s": ["400"] }, "Patua One": { "x": 399, "y": 680, "w": 117, "ch": 9, "s": ["400"] }, "Pavanam": { "x": 530, "y": 680, "w": 97, "ch": 9, "s": ["400"] }, "Paytone One": { "x": 641, "y": 680, "w": 164, "ch": 9, "s": ["400"] }, "Peddana": { "x": 819, "y": 680, "w": 67, "ch": 9, "s": ["400"] }, "Peralta": { "x": 900, "y": 680, "w": 108, "ch": 9, "s": ["400"] }, "Permanent Marker": { "x": 0, "y": 720, "w": 234, "ch": 9, "s": ["400"] }, "Petemoss": { "x": 248, "y": 720, "w": 60, "ch": 9, "s": ["400"] }, "Petit Formal Script": { "x": 322, "y": 720, "w": 255, "ch": 9, "s": ["400"] }, "Petrona": { "x": 591, "y": 720, "w": 89, "ch": 9, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Philosopher": { "x": 694, "y": 720, "w": 129, "ch": 9, "s": ["400", "400i", "700", "700i"] }, "Phudu": { "x": 837, "y": 720, "w": 76, "ch": 9, "s": ["300", "400", "500", "600", "700", "800", "900"] }, "Piazzolla": { "x": 927, "y": 720, "w": 107, "ch": 9, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Piedra": { "x": 1048, "y": 720, "w": 68, "ch": 9, "s": ["400"] }, "Pinyon Script": { "x": 0, "y": 760, "w": 128, "ch": 9, "s": ["400"] }, "Pirata One": { "x": 142, "y": 760, "w": 92, "ch": 9, "s": ["400"] }, "Pixelify Sans": { "x": 248, "y": 760, "w": 153, "ch": 9, "s": ["400", "500", "600", "700"] }, "Plaster": { "x": 415, "y": 760, "w": 126, "ch": 9, "s": ["400"] }, "Platypi": { "x": 555, "y": 760, "w": 90, "ch": 9, "s": [ "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i" ] }, "Play": { "x": 659, "y": 760, "w": 52, "ch": 9, "s": ["400", "700"] }, "Playball": { "x": 725, "y": 760, "w": 83, "ch": 9, "s": ["400"] }, "Playfair": { "x": 822, "y": 760, "w": 90, "ch": 9, "s": [ "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Playfair Display": { "x": 926, "y": 760, "w": 176, "ch": 9, "s": [ "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Playfair Display SC": { "x": 0, "y": 0, "w": 247, "ch": 10, "s": ["400", "400i", "700", "700i", "900", "900i"] }, "Playpen Sans": { "x": 261, "y": 0, "w": 161, "ch": 10, "s": ["100", "200", "300", "400", "500", "600", "700", "800"] }, "Playpen Sans Arabic": { "x": 436, "y": 0, "w": 247, "ch": 10, "s": ["100", "200", "300", "400", "500", "600", "700", "800"] }, "Playpen Sans Deva": { "x": 697, "y": 0, "w": 227, "ch": 10, "s": ["100", "200", "300", "400", "500", "600", "700", "800"] }, "Playpen Sans Hebrew": { "x": 938, "y": 0, "w": 255, "ch": 10, "s": ["100", "200", "300", "400", "500", "600", "700", "800"] }, "Playpen Sans Thai": { "x": 0, "y": 40, "w": 220, "ch": 10, "s": ["100", "200", "300", "400", "500", "600", "700", "800"] }, "Playwrite AR": { "x": 234, "y": 40, "w": 199, "ch": 10, "s": ["100", "200", "300", "400"] }, "Playwrite AR Guides": { "x": 447, "y": 40, "w": 294, "ch": 10, "s": ["400"] }, "Playwrite AT": { "x": 755, "y": 40, "w": 180, "ch": 10, "s": ["100", "100i", "200", "200i", "300", "300i", "400", "400i"] }, "Playwrite AT Guides": { "x": 0, "y": 80, "w": 273, "ch": 10, "s": ["400", "400i"] }, "Playwrite AU NSW": { "x": 287, "y": 80, "w": 257, "ch": 10, "s": ["100", "200", "300", "400"] }, "Playwrite AU NSW Guides": { "x": 558, "y": 80, "w": 350, "ch": 10, "s": ["400"] }, "Playwrite AU QLD": { "x": 922, "y": 80, "w": 249, "ch": 10, "s": ["100", "200", "300", "400"] }, "Playwrite AU QLD Guides": { "x": 0, "y": 120, "w": 344, "ch": 10, "s": ["400"] }, "Playwrite AU SA": { "x": 358, "y": 120, "w": 217, "ch": 10, "s": ["100", "200", "300", "400"] }, "Playwrite AU SA Guides": { "x": 589, "y": 120, "w": 312, "ch": 10, "s": ["400"] }, "Playwrite AU TAS": { "x": 915, "y": 120, "w": 235, "ch": 10, "s": ["100", "200", "300", "400"] }, "Playwrite AU TAS Guides": { "x": 0, "y": 160, "w": 329, "ch": 10, "s": ["400"] }, "Playwrite AU VIC": { "x": 343, "y": 160, "w": 234, "ch": 10, "s": ["100", "200", "300", "400"] }, "Playwrite AU VIC Guides": { "x": 591, "y": 160, "w": 327, "ch": 10, "s": ["400"] }, "Playwrite BE VLG": { "x": 932, "y": 160, "w": 266, "ch": 10, "s": ["100", "200", "300", "400"] }, "Playwrite BE VLG Guides": { "x": 0, "y": 200, "w": 359, "ch": 10, "s": ["400"] }, "Playwrite BE WAL": { "x": 373, "y": 200, "w": 319, "ch": 10, "s": ["100", "200", "300", "400"] }, "Playwrite BE WAL Guides": { "x": 706, "y": 200, "w": 420, "ch": 10, "s": ["400"] }, "Playwrite BR": { "x": 0, "y": 240, "w": 201, "ch": 10, "s": ["100", "200", "300", "400"] }, "Playwrite BR Guides": { "x": 215, "y": 240, "w": 298, "ch": 10, "s": ["400"] }, "Playwrite CA": { "x": 527, "y": 240, "w": 191, "ch": 10, "s": ["100", "200", "300", "400"] }, "Playwrite CA Guides": { "x": 732, "y": 240, "w": 288, "ch": 10, "s": ["400"] }, "Playwrite CL": { "x": 0, "y": 280, "w": 194, "ch": 10, "s": ["100", "200", "300", "400"] }, "Playwrite CL Guides": { "x": 208, "y": 280, "w": 290, "ch": 10, "s": ["400"] }, "Playwrite CO": { "x": 512, "y": 280, "w": 189, "ch": 10, "s": ["100", "200", "300", "400"] }, "Playwrite CO Guides": { "x": 715, "y": 280, "w": 287, "ch": 10, "s": ["400"] }, "Playwrite CU": { "x": 0, "y": 320, "w": 192, "ch": 10, "s": ["100", "200", "300", "400"] }, "Playwrite CU Guides": { "x": 206, "y": 320, "w": 289, "ch": 10, "s": ["400"] }, "Playwrite CZ": { "x": 509, "y": 320, "w": 192, "ch": 10, "s": ["100", "200", "300", "400"] }, "Playwrite CZ Guides": { "x": 715, "y": 320, "w": 287, "ch": 10, "s": ["400"] }, "Playwrite DE Grund": { "x": 0, "y": 360, "w": 251, "ch": 10, "s": ["100", "200", "300", "400"] }, "Playwrite DE Grund Guides": { "x": 265, "y": 360, "w": 342, "ch": 10, "s": ["400"] }, "Playwrite DE LA": { "x": 621, "y": 360, "w": 232, "ch": 10, "s": ["100", "200", "300", "400"] }, "Playwrite DE LA Guides": { "x": 867, "y": 360, "w": 325, "ch": 10, "s": ["400"] }, "Playwrite DE SAS": { "x": 0, "y": 400, "w": 241, "ch": 10, "s": ["100", "200", "300", "400"] }, "Playwrite DE SAS Guides": { "x": 255, "y": 400, "w": 333, "ch": 10, "s": ["400"] }, "Playwrite DE VA": { "x": 602, "y": 400, "w": 225, "ch": 10, "s": ["100", "200", "300", "400"] }, "Playwrite DE VA Guides": { "x": 841, "y": 400, "w": 320, "ch": 10, "s": ["400"] }, "Playwrite DK Loopet": { "x": 0, "y": 440, "w": 254, "ch": 10, "s": ["100", "200", "300", "400"] }, "Playwrite DK Loopet Guides": { "x": 268, "y": 440, "w": 345, "ch": 10, "s": ["400"] }, "Playwrite DK Uloopet": { "x": 627, "y": 440, "w": 262, "ch": 10, "s": ["100", "200", "300", "400"] }, "Playwrite DK Uloopet Guides": { "x": 0, "y": 480, "w": 353, "ch": 10, "s": ["400"] }, "Playwrite ES": { "x": 367, "y": 480, "w": 176, "ch": 10, "s": ["100", "200", "300", "400"] }, "Playwrite ES Deco": { "x": 557, "y": 480, "w": 256, "ch": 10, "s": ["100", "200", "300", "400"] }, "Playwrite ES Deco Guides": { "x": 827, "y": 480, "w": 350, "ch": 10, "s": ["400"] }, "Playwrite ES Guides": { "x": 0, "y": 520, "w": 268, "ch": 10, "s": ["400"] }, "Playwrite FR Moderne": { "x": 282, "y": 520, "w": 287, "ch": 10, "s": ["100", "200", "300", "400"] }, "Playwrite FR Moderne Guides": { "x": 583, "y": 520, "w": 383, "ch": 10, "s": ["400"] }, "Playwrite FR Trad": { "x": 0, "y": 560, "w": 281, "ch": 10, "s": ["100", "200", "300", "400"] }, "Playwrite FR Trad Guides": { "x": 295, "y": 560, "w": 384, "ch": 10, "s": ["400"] }, "Playwrite GB J": { "x": 693, "y": 560, "w": 191, "ch": 10, "s": ["100", "100i", "200", "200i", "300", "300i", "400", "400i"] }, "Playwrite GB J Guides": { "x": 898, "y": 560, "w": 284, "ch": 10, "s": ["400", "400i"] }, "Playwrite GB S": { "x": 0, "y": 600, "w": 190, "ch": 10, "s": ["100", "100i", "200", "200i", "300", "300i", "400", "400i"] }, "Playwrite GB S Guides": { "x": 204, "y": 600, "w": 282, "ch": 10, "s": ["400", "400i"] }, "Playwrite HR": { "x": 500, "y": 600, "w": 191, "ch": 10, "s": ["100", "200", "300", "400"] }, "Playwrite HR Guides": { "x": 705, "y": 600, "w": 286, "ch": 10, "s": ["400"] }, "Playwrite HR Lijeva": { "x": 0, "y": 640, "w": 276, "ch": 10, "s": ["100", "200", "300", "400"] }, "Playwrite HR Lijeva Guides": { "x": 290, "y": 640, "w": 370, "ch": 10, "s": ["400"] }, "Playwrite HU": { "x": 674, "y": 640, "w": 190, "ch": 10, "s": ["100", "200", "300", "400"] }, "Playwrite HU Guides": { "x": 878, "y": 640, "w": 285, "ch": 10, "s": ["400"] }, "Playwrite ID": { "x": 0, "y": 680, "w": 194, "ch": 10, "s": ["100", "200", "300", "400"] }, "Playwrite ID Guides": { "x": 208, "y": 680, "w": 296, "ch": 10, "s": ["400"] }, "Playwrite IE": { "x": 518, "y": 680, "w": 175, "ch": 10, "s": ["100", "200", "300", "400"] }, "Playwrite IE Guides": { "x": 707, "y": 680, "w": 271, "ch": 10, "s": ["400"] }, "Playwrite IN": { "x": 992, "y": 680, "w": 189, "ch": 10, "s": ["100", "200", "300", "400"] }, "Playwrite IN Guides": { "x": 0, "y": 720, "w": 285, "ch": 10, "s": ["400"] }, "Playwrite IS": { "x": 299, "y": 720, "w": 156, "ch": 10, "s": ["100", "200", "300", "400"] }, "Playwrite IS Guides": { "x": 469, "y": 720, "w": 249, "ch": 10, "s": ["400"] }, "Playwrite IT Moderna": { "x": 732, "y": 720, "w": 273, "ch": 10, "s": ["100", "200", "300", "400"] }, "Playwrite IT Moderna Guides": { "x": 0, "y": 760, "w": 365, "ch": 10, "s": ["400"] }, "Playwrite IT Trad": { "x": 379, "y": 760, "w": 243, "ch": 10, "s": ["100", "200", "300", "400"] }, "Playwrite IT Trad Guides": { "x": 636, "y": 760, "w": 336, "ch": 10, "s": ["400"] }, "Playwrite MX": { "x": 986, "y": 760, "w": 200, "ch": 10, "s": ["100", "200", "300", "400"] }, "Playwrite MX Guides": { "x": 0, "y": 0, "w": 297, "ch": 11, "s": ["400"] }, "Playwrite NG Modern": { "x": 311, "y": 0, "w": 279, "ch": 11, "s": ["100", "200", "300", "400"] }, "Playwrite NG Modern Guides": { "x": 604, "y": 0, "w": 373, "ch": 11, "s": ["400"] }, "Playwrite NL": { "x": 991, "y": 0, "w": 207, "ch": 11, "s": ["100", "200", "300", "400"] }, "Playwrite NL Guides": { "x": 0, "y": 40, "w": 306, "ch": 11, "s": ["400"] }, "Playwrite NO": { "x": 320, "y": 40, "w": 189, "ch": 11, "s": ["100", "200", "300", "400"] }, "Playwrite NO Guides": { "x": 523, "y": 40, "w": 285, "ch": 11, "s": ["400"] }, "Playwrite NZ": { "x": 822, "y": 40, "w": 164, "ch": 11, "s": ["100", "200", "300", "400"] }, "Playwrite NZ Basic": { "x": 0, "y": 80, "w": 231, "ch": 11, "s": ["100", "200", "300", "400"] }, "Playwrite NZ Basic Guides": { "x": 245, "y": 80, "w": 319, "ch": 11, "s": ["400"] }, "Playwrite NZ Guides": { "x": 578, "y": 80, "w": 256, "ch": 11, "s": ["400"] }, "Playwrite PE": { "x": 848, "y": 80, "w": 185, "ch": 11, "s": ["100", "200", "300", "400"] }, "Playwrite PE Guides": { "x": 0, "y": 120, "w": 282, "ch": 11, "s": ["400"] }, "Playwrite PL": { "x": 296, "y": 120, "w": 178, "ch": 11, "s": ["100", "200", "300", "400"] }, "Playwrite PL Guides": { "x": 488, "y": 120, "w": 272, "ch": 11, "s": ["400"] }, "Playwrite PT": { "x": 774, "y": 120, "w": 188, "ch": 11, "s": ["100", "200", "300", "400"] }, "Playwrite PT Guides": { "x": 0, "y": 160, "w": 284, "ch": 11, "s": ["400"] }, "Playwrite RO": { "x": 298, "y": 160, "w": 197, "ch": 11, "s": ["100", "200", "300", "400"] }, "Playwrite RO Guides": { "x": 509, "y": 160, "w": 286, "ch": 11, "s": ["400"] }, "Playwrite SK": { "x": 809, "y": 160, "w": 195, "ch": 11, "s": ["100", "200", "300", "400"] }, "Playwrite SK Guides": { "x": 0, "y": 200, "w": 289, "ch": 11, "s": ["400"] }, "Playwrite TZ": { "x": 303, "y": 200, "w": 182, "ch": 11, "s": ["100", "200", "300", "400"] }, "Playwrite TZ Guides": { "x": 499, "y": 200, "w": 271, "ch": 11, "s": ["400"] }, "Playwrite US Modern": { "x": 784, "y": 200, "w": 277, "ch": 11, "s": ["100", "200", "300", "400"] }, "Playwrite US Modern Guides": { "x": 0, "y": 240, "w": 369, "ch": 11, "s": ["400"] }, "Playwrite US Trad": { "x": 383, "y": 240, "w": 258, "ch": 11, "s": ["100", "200", "300", "400"] }, "Playwrite US Trad Guides": { "x": 655, "y": 240, "w": 355, "ch": 11, "s": ["400"] }, "Playwrite VN": { "x": 0, "y": 280, "w": 212, "ch": 11, "s": ["100", "200", "300", "400"] }, "Playwrite VN Guides": { "x": 226, "y": 280, "w": 311, "ch": 11, "s": ["400"] }, "Playwrite ZA": { "x": 551, "y": 280, "w": 182, "ch": 11, "s": ["100", "200", "300", "400"] }, "Playwrite ZA Guides": { "x": 747, "y": 280, "w": 276, "ch": 11, "s": ["400"] }, "Plus Jakarta Sans": { "x": 0, "y": 320, "w": 199, "ch": 11, "s": [ "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i" ] }, "Pochaevsk": { "x": 213, "y": 320, "w": 111, "ch": 11, "s": ["400"] }, "Podkova": { "x": 338, "y": 320, "w": 97, "ch": 11, "s": ["400", "500", "600", "700", "800"] }, "Poetsen One": { "x": 449, "y": 320, "w": 148, "ch": 11, "s": ["400"] }, "Poiret One": { "x": 611, "y": 320, "w": 114, "ch": 11, "s": ["400"] }, "Poller One": { "x": 739, "y": 320, "w": 164, "ch": 11, "s": ["400"] }, "Poltawski Nowy": { "x": 917, "y": 320, "w": 182, "ch": 11, "s": ["400", "400i", "500", "500i", "600", "600i", "700", "700i"] }, "Poly": { "x": 1113, "y": 320, "w": 54, "ch": 11, "s": ["400", "400i"] }, "Pompiere": { "x": 0, "y": 360, "w": 75, "ch": 11, "s": ["400"] }, "Ponnala": { "x": 89, "y": 360, "w": 70, "ch": 11, "s": ["400"] }, "Ponomar": { "x": 173, "y": 360, "w": 97, "ch": 11, "s": ["400"] }, "Pontano Sans": { "x": 284, "y": 360, "w": 149, "ch": 11, "s": ["300", "400", "500", "600", "700"] }, "Poor Story": { "x": 447, "y": 360, "w": 99, "ch": 11, "s": ["400"] }, "Poppins": { "x": 560, "y": 360, "w": 104, "ch": 11, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Port Lligat Sans": { "x": 678, "y": 360, "w": 157, "ch": 11, "s": ["400"] }, "Port Lligat Slab": { "x": 849, "y": 360, "w": 153, "ch": 11, "s": ["400"] }, "Potta One": { "x": 1016, "y": 360, "w": 143, "ch": 11, "s": ["400"] }, "Pragati Narrow": { "x": 0, "y": 400, "w": 129, "ch": 11, "s": ["400", "700"] }, "Praise": { "x": 143, "y": 400, "w": 59, "ch": 11, "s": ["400"] }, "Prata": { "x": 216, "y": 400, "w": 71, "ch": 11, "s": ["400"] }, "Preahvihear": { "x": 301, "y": 400, "w": 161, "ch": 11, "s": ["400"] }, "Press Start 2P": { "x": 476, "y": 400, "w": 344, "ch": 11, "s": ["400"] }, "Pridi": { "x": 834, "y": 400, "w": 60, "ch": 11, "s": ["200", "300", "400", "500", "600", "700"] }, "Princess Sofia": { "x": 908, "y": 400, "w": 131, "ch": 11, "s": ["400"] }, "Prociono": { "x": 1053, "y": 400, "w": 101, "ch": 11, "s": ["400"] }, "Prompt": { "x": 0, "y": 440, "w": 92, "ch": 11, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Prosto One": { "x": 106, "y": 440, "w": 159, "ch": 11, "s": ["400"] }, "Protest Guerrilla": { "x": 279, "y": 440, "w": 177, "ch": 11, "s": ["400"] }, "Protest Revolution": { "x": 470, "y": 440, "w": 200, "ch": 11, "s": ["400"] }, "Protest Riot": { "x": 684, "y": 440, "w": 131, "ch": 11, "s": ["400"] }, "Protest Strike": { "x": 829, "y": 440, "w": 151, "ch": 11, "s": ["400"] }, "Proza Libre": { "x": 994, "y": 440, "w": 137, "ch": 11, "s": [ "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i" ] }, "PT Mono": { "x": 0, "y": 480, "w": 109, "ch": 11, "s": ["400"] }, "PT Sans": { "x": 123, "y": 480, "w": 88, "ch": 11, "s": ["400", "400i", "700", "700i"] }, "PT Sans Caption": { "x": 225, "y": 480, "w": 197, "ch": 11, "s": ["400", "700"] }, "PT Sans Narrow": { "x": 436, "y": 480, "w": 138, "ch": 11, "s": ["400", "700"] }, "PT Serif": { "x": 588, "y": 480, "w": 93, "ch": 11, "s": ["400", "400i", "700", "700i"] }, "PT Serif Caption": { "x": 695, "y": 480, "w": 205, "ch": 11, "s": ["400", "400i"] }, "Public Sans": { "x": 914, "y": 480, "w": 139, "ch": 11, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Puppies Play": { "x": 1067, "y": 480, "w": 85, "ch": 11, "s": ["400"] }, "Puritan": { "x": 0, "y": 520, "w": 78, "ch": 11, "s": ["400", "400i", "700", "700i"] }, "Purple Purse": { "x": 92, "y": 520, "w": 150, "ch": 11, "s": ["400"] }, "Pushster": { "x": 256, "y": 520, "w": 89, "ch": 11, "s": ["400"] }, "Qahiri": { "x": 359, "y": 520, "w": 51, "ch": 11, "s": ["400"] }, "Quando": { "x": 424, "y": 520, "w": 107, "ch": 11, "s": ["400"] }, "Quantico": { "x": 545, "y": 520, "w": 109, "ch": 11, "s": ["400", "400i", "700", "700i"] }, "Quattrocento": { "x": 668, "y": 520, "w": 153, "ch": 11, "s": ["400", "700"] }, "Quattrocento Sans": { "x": 835, "y": 520, "w": 201, "ch": 11, "s": ["400", "400i", "700", "700i"] }, "Questrial": { "x": 1050, "y": 520, "w": 105, "ch": 11, "s": ["400"] }, "Quicksand": { "x": 0, "y": 560, "w": 125, "ch": 11, "s": ["300", "400", "500", "600", "700"] }, "Quintessential": { "x": 139, "y": 560, "w": 144, "ch": 11, "s": ["400"] }, "Qwigley": { "x": 297, "y": 560, "w": 59, "ch": 11, "s": ["400"] }, "Qwitcher Grypen": { "x": 370, "y": 560, "w": 111, "ch": 11, "s": ["400", "700"] }, "Racing Sans One": { "x": 495, "y": 560, "w": 190, "ch": 11, "s": ["400"] }, "Radio Canada": { "x": 699, "y": 560, "w": 161, "ch": 11, "s": [ "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i" ] }, "Radio Canada Big": { "x": 874, "y": 560, "w": 196, "ch": 11, "s": ["400", "400i", "500", "500i", "600", "600i", "700", "700i"] }, "Radley": { "x": 1084, "y": 560, "w": 81, "ch": 11, "s": ["400", "400i"] }, "Rajdhani": { "x": 0, "y": 600, "w": 92, "ch": 11, "s": ["300", "400", "500", "600", "700"] }, "Rakkas": { "x": 106, "y": 600, "w": 79, "ch": 11, "s": ["400"] }, "Raleway": { "x": 199, "y": 600, "w": 102, "ch": 11, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Raleway Dots": { "x": 315, "y": 600, "w": 156, "ch": 11, "s": ["400"] }, "Ramabhadra": { "x": 485, "y": 600, "w": 154, "ch": 11, "s": ["400"] }, "Ramaraja": { "x": 653, "y": 600, "w": 96, "ch": 11, "s": ["400"] }, "Rambla": { "x": 763, "y": 600, "w": 83, "ch": 11, "s": ["400", "400i", "700", "700i"] }, "Rammetto One": { "x": 860, "y": 600, "w": 234, "ch": 11, "s": ["400"] }, "Rampart One": { "x": 0, "y": 640, "w": 170, "ch": 11, "s": ["400"] }, "Ranchers": { "x": 184, "y": 640, "w": 97, "ch": 11, "s": ["400"] }, "Rancho": { "x": 295, "y": 640, "w": 62, "ch": 11, "s": ["400"] }, "Ranga": { "x": 371, "y": 640, "w": 52, "ch": 11, "s": ["400", "700"] }, "Rasa": { "x": 437, "y": 640, "w": 53, "ch": 11, "s": [ "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i" ] }, "Rationale": { "x": 504, "y": 640, "w": 90, "ch": 11, "s": ["400"] }, "Ravi Prakash": { "x": 608, "y": 640, "w": 132, "ch": 11, "s": ["400"] }, "Readex Pro": { "x": 754, "y": 640, "w": 140, "ch": 11, "s": ["200", "300", "400", "500", "600", "700"] }, "Recursive": { "x": 908, "y": 640, "w": 123, "ch": 11, "s": ["300", "400", "500", "600", "700", "800", "900"] }, "Red Hat Display": { "x": 0, "y": 680, "w": 175, "ch": 11, "s": [ "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Red Hat Mono": { "x": 189, "y": 680, "w": 181, "ch": 11, "s": [ "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i" ] }, "Red Hat Text": { "x": 384, "y": 680, "w": 146, "ch": 11, "s": [ "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i" ] }, "Red Rose": { "x": 544, "y": 680, "w": 117, "ch": 11, "s": ["300", "400", "500", "600", "700"] }, "Redacted": { "x": 675, "y": 680, "w": 80, "ch": 11, "s": ["400"] }, "Redacted Script": { "x": 769, "y": 680, "w": 201, "ch": 11, "s": ["300", "400", "700"] }, "Reddit Mono": { "x": 984, "y": 680, "w": 157, "ch": 11, "s": ["200", "300", "400", "500", "600", "700", "800", "900"] }, "Reddit Sans": { "x": 0, "y": 720, "w": 134, "ch": 11, "s": [ "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Reddit Sans Condensed": { "x": 148, "y": 720, "w": 232, "ch": 11, "s": ["200", "300", "400", "500", "600", "700", "800", "900"] }, "Redressed": { "x": 394, "y": 720, "w": 96, "ch": 11, "s": ["400"] }, "Reem Kufi": { "x": 504, "y": 720, "w": 118, "ch": 11, "s": ["400", "500", "600", "700"] }, "Reem Kufi Fun": { "x": 636, "y": 720, "w": 161, "ch": 11, "s": ["400", "500", "600", "700"] }, "Reem Kufi Ink": { "x": 811, "y": 720, "w": 154, "ch": 11, "s": ["400"] }, "Reenie Beanie": { "x": 979, "y": 720, "w": 122, "ch": 11, "s": ["400"] }, "Reggae One": { "x": 0, "y": 760, "w": 161, "ch": 11, "s": ["400"] }, "REM": { "x": 175, "y": 760, "w": 63, "ch": 11, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Rethink Sans": { "x": 252, "y": 760, "w": 150, "ch": 11, "s": [ "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i" ] }, "Revalia": { "x": 416, "y": 760, "w": 121, "ch": 11, "s": ["400"] }, "Rhodium Libre": { "x": 551, "y": 760, "w": 187, "ch": 11, "s": ["400"] }, "Ribeye": { "x": 752, "y": 760, "w": 92, "ch": 11, "s": ["400"] }, "Ribeye Marrow": { "x": 858, "y": 760, "w": 201, "ch": 11, "s": ["400"] }, "Righteous": { "x": 1073, "y": 760, "w": 121, "ch": 11, "s": ["400"] }, "Risque": { "x": 0, "y": 0, "w": 77, "ch": 12, "s": ["400"] }, "Road Rage": { "x": 91, "y": 0, "w": 72, "ch": 12, "s": ["400"] }, "Roboto": { "x": 177, "y": 0, "w": 85, "ch": 12, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Roboto Condensed": { "x": 276, "y": 0, "w": 188, "ch": 12, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Roboto Flex": { "x": 478, "y": 0, "w": 134, "ch": 12, "s": ["400"] }, "Roboto Mono": { "x": 626, "y": 0, "w": 167, "ch": 12, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i" ] }, "Roboto Serif": { "x": 807, "y": 0, "w": 162, "ch": 12, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Roboto Slab": { "x": 983, "y": 0, "w": 141, "ch": 12, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Rochester": { "x": 0, "y": 40, "w": 85, "ch": 12, "s": ["400"] }, "Rock 3D": { "x": 99, "y": 40, "w": 137, "ch": 12, "s": ["400"] }, "Rock Salt": { "x": 250, "y": 40, "w": 153, "ch": 12, "s": ["400"] }, "RocknRoll One": { "x": 417, "y": 40, "w": 190, "ch": 12, "s": ["400"] }, "Rokkitt": { "x": 621, "y": 40, "w": 79, "ch": 12, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Romanesco": { "x": 714, "y": 40, "w": 73, "ch": 12, "s": ["400"] }, "Ropa Sans": { "x": 801, "y": 40, "w": 102, "ch": 12, "s": ["400", "400i"] }, "Rosario": { "x": 917, "y": 40, "w": 84, "ch": 12, "s": [ "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i" ] }, "Rosarivo": { "x": 1015, "y": 40, "w": 106, "ch": 12, "s": ["400", "400i"] }, "Rouge Script": { "x": 0, "y": 80, "w": 104, "ch": 12, "s": ["400"] }, "Rowdies": { "x": 118, "y": 80, "w": 106, "ch": 12, "s": ["300", "400", "700"] }, "Rozha One": { "x": 238, "y": 80, "w": 126, "ch": 12, "s": ["400"] }, "Rubik": { "x": 378, "y": 80, "w": 71, "ch": 12, "s": [ "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Rubik 80s Fade": { "x": 463, "y": 80, "w": 199, "ch": 12, "s": ["400"] }, "Rubik Beastly": { "x": 676, "y": 80, "w": 183, "ch": 12, "s": ["400"] }, "Rubik Broken Fax": { "x": 873, "y": 80, "w": 227, "ch": 12, "s": ["400"] }, "Rubik Bubbles": { "x": 0, "y": 120, "w": 188, "ch": 12, "s": ["400"] }, "Rubik Burned": { "x": 202, "y": 120, "w": 179, "ch": 12, "s": ["400"] }, "Rubik Dirt": { "x": 395, "y": 120, "w": 137, "ch": 12, "s": ["400"] }, "Rubik Distressed": { "x": 546, "y": 120, "w": 225, "ch": 12, "s": ["400"] }, "Rubik Doodle Shadow": { "x": 785, "y": 120, "w": 277, "ch": 12, "s": ["400"] }, "Rubik Doodle Triangles": { "x": 0, "y": 160, "w": 296, "ch": 12, "s": ["400"] }, "Rubik Gemstones": { "x": 310, "y": 160, "w": 229, "ch": 12, "s": ["400"] }, "Rubik Glitch": { "x": 553, "y": 160, "w": 165, "ch": 12, "s": ["400"] }, "Rubik Glitch Pop": { "x": 732, "y": 160, "w": 217, "ch": 12, "s": ["400"] }, "Rubik Iso": { "x": 963, "y": 160, "w": 124, "ch": 12, "s": ["400"] }, "Rubik Lines": { "x": 0, "y": 200, "w": 155, "ch": 12, "s": ["400"] }, "Rubik Maps": { "x": 169, "y": 200, "w": 151, "ch": 12, "s": ["400"] }, "Rubik Marker Hatch": { "x": 334, "y": 200, "w": 257, "ch": 12, "s": ["400"] }, "Rubik Maze": { "x": 605, "y": 200, "w": 150, "ch": 12, "s": ["400"] }, "Rubik Microbe": { "x": 769, "y": 200, "w": 188, "ch": 12, "s": ["400"] }, "Rubik Mono One": { "x": 0, "y": 240, "w": 294, "ch": 12, "s": ["400"] }, "Rubik Moonrocks": { "x": 308, "y": 240, "w": 225, "ch": 12, "s": ["400"] }, "Rubik Pixels": { "x": 547, "y": 240, "w": 162, "ch": 12, "s": ["400"] }, "Rubik Puddles": { "x": 723, "y": 240, "w": 187, "ch": 12, "s": ["400"] }, "Rubik Scribble": { "x": 924, "y": 240, "w": 192, "ch": 12, "s": ["400"] }, "Rubik Spray Paint": { "x": 0, "y": 280, "w": 233, "ch": 12, "s": ["400"] }, "Rubik Storm": { "x": 247, "y": 280, "w": 165, "ch": 12, "s": ["400"] }, "Rubik Vinyl": { "x": 426, "y": 280, "w": 150, "ch": 12, "s": ["400"] }, "Rubik Wet Paint": { "x": 590, "y": 280, "w": 207, "ch": 12, "s": ["400"] }, "Ruda": { "x": 811, "y": 280, "w": 66, "ch": 12, "s": ["400", "500", "600", "700", "800", "900"] }, "Rufina": { "x": 891, "y": 280, "w": 82, "ch": 12, "s": ["400", "700"] }, "Ruge Boogie": { "x": 987, "y": 280, "w": 99, "ch": 12, "s": ["400"] }, "Ruluko": { "x": 1100, "y": 280, "w": 78, "ch": 12, "s": ["400"] }, "Rum Raisin": { "x": 0, "y": 320, "w": 100, "ch": 12, "s": ["400"] }, "Ruslan Display": { "x": 114, "y": 320, "w": 220, "ch": 12, "s": ["400"] }, "Russo One": { "x": 348, "y": 320, "w": 138, "ch": 12, "s": ["400"] }, "Ruthie": { "x": 500, "y": 320, "w": 64, "ch": 12, "s": ["400"] }, "Ruwudu": { "x": 578, "y": 320, "w": 94, "ch": 12, "s": ["400", "500", "600", "700"] }, "Rye": { "x": 686, "y": 320, "w": 55, "ch": 12, "s": ["400"] }, "Sacramento": { "x": 755, "y": 320, "w": 104, "ch": 12, "s": ["400"] }, "Sahitya": { "x": 873, "y": 320, "w": 81, "ch": 12, "s": ["400", "700"] }, "Sail": { "x": 968, "y": 320, "w": 50, "ch": 12, "s": ["400"] }, "Saira": { "x": 1032, "y": 320, "w": 66, "ch": 12, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Saira Condensed": { "x": 0, "y": 360, "w": 149, "ch": 12, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Saira Extra Condensed": { "x": 163, "y": 360, "w": 167, "ch": 12, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Saira Semi Condensed": { "x": 344, "y": 360, "w": 227, "ch": 12, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Saira Stencil One": { "x": 585, "y": 360, "w": 201, "ch": 12, "s": ["400"] }, "Salsa": { "x": 800, "y": 360, "w": 66, "ch": 12, "s": ["400"] }, "Sanchez": { "x": 880, "y": 360, "w": 105, "ch": 12, "s": ["400", "400i"] }, "Sancreek": { "x": 999, "y": 360, "w": 111, "ch": 12, "s": ["400"] }, "Sankofa Display": { "x": 0, "y": 400, "w": 163, "ch": 12, "s": ["400"] }, "Sansation": { "x": 177, "y": 400, "w": 119, "ch": 12, "s": ["300", "300i", "400", "400i", "700", "700i"] }, "Sansita": { "x": 310, "y": 400, "w": 80, "ch": 12, "s": ["400", "400i", "700", "700i", "800", "800i", "900", "900i"] }, "Sansita Swashed": { "x": 404, "y": 400, "w": 177, "ch": 12, "s": ["300", "400", "500", "600", "700", "800", "900"] }, "Sarabun": { "x": 595, "y": 400, "w": 94, "ch": 12, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i" ] }, "Sarala": { "x": 703, "y": 400, "w": 74, "ch": 12, "s": ["400", "700"] }, "Sarina": { "x": 791, "y": 400, "w": 112, "ch": 12, "s": ["400"] }, "Sarpanch": { "x": 917, "y": 400, "w": 122, "ch": 12, "s": ["400", "500", "600", "700", "800", "900"] }, "Sassy Frass": { "x": 1053, "y": 400, "w": 78, "ch": 12, "s": ["400"] }, "Satisfy": { "x": 0, "y": 440, "w": 70, "ch": 12, "s": ["400"] }, "Savate": { "x": 84, "y": 440, "w": 81, "ch": 12, "s": [ "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Sawarabi Gothic": { "x": 179, "y": 440, "w": 186, "ch": 12, "s": ["400"] }, "Sawarabi Mincho": { "x": 379, "y": 440, "w": 204, "ch": 12, "s": ["400"] }, "Scada": { "x": 597, "y": 440, "w": 69, "ch": 12, "s": ["400", "400i", "700", "700i"] }, "Scheherazade New": { "x": 680, "y": 440, "w": 175, "ch": 12, "s": ["400", "500", "600", "700"] }, "Schibsted Grotesk": { "x": 869, "y": 440, "w": 212, "ch": 12, "s": [ "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Schoolbell": { "x": 1095, "y": 440, "w": 102, "ch": 12, "s": ["400"] }, "Science Gothic": { "x": 0, "y": 480, "w": 212, "ch": 12, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Scope One": { "x": 226, "y": 480, "w": 120, "ch": 12, "s": ["400"] }, "Seaweed Script": { "x": 360, "y": 480, "w": 137, "ch": 12, "s": ["400"] }, "Secular One": { "x": 511, "y": 480, "w": 144, "ch": 12, "s": ["400"] }, "Sedan": { "x": 669, "y": 480, "w": 72, "ch": 12, "s": ["400", "400i"] }, "Sedan SC": { "x": 755, "y": 480, "w": 111, "ch": 12, "s": ["400"] }, "Sedgwick Ave": { "x": 880, "y": 480, "w": 140, "ch": 12, "s": ["400"] }, "Sedgwick Ave Display": { "x": 0, "y": 520, "w": 224, "ch": 12, "s": ["400"] }, "Sekuya": { "x": 238, "y": 520, "w": 138, "ch": 12, "s": ["400"] }, "Sen": { "x": 390, "y": 520, "w": 48, "ch": 12, "s": ["400", "500", "600", "700", "800"] }, "Send Flowers": { "x": 452, "y": 520, "w": 129, "ch": 12, "s": ["400"] }, "Sevillana": { "x": 595, "y": 520, "w": 96, "ch": 12, "s": ["400"] }, "Seymour One": { "x": 705, "y": 520, "w": 241, "ch": 12, "s": ["400"] }, "Shadows Into Light": { "x": 960, "y": 520, "w": 172, "ch": 12, "s": ["400"] }, "Shadows Into Light Two": { "x": 0, "y": 560, "w": 234, "ch": 12, "s": ["400"] }, "Shafarik": { "x": 248, "y": 560, "w": 89, "ch": 12, "s": ["400"] }, "Shalimar": { "x": 351, "y": 560, "w": 59, "ch": 12, "s": ["400"] }, "Shantell Sans": { "x": 424, "y": 560, "w": 166, "ch": 12, "s": [ "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i" ] }, "Shanti": { "x": 604, "y": 560, "w": 75, "ch": 12, "s": ["400"] }, "Share": { "x": 693, "y": 560, "w": 62, "ch": 12, "s": ["400", "400i", "700", "700i"] }, "Share Tech": { "x": 769, "y": 560, "w": 109, "ch": 12, "s": ["400"] }, "Share Tech Mono": { "x": 892, "y": 560, "w": 203, "ch": 12, "s": ["400"] }, "Shippori Antique": { "x": 0, "y": 600, "w": 206, "ch": 12, "s": ["400"] }, "Shippori Antique B1": { "x": 220, "y": 600, "w": 241, "ch": 12, "s": ["400"] }, "Shippori Mincho": { "x": 475, "y": 600, "w": 202, "ch": 12, "s": ["400", "500", "600", "700", "800"] }, "Shippori Mincho B1": { "x": 691, "y": 600, "w": 237, "ch": 12, "s": ["400", "500", "600", "700", "800"] }, "Shizuru": { "x": 942, "y": 600, "w": 82, "ch": 12, "s": ["400"] }, "Shojumaru": { "x": 0, "y": 640, "w": 184, "ch": 12, "s": ["400"] }, "Short Stack": { "x": 198, "y": 640, "w": 168, "ch": 12, "s": ["400"] }, "Shrikhand": { "x": 380, "y": 640, "w": 147, "ch": 12, "s": ["400"] }, "Sigmar": { "x": 541, "y": 640, "w": 99, "ch": 12, "s": ["400"] }, "Sigmar One": { "x": 654, "y": 640, "w": 172, "ch": 12, "s": ["400"] }, "Signika": { "x": 840, "y": 640, "w": 85, "ch": 12, "s": ["300", "400", "500", "600", "700"] }, "Signika Negative": { "x": 939, "y": 640, "w": 180, "ch": 12, "s": ["300", "400", "500", "600", "700"] }, "Silkscreen": { "x": 0, "y": 680, "w": 173, "ch": 12, "s": ["400", "700"] }, "Simonetta": { "x": 187, "y": 680, "w": 108, "ch": 12, "s": ["400", "400i", "900", "900i"] }, "Sintony": { "x": 309, "y": 680, "w": 97, "ch": 12, "s": ["400", "700"] }, "Sirin Stencil": { "x": 420, "y": 680, "w": 122, "ch": 12, "s": ["400"] }, "Sirivennela": { "x": 556, "y": 680, "w": 98, "ch": 12, "s": ["400"] }, "Six Caps": { "x": 668, "y": 680, "w": 47, "ch": 12, "s": ["400"] }, "Sixtyfour": { "x": 729, "y": 680, "w": 224, "ch": 12, "s": ["400"] }, "Sixtyfour Convergence": { "x": 0, "y": 720, "w": 512, "ch": 12, "s": ["400"] }, "Skranji": { "x": 526, "y": 720, "w": 78, "ch": 12, "s": ["400", "700"] }, "Slabo 13px": { "x": 618, "y": 720, "w": 127, "ch": 12, "s": ["400"] }, "Slabo 27px": { "x": 759, "y": 720, "w": 111, "ch": 12, "s": ["400"] }, "Slackey": { "x": 884, "y": 720, "w": 124, "ch": 12, "s": ["400"] }, "Slackside One": { "x": 1022, "y": 720, "w": 137, "ch": 12, "s": ["400"] }, "Smokum": { "x": 0, "y": 760, "w": 76, "ch": 12, "s": ["400"] }, "Smooch": { "x": 90, "y": 760, "w": 73, "ch": 12, "s": ["400"] }, "Smooch Sans": { "x": 177, "y": 760, "w": 110, "ch": 12, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Smythe": { "x": 301, "y": 760, "w": 64, "ch": 12, "s": ["400"] }, "SN Pro": { "x": 379, "y": 760, "w": 84, "ch": 12, "s": [ "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Sniglet": { "x": 477, "y": 760, "w": 82, "ch": 12, "s": ["400", "800"] }, "Snippet": { "x": 573, "y": 760, "w": 89, "ch": 12, "s": ["400"] }, "Snowburst One": { "x": 676, "y": 760, "w": 197, "ch": 12, "s": ["400"] }, "Sofadi One": { "x": 887, "y": 760, "w": 137, "ch": 12, "s": ["400"] }, "Sofia": { "x": 1038, "y": 760, "w": 61, "ch": 12, "s": ["400"] }, "Sofia Sans": { "x": 0, "y": 0, "w": 115, "ch": 13, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Sofia Sans Condensed": { "x": 129, "y": 0, "w": 184, "ch": 13, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Sofia Sans Extra Condensed": { "x": 327, "y": 0, "w": 188, "ch": 13, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Sofia Sans Semi Condensed": { "x": 529, "y": 0, "w": 280, "ch": 13, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Solitreo": { "x": 823, "y": 0, "w": 83, "ch": 13, "s": ["400"] }, "Solway": { "x": 920, "y": 0, "w": 90, "ch": 13, "s": ["300", "400", "500", "700", "800"] }, "Sometype Mono": { "x": 0, "y": 40, "w": 189, "ch": 13, "s": ["400", "400i", "500", "500i", "600", "600i", "700", "700i"] }, "Sono": { "x": 203, "y": 40, "w": 68, "ch": 13, "s": ["200", "300", "400", "500", "600", "700", "800"] }, "Sonsie One": { "x": 285, "y": 40, "w": 194, "ch": 13, "s": ["400"] }, "Sora": { "x": 493, "y": 40, "w": 65, "ch": 13, "s": ["100", "200", "300", "400", "500", "600", "700", "800"] }, "Sorts Mill Goudy": { "x": 572, "y": 40, "w": 187, "ch": 13, "s": ["400", "400i"] }, "Sour Gummy": { "x": 773, "y": 40, "w": 149, "ch": 13, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Source Code Pro": { "x": 936, "y": 40, "w": 224, "ch": 13, "s": [ "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Source Sans 3": { "x": 0, "y": 80, "w": 147, "ch": 13, "s": [ "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Source Sans Pro": { "x": 161, "y": 80, "w": 170, "ch": 13, "s": [ "200", "200i", "300", "300i", "400", "400i", "600", "600i", "700", "700i", "900", "900i" ] }, "Source Serif 4": { "x": 345, "y": 80, "w": 164, "ch": 13, "s": [ "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Source Serif Pro": { "x": 523, "y": 80, "w": 180, "ch": 13, "s": [ "200", "200i", "300", "300i", "400", "400i", "600", "600i", "700", "700i", "900", "900i" ] }, "Space Grotesk": { "x": 717, "y": 80, "w": 176, "ch": 13, "s": ["300", "400", "500", "600", "700"] }, "Space Mono": { "x": 907, "y": 80, "w": 155, "ch": 13, "s": ["400", "400i", "700", "700i"] }, "Special Elite": { "x": 0, "y": 120, "w": 177, "ch": 13, "s": ["400"] }, "Special Gothic": { "x": 191, "y": 120, "w": 156, "ch": 13, "s": ["400", "500", "600", "700"] }, "Special Gothic Condensed One": { "x": 361, "y": 120, "w": 247, "ch": 13, "s": ["400"] }, "Special Gothic Expanded One": { "x": 622, "y": 120, "w": 424, "ch": 13, "s": ["400"] }, "Spectral": { "x": 1060, "y": 120, "w": 95, "ch": 13, "s": [ "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i" ] }, "Spectral SC": { "x": 0, "y": 160, "w": 159, "ch": 13, "s": [ "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i" ] }, "Spicy Rice": { "x": 173, "y": 160, "w": 111, "ch": 13, "s": ["400"] }, "Spinnaker": { "x": 298, "y": 160, "w": 128, "ch": 13, "s": ["400"] }, "Spirax": { "x": 440, "y": 160, "w": 73, "ch": 13, "s": ["400"] }, "Splash": { "x": 527, "y": 160, "w": 88, "ch": 13, "s": ["400"] }, "Spline Sans": { "x": 629, "y": 160, "w": 133, "ch": 13, "s": ["300", "400", "500", "600", "700"] }, "Spline Sans Mono": { "x": 776, "y": 160, "w": 239, "ch": 13, "s": [ "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i" ] }, "Squada One": { "x": 1029, "y": 160, "w": 111, "ch": 13, "s": ["400"] }, "Square Peg": { "x": 0, "y": 200, "w": 73, "ch": 13, "s": ["400"] }, "Sree Krushnadevaraya": { "x": 87, "y": 200, "w": 222, "ch": 13, "s": ["400"] }, "Sriracha": { "x": 323, "y": 200, "w": 99, "ch": 13, "s": ["400"] }, "Srisakdi": { "x": 436, "y": 200, "w": 90, "ch": 13, "s": ["400", "700"] }, "Staatliches": { "x": 540, "y": 200, "w": 114, "ch": 13, "s": ["400"] }, "Stack Sans Headline": { "x": 668, "y": 200, "w": 235, "ch": 13, "s": ["200", "300", "400", "500", "600", "700"] }, "Stack Sans Notch": { "x": 917, "y": 200, "w": 205, "ch": 13, "s": ["200", "300", "400", "500", "600", "700"] }, "Stack Sans Text": { "x": 0, "y": 240, "w": 195, "ch": 13, "s": ["200", "300", "400", "500", "600", "700"] }, "Stalemate": { "x": 209, "y": 240, "w": 62, "ch": 13, "s": ["400"] }, "Stalinist One": { "x": 285, "y": 240, "w": 280, "ch": 13, "s": ["400"] }, "Stardos Stencil": { "x": 579, "y": 240, "w": 168, "ch": 13, "s": ["400", "700"] }, "Stick": { "x": 761, "y": 240, "w": 65, "ch": 13, "s": ["400"] }, "Stick No Bills": { "x": 840, "y": 240, "w": 123, "ch": 13, "s": ["200", "300", "400", "500", "600", "700", "800"] }, "Stint Ultra Condensed": { "x": 977, "y": 240, "w": 139, "ch": 13, "s": ["400"] }, "Stint Ultra Expanded": { "x": 0, "y": 280, "w": 310, "ch": 13, "s": ["400"] }, "STIX Two Text": { "x": 324, "y": 280, "w": 156, "ch": 13, "s": ["400", "400i", "500", "500i", "600", "600i", "700", "700i"] }, "Stoke": { "x": 494, "y": 280, "w": 84, "ch": 13, "s": ["300", "400"] }, "Story Script": { "x": 592, "y": 280, "w": 113, "ch": 13, "s": ["400"] }, "Strait": { "x": 719, "y": 280, "w": 62, "ch": 13, "s": ["400"] }, "Style Script": { "x": 795, "y": 280, "w": 97, "ch": 13, "s": ["400"] }, "Sue Ellen Francisco": { "x": 906, "y": 280, "w": 137, "ch": 13, "s": ["400"] }, "Suez One": { "x": 1057, "y": 280, "w": 113, "ch": 13, "s": ["400"] }, "Sulphur Point": { "x": 0, "y": 320, "w": 144, "ch": 13, "s": ["300", "400", "700"] }, "Sumana": { "x": 158, "y": 320, "w": 92, "ch": 13, "s": ["400", "700"] }, "Sunshiney": { "x": 264, "y": 320, "w": 94, "ch": 13, "s": ["400"] }, "Supermercado One": { "x": 372, "y": 320, "w": 196, "ch": 13, "s": ["400"] }, "Sura": { "x": 582, "y": 320, "w": 58, "ch": 13, "s": ["400", "700"] }, "Suranna": { "x": 654, "y": 320, "w": 86, "ch": 13, "s": ["400"] }, "Suravaram": { "x": 754, "y": 320, "w": 104, "ch": 13, "s": ["400"] }, "SUSE": { "x": 872, "y": 320, "w": 65, "ch": 13, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "SUSE Mono": { "x": 951, "y": 320, "w": 138, "ch": 13, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i" ] }, "Suwannaphum": { "x": 0, "y": 360, "w": 184, "ch": 13, "s": ["100", "300", "400", "700", "900"] }, "Swanky and Moo Moo": { "x": 198, "y": 360, "w": 203, "ch": 13, "s": ["400"] }, "Syncopate": { "x": 415, "y": 360, "w": 195, "ch": 13, "s": ["400", "700"] }, "Syne": { "x": 624, "y": 360, "w": 60, "ch": 13, "s": ["400", "500", "600", "700", "800"] }, "Syne Mono": { "x": 698, "y": 360, "w": 127, "ch": 13, "s": ["400"] }, "Syne Tactile": { "x": 839, "y": 360, "w": 119, "ch": 13, "s": ["400"] }, "Tac One": { "x": 972, "y": 360, "w": 73, "ch": 13, "s": ["400"] }, "Tagesschrift": { "x": 0, "y": 400, "w": 142, "ch": 13, "s": ["400"] }, "Tai Heritage Pro": { "x": 156, "y": 400, "w": 171, "ch": 13, "s": ["400", "700"] }, "Tajawal": { "x": 341, "y": 400, "w": 82, "ch": 13, "s": ["200", "300", "400", "500", "700", "800", "900"] }, "Tangerine": { "x": 437, "y": 400, "w": 67, "ch": 13, "s": ["400", "700"] }, "Tapestry": { "x": 518, "y": 400, "w": 100, "ch": 13, "s": ["400"] }, "Taprom": { "x": 632, "y": 400, "w": 70, "ch": 13, "s": ["400"] }, "TASA Explorer": { "x": 716, "y": 400, "w": 161, "ch": 13, "s": ["400", "500", "600", "700", "800"] }, "TASA Orbiter": { "x": 891, "y": 400, "w": 153, "ch": 13, "s": ["400", "500", "600", "700", "800"] }, "Tauri": { "x": 1058, "y": 400, "w": 66, "ch": 13, "s": ["400"] }, "Taviraj": { "x": 0, "y": 440, "w": 84, "ch": 13, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Teachers": { "x": 98, "y": 440, "w": 100, "ch": 13, "s": [ "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i" ] }, "Teko": { "x": 212, "y": 440, "w": 42, "ch": 13, "s": ["300", "400", "500", "600", "700"] }, "Tektur": { "x": 268, "y": 440, "w": 86, "ch": 13, "s": ["400", "500", "600", "700", "800", "900"] }, "Telex": { "x": 368, "y": 440, "w": 64, "ch": 13, "s": ["400"] }, "Tenali Ramakrishna": { "x": 446, "y": 440, "w": 170, "ch": 13, "s": ["400"] }, "Tenor Sans": { "x": 630, "y": 440, "w": 134, "ch": 13, "s": ["400"] }, "Text Me One": { "x": 778, "y": 440, "w": 140, "ch": 13, "s": ["400"] }, "Texturina": { "x": 932, "y": 440, "w": 113, "ch": 13, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Thasadith": { "x": 1059, "y": 440, "w": 101, "ch": 13, "s": ["400", "400i", "700", "700i"] }, "The Girl Next Door": { "x": 0, "y": 480, "w": 215, "ch": 13, "s": ["400"] }, "The Nautigal": { "x": 229, "y": 480, "w": 102, "ch": 13, "s": ["400", "700"] }, "Tienne": { "x": 345, "y": 480, "w": 92, "ch": 13, "s": ["400", "700", "900"] }, "TikTok Sans": { "x": 451, "y": 480, "w": 141, "ch": 13, "s": ["300", "400", "500", "600", "700", "800", "900"] }, "Tillana": { "x": 606, "y": 480, "w": 75, "ch": 13, "s": ["400", "500", "600", "700", "800"] }, "Tilt Neon": { "x": 695, "y": 480, "w": 101, "ch": 13, "s": ["400"] }, "Tilt Prism": { "x": 810, "y": 480, "w": 121, "ch": 13, "s": ["400"] }, "Tilt Warp": { "x": 945, "y": 480, "w": 109, "ch": 13, "s": ["400"] }, "Timmana": { "x": 1068, "y": 480, "w": 90, "ch": 13, "s": ["400"] }, "Tinos": { "x": 0, "y": 520, "w": 62, "ch": 13, "s": ["400", "400i", "700", "700i"] }, "Tiny5": { "x": 76, "y": 520, "w": 62, "ch": 13, "s": ["400"] }, "Tiro Bangla": { "x": 152, "y": 520, "w": 134, "ch": 13, "s": ["400", "400i"] }, "Tiro Devanagari Hindi": { "x": 300, "y": 520, "w": 251, "ch": 13, "s": ["400", "400i"] }, "Tiro Devanagari Marathi": { "x": 565, "y": 520, "w": 274, "ch": 13, "s": ["400", "400i"] }, "Tiro Devanagari Sanskrit": { "x": 853, "y": 520, "w": 276, "ch": 13, "s": ["400", "400i"] }, "Tiro Gurmukhi": { "x": 0, "y": 560, "w": 173, "ch": 13, "s": ["400", "400i"] }, "Tiro Kannada": { "x": 187, "y": 560, "w": 156, "ch": 13, "s": ["400", "400i"] }, "Tiro Tamil": { "x": 357, "y": 560, "w": 122, "ch": 13, "s": ["400", "400i"] }, "Tiro Telugu": { "x": 493, "y": 560, "w": 134, "ch": 13, "s": ["400", "400i"] }, "Tirra": { "x": 641, "y": 560, "w": 57, "ch": 13, "s": ["400", "500", "600", "700", "800", "900"] }, "Titan One": { "x": 712, "y": 560, "w": 131, "ch": 13, "s": ["400"] }, "Titillium Web": { "x": 857, "y": 560, "w": 140, "ch": 13, "s": [ "200", "200i", "300", "300i", "400", "400i", "600", "600i", "700", "700i", "900", "900i" ] }, "Tomorrow": { "x": 1011, "y": 560, "w": 126, "ch": 13, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Tourney": { "x": 0, "y": 600, "w": 107, "ch": 13, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Trade Winds": { "x": 121, "y": 600, "w": 160, "ch": 13, "s": ["400"] }, "Train One": { "x": 295, "y": 600, "w": 132, "ch": 13, "s": ["400"] }, "Triodion": { "x": 441, "y": 600, "w": 94, "ch": 13, "s": ["400"] }, "Trirong": { "x": 549, "y": 600, "w": 92, "ch": 13, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Trispace": { "x": 655, "y": 600, "w": 128, "ch": 13, "s": ["100", "200", "300", "400", "500", "600", "700", "800"] }, "Trocchi": { "x": 797, "y": 600, "w": 100, "ch": 13, "s": ["400"] }, "Trochut": { "x": 911, "y": 600, "w": 75, "ch": 13, "s": ["400", "400i", "700", "700i"] }, "Truculenta": { "x": 1000, "y": 600, "w": 100, "ch": 13, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Trykker": { "x": 0, "y": 640, "w": 98, "ch": 13, "s": ["400"] }, "Tsukimi Rounded": { "x": 112, "y": 640, "w": 209, "ch": 13, "s": ["300", "400", "500", "600", "700"] }, "Tuffy": { "x": 335, "y": 640, "w": 63, "ch": 13, "s": ["400", "400i", "700", "700i"] }, "Tulpen One": { "x": 412, "y": 640, "w": 59, "ch": 13, "s": ["400"] }, "Turret Road": { "x": 485, "y": 640, "w": 142, "ch": 13, "s": ["200", "300", "400", "500", "700", "800"] }, "Twinkle Star": { "x": 641, "y": 640, "w": 137, "ch": 13, "s": ["400"] }, "Ubuntu": { "x": 792, "y": 640, "w": 90, "ch": 13, "s": ["300", "300i", "400", "400i", "500", "500i", "700", "700i"] }, "Ubuntu Condensed": { "x": 896, "y": 640, "w": 177, "ch": 13, "s": ["400"] }, "Ubuntu Mono": { "x": 0, "y": 680, "w": 156, "ch": 13, "s": ["400", "400i", "700", "700i"] }, "Ubuntu Sans": { "x": 170, "y": 680, "w": 144, "ch": 13, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i" ] }, "Ubuntu Sans Mono": { "x": 328, "y": 680, "w": 224, "ch": 13, "s": ["400", "400i", "500", "500i", "600", "600i", "700", "700i"] }, "Uchen": { "x": 566, "y": 680, "w": 75, "ch": 13, "s": ["400"] }, "Ultra": { "x": 655, "y": 680, "w": 85, "ch": 13, "s": ["400"] }, "Unbounded": { "x": 754, "y": 680, "w": 174, "ch": 13, "s": ["200", "300", "400", "500", "600", "700", "800", "900"] }, "Uncial Antiqua": { "x": 942, "y": 680, "w": 211, "ch": 13, "s": ["400"] }, "Underdog": { "x": 0, "y": 720, "w": 113, "ch": 13, "s": ["400"] }, "Unica One": { "x": 127, "y": 720, "w": 106, "ch": 13, "s": ["400"] }, "UnifrakturMaguntia": { "x": 247, "y": 720, "w": 204, "ch": 13, "s": ["400"] }, "Unkempt": { "x": 465, "y": 720, "w": 99, "ch": 13, "s": ["400", "700"] }, "Unlock": { "x": 578, "y": 720, "w": 93, "ch": 13, "s": ["400"] }, "Unna": { "x": 685, "y": 720, "w": 60, "ch": 13, "s": ["400", "400i", "700", "700i"] }, "UoqMunThenKhung": { "x": 759, "y": 720, "w": 245, "ch": 13, "s": ["400"] }, "Updock": { "x": 1018, "y": 720, "w": 58, "ch": 13, "s": ["400"] }, "Urbanist": { "x": 1090, "y": 720, "w": 97, "ch": 13, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Vampiro One": { "x": 0, "y": 760, "w": 165, "ch": 13, "s": ["400"] }, "Varela": { "x": 179, "y": 760, "w": 81, "ch": 13, "s": ["400"] }, "Varela Round": { "x": 274, "y": 760, "w": 161, "ch": 13, "s": ["400"] }, "Varta": { "x": 449, "y": 760, "w": 61, "ch": 13, "s": ["300", "400", "500", "600", "700"] }, "Vast Shadow": { "x": 524, "y": 760, "w": 227, "ch": 13, "s": ["400"] }, "Vazirmatn": { "x": 765, "y": 760, "w": 117, "ch": 13, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Vend Sans": { "x": 896, "y": 760, "w": 121, "ch": 13, "s": [ "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i" ] }, "Vesper Libre": { "x": 1031, "y": 760, "w": 136, "ch": 13, "s": ["400", "500", "700", "900"] }, "Viaoda Libre": { "x": 0, "y": 0, "w": 126, "ch": 14, "s": ["400"] }, "Vibes": { "x": 140, "y": 0, "w": 53, "ch": 14, "s": ["400"] }, "Vibur": { "x": 207, "y": 0, "w": 61, "ch": 14, "s": ["400"] }, "Victor Mono": { "x": 282, "y": 0, "w": 167, "ch": 14, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i" ] }, "Vidaloka": { "x": 463, "y": 0, "w": 99, "ch": 14, "s": ["400"] }, "Viga": { "x": 576, "y": 0, "w": 55, "ch": 14, "s": ["400"] }, "Vina Sans": { "x": 645, "y": 0, "w": 86, "ch": 14, "s": ["400"] }, "Voces": { "x": 745, "y": 0, "w": 72, "ch": 14, "s": ["400"] }, "Volkhov": { "x": 831, "y": 0, "w": 104, "ch": 14, "s": ["400", "400i", "700", "700i"] }, "Vollkorn": { "x": 949, "y": 0, "w": 99, "ch": 14, "s": [ "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Vollkorn SC": { "x": 0, "y": 40, "w": 166, "ch": 14, "s": ["400", "600", "700", "900"] }, "Voltaire": { "x": 180, "y": 40, "w": 73, "ch": 14, "s": ["400"] }, "VT323": { "x": 267, "y": 40, "w": 56, "ch": 14, "s": ["400"] }, "Vujahday Script": { "x": 337, "y": 40, "w": 151, "ch": 14, "s": ["400"] }, "Waiting for the Sunrise": { "x": 502, "y": 40, "w": 230, "ch": 14, "s": ["400"] }, "Wallpoet": { "x": 746, "y": 40, "w": 127, "ch": 14, "s": ["400"] }, "Walter Turncoat": { "x": 887, "y": 40, "w": 206, "ch": 14, "s": ["400"] }, "Warnes": { "x": 0, "y": 80, "w": 120, "ch": 14, "s": ["400"] }, "Water Brush": { "x": 134, "y": 80, "w": 116, "ch": 14, "s": ["400"] }, "Waterfall": { "x": 264, "y": 80, "w": 77, "ch": 14, "s": ["400"] }, "Wavefont": { "x": 355, "y": 80, "w": 28, "ch": 14, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "WDXL Lubrifont JP N": { "x": 397, "y": 80, "w": 185, "ch": 14, "s": ["400"] }, "WDXL Lubrifont SC": { "x": 596, "y": 80, "w": 170, "ch": 14, "s": ["400"] }, "WDXL Lubrifont TC": { "x": 780, "y": 80, "w": 168, "ch": 14, "s": ["400"] }, "Wellfleet": { "x": 962, "y": 80, "w": 117, "ch": 14, "s": ["400"] }, "Wendy One": { "x": 0, "y": 120, "w": 142, "ch": 14, "s": ["400"] }, "Whisper": { "x": 156, "y": 120, "w": 76, "ch": 14, "s": ["400"] }, "WindSong": { "x": 246, "y": 120, "w": 133, "ch": 14, "s": ["400", "500"] }, "Winky Rough": { "x": 393, "y": 120, "w": 139, "ch": 14, "s": [ "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Winky Sans": { "x": 546, "y": 120, "w": 120, "ch": 14, "s": [ "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Wire One": { "x": 680, "y": 120, "w": 57, "ch": 14, "s": ["400"] }, "Wittgenstein": { "x": 751, "y": 120, "w": 151, "ch": 14, "s": [ "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Wix Madefor Display": { "x": 916, "y": 120, "w": 238, "ch": 14, "s": ["400", "500", "600", "700", "800"] }, "Wix Madefor Text": { "x": 0, "y": 160, "w": 202, "ch": 14, "s": [ "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i" ] }, "Work Sans": { "x": 216, "y": 160, "w": 132, "ch": 14, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Workbench": { "x": 362, "y": 160, "w": 116, "ch": 14, "s": ["400"] }, "Xanh Mono": { "x": 492, "y": 160, "w": 116, "ch": 14, "s": ["400", "400i"] }, "Yaldevi": { "x": 622, "y": 160, "w": 83, "ch": 14, "s": ["200", "300", "400", "500", "600", "700"] }, "Yanone Kaffeesatz": { "x": 719, "y": 160, "w": 142, "ch": 14, "s": ["200", "300", "400", "500", "600", "700"] }, "Yantramanav": { "x": 875, "y": 160, "w": 138, "ch": 14, "s": ["100", "300", "400", "500", "700", "900"] }, "Yarndings 12": { "x": 0, "y": 200, "w": 226, "ch": 14, "s": ["400"] }, "Yarndings 12 Charted": { "x": 240, "y": 200, "w": 365, "ch": 14, "s": ["400"] }, "Yarndings 20": { "x": 619, "y": 200, "w": 219, "ch": 14, "s": ["400"] }, "Yarndings 20 Charted": { "x": 0, "y": 240, "w": 357, "ch": 14, "s": ["400"] }, "Yatra One": { "x": 371, "y": 240, "w": 127, "ch": 14, "s": ["400"] }, "Yellowtail": { "x": 512, "y": 240, "w": 93, "ch": 14, "s": ["400"] }, "Yeon Sung": { "x": 619, "y": 240, "w": 107, "ch": 14, "s": ["400"] }, "Yeseva One": { "x": 740, "y": 240, "w": 144, "ch": 14, "s": ["400"] }, "Yesteryear": { "x": 898, "y": 240, "w": 94, "ch": 14, "s": ["400"] }, "Yomogi": { "x": 1006, "y": 240, "w": 80, "ch": 14, "s": ["400"] }, "Young Serif": { "x": 0, "y": 280, "w": 149, "ch": 14, "s": ["400"] }, "Yrsa": { "x": 163, "y": 280, "w": 49, "ch": 14, "s": [ "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i" ] }, "Ysabeau": { "x": 226, "y": 280, "w": 88, "ch": 14, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Ysabeau Infant": { "x": 328, "y": 280, "w": 161, "ch": 14, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Ysabeau Office": { "x": 503, "y": 280, "w": 156, "ch": 14, "s": [ "100", "100i", "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Ysabeau SC": { "x": 673, "y": 280, "w": 130, "ch": 14, "s": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, "Yuji Boku": { "x": 817, "y": 280, "w": 135, "ch": 14, "s": ["400"] }, "Yuji Hentaigana Akari": { "x": 0, "y": 320, "w": 335, "ch": 14, "s": ["400"] }, "Yuji Hentaigana Akebono": { "x": 349, "y": 320, "w": 374, "ch": 14, "s": ["400"] }, "Yuji Mai": { "x": 737, "y": 320, "w": 117, "ch": 14, "s": ["400"] }, "Yuji Syuku": { "x": 868, "y": 320, "w": 144, "ch": 14, "s": ["400"] }, "Yusei Magic": { "x": 1026, "y": 320, "w": 138, "ch": 14, "s": ["400"] }, "Zain": { "x": 0, "y": 360, "w": 54, "ch": 14, "s": [ "200", "200i", "300", "300i", "400", "400i", "700", "700i", "800", "800i", "900", "900i" ] }, "Zalando Sans": { "x": 68, "y": 360, "w": 161, "ch": 14, "s": [ "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Zalando Sans Expanded": { "x": 243, "y": 360, "w": 335, "ch": 14, "s": [ "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "Zalando Sans SemiExpanded": { "x": 592, "y": 360, "w": 361, "ch": 14, "s": [ "200", "200i", "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i", "800", "800i", "900", "900i" ] }, "ZCOOL KuaiLe": { "x": 967, "y": 360, "w": 180, "ch": 14, "s": ["400"] }, "ZCOOL QingKe HuangYou": { "x": 0, "y": 400, "w": 222, "ch": 14, "s": ["400"] }, "ZCOOL XiaoWei": { "x": 236, "y": 400, "w": 173, "ch": 14, "s": ["400"] }, "Zen Antique": { "x": 423, "y": 400, "w": 149, "ch": 14, "s": ["400"] }, "Zen Antique Soft": { "x": 586, "y": 400, "w": 204, "ch": 14, "s": ["400"] }, "Zen Dots": { "x": 804, "y": 400, "w": 147, "ch": 14, "s": ["400"] }, "Zen Kaku Gothic Antique": { "x": 0, "y": 440, "w": 267, "ch": 14, "s": ["300", "400", "500", "700", "900"] }, "Zen Kaku Gothic New": { "x": 281, "y": 440, "w": 231, "ch": 14, "s": ["300", "400", "500", "700", "900"] }, "Zen Kurenaido": { "x": 526, "y": 440, "w": 145, "ch": 14, "s": ["400"] }, "Zen Loop": { "x": 685, "y": 440, "w": 65, "ch": 14, "s": ["400", "400i"] }, "Zen Maru Gothic": { "x": 764, "y": 440, "w": 181, "ch": 14, "s": ["300", "400", "500", "700", "900"] }, "Zen Old Mincho": { "x": 959, "y": 440, "w": 173, "ch": 14, "s": ["400", "500", "600", "700", "900"] }, "Zen Tokyo Zoo": { "x": 0, "y": 480, "w": 154, "ch": 14, "s": ["400"] }, "Zeyada": { "x": 168, "y": 480, "w": 70, "ch": 14, "s": ["400"] }, "Zhi Mang Xing": { "x": 252, "y": 480, "w": 121, "ch": 14, "s": ["400"] }, "Zilla Slab": { "x": 387, "y": 480, "w": 102, "ch": 14, "s": [ "300", "300i", "400", "400i", "500", "500i", "600", "600i", "700", "700i" ] }, "Zilla Slab Highlight": { "x": 503, "y": 480, "w": 207, "ch": 14, "s": ["400", "700"] } } } ================================================ FILE: apps/web/public/manifest.json ================================================ { "name": "OpenCut", "description": "A simple but powerful video editor that gets the job done. In your browser.", "display": "standalone", "start_url": "/", "icons": [ { "src": "/icons/android-icon-36x36.png", "sizes": "36x36", "type": "image\/png", "density": "0.75" }, { "src": "/icons/android-icon-48x48.png", "sizes": "48x48", "type": "image\/png", "density": "1.0" }, { "src": "/icons/android-icon-72x72.png", "sizes": "72x72", "type": "image\/png", "density": "1.5" }, { "src": "/icons/android-icon-96x96.png", "sizes": "96x96", "type": "image\/png", "density": "2.0" }, { "src": "/icons/android-icon-144x144.png", "sizes": "144x144", "type": "image\/png", "density": "3.0" }, { "src": "/icons/android-icon-192x192.png", "sizes": "192x192", "type": "image\/png", "density": "4.0" } ] } ================================================ FILE: apps/web/scripts/generate-font-sprites.ts ================================================ /** * Generates font sprite atlas for the font picker. * * Downloads Google Fonts from Fontsource, renders each font name as a sprite, * packs them into chunk images, and outputs a JSON atlas + AVIF images. * * Run: npx tsx scripts/generate-font-sprites.ts * Deps: @napi-rs/canvas sharp (install as devDependencies) */ import { createCanvas, GlobalFonts } from "@napi-rs/canvas"; import sharp from "sharp"; import { mkdir, writeFile, readFile } from "node:fs/promises"; import { existsSync } from "node:fs"; import { join, dirname } from "node:path"; import { fileURLToPath } from "node:url"; const __dirname = dirname(fileURLToPath(import.meta.url)); const FONT_SIZE = 24; const ROW_HEIGHT = 40; const CANVAS_WIDTH = 1200; const MAX_CHUNK_HEIGHT = 800; const PADDING_X = 14; const WIDTH_BUFFER = 8; const CONCURRENT_DOWNLOADS = 30; const OUTPUT_DIR = join(__dirname, "..", "public", "fonts"); const CACHE_DIR = join(__dirname, "..", ".font-cache"); const FONTSOURCE_API = "https://api.fontsource.org/v1/fonts"; interface FontsourceFont { id: string; family: string; subsets: string[]; weights: number[]; styles: string[]; category: string; type: string; } interface MeasuredFont { family: string; width: number; styles: string[]; } interface PackedFont { family: string; x: number; y: number; w: number; } interface AtlasEntry { x: number; y: number; w: number; ch: number; s: string[]; } async function fetchFontList(): Promise { console.log("Fetching font list from Fontsource..."); const response = await fetch(FONTSOURCE_API); if (!response.ok) throw new Error(`Fontsource API error: ${response.status}`); const data: FontsourceFont[] = await response.json(); const filtered = data.filter( (font) => font.type === "google" && font.subsets.includes("latin") && font.weights.includes(400), ); console.log( ` ${filtered.length} Google fonts with Latin subset + 400 weight`, ); return filtered; } async function downloadFont({ id }: { id: string }): Promise { const cachePath = join(CACHE_DIR, `${id}.woff2`); if (existsSync(cachePath)) { return readFile(cachePath); } // Fontsource CDN serves woff2 for all Google fonts const url = `https://cdn.jsdelivr.net/fontsource/fonts/${id}@latest/latin-400-normal.woff2`; try { const response = await fetch(url); if (!response.ok) throw new Error(`HTTP ${response.status}`); const buffer = Buffer.from(await response.arrayBuffer()); await writeFile(cachePath, buffer); return buffer; } catch { return null; } } async function downloadAllFonts({ fonts, concurrency, }: { fonts: FontsourceFont[]; concurrency: number; }): Promise> { console.log( `Downloading ${fonts.length} font files (${concurrency} concurrent)...`, ); const results = new Map(); let nextIndex = 0; let completed = 0; async function worker() { while (nextIndex < fonts.length) { const index = nextIndex++; const font = fonts[index]; const buffer = await downloadFont({ id: font.id }); if (buffer) results.set(font.id, buffer); completed++; if (completed % 100 === 0 || completed === fonts.length) { process.stdout.write(`\r ${completed}/${fonts.length}`); } } } await Promise.all(Array.from({ length: concurrency }, () => worker())); console.log(`\n Downloaded ${results.size}/${fonts.length} fonts`); return results; } function measureFonts({ fonts, fontBuffers, }: { fonts: FontsourceFont[]; fontBuffers: Map; }): MeasuredFont[] { console.log("Registering fonts and measuring text..."); const measured: MeasuredFont[] = []; const canvas = createCanvas(CANVAS_WIDTH, ROW_HEIGHT); const ctx = canvas.getContext("2d"); for (const font of fonts) { const buffer = fontBuffers.get(font.id); if (!buffer) continue; try { const ok = GlobalFonts.register(buffer, font.family); if (!ok) continue; ctx.font = `${FONT_SIZE}px "${font.family}"`; const metrics = ctx.measureText(font.family); const width = Math.ceil(metrics.width) + WIDTH_BUFFER; const styles: string[] = []; for (const weight of font.weights) { if (font.styles.includes("normal")) styles.push(String(weight)); if (font.styles.includes("italic")) styles.push(`${weight}i`); } measured.push({ family: font.family, width, styles }); } catch { // skip fonts that fail to register } } measured.sort((a, b) => a.family.localeCompare(b.family)); console.log(` ${measured.length} fonts measured`); return measured; } function packIntoChunks({ measured }: { measured: MeasuredFont[] }): { atlas: Record; chunks: PackedFont[][]; } { console.log("Packing into sprite chunks..."); const atlas: Record = {}; const chunks: PackedFont[][] = [[]]; let chunkIndex = 0; let cursorX = 0; let cursorY = 0; for (const font of measured) { // New row if doesn't fit horizontally if (cursorX + font.width > CANVAS_WIDTH) { cursorX = 0; cursorY += ROW_HEIGHT; } // New chunk if doesn't fit vertically if (cursorY + ROW_HEIGHT > MAX_CHUNK_HEIGHT) { chunkIndex++; chunks.push([]); cursorX = 0; cursorY = 0; } // Same data goes to BOTH the atlas and the chunk render list const x = cursorX; const y = cursorY; atlas[font.family] = { x, y, w: font.width, ch: chunkIndex, s: font.styles, }; chunks[chunkIndex].push({ family: font.family, x, y, w: font.width }); cursorX += font.width + PADDING_X; } console.log(` ${chunks.length} chunks`); return { atlas, chunks }; } async function renderChunks({ chunks, }: { chunks: PackedFont[][]; }): Promise { console.log("Rendering sprite chunks..."); for (let i = 0; i < chunks.length; i++) { const chunk = chunks[i]; const chunkHeight = Math.max(...chunk.map((f) => f.y)) + ROW_HEIGHT; const canvas = createCanvas(CANVAS_WIDTH, chunkHeight); const ctx = canvas.getContext("2d"); for (const font of chunk) { ctx.font = `${FONT_SIZE}px "${font.family}"`; ctx.fillStyle = "#000000"; ctx.textBaseline = "middle"; ctx.fillText(font.family, font.x, font.y + ROW_HEIGHT / 2); } const pngBuffer = canvas.toBuffer("image/png"); await sharp(pngBuffer) .avif({ quality: 80 }) .toFile(join(OUTPUT_DIR, `font-chunk-${i}.avif`)); console.log( ` Chunk ${i}: ${chunk.length} fonts, ${CANVAS_WIDTH}×${chunkHeight}`, ); } } async function main() { await mkdir(OUTPUT_DIR, { recursive: true }); await mkdir(CACHE_DIR, { recursive: true }); const fonts = await fetchFontList(); const fontBuffers = await downloadAllFonts({ fonts, concurrency: CONCURRENT_DOWNLOADS, }); const measured = measureFonts({ fonts, fontBuffers }); const { atlas, chunks } = packIntoChunks({ measured }); await renderChunks({ chunks }); // Write atlas JSON (compact for smaller file size) await writeFile( join(OUTPUT_DIR, "font-atlas.json"), JSON.stringify({ fonts: atlas }), ); const totalFonts = Object.keys(atlas).length; console.log( `\nDone! ${totalFonts} fonts in ${chunks.length} chunks → ${OUTPUT_DIR}`, ); } main().catch((error) => { console.error("Failed:", error); process.exit(1); }); ================================================ FILE: apps/web/src/app/api/auth/[...all]/route.ts ================================================ import { auth } from "@/lib/auth/server"; import { toNextJsHandler } from "better-auth/next-js"; export const { POST, GET } = toNextJsHandler(auth); ================================================ FILE: apps/web/src/app/api/health/route.ts ================================================ export async function GET() { return new Response("OK", { status: 200 }); } ================================================ FILE: apps/web/src/app/api/sounds/search/route.ts ================================================ import { webEnv } from "@opencut/env/web"; import { type NextRequest, NextResponse } from "next/server"; import { z } from "zod"; import { checkRateLimit } from "@/lib/rate-limit"; const searchParamsSchema = z.object({ q: z.string().max(500, "Query too long").optional(), type: z.enum(["songs", "effects"]).optional(), page: z.coerce.number().int().min(1).max(1000).default(1), page_size: z.coerce.number().int().min(1).max(150).default(20), sort: z .enum(["downloads", "rating", "created", "score"]) .default("downloads"), min_rating: z.coerce.number().min(0).max(5).default(3), commercial_only: z.coerce.boolean().default(true), }); const freesoundResultSchema = z.object({ id: z.number(), name: z.string(), description: z.string(), url: z.string().url(), previews: z .object({ "preview-hq-mp3": z.string().url(), "preview-lq-mp3": z.string().url(), "preview-hq-ogg": z.string().url(), "preview-lq-ogg": z.string().url(), }) .optional(), download: z.string().url().optional(), duration: z.number(), filesize: z.number(), type: z.string(), channels: z.number(), bitrate: z.number(), bitdepth: z.number(), samplerate: z.number(), username: z.string(), tags: z.array(z.string()), license: z.string(), created: z.string(), num_downloads: z.number().optional(), avg_rating: z.number().optional(), num_ratings: z.number().optional(), }); const freesoundResponseSchema = z.object({ count: z.number(), next: z.string().url().nullable(), previous: z.string().url().nullable(), results: z.array(freesoundResultSchema), }); const transformedResultSchema = z.object({ id: z.number(), name: z.string(), description: z.string(), url: z.string(), previewUrl: z.string().optional(), downloadUrl: z.string().optional(), duration: z.number(), filesize: z.number(), type: z.string(), channels: z.number(), bitrate: z.number(), bitdepth: z.number(), samplerate: z.number(), username: z.string(), tags: z.array(z.string()), license: z.string(), created: z.string(), downloads: z.number().optional(), rating: z.number().optional(), ratingCount: z.number().optional(), }); const apiResponseSchema = z.object({ count: z.number(), next: z.string().nullable(), previous: z.string().nullable(), results: z.array(transformedResultSchema), query: z.string().optional(), type: z.string(), page: z.number(), pageSize: z.number(), sort: z.string(), minRating: z.number().optional(), }); function buildSortParameter({ query, sort }: { query?: string; sort: string }) { if (!query) return `${sort}_desc`; return sort === "score" ? "score" : `${sort}_desc`; } function applyEffectsFilters({ params, min_rating, commercial_only, }: { params: URLSearchParams; min_rating: number; commercial_only: boolean; }) { params.append("filter", "duration:[* TO 30.0]"); params.append("filter", `avg_rating:[${min_rating} TO *]`); if (commercial_only) { params.append( "filter", 'license:("Attribution" OR "Creative Commons 0" OR "Attribution Noncommercial" OR "Attribution Commercial")', ); } params.append( "filter", "tag:sound-effect OR tag:sfx OR tag:foley OR tag:ambient OR tag:nature OR tag:mechanical OR tag:electronic OR tag:impact OR tag:whoosh OR tag:explosion", ); } function transformFreesoundResult( result: z.infer, ) { return { id: result.id, name: result.name, description: result.description, url: result.url, previewUrl: result.previews?.["preview-hq-mp3"] || result.previews?.["preview-lq-mp3"], downloadUrl: result.download, duration: result.duration, filesize: result.filesize, type: result.type, channels: result.channels, bitrate: result.bitrate, bitdepth: result.bitdepth, samplerate: result.samplerate, username: result.username, tags: result.tags, license: result.license, created: result.created, downloads: result.num_downloads || 0, rating: result.avg_rating || 0, ratingCount: result.num_ratings || 0, }; } export async function GET(request: NextRequest) { try { const { limited } = await checkRateLimit({ request }); if (limited) { return NextResponse.json({ error: "Too many requests" }, { status: 429 }); } const { searchParams } = new URL(request.url); const validationResult = searchParamsSchema.safeParse({ q: searchParams.get("q") || undefined, type: searchParams.get("type") || undefined, page: searchParams.get("page") || undefined, page_size: searchParams.get("page_size") || undefined, sort: searchParams.get("sort") || undefined, min_rating: searchParams.get("min_rating") || undefined, }); if (!validationResult.success) { return NextResponse.json( { error: "Invalid parameters", details: validationResult.error.flatten().fieldErrors, }, { status: 400 }, ); } const { q: query, type, page, page_size: pageSize, sort, min_rating, commercial_only, } = validationResult.data; if (type === "songs") { return NextResponse.json( { error: "Songs are not available yet", message: "Song search functionality is coming soon. Try searching for sound effects instead.", }, { status: 501 }, ); } const baseUrl = "https://freesound.org/apiv2/search/text/"; const sortParam = buildSortParameter({ query, sort }); const params = new URLSearchParams({ query: query || "", token: webEnv.FREESOUND_API_KEY, page: page.toString(), page_size: pageSize.toString(), sort: sortParam, fields: "id,name,description,url,previews,download,duration,filesize,type,channels,bitrate,bitdepth,samplerate,username,tags,license,created,num_downloads,avg_rating,num_ratings", }); const isEffectsSearch = type === "effects" || !type; if (isEffectsSearch) { applyEffectsFilters({ params, min_rating, commercial_only }); } const response = await fetch(`${baseUrl}?${params.toString()}`); if (!response.ok) { const errorText = await response.text(); console.error("Freesound API error:", response.status, errorText); return NextResponse.json( { error: "Failed to search sounds" }, { status: response.status }, ); } const rawData = await response.json(); const freesoundValidation = freesoundResponseSchema.safeParse(rawData); if (!freesoundValidation.success) { console.error( "Invalid Freesound API response:", freesoundValidation.error, ); return NextResponse.json( { error: "Invalid response from Freesound API" }, { status: 502 }, ); } const data = freesoundValidation.data; const transformedResults = data.results.map(transformFreesoundResult); const responseData = { count: data.count, next: data.next, previous: data.previous, results: transformedResults, query: query || "", type: type || "effects", page, pageSize, sort, minRating: min_rating, }; const responseValidation = apiResponseSchema.safeParse(responseData); if (!responseValidation.success) { console.error( "Invalid API response structure:", responseValidation.error, ); return NextResponse.json( { error: "Internal response formatting error" }, { status: 500 }, ); } return NextResponse.json(responseValidation.data); } catch (error) { console.error("Error searching sounds:", error); return NextResponse.json( { error: "Internal server error" }, { status: 500 }, ); } } ================================================ FILE: apps/web/src/app/base-page.tsx ================================================ import { Header } from "@/components/header"; import { Footer } from "@/components/footer"; import { cn } from "@/utils/ui"; interface BasePageProps { children: React.ReactNode; className?: string; mainClassName?: string; maxWidth?: "3xl" | "6xl" | "full"; title?: string; description?: React.ReactNode; action?: React.ReactNode; } export function BasePage({ children, className = "", mainClassName = "", maxWidth = "3xl", title, description, action, }: BasePageProps) { const maxWidthClass = { "3xl": "max-w-3xl", "6xl": "max-w-6xl", full: "max-w-full", }[maxWidth]; return (
{title && description && (

{title}

{description}

{action}
)} {children}
); } ================================================ FILE: apps/web/src/app/blog/[slug]/page.tsx ================================================ import type { Metadata } from "next"; import Image from "next/image"; import { notFound } from "next/navigation"; import { BasePage } from "@/app/base-page"; import Prose from "@/components/ui/prose"; import { Separator } from "@/components/ui/separator"; import { getPosts, getSinglePost, processHtmlContent } from "@/lib/blog/query"; import type { Author, Post } from "@/types/blog"; type PageProps = { params: Promise<{ slug: string }>; searchParams: Promise<{ [key: string]: string | string[] | undefined }>; }; export async function generateMetadata({ params, }: PageProps): Promise { const slug = (await params).slug; const data = await getSinglePost({ slug }); if (!data || !data.post) return {}; return { title: data.post.title, description: data.post.description, twitter: { title: `${data.post.title}`, description: `${data.post.description}`, card: "summary_large_image", images: [ { url: data.post.coverImage, width: "1200", height: "630", alt: data.post.title, }, ], }, openGraph: { type: "article", images: [ { url: data.post.coverImage, width: "1200", height: "630", alt: data.post.title, }, ], title: data.post.title, description: data.post.description, publishedTime: new Date(data.post.publishedAt).toISOString(), authors: data.post.authors.map((author: Author) => author.name), }, }; } export async function generateStaticParams() { const data = await getPosts(); if (!data || !data.posts.length) return []; return data.posts.map((post) => ({ slug: post.slug, })); } export default async function BlogPostPage({ params }: PageProps) { const slug = (await params).slug; const data = await getSinglePost({ slug }); if (!data || !data.post) return notFound(); const html = await processHtmlContent({ html: data.post.content }); return ( ); } function PostHeader({ post }: { post: Post }) { const formattedDate = new Date(post.publishedAt).toLocaleDateString("en-US", { day: "numeric", month: "long", year: "numeric", }); return (
{post.coverImage && }
); } function PostCoverImage({ post }: { post: Post }) { return (
{post.title}
); } function PostMeta({ date, publishedAt }: { date: string; publishedAt: Date }) { return (
); } function PostTitle({ title }: { title: string }) { return (

{title}

); } function PostContent({ html }: { html: string }) { return (
); } ================================================ FILE: apps/web/src/app/blog/page.tsx ================================================ import type { Metadata } from "next"; import Link from "next/link"; import { BasePage } from "@/app/base-page"; import { Separator } from "@/components/ui/separator"; import { getPosts } from "@/lib/blog/query"; import type { Post } from "@/types/blog"; export const metadata: Metadata = { title: "Blog - OpenCut", description: "Read the latest news and updates about OpenCut, the free and open-source video editor.", openGraph: { title: "Blog - OpenCut", description: "Read the latest news and updates about OpenCut, the free and open-source video editor.", type: "website", }, }; export default async function BlogPage() { const data = await getPosts(); if (!data || !data.posts) return
No posts yet
; return (
{data.posts.map((post) => (
))}
); } function BlogPostItem({ post }: { post: Post }) { return (

{post.title}

{post.description}

); } ================================================ FILE: apps/web/src/app/brand/page.tsx ================================================ "use client"; import type { CSSProperties } from "react"; import Image from "next/image"; import Link from "next/link"; import { Check, Copy, Download } from "lucide-react"; import { useState } from "react"; import { BasePage } from "@/app/base-page"; import { Button } from "@/components/ui/button"; import { Card } from "@/components/ui/card"; import { Separator } from "@/components/ui/separator"; import { cn } from "@/utils/ui"; function downloadAsset(src: string) { const filename = src.split("/").pop() ?? "asset.svg"; const a = document.createElement("a"); a.href = src; a.download = filename; a.click(); } async function copyAsset(src: string) { const res = await fetch(src); const text = await res.text(); await navigator.clipboard.writeText(text); } const ALL_ASSETS = () => ASSET_SECTIONS.flatMap((s) => s.assets); type AssetTheme = "dark" | "light" | "icon"; interface AssetVariant { src: string; theme: AssetTheme; label: string; width: number; height: number; } interface AssetSection { title: string; description: string; cols: "1" | "2"; assets: AssetVariant[]; } const ASSET_SECTIONS: AssetSection[] = [ { title: "Symbol", description: "Use the symbol on its own when the OpenCut name is already present nearby or space is limited.", cols: "2", assets: [ { src: "/logos/opencut/symbol.svg", theme: "dark", label: "Symbol", width: 400, height: 400, }, { src: "/logos/opencut/symbol-light.svg", theme: "light", label: "Symbol", width: 400, height: 400, }, ], }, { title: "Lockup", description: "The full lockup combines the symbol and wordmark. Prefer this in most contexts where you have enough horizontal space.", cols: "2", assets: [ { src: "/logos/opencut/logo.svg", theme: "dark", label: "Logo", width: 1809, height: 400, }, { src: "/logos/opencut/logo-light.svg", theme: "light", label: "Logo", width: 1809, height: 400, }, { src: "/logos/opencut/text.svg", theme: "dark", label: "Text", width: 1760, height: 400, }, { src: "/logos/opencut/text-light.svg", theme: "light", label: "Text", width: 1760, height: 400, }, ], }, ]; export default function BrandPage() { return ( Download OpenCut brand assets for use in your projects.{" "} document .getElementById("guidelines") ?.scrollIntoView({ behavior: "smooth" }) } > Read the brand guidelines. } action={ } >
{ASSET_SECTIONS.map((section) => (

{section.title}

{section.description}

{section.assets.map((variant) => ( ))}
))}

Usage

OpenCut is open source — the code is free to use under its license. That license does not cover the name or logo. You can say you use OpenCut, that your project integrates with OpenCut, or that it was built on top of OpenCut. You cannot name your product OpenCut, imply we made or endorse your product, or use the marks commercially without asking first. For anything unclear, reach out at{" "} brand@opencut.app .

What's not allowed

    {[ "Using OpenCut in the name of your product, service, or domain.", "Implying that OpenCut made, sponsors, or endorses your work.", "Using the logo or name on merchandise or commercial marketing.", "Modifying the marks.", ].map((item) => (
  • - {item}
  • ))}
); } const CHECKER_STYLES: Record<"dark" | "light", CSSProperties> = { light: { backgroundImage: "linear-gradient(45deg, #292929 25%, transparent 25%), linear-gradient(-45deg, #292929 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #292929 75%), linear-gradient(-45deg, transparent 75%, #292929 75%)", backgroundSize: "18px 18px", backgroundPosition: "0 0, 0 9px, 9px -9px, -9px 0px", backgroundColor: "#000", }, dark: { backgroundImage: "linear-gradient(45deg, #e0e0e0 25%, transparent 25%), linear-gradient(-45deg, #e0e0e0 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #e0e0e0 75%), linear-gradient(-45deg, transparent 75%, #e0e0e0 75%)", backgroundSize: "18px 18px", backgroundPosition: "0 0, 0 9px, 9px -9px, -9px 0px", backgroundColor: "#f5f5f5", }, }; function AssetCard({ variant }: { variant: AssetVariant }) { const [copied, setCopied] = useState(false); async function handleCopy() { await copyAsset(variant.src); setCopied(true); setTimeout(() => setCopied(false), 2000); } return (
{variant.label}
); } ================================================ FILE: apps/web/src/app/changelog/[version]/page.tsx ================================================ import type { Metadata } from "next"; import Link from "next/link"; import { notFound } from "next/navigation"; import { BasePage } from "@/app/base-page"; import { allChangelogs } from "content-collections"; import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react"; import { getSortedReleases } from "../utils"; import { ReleaseArticle, ReleaseMeta, ReleaseTitle, ReleaseDescription, ReleaseChanges, } from "../components/release"; import { CopyMarkdownButton } from "../components/copy-markdown-button"; type Props = { params: Promise<{ version: string }> }; export async function generateStaticParams() { return allChangelogs.map((r) => ({ version: r.version })); } export async function generateMetadata({ params }: Props): Promise { const { version } = await params; const release = allChangelogs.find((r) => r.version === version); if (!release) return {}; return { title: `${release.title} (${release.version}) - OpenCut Changelog`, description: release.description, }; } export default async function ReleaseDetailPage({ params }: Props) { const { version } = await params; const releases = getSortedReleases(); const index = releases.findIndex((r) => r.version === version); if (index === -1) notFound(); const release = releases[index]; const newer = index > 0 ? releases[index - 1] : null; const older = index < releases.length - 1 ? releases[index + 1] : null; return (
All releases
{release.title} {release.description && ( {release.description} )}
); } ================================================ FILE: apps/web/src/app/changelog/components/copy-markdown-button.tsx ================================================ "use client"; import { useState } from "react"; import { CheckIcon, ClipboardIcon } from "lucide-react"; import { getSectionTitle, groupAndOrderChanges } from "../utils"; import type { Change } from "../utils"; import { cn } from "@/utils/ui"; import { Button } from "@/components/ui/button"; function buildMarkdown({ description, changes, }: { description?: string; changes: Change[]; }): string { const lines: string[] = []; if (description) { lines.push(description, ""); } const { grouped, orderedTypes } = groupAndOrderChanges({ changes }); for (const type of orderedTypes) { lines.push(`## ${getSectionTitle(type)}`); for (const change of grouped[type]) { lines.push(`- ${change.text}`); } lines.push(""); } return lines.join("\n").trimEnd(); } export function CopyMarkdownButton({ description, changes, }: { description?: string; changes: Change[]; }) { const [copied, setCopied] = useState(false); const handleCopy = async () => { const markdown = buildMarkdown({ description, changes }); await navigator.clipboard.writeText(markdown); setCopied(true); setTimeout(() => setCopied(false), 2000); }; return ( ); } ================================================ FILE: apps/web/src/app/changelog/components/release.tsx ================================================ import type { ReactNode } from "react"; import Link from "next/link"; import { cn } from "@/utils/ui"; import { getSectionTitle, groupAndOrderChanges } from "../utils"; import type { Release } from "../utils"; export function ReleaseArticle({ variant, isLatest, children, }: { variant: "list" | "detail"; isLatest?: boolean; children: ReactNode; }) { if (variant === "list") { return (
{children}
); } return
{children}
; } export function ReleaseMeta({ release }: { release: Release }) { return ( {release.version} — {release.date} ); } const titleSizes: Record<"h1" | "h2", string> = { h1: "text-4xl", h2: "text-2xl", }; export function ReleaseTitle({ as: As, href, children, }: { as: "h1" | "h2"; href?: string; children: ReactNode; }) { return ( {href ? ( {children} ) : ( children )} ); } export function ReleaseDescription({ children }: { children: ReactNode }) { return (

{children}

); } export function ReleaseChanges({ release }: { release: Release }) { const { grouped, orderedTypes } = groupAndOrderChanges({ changes: release.changes, }); return (
{orderedTypes.map((type) => (

{getSectionTitle(type)}:

    {grouped[type].map((change) => (
  • {change.text}
  • ))}
))}
); } ================================================ FILE: apps/web/src/app/changelog/page.tsx ================================================ import type { Metadata } from "next"; import { BasePage } from "@/app/base-page"; import { Separator } from "@/components/ui/separator"; import { type Release as ReleaseType, getSortedReleases } from "./utils"; import { ReleaseArticle, ReleaseMeta, ReleaseTitle, ReleaseDescription, ReleaseChanges, } from "./components/release"; export const metadata: Metadata = { title: "Changelog - OpenCut", description: "What's new in OpenCut", openGraph: { title: "Changelog - OpenCut", description: "Every update, improvement, and fix to OpenCut — documented.", type: "website", images: [ { url: "/open-graph/changlog.jpg", width: 1200, height: 630, alt: "OpenCut Changelog", }, ], }, twitter: { card: "summary_large_image", title: "Changelog - OpenCut", description: "What's new in OpenCut", images: ["/open-graph/changlog.jpg"], }, }; export default function ChangelogPage() { const releases = getSortedReleases(); return (
{releases.map((release, releaseIndex) => (
{releaseIndex < releases.length - 1 && ( )}
))}
); } function ReleaseEntry({ release }: { release: ReleaseType }) { return (
{release.title} {release.description && ( {release.description} )}
); } ================================================ FILE: apps/web/src/app/changelog/utils.ts ================================================ import { allChangelogs } from "content-collections"; export type Change = { type: string; text: string }; export type Release = (typeof allChangelogs)[number]; const knownSectionOrder = ["new", "improved", "fixed", "breaking"]; const knownSectionTitles: Record = { new: "Features", improved: "Improvements", fixed: "Fixes", breaking: "Breaking Changes", }; export function getSectionTitle(type: string): string { return ( knownSectionTitles[type] ?? type.charAt(0).toUpperCase() + type.slice(1) ); } export function groupAndOrderChanges({ changes }: { changes: Change[] }) { const grouped = changes.reduce>((acc, change) => { if (!acc[change.type]) acc[change.type] = []; acc[change.type].push(change); return acc; }, {}); const customTypes = Object.keys(grouped).filter( (type) => !knownSectionOrder.includes(type), ); const orderedTypes = [ ...knownSectionOrder.filter((type) => grouped[type]?.length > 0), ...customTypes, ]; return { grouped, orderedTypes }; } export function getSortedReleases() { return [...allChangelogs].sort((a, b) => b.version.localeCompare(a.version, undefined, { numeric: true }), ); } ================================================ FILE: apps/web/src/app/contributors/page.tsx ================================================ import type { Metadata } from "next"; import Link from "next/link"; import { GitHubContributeSection } from "@/components/gitHub-contribute-section"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { Card, CardContent } from "@/components/ui/card"; import { EXTERNAL_TOOLS } from "@/constants/site-constants"; import { BasePage } from "../base-page"; export const metadata: Metadata = { title: "Contributors - OpenCut", description: "Meet the amazing people who contribute to OpenCut, the free and open-source video editor.", openGraph: { title: "Contributors - OpenCut", description: "Meet the amazing people who contribute to OpenCut, the free and open-source video editor.", type: "website", }, }; interface Contributor { id: number; login: string; avatar_url: string; html_url: string; contributions: number; type: string; } async function getContributors(): Promise { try { const response = await fetch( "https://api.github.com/repos/OpenCut-app/OpenCut/contributors?per_page=100", { headers: { Accept: "application/vnd.github.v3+json", "User-Agent": "OpenCut-Web-App", }, next: { revalidate: 600 }, // 10 minutes }, ); if (!response.ok) { console.error("Failed to fetch contributors"); return []; } const contributors = (await response.json()) as Contributor[]; const filteredContributors = contributors.filter( (contributor) => contributor.type === "User", ); return filteredContributors; } catch (error) { console.error("Error fetching contributors:", error); return []; } } export default async function ContributorsPage() { const contributors = await getContributors(); const topContributors = contributors.slice(0, 2); const otherContributors = contributors.slice(2); const totalContributions = contributors.reduce( (sum, c) => sum + c.contributions, 0, ); return (
{topContributors.length > 0 && ( )} {otherContributors.length > 0 && ( )}
); } function StatItem({ value, label }: { value: number; label: string }) { return (
{value} {label}
); } function TopContributorsSection({ contributors, }: { contributors: Contributor[]; }) { return (

Top contributors

Leading the way in contributions

{contributors.map((contributor) => ( ))}
); } function TopContributorCard({ contributor }: { contributor: Contributor }) { return ( {contributor.login.charAt(0).toUpperCase()}

{contributor.login}

{contributor.contributions} contributions
); } function AllContributorsSection({ contributors, }: { contributors: Contributor[]; }) { return (

All contributors

Everyone who makes OpenCut better

{contributors.map((contributor) => (
{contributor.login.charAt(0).toUpperCase()}

{contributor.login}

{contributor.contributions}

))}
); } function ExternalToolsSection() { return (

External tools

Tools we use to build OpenCut

{EXTERNAL_TOOLS.map((tool, index) => (

{tool.name}

{tool.description}

))}
); } ================================================ FILE: apps/web/src/app/editor/[project_id]/page.tsx ================================================ "use client"; import { useParams } from "next/navigation"; import { ResizablePanelGroup, ResizablePanel, ResizableHandle, } from "@/components/ui/resizable"; import { AssetsPanel } from "@/components/editor/panels/assets"; import { PropertiesPanel } from "@/components/editor/panels/properties"; import { Timeline } from "@/components/editor/panels/timeline"; import { PreviewPanel } from "@/components/editor/panels/preview"; import { EditorHeader } from "@/components/editor/editor-header"; import { EditorProvider } from "@/components/providers/editor-provider"; import { Onboarding } from "@/components/editor/onboarding"; import { MigrationDialog } from "@/components/editor/dialogs/migration-dialog"; import { usePanelStore } from "@/stores/panel-store"; import { usePasteMedia } from "@/hooks/use-paste-media"; import { MobileGate } from "@/components/editor/mobile-gate"; export default function Editor() { const params = useParams(); const projectId = params.project_id as string; return (
); } function EditorLayout() { usePasteMedia(); const { panels, setPanel } = usePanelStore(); return ( { setPanel("mainContent", sizes[0] ?? panels.mainContent); setPanel("timeline", sizes[1] ?? panels.timeline); }} > { setPanel("tools", sizes[0] ?? panels.tools); setPanel("preview", sizes[1] ?? panels.preview); setPanel("properties", sizes[2] ?? panels.properties); }} > ); } ================================================ FILE: apps/web/src/app/globals.css ================================================ @import "tailwindcss"; /* Custom variant for dark mode */ @custom-variant dark (&:where(.dark, .dark *)); /* Plugins */ @plugin "@tailwindcss/typography"; @plugin "tailwindcss-animate"; :root { --background: hsl(0, 0%, 100%); --foreground: hsl(0 0% 11%); --card: hsl(0, 0%, 100%); --card-foreground: hsl(0 0% 11%); --popover: hsl(0, 0%, 100%); --popover-hover: hsl(0, 0%, 96%); --popover-foreground: hsl(0 0% 2%); --primary: #009dff; --primary-foreground: hsl(0, 0%, 100%); --secondary: hsl(204, 100%, 97%); --secondary-border: hsl(204, 100%, 94%); --secondary-foreground: hsl(200, 98%, 39%); --muted: hsl(0 0% 85.1%); --muted-foreground: hsl(0 0% 50%); --accent: hsl(0, 0%, 96%); --accent-foreground: hsl(0 0% 2%); --destructive: hsl(0, 83%, 50%); --destructive-foreground: hsl(0, 0%, 100%); --constructive: hsl(141, 71%, 48%); --constructive-foreground: hsl(0, 0%, 100%); --border: hsl(0 0% 91%); --input: hsl(0, 0%, 100%); --ring: hsl(0, 0%, 55%); --chart-1: hsl(220 70% 50%); --chart-2: hsl(160 60% 45%); --chart-3: hsl(30 80% 55%); --chart-4: hsl(280 65% 60%); --chart-5: hsl(340 75% 55%); --sidebar-background: hsl(0 0% 96.1%); --sidebar-foreground: hsl(0 0% 2%); --sidebar-primary: hsl(0 0% 2%); --sidebar-primary-foreground: hsl(0 0% 91%); --sidebar-accent: hsl(0 0% 85.1%); --sidebar-accent-foreground: hsl(0 0% 2%); --sidebar-border: hsl(0 0% 85.1%); --sidebar-ring: hsl(0 0% 16.9%); --sidebar: hsl(0 0% 98%); } .panel { --background: hsl(216 13% 98%); --foreground: hsl(0 0% 13%); --card: hsl(0, 0%, 98%); --card-foreground: hsl(0 0% 13%); --primary-foreground: hsl(0, 0%, 98%); --secondary: hsl(204, 100%, 95%); --secondary-border: hsl(204, 100%, 92%); --secondary-foreground: hsl(200, 98%, 37%); --muted: hsl(0 0% 83.1%); --muted-foreground: hsl(0 0% 48%); --accent: hsl(0, 0%, 93%); --accent-foreground: hsl(0 0% 5%); --destructive-foreground: hsl(0, 0%, 98%); --constructive-foreground: hsl(0, 0%, 98%); --border: hsl(0 0% 89%); --input: hsl(0 0% 93%); --ring: hsl(0, 0%, 53%); } .dark { --background: hsl(0, 0%, 5%); --foreground: hsl(0 0% 87%); --card: hsl(0, 0%, 5%); --card-foreground: hsl(0 0% 87%); --popover: hsl(0, 0%, 5%); --popover-hover: hsl(0, 0%, 22%); --popover-foreground: hsl(0 0% 95%); --secondary: hsl(204, 100%, 12%); --secondary-border: hsl(204, 100%, 15%); --secondary-foreground: hsl(200, 98%, 61%); --muted: hsl(0 0% 20%); --accent: hsl(0, 0%, 14%); --accent-foreground: hsl(0 0% 95%); --border: hsl(0 0% 16%); --input: hsl(0 0% 5%); --ring: hsl(0, 0%, 50%); --sidebar-background: hsl(0 0% 8%); --sidebar-foreground: hsl(0 0% 95%); --sidebar-primary: hsl(0 0% 95%); --sidebar-primary-foreground: hsl(0 0% 15%); --sidebar-accent: hsl(0 0% 20%); --sidebar-accent-foreground: hsl(0 0% 95%); --sidebar-border: hsl(0 0% 20%); --sidebar-ring: hsl(0 0% 83.1%); --sidebar: hsl(0 0% 6%); } .dark .panel { --background: hsl(0 0% 10%); --foreground: hsl(0 0% 85%); --card: hsl(0, 0%, 10%); --card-foreground: hsl(0 0% 85%); --secondary: hsl(204, 67%, 9%); --secondary-border: hsl(204, 100%, 14%); --secondary-foreground: hsl(200, 98%, 63%); --muted: hsl(0 0% 22%); --accent: hsl(0, 0%, 15%); --accent-foreground: hsl(0 0% 93%); --border: hsl(0 0% 18%); --input: hsl(0 0% 22%); --ring: hsl(0, 0%, 52%); } @layer base { /* The default border color has changed to `currentcolor` in Tailwind CSS v4, so we've added these compatibility styles to make sure everything still looks the same as it did with Tailwind CSS v3. If we ever want to remove these styles, we need to add an explicit border color utility to any element that depends on these defaults. */ *, ::after, ::before, ::backdrop, ::file-selector-button { border-color: var(--color-gray-200, currentcolor); } /* Other default base styles */ * { @apply border-border; } body { @apply bg-background text-foreground; /* Prevent back/forward swipe */ overscroll-behavior-x: contain; } ::selection { @apply bg-primary/35 selection:text-primary-foreground; } } @theme inline { /* Responsive breakpoints */ --breakpoint-xs: 30rem; /* Typography */ --font-sans: var(--font-inter), sans-serif; /* Font sizes */ --text-xs: 0.72rem; --text-sm: 0.79rem; --text-base: 0.92rem; --text-base--line-height: calc(1.5 / 0.95); --text-xs--line-height: calc(1 / 0.8); /* Border radius */ --radius-lg: 0.82rem; --radius-md: 0.65rem; --radius-sm: 0.35rem; /* Palette mapped to root design tokens */ --color-background: var(--background); --color-foreground: var(--foreground); --color-card: var(--card); --color-card-foreground: var(--card-foreground); --color-popover: var(--popover); --color-popover-hover: var(--popover-hover); --color-popover-foreground: var(--popover-foreground); --color-primary: var(--primary); --color-primary-foreground: var(--primary-foreground); --color-secondary: var(--secondary); --color-secondary-border: var(--secondary-border); --color-secondary-foreground: var(--secondary-foreground); --color-muted: var(--muted); --color-muted-foreground: var(--muted-foreground); --color-accent: var(--accent); --color-accent-foreground: var(--accent-foreground); --color-destructive: var(--destructive); --color-destructive-foreground: var(--destructive-foreground); --color-constructive: var(--constructive); --color-constructive-foreground: var(--constructive-foreground); --color-border: var(--border); --color-input: var(--input); --color-ring: var(--ring); /* Chart colors */ --color-chart-1: var(--chart-1); --color-chart-2: var(--chart-2); --color-chart-3: var(--chart-3); --color-chart-4: var(--chart-4); --color-chart-5: var(--chart-5); /* Sidebar */ --color-sidebar: var(--sidebar-background); --color-sidebar-foreground: var(--sidebar-foreground); --color-sidebar-primary: var(--sidebar-primary); --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); --color-sidebar-accent: var(--sidebar-accent); --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); --color-sidebar-border: var(--sidebar-border); --color-sidebar-ring: var(--sidebar-ring); /* Animations */ --animate-accordion-down: accordion-down 0.2s ease-out; --animate-accordion-up: accordion-up 0.2s ease-out; @keyframes accordion-down { from { height: 0; } to { height: var(--radix-accordion-content-height); } } @keyframes accordion-up { from { height: var(--radix-accordion-content-height); } to { height: 0; } } } @utility scrollbar-hidden { -ms-overflow-style: none; scrollbar-width: none; &::-webkit-scrollbar { display: none; } } @utility scrollbar-x-hidden { -ms-overflow-style: none; scrollbar-width: none; &::-webkit-scrollbar:horizontal { display: none; } } @utility scrollbar-y-hidden { -ms-overflow-style: none; scrollbar-width: none; &::-webkit-scrollbar:vertical { display: none; } } @utility scrollbar-thin { &::-webkit-scrollbar { width: 6px; height: 8px; } &::-webkit-scrollbar-track { background: transparent; } &::-webkit-scrollbar-thumb { background: var(--border); border-radius: 4px; } &::-webkit-scrollbar-thumb:hover { background: var(--muted-foreground); } } @layer base { * { @apply border-border outline-ring/50; } body { @apply bg-background text-foreground; } } ================================================ FILE: apps/web/src/app/layout.tsx ================================================ import { ThemeProvider } from "next-themes"; import Script from "next/script"; import "./globals.css"; import { Toaster } from "../components/ui/sonner"; import { TooltipProvider } from "../components/ui/tooltip"; import { baseMetaData } from "./metadata"; import { BotIdClient } from "botid/client"; import { webEnv } from "@opencut/env/web"; import { Inter } from "next/font/google"; const siteFont = Inter({ subsets: ["latin"] }); export const metadata = baseMetaData; const protectedRoutes = [ { path: "/none", method: "GET", }, ]; export default function RootLayout({ children, }: Readonly<{ children: React.ReactNode; }>) { return ( {process.env.NODE_ENV === "development" && (