diff --git a/.cursor/commands/review.md b/.cursor/commands/review.md deleted file mode 100644 index 4cc5de367..000000000 --- a/.cursor/commands/review.md +++ /dev/null @@ -1,172 +0,0 @@ -# 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. diff --git a/.cursor/rules/codebase-index.mdc b/.cursor/rules/codebase-index.mdc deleted file mode 100644 index 547d05661..000000000 --- a/.cursor/rules/codebase-index.mdc +++ /dev/null @@ -1,2515 +0,0 @@ ---- -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)_ diff --git a/.cursor/rules/comments.mdc b/.cursor/rules/comments.mdc deleted file mode 100644 index 4b9180cf1..000000000 --- a/.cursor/rules/comments.mdc +++ /dev/null @@ -1,34 +0,0 @@ ---- -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. \ No newline at end of file diff --git a/.cursor/rules/handling-uncertainty.mdc b/.cursor/rules/handling-uncertainty.mdc deleted file mode 100644 index 59cfb5bda..000000000 --- a/.cursor/rules/handling-uncertainty.mdc +++ /dev/null @@ -1,20 +0,0 @@ ---- -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. \ No newline at end of file diff --git a/.cursor/rules/readability.mdc b/.cursor/rules/readability.mdc deleted file mode 100644 index 22bb5397e..000000000 --- a/.cursor/rules/readability.mdc +++ /dev/null @@ -1,8 +0,0 @@ ---- -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. diff --git a/.cursor/rules/separation-of-concerns.mdc b/.cursor/rules/separation-of-concerns.mdc deleted file mode 100644 index 4abb88089..000000000 --- a/.cursor/rules/separation-of-concerns.mdc +++ /dev/null @@ -1,51 +0,0 @@ ---- -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 \ No newline at end of file diff --git a/.cursor/rules/ultracite.mdc b/.cursor/rules/ultracite.mdc deleted file mode 100644 index 83e14c18c..000000000 --- a/.cursor/rules/ultracite.mdc +++ /dev/null @@ -1,101 +0,0 @@ ---- -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); -} -``` diff --git a/.cursor/rules/writing-scannable-code.mdc b/.cursor/rules/writing-scannable-code.mdc deleted file mode 100644 index 2c36d4966..000000000 --- a/.cursor/rules/writing-scannable-code.mdc +++ /dev/null @@ -1,52 +0,0 @@ ---- -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. \ No newline at end of file diff --git a/.cursor/settings.json b/.cursor/settings.json deleted file mode 100644 index 28a77607e..000000000 --- a/.cursor/settings.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "plugins": { - "figma": { - "enabled": true - } - } -} diff --git a/.cursor/skills/design/SKILL.md b/.cursor/skills/design/SKILL.md deleted file mode 100644 index d5a05e27b..000000000 --- a/.cursor/skills/design/SKILL.md +++ /dev/null @@ -1,45 +0,0 @@ ---- -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. diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md index 2661e1829..227410ea2 100644 --- a/.github/CODE_OF_CONDUCT.md +++ b/.github/CODE_OF_CONDUCT.md @@ -1,89 +1,89 @@ -# 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 +# 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 diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index dbf7a530c..5edc56fbe 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -20,6 +20,7 @@ Thank you for your interest in contributing to OpenCut! This document provides g - [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/) +- Rust toolchain (only needed for `apps/desktop`) > **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! @@ -44,10 +45,13 @@ Thank you for your interest in contributing to OpenCut! This document provides g 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: +> **Note:** Web development uses the published `opencut-wasm` package by default, so a fresh clone does not need a local WASM build. > -> 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**. +> If you are editing `rust/wasm`, run `bun run build:wasm`, then `cd rust/wasm/pkg && bun link`, then `cd ../../../apps/web && bun link opencut-wasm`. + +### Desktop setup + +Only needed if you're working on `apps/desktop`. See [`apps/desktop/README.md`](../apps/desktop/README.md) — it's a two-step process: Rust toolchain first via `script/setup-rust`, then desktop native dependencies via `apps/desktop/script/setup`. ## What to Focus On @@ -140,6 +144,10 @@ If you're unsure whether your idea falls into the preview category, feel free to 5. Run database migrations: `bun run db:migrate` 6. Start the development server: `bun run dev` +### Desktop + +Working on `apps/desktop`? See [`apps/desktop/README.md`](../apps/desktop/README.md) for setup. Web-only contributors can ignore this entirely. + ## How to Contribute ### Reporting Bugs @@ -158,16 +166,18 @@ If you're unsure whether your idea falls into the preview category, feel free to 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 +3. Run the relevant checks for the area you touched: + + - Web changes: from `apps/web`, run `bun run lint` and `bun run format` + - Desktop changes: run `./apps/desktop/script/setup` if your environment isn't set up yet + +4. Commit your changes with a descriptive message +5. 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 +- We use ESLint for linting and Prettier for formatting +- Run `bun run format` 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 diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 01965d41c..001466ae1 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -1,70 +1,70 @@ -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 +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 diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index 336e2d6a1..cdeea8d93 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -1,42 +1,42 @@ -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 +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 diff --git a/.github/SECURITY.md b/.github/SECURITY.md index 1369bbabb..3105845f5 100644 --- a/.github/SECURITY.md +++ b/.github/SECURITY.md @@ -1,28 +1,28 @@ -# 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! +# 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! diff --git a/.github/SUPPORT.md b/.github/SUPPORT.md index 1df3d7bd6..88b5797aa 100644 --- a/.github/SUPPORT.md +++ b/.github/SUPPORT.md @@ -1,27 +1,27 @@ -# 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! +# 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! diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 77e361d14..77d2c4f0a 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -4,12 +4,10 @@ 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. +Strict type safety, accessibility standards, and consistent code quality for JavaScript/TypeScript projects, enforced via ESLint (linting) and Prettier (formatting). ## Key Principles -- Zero configuration required -- Subsecond performance - Maximum type safety - AI-friendly code generation diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index c7bdff473..1616f5c0b 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,11 +1,11 @@ -⚠️ 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: - +⚠️ 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. \ No newline at end of file diff --git a/.github/workflows/bun-ci.yml b/.github/workflows/bun-ci.yml index 83c3eac9b..386b42bc0 100644 --- a/.github/workflows/bun-ci.yml +++ b/.github/workflows/bun-ci.yml @@ -1,66 +1,81 @@ -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 +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" + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install Rust wasm target + run: rustup target add wasm32-unknown-unknown + + - name: Install wasm-pack + uses: jetli/wasm-pack-action@v0.4.0 + with: + version: latest + + - name: Cache Rust build artifacts + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: ${{ runner.os }}-cargo-${{ hashFiles('Cargo.lock') }} + + - name: Build WASM + run: wasm-pack build rust/wasm --target bundler --out-dir pkg + + - 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 diff --git a/.gitignore b/.gitignore index fa3b5f687..f5d4d26e9 100644 --- a/.gitignore +++ b/.gitignore @@ -17,4 +17,12 @@ bun.lockb .content-collections # Twiggy -.cursor/rules/file-structure.mdc +.cursor/ + +.open-next + +target/ + +.cargo-tools/ + +rust-migration-notes.md \ No newline at end of file diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 000000000..13be308b2 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,6 @@ +.next +node_modules +dist +build +target +rust/wasm/pkg diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 000000000..c95908767 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,3 @@ +{ + "useTabs": true +} diff --git a/.tokeignore b/.tokeignore new file mode 100644 index 000000000..244d1c392 --- /dev/null +++ b/.tokeignore @@ -0,0 +1,7 @@ +*.json +*.svg +*.xml +*.lock +**/dist/** +**/build/** +**/node_modules/** \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 6caa16da7..2fc093b55 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,20 +1,13 @@ -{ - "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" - } -} +{ + "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.eslint": "explicit" + }, + "emmet.showExpandedAbbreviation": "never" +} diff --git a/AGENTS.md b/AGENTS.md index 8ebac930d..cd59a85c8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,121 +1,23 @@ -# AGENTS.md +# Agents.md -## Overview +## Architecture -Privacy-first video editor, with a focus on simplicity and ease of use. +An ongoing migration is moving all business logic into `rust/`. Each app under `apps/` is a UI shell — it owns rendering, interaction, and platform-specific concerns, but never owns logic. The UI framework for any given app is a replaceable detail. -## Lib vs Utils +### `rust/` -- `lib/` - domain logic (specific to this app) -- `utils/` - small helper utils (generic, could be copy-pasted into any other app) +The single source of truth for all non-UI code. Everything platform-agnostic belongs here: no components, no hooks, no framework imports. -## Core Editor System +### `apps/` -The editor uses a **singleton EditorCore** that manages all editor state through specialized managers. +Each app is a frontend that calls into Rust. Logic is never duplicated between apps — only UI is, because each platform may use an entirely different framework and language to build it. -### Architecture +- `web/` — Next.js +- `desktop/` — GPUI -``` -EditorCore (singleton) -├── playback: PlaybackManager -├── timeline: TimelineManager -├── scene: SceneManager -├── project: ProjectManager -├── media: MediaManager -└── renderer: RendererManager -``` +## Web -### When to Use What +### React -#### In React Components +- Read components before using them. They may already apply classes, which affects what you need to pass and how to override them. -**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)". diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 000000000..04c00a6cc --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,7885 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", + "zeroize", +] + +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "const-random", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "aligned" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee4508988c62edf04abd8d92897fca0c2995d907ce1dfeaf369dac3716a40685" +dependencies = [ + "as-slice", +] + +[[package]] +name = "aligned-vec" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc890384c8602f339876ded803c97ad529f3842aba97f6392b3dba0dd171769b" +dependencies = [ + "equator", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "ar_archive_writer" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eb93bbb63b9c227414f6eb3a0adfddca591a8ce1e9b60661bb08969b87e340b" +dependencies = [ + "object", +] + +[[package]] +name = "arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" + +[[package]] +name = "arg_enum_proc_macro" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "as-raw-xcb-connection" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b" + +[[package]] +name = "as-slice" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "516b6b4f0e40d50dcda9365d53964ec74560ad4284da2e7fc97122cd83174516" +dependencies = [ + "stable_deref_trait", +] + +[[package]] +name = "ash" +version = "0.38.0+1.3.281" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb44936d800fea8f016d7f2311c6a4f97aebd5dc86f09906139ec848cf3a46f" +dependencies = [ + "libloading", +] + +[[package]] +name = "ash-window" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52bca67b61cb81e5553babde81b8211f713cb6db79766f80168f3e5f40ea6c82" +dependencies = [ + "ash", + "raw-window-handle", + "raw-window-metal 0.4.0", +] + +[[package]] +name = "ashpd" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2f3f79755c74fd155000314eb349864caa787c6592eace6c6882dad873d9c39" +dependencies = [ + "async-fs", + "async-net", + "enumflags2", + "futures-channel", + "futures-util", + "rand 0.9.2", + "serde", + "serde_repr", + "url", + "wayland-backend", + "wayland-client", + "wayland-protocols 0.32.11", + "zbus", +] + +[[package]] +name = "ashpd" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33a3c86f3fd70c0ffa500ed189abfa90b5a52398a45d5dc372fcc38ebeb7a645" +dependencies = [ + "async-fs", + "async-net", + "enumflags2", + "futures-channel", + "futures-util", + "rand 0.9.2", + "serde", + "serde_repr", + "url", + "zbus", +] + +[[package]] +name = "async-broadcast" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" +dependencies = [ + "event-listener 5.4.1", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-channel" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +dependencies = [ + "concurrent-queue", + "event-listener 2.5.3", + "futures-core", +] + +[[package]] +name = "async-channel" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-compression" +version = "0.4.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0f9ee0f6e02ffd7ad5816e9464499fba7b3effd01123b515c41d1697c43dad1" +dependencies = [ + "compression-codecs", + "compression-core", + "futures-io", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand 2.3.0", + "futures-lite 2.6.1", + "pin-project-lite", + "slab", +] + +[[package]] +name = "async-fs" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8034a681df4aed8b8edbd7fbe472401ecf009251c8b40556b304567052e294c5" +dependencies = [ + "async-lock", + "blocking", + "futures-lite 2.6.1", +] + +[[package]] +name = "async-global-executor" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" +dependencies = [ + "async-channel 2.5.0", + "async-executor", + "async-io", + "async-lock", + "blocking", + "futures-lite 2.6.1", + "once_cell", +] + +[[package]] +name = "async-io" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" +dependencies = [ + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite 2.6.1", + "parking", + "polling", + "rustix 1.1.4", + "slab", + "windows-sys 0.61.2", +] + +[[package]] +name = "async-lock" +version = "3.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" +dependencies = [ + "event-listener 5.4.1", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-net" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7" +dependencies = [ + "async-io", + "blocking", + "futures-lite 2.6.1", +] + +[[package]] +name = "async-process" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75" +dependencies = [ + "async-channel 2.5.0", + "async-io", + "async-lock", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener 5.4.1", + "futures-lite 2.6.1", + "rustix 1.1.4", +] + +[[package]] +name = "async-recursion" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "async-signal" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43c070bbf59cd3570b6b2dd54cd772527c7c3620fce8be898406dd3ed6adc64c" +dependencies = [ + "async-io", + "async-lock", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix 1.1.4", + "signal-hook-registry", + "slab", + "windows-sys 0.61.2", +] + +[[package]] +name = "async-std" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c8e079a4ab67ae52b7403632e4618815d6db36d2a010cfe41b02c1b1578f93b" +dependencies = [ + "async-channel 1.9.0", + "async-global-executor", + "async-io", + "async-lock", + "async-process", + "crossbeam-utils", + "futures-channel", + "futures-core", + "futures-io", + "futures-lite 2.6.1", + "gloo-timers", + "kv-log-macro", + "log", + "memchr", + "once_cell", + "pin-project-lite", + "pin-utils", + "slab", + "wasm-bindgen-futures", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "async_zip" +version = "0.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b9f7252833d5ed4b00aa9604b563529dd5e11de9c23615de2dcdf91eb87b52" +dependencies = [ + "async-compression", + "crc32fast", + "futures-lite 2.6.1", + "pin-project", + "thiserror 1.0.69", +] + +[[package]] +name = "atomic" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba" + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "av-scenechange" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f321d77c20e19b92c39e7471cf986812cbb46659d2af674adc4331ef3f18394" +dependencies = [ + "aligned", + "anyhow", + "arg_enum_proc_macro", + "arrayvec", + "log", + "num-rational", + "num-traits", + "pastey", + "rayon", + "thiserror 2.0.18", + "v_frame", + "y4m", +] + +[[package]] +name = "av1-grain" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cfddb07216410377231960af4fcab838eaa12e013417781b78bd95ee22077f8" +dependencies = [ + "anyhow", + "arrayvec", + "log", + "nom 8.0.0", + "num-rational", + "v_frame", +] + +[[package]] +name = "avif-serialize" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "375082f007bd67184fb9c0374614b29f9aaa604ec301635f72338bb65386a53d" +dependencies = [ + "arrayvec", +] + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bindgen" +version = "0.71.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3" +dependencies = [ + "bitflags 2.11.0", + "cexpr", + "clang-sys", + "itertools 0.13.0", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash 2.1.1", + "shlex", + "syn 2.0.117", +] + +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec 0.8.0", +] + +[[package]] +name = "bit-set" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34ddef2995421ab6a5c779542c81ee77c115206f4ad9d5a8e05f4ff49716a3dd" +dependencies = [ + "bit-vec 0.9.1", +] + +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + +[[package]] +name = "bit-vec" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71798fca2c1fe1086445a7258a4bc81e6e49dcd24c8d0dd9a1e57395b603f51" + +[[package]] +name = "bit_field" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" + +[[package]] +name = "bitstream-io" +version = "4.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60d4bd9d1db2c6bdf285e223a7fa369d5ce98ec767dec949c6ca62863ce61757" +dependencies = [ + "core2", +] + +[[package]] +name = "blade-graphics" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e71cfb73b98eb9f58ee84048aa1bdf4e7497fd20c141b57523499fa066b48fed" +dependencies = [ + "ash", + "ash-window", + "bitflags 2.11.0", + "bytemuck", + "codespan-reporting 0.12.0", + "glow 0.16.0", + "gpu-alloc", + "gpu-alloc-ash", + "hidden-trait", + "js-sys", + "khronos-egl", + "libloading", + "log", + "mint", + "naga 25.0.1", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", + "objc2-metal", + "objc2-quartz-core", + "objc2-ui-kit", + "once_cell", + "raw-window-handle", + "slab", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "blade-macros" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27142319e2f4c264581067eaccb9f80acccdde60d8b4bf57cc50cd3152f109ca" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "blade-util" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a6be3a82c001ba7a17b6f8e413ede5d1004e6047213f8efaf0ffc15b5c4904c" +dependencies = [ + "blade-graphics", + "bytemuck", + "log", + "profiling", +] + +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-padding" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" +dependencies = [ + "objc2", +] + +[[package]] +name = "blocking" +version = "1.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" +dependencies = [ + "async-channel 2.5.0", + "async-task", + "futures-io", + "futures-lite 2.6.1", + "piper", +] + +[[package]] +name = "bridge" +version = "0.1.0" +dependencies = [ + "quote", + "syn 2.0.117", +] + +[[package]] +name = "bstr" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "built" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4ad8f11f288f48ca24471bbd51ac257aaeaaa07adae295591266b792902ae64" + +[[package]] +name = "bumpalo" +version = "3.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" + +[[package]] +name = "bytemuck" +version = "1.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" + +[[package]] +name = "calloop" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" +dependencies = [ + "bitflags 2.11.0", + "log", + "polling", + "rustix 0.38.44", + "slab", + "thiserror 1.0.69", +] + +[[package]] +name = "calloop-wayland-source" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a66a987056935f7efce4ab5668920b5d0dac4a7c99991a67395f13702ddd20" +dependencies = [ + "calloop", + "rustix 0.38.44", + "wayland-backend", + "wayland-client", +] + +[[package]] +name = "cbc" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" +dependencies = [ + "cipher", +] + +[[package]] +name = "cbindgen" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eadd868a2ce9ca38de7eeafdcec9c7065ef89b42b32f0839278d55f35c54d1ff" +dependencies = [ + "heck 0.4.1", + "indexmap", + "log", + "proc-macro2", + "quote", + "serde", + "serde_json", + "syn 2.0.117", + "tempfile", + "toml 0.8.23", +] + +[[package]] +name = "cc" +version = "1.2.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1e928d4b69e3077709075a938a05ffbedfa53a84c8f766efbf8220bb1ff60e1" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom 7.1.3", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "cgl" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ced0551234e87afee12411d535648dd89d2e7f34c78b753395567aff3d447ff" +dependencies = [ + "libc", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", + "zeroize", +] + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "cocoa" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6140449f97a6e97f9511815c5632d84c8aacf8ac271ad77c559218161a1373c" +dependencies = [ + "bitflags 1.3.2", + "block", + "cocoa-foundation 0.1.2", + "core-foundation 0.9.4", + "core-graphics 0.23.2", + "foreign-types", + "libc", + "objc", +] + +[[package]] +name = "cocoa" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f79398230a6e2c08f5c9760610eb6924b52aa9e7950a619602baba59dcbbdbb2" +dependencies = [ + "bitflags 2.11.0", + "block", + "cocoa-foundation 0.2.0", + "core-foundation 0.10.0", + "core-graphics 0.24.0", + "foreign-types", + "libc", + "objc", +] + +[[package]] +name = "cocoa-foundation" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c6234cbb2e4c785b456c0644748b1ac416dd045799740356f8363dfe00c93f7" +dependencies = [ + "bitflags 1.3.2", + "block", + "core-foundation 0.9.4", + "core-graphics-types 0.1.3", + "libc", + "objc", +] + +[[package]] +name = "cocoa-foundation" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14045fb83be07b5acf1c0884b2180461635b433455fa35d1cd6f17f1450679d" +dependencies = [ + "bitflags 2.11.0", + "block", + "core-foundation 0.10.0", + "core-graphics-types 0.2.0", + "libc", + "objc", +] + +[[package]] +name = "codespan-reporting" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81" +dependencies = [ + "serde", + "termcolor", + "unicode-width", +] + +[[package]] +name = "codespan-reporting" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af491d569909a7e4dee0ad7db7f5341fef5c614d5b8ec8cf765732aba3cff681" +dependencies = [ + "serde", + "termcolor", + "unicode-width", +] + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "command-fds" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f849b92c694fe237ecd8fafd1ba0df7ae0d45c1df6daeb7f68ed4220d51640bd" +dependencies = [ + "nix 0.30.1", + "thiserror 2.0.18", +] + +[[package]] +name = "compositor" +version = "0.1.0" +dependencies = [ + "bytemuck", + "effects", + "gpu", + "masks", + "serde", + "thiserror 2.0.18", +] + +[[package]] +name = "compression-codecs" +version = "0.4.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb7b51a7d9c967fc26773061ba86150f19c50c0d65c887cb1fbe295fd16619b7" +dependencies = [ + "compression-core", + "deflate64", + "flate2", + "memchr", +] + +[[package]] +name = "compression-core" +version = "0.4.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75984efb6ed102a0d42db99afb6c1948f0380d1d91808d5529916e6c08b49d8d" + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if", + "wasm-bindgen", +] + +[[package]] +name = "const-random" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" +dependencies = [ + "const-random-macro", +] + +[[package]] +name = "const-random-macro" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" +dependencies = [ + "getrandom 0.2.17", + "once_cell", + "tiny-keccak", +] + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "core-graphics" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" +dependencies = [ + "bitflags 1.3.2", + "core-foundation 0.9.4", + "core-graphics-types 0.1.3", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1" +dependencies = [ + "bitflags 2.11.0", + "core-foundation 0.10.0", + "core-graphics-types 0.2.0", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-helmer-fork" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32eb7c354ae9f6d437a6039099ce7ecd049337a8109b23d73e48e8ffba8e9cd5" +dependencies = [ + "bitflags 2.11.0", + "core-foundation 0.9.4", + "core-graphics-types 0.1.3", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" +dependencies = [ + "bitflags 1.3.2", + "core-foundation 0.9.4", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" +dependencies = [ + "bitflags 2.11.0", + "core-foundation 0.10.0", + "libc", +] + +[[package]] +name = "core-graphics2" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e4583956b9806b69f73fcb23aee05eb3620efc282972f08f6a6db7504f8334d" +dependencies = [ + "bitflags 2.11.0", + "block", + "cfg-if", + "core-foundation 0.10.0", + "libc", +] + +[[package]] +name = "core-text" +version = "21.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a593227b66cbd4007b2a050dfdd9e1d1318311409c8d600dc82ba1b15ca9c130" +dependencies = [ + "core-foundation 0.10.0", + "core-graphics 0.24.0", + "foreign-types", + "libc", +] + +[[package]] +name = "core-video" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d45e71d5be22206bed53c3c3cb99315fc4c3d31b8963808c6bc4538168c4f8ef" +dependencies = [ + "block", + "core-foundation 0.10.0", + "core-graphics2", + "io-surface", + "libc", + "metal", +] + +[[package]] +name = "core2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" +dependencies = [ + "memchr", +] + +[[package]] +name = "core_maths" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77745e017f5edba1a9c1d854f6f3a52dac8a12dd5af5d2f54aecf61e43d80d30" +dependencies = [ + "libm", +] + +[[package]] +name = "cosmic-text" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da46a9d5a8905cc538a4a5bceb6a4510de7a51049c5588c0114efce102bcbbe8" +dependencies = [ + "bitflags 2.11.0", + "fontdb 0.16.2", + "log", + "rangemap", + "rustc-hash 1.1.0", + "rustybuzz 0.14.1", + "self_cell", + "smol_str", + "swash", + "sys-locale", + "ttf-parser 0.21.1", + "unicode-bidi", + "unicode-linebreak", + "unicode-script", + "unicode-segmentation", +] + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-queue" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "typenum", +] + +[[package]] +name = "ctor" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec09e802f5081de6157da9a75701d6c713d8dc3ba52571fd4bd25f412644e8a6" +dependencies = [ + "ctor-proc-macro", + "dtor", +] + +[[package]] +name = "ctor-proc-macro" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2931af7e13dc045d8e9d26afccc6fa115d64e115c9c84b1166288b46f6782c2" + +[[package]] +name = "data-url" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376" + +[[package]] +name = "deflate64" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac6b926516df9c60bfa16e107b21086399f8285a44ca9711344b9e553c5146e2" + +[[package]] +name = "derive_more" +version = "0.99.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.117", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "dirs" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" +dependencies = [ + "dirs-sys 0.3.7", +] + +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys 0.4.1", +] + +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + +[[package]] +name = "dispatch" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" + +[[package]] +name = "dispatch2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" +dependencies = [ + "bitflags 2.11.0", + "objc2", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "dlib" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab8ecd87370524b461f8557c119c405552c396ed91fc0a8eec68679eab26f94a" +dependencies = [ + "libloading", +] + +[[package]] +name = "document-features" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +dependencies = [ + "litrs", +] + +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + +[[package]] +name = "dtor" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97cbdf2ad6846025e8e25df05171abfb30e3ababa12ee0a0e44b9bbe570633a8" +dependencies = [ + "dtor-proc-macro", +] + +[[package]] +name = "dtor-proc-macro" +version = "0.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7454e41ff9012c00d53cf7f475c5e3afa3b91b7c90568495495e8d9bf47a1055" + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "dwrote" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b35532432acc8b19ceed096e35dfa088d3ea037fe4f3c085f1f97f33b4d02" +dependencies = [ + "lazy_static", + "libc", + "winapi", + "wio", +] + +[[package]] +name = "dyn-clone" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" + +[[package]] +name = "effects" +version = "0.1.0" +dependencies = [ + "bytemuck", + "gpu", + "thiserror 2.0.18", + "wgpu", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "embed-resource" +version = "3.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63a1d0de4f2249aa0ff5884d7080814f446bb241a559af6c170a41e878ed2d45" +dependencies = [ + "cc", + "memchr", + "rustc_version", + "toml 0.9.12+spec-1.1.0", + "vswhom", + "winreg", +] + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "endi" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099" + +[[package]] +name = "enumflags2" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef" +dependencies = [ + "enumflags2_derive", + "serde", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "equator" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4711b213838dfee0117e3be6ac926007d7f433d7bbe33595975d4190cb07e6fc" +dependencies = [ + "equator-macro", +] + +[[package]] +name = "equator-macro" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "erased-serde" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2add8a07dd6a8d93ff627029c51de145e12686fbc36ecb298ac22e74cf02dec" +dependencies = [ + "serde", + "serde_core", + "typeid", +] + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "etagere" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc89bf99e5dc15954a60f707c1e09d7540e5cd9af85fa75caa0b510bc08c5342" +dependencies = [ + "euclid", + "svg_fmt", +] + +[[package]] +name = "euclid" +version = "0.22.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1a05365e3b1c6d1650318537c7460c6923f1abdd272ad6842baa2b509957a06" +dependencies = [ + "num-traits", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "event-listener" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener 5.4.1", + "pin-project-lite", +] + +[[package]] +name = "exr" +version = "1.74.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4300e043a56aa2cb633c01af81ca8f699a321879a7854d3896a0ba89056363be" +dependencies = [ + "bit_field", + "half", + "lebe", + "miniz_oxide", + "rayon-core", + "smallvec", + "zune-inflate", +] + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "fax" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f05de7d48f37cd6730705cbca900770cab77a89f413d23e100ad7fad7795a0ab" +dependencies = [ + "fax_derive", +] + +[[package]] +name = "fax_derive" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0aca10fb742cb43f9e7bb8467c91aa9bcb8e3ffbc6a6f7389bb93ffc920577d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "filedescriptor" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e40758ed24c9b2eeb76c35fb0aebc66c626084edd827e07e1552279814c6682d" +dependencies = [ + "libc", + "thiserror 1.0.69", + "winapi", +] + +[[package]] +name = "filetime" +version = "0.2.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98844151eee8917efc50bd9e8318cb963ae8b297431495d3f758616ea5c57db" +dependencies = [ + "cfg-if", + "libc", + "libredox", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "float-cmp" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" + +[[package]] +name = "float-ord" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce81f49ae8a0482e4c55ea62ebbd7e5a686af544c00b9d090bba3ff9be97b3d" + +[[package]] +name = "float_next_after" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bf7cc16383c4b8d58b9905a8509f02926ce3058053c056376248d958c9df1e8" + +[[package]] +name = "flume" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" +dependencies = [ + "futures-core", + "futures-sink", + "nanorand", + "spin", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "font-types" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73829a7b5c91198af28a99159b7ae4afbb252fb906159ff7f189f3a2ceaa3df2" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "fontconfig-parser" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbc773e24e02d4ddd8395fd30dc147524273a83e54e0f312d986ea30de5f5646" +dependencies = [ + "roxmltree", +] + +[[package]] +name = "fontdb" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0299020c3ef3f60f526a4f64ab4a3d4ce116b1acbf24cdd22da0068e5d81dc3" +dependencies = [ + "fontconfig-parser", + "log", + "memmap2", + "slotmap", + "tinyvec", + "ttf-parser 0.20.0", +] + +[[package]] +name = "fontdb" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "457e789b3d1202543297a350643cf459f836cade38934e7a4cf6a39e7cde2905" +dependencies = [ + "fontconfig-parser", + "log", + "memmap2", + "slotmap", + "tinyvec", + "ttf-parser 0.25.1", +] + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "freetype-sys" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7edc5b9669349acfda99533e9e0bcf26a51862ab43b08ee7745c55d28eb134" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + +[[package]] +name = "futf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" +dependencies = [ + "mac", + "new_debug_unreachable", +] + +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-lite" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" +dependencies = [ + "fastrand 1.9.0", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite", + "waker-fn", +] + +[[package]] +name = "futures-lite" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" +dependencies = [ + "fastrand 2.3.0", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "gethostname" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8" +dependencies = [ + "rustix 1.1.4", + "windows-link 0.2.1", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi 5.3.0", + "wasip2", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi 6.0.0", + "wasip2", + "wasip3", +] + +[[package]] +name = "gif" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5df2ba84018d80c213569363bdcd0c64e6933c67fe4c1d60ecf822971a3c35e" +dependencies = [ + "color_quant", + "weezl", +] + +[[package]] +name = "gl_generator" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d" +dependencies = [ + "khronos_api", + "log", + "xml-rs", +] + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "globset" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52dfc19153a48bde0cbd630453615c8151bce3a5adfac7a0aebfbf0a1e1f57e3" +dependencies = [ + "aho-corasick", + "bstr", + "log", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "gloo-timers" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "gloo-utils" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa" +dependencies = [ + "js-sys", + "serde", + "serde_json", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "glow" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e5ea60d70410161c8bf5da3fdfeaa1c72ed2c15f8bbb9d19fe3a4fad085f08" +dependencies = [ + "js-sys", + "slotmap", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "glow" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29038e1c483364cc6bb3cf78feee1816002e127c331a1eec55a4d202b9e1adb5" +dependencies = [ + "js-sys", + "slotmap", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "glutin_wgl_sys" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c4ee00b289aba7a9e5306d57c2d05499b2e5dc427f84ac708bd2c090212cf3e" +dependencies = [ + "gl_generator", +] + +[[package]] +name = "gpu" +version = "0.1.0" +dependencies = [ + "bytemuck", + "thiserror 2.0.18", + "wasm-bindgen", + "web-sys", + "wgpu", +] + +[[package]] +name = "gpu-alloc" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171" +dependencies = [ + "bitflags 2.11.0", + "gpu-alloc-types", +] + +[[package]] +name = "gpu-alloc-ash" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbda7a18a29bc98c2e0de0435c347df935bf59489935d0cbd0b73f1679b6f79a" +dependencies = [ + "ash", + "gpu-alloc-types", + "tinyvec", +] + +[[package]] +name = "gpu-alloc-types" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4" +dependencies = [ + "bitflags 2.11.0", +] + +[[package]] +name = "gpu-allocator" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51255ea7cfaadb6c5f1528d43e92a82acb2b96c43365989a28b2d44ee38f8795" +dependencies = [ + "ash", + "hashbrown 0.16.1", + "log", + "presser", + "thiserror 2.0.18", + "windows 0.62.2", +] + +[[package]] +name = "gpu-descriptor" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b89c83349105e3732062a895becfc71a8f921bb71ecbbdd8ff99263e3b53a0ca" +dependencies = [ + "bitflags 2.11.0", + "gpu-descriptor-types", + "hashbrown 0.15.5", +] + +[[package]] +name = "gpu-descriptor-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91" +dependencies = [ + "bitflags 2.11.0", +] + +[[package]] +name = "gpui" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "979b45cfa6ec723b6f42330915a1b3769b930d02b2d505f9697f8ca602bee707" +dependencies = [ + "anyhow", + "as-raw-xcb-connection", + "ashpd 0.11.1", + "async-task", + "bindgen", + "blade-graphics", + "blade-macros", + "blade-util", + "block", + "bytemuck", + "calloop", + "calloop-wayland-source", + "cbindgen", + "cocoa 0.26.0", + "cocoa-foundation 0.2.0", + "core-foundation 0.10.0", + "core-foundation-sys", + "core-graphics 0.24.0", + "core-text", + "core-video", + "cosmic-text", + "ctor", + "derive_more", + "embed-resource", + "etagere", + "filedescriptor", + "flume", + "foreign-types", + "futures", + "gpui-macros", + "gpui_collections", + "gpui_http_client", + "gpui_media", + "gpui_refineable", + "gpui_semantic_version", + "gpui_sum_tree", + "gpui_util", + "gpui_util_macros", + "image", + "inventory", + "itertools 0.14.0", + "libc", + "log", + "lyon", + "metal", + "naga 25.0.1", + "num_cpus", + "objc", + "oo7", + "open", + "parking", + "parking_lot", + "pathfinder_geometry", + "pin-project", + "postage", + "profiling", + "rand 0.9.2", + "raw-window-handle", + "resvg", + "schemars", + "seahash", + "serde", + "serde_json", + "slotmap", + "smallvec", + "smol", + "stacksafe", + "strum 0.27.2", + "taffy", + "thiserror 2.0.18", + "usvg", + "uuid", + "waker-fn", + "wayland-backend", + "wayland-client", + "wayland-cursor", + "wayland-protocols 0.31.2", + "wayland-protocols-plasma", + "windows 0.61.3", + "windows-core 0.61.2", + "windows-numerics 0.2.0", + "windows-registry 0.5.3", + "x11-clipboard", + "x11rb", + "xkbcommon", + "zed-font-kit", + "zed-scap", + "zed-xim", +] + +[[package]] +name = "gpui-macros" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcb02dd63a2859714ac7b6b476937617c3c744157af1b49f7c904023a79039be" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "gpui_collections" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae39dc6d3d201be97e4bc08d96dbef2bc5b5c3d5734e05786e8cc3043342351c" +dependencies = [ + "indexmap", + "rustc-hash 2.1.1", +] + +[[package]] +name = "gpui_derive_refineable" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "644de174341a87b3478bd65b66bca38af868bcf2b2e865700523734f83cfc664" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "gpui_http_client" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23822b0a6d2c5e6a42507980a0ab3848610ea908942c8ef98187f646f690335e" +dependencies = [ + "anyhow", + "async-compression", + "async-fs", + "bytes", + "derive_more", + "futures", + "gpui_util", + "http", + "http-body", + "log", + "parking_lot", + "serde", + "serde_json", + "sha2", + "tempfile", + "url", + "zed-async-tar", + "zed-reqwest", +] + +[[package]] +name = "gpui_media" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05cb8912ae17371725132d2b7eec6797a255accc95d58ee5c1134b529810f14b" +dependencies = [ + "anyhow", + "bindgen", + "core-foundation 0.10.0", + "core-video", + "ctor", + "foreign-types", + "metal", + "objc", +] + +[[package]] +name = "gpui_perf" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40a0961dcf598955130e867f4b731150a20546427b41b1a63767c1037a86d77" +dependencies = [ + "gpui_collections", + "serde", + "serde_json", +] + +[[package]] +name = "gpui_refineable" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "258cb099254e9468181aee5614410fba61db4ae115fc1d51b4a0b985f60d6641" +dependencies = [ + "gpui_derive_refineable", +] + +[[package]] +name = "gpui_semantic_version" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "201e45eff7b695528fb3af6560a534943fbc2db5323d755b9d198bd743948e35" +dependencies = [ + "anyhow", + "serde", +] + +[[package]] +name = "gpui_sum_tree" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4f3bedd573fafafa13d1200b356c588cf094fb2786e3684bb3f5ea59b549fa9" +dependencies = [ + "arrayvec", + "log", + "rayon", +] + +[[package]] +name = "gpui_util" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68faea25903ae524de9af83990b9aa51bcbc8dd085929ac0aea7fd41905e05c3" +dependencies = [ + "anyhow", + "async-fs", + "async_zip", + "command-fds", + "dirs 4.0.0", + "dunce", + "futures", + "futures-lite 1.13.0", + "globset", + "gpui_collections", + "itertools 0.14.0", + "libc", + "log", + "nix 0.29.0", + "regex", + "rust-embed", + "schemars", + "serde", + "serde_json", + "serde_json_lenient", + "shlex", + "smol", + "take-until", + "tempfile", + "tendril", + "unicase", + "walkdir", + "which", +] + +[[package]] +name = "gpui_util_macros" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c28f65ef47fb97e21e82fd4dd75ccc2506eda010c846dc8054015ea234f1a22" +dependencies = [ + "gpui_perf", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "grid" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12101ecc8225ea6d675bc70263074eab6169079621c2186fe0c66590b2df9681" + +[[package]] +name = "h2" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "num-traits", + "zerocopy", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash 0.1.5", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash 0.2.0", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hexf-parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" + +[[package]] +name = "hidden-trait" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ed9e850438ac849bec07e7d09fbe9309cbd396a5988c30b010580ce08860df" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "home" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "http" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "hyper" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "h2", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "pin-utils", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-native-certs", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "libc", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + +[[package]] +name = "icu_collections" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" + +[[package]] +name = "icu_properties" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" + +[[package]] +name = "icu_provider" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "image" +version = "0.25.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104" +dependencies = [ + "bytemuck", + "byteorder-lite", + "color_quant", + "exr", + "gif", + "image-webp", + "moxcms", + "num-traits", + "png 0.18.1", + "qoi", + "ravif", + "rayon", + "rgb", + "tiff", + "zune-core", + "zune-jpeg", +] + +[[package]] +name = "image-webp" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525e9ff3e1a4be2fbea1fdf0e98686a6d98b4d8f937e1bf7402245af1909e8c3" +dependencies = [ + "byteorder-lite", + "quick-error", +] + +[[package]] +name = "imagesize" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edcd27d72f2f071c64249075f42e205ff93c9a4c5f6c6da53e79ed9f9832c285" + +[[package]] +name = "imgref" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c5cedc30da3a610cac6b4ba17597bdf7152cf974e8aab3afb3d54455e371c8" + +[[package]] +name = "indexmap" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +dependencies = [ + "equivalent", + "hashbrown 0.16.1", + "serde", + "serde_core", +] + +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "block-padding", + "generic-array", +] + +[[package]] +name = "instant" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "interpolate_name" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "inventory" +version = "0.3.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "009ae045c87e7082cb72dab0ccd01ae075dd00141ddc108f43a0ea150a9e7227" +dependencies = [ + "rustversion", +] + +[[package]] +name = "io-surface" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "554b8c5d64ec09a3a520fe58e4d48a73e00ff32899cdcbe32a4877afd4968b8e" +dependencies = [ + "cgl", + "core-foundation 0.10.0", + "core-foundation-sys", + "leaky-cow", +] + +[[package]] +name = "ipnet" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" + +[[package]] +name = "is-docker" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "928bae27f42bc99b60d9ac7334e3a21d10ad8f1835a4e12ec3ec0464765ed1b3" +dependencies = [ + "once_cell", +] + +[[package]] +name = "is-wsl" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "173609498df190136aa7dea1a91db051746d339e18476eed5ca40521f02d7aa5" +dependencies = [ + "is-docker", + "once_cell", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "jni-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258" +dependencies = [ + "jni-sys 0.4.1", +] + +[[package]] +name = "jni-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" +dependencies = [ + "jni-sys-macros", +] + +[[package]] +name = "jni-sys-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" +dependencies = [ + "quote", + "syn 2.0.117", +] + +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom 0.3.4", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "797146bb2677299a1eb6b7b50a890f4c361b29ef967addf5b2fa45dae1bb6d7d" +dependencies = [ + "cfg-if", + "futures-util", + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "khronos-egl" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76" +dependencies = [ + "libc", + "libloading", + "pkg-config", +] + +[[package]] +name = "khronos_api" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" + +[[package]] +name = "kurbo" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62026ae44756f8a599ba21140f350303d4f08dcdcc71b5ad9c9bb8128c13c62" +dependencies = [ + "arrayvec", + "euclid", + "smallvec", +] + +[[package]] +name = "kv-log-macro" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" +dependencies = [ + "log", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] + +[[package]] +name = "leak" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd100e01f1154f2908dfa7d02219aeab25d0b9c7fa955164192e3245255a0c73" + +[[package]] +name = "leaky-cow" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40a8225d44241fd324a8af2806ba635fc7c8a7e9a7de4d5cf3ef54e71f5926fc" +dependencies = [ + "leak", +] + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "lebe" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8" + +[[package]] +name = "libc" +version = "0.2.183" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" + +[[package]] +name = "libfuzzer-sys" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f12a681b7dd8ce12bff52488013ba614b869148d54dd79836ab85aafdd53f08d" +dependencies = [ + "arbitrary", + "cc", +] + +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link 0.2.1", +] + +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + +[[package]] +name = "libredox" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ddbf48fd451246b1f8c2610bd3b4ac0cc6e149d89832867093ab69a17194f08" +dependencies = [ + "bitflags 2.11.0", + "libc", + "plain", + "redox_syscall 0.7.3", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "litemap" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" + +[[package]] +name = "litrs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +dependencies = [ + "serde_core", + "value-bag", +] + +[[package]] +name = "loop9" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062" +dependencies = [ + "imgref", +] + +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + +[[package]] +name = "lyon" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0578bdecb7d6d88987b8b2b1e3a4e2f81df9d0ece1078623324a567904e7b7" +dependencies = [ + "lyon_algorithms", + "lyon_tessellation", +] + +[[package]] +name = "lyon_algorithms" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9815fac08e6fd96733a11dce4f9d15a3f338e96a2e2311ee21e1b738efc2bc0f" +dependencies = [ + "lyon_path", + "num-traits", +] + +[[package]] +name = "lyon_geom" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4336502e29e32af93cf2dad2214ed6003c17ceb5bd499df77b1de663b9042b92" +dependencies = [ + "arrayvec", + "euclid", + "num-traits", +] + +[[package]] +name = "lyon_path" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c463f9c428b7fc5ec885dcd39ce4aa61e29111d0e33483f6f98c74e89d8621e" +dependencies = [ + "lyon_geom", + "num-traits", +] + +[[package]] +name = "lyon_tessellation" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e43b7e44161571868f5c931d12583592c223c5583eef86b08aa02b7048a3552" +dependencies = [ + "float_next_after", + "lyon_path", + "num-traits", +] + +[[package]] +name = "mac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "masks" +version = "0.1.0" +dependencies = [ + "bytemuck", + "gpu", + "wgpu", +] + +[[package]] +name = "maybe-rayon" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" +dependencies = [ + "cfg-if", + "rayon", +] + +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest", +] + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "memmap2" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714098028fe011992e1c3962653c96b2d578c4b4bce9036e15ff220319b1e0e3" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "metal" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ecfd3296f8c56b7c1f6fbac3c71cefa9d78ce009850c45000015f206dc7fa21" +dependencies = [ + "bitflags 2.11.0", + "block", + "core-graphics-types 0.1.3", + "foreign-types", + "log", + "objc", + "paste", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "mint" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e53debba6bda7a793e5f99b8dacf19e626084f525f7829104ba9898f367d85ff" + +[[package]] +name = "mio" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "moxcms" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb85c154ba489f01b25c0d36ae69a87e4a1c73a72631fc6c0eb6dde34a73e44b" +dependencies = [ + "num-traits", + "pxfm", +] + +[[package]] +name = "naga" +version = "25.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b977c445f26e49757f9aca3631c3b8b836942cb278d69a92e7b80d3b24da632" +dependencies = [ + "arrayvec", + "bit-set 0.8.0", + "bitflags 2.11.0", + "cfg_aliases", + "codespan-reporting 0.12.0", + "half", + "hashbrown 0.15.5", + "hexf-parse", + "indexmap", + "log", + "num-traits", + "once_cell", + "rustc-hash 1.1.0", + "spirv 0.3.0+sdk-1.3.268.0", + "strum 0.26.3", + "thiserror 2.0.18", + "unicode-ident", +] + +[[package]] +name = "naga" +version = "29.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2630921705b9b01dcdd0b6864b9562ca3c1951eecd0f0c4f5f04f61e412647" +dependencies = [ + "arrayvec", + "bit-set 0.9.1", + "bitflags 2.11.0", + "cfg-if", + "cfg_aliases", + "codespan-reporting 0.13.1", + "half", + "hashbrown 0.16.1", + "hexf-parse", + "indexmap", + "libm", + "log", + "num-traits", + "once_cell", + "rustc-hash 1.1.0", + "spirv 0.4.0+sdk-1.4.341.0", + "thiserror 2.0.18", + "unicode-ident", +] + +[[package]] +name = "nanorand" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" +dependencies = [ + "getrandom 0.2.17", +] + +[[package]] +name = "ndk-sys" +version = "0.6.0+11769913" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" +dependencies = [ + "jni-sys 0.3.1", +] + +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags 2.11.0", + "cfg-if", + "cfg_aliases", + "libc", +] + +[[package]] +name = "nix" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" +dependencies = [ + "bitflags 2.11.0", + "cfg-if", + "cfg_aliases", + "libc", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "nom" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" +dependencies = [ + "memchr", +] + +[[package]] +name = "noop_proc_macro" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" + +[[package]] +name = "ntapi" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3b335231dfd352ffb0f8017f3b6027a4917f7df785ea2143d8af2adc66980ae" +dependencies = [ + "winapi", +] + +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-bigint-dig" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7" +dependencies = [ + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand 0.8.5", + "serde", + "smallvec", + "zeroize", +] + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "num_cpus" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", + "objc_exception", +] + +[[package]] +name = "objc-foundation" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" +dependencies = [ + "block", + "objc", + "objc_id", +] + +[[package]] +name = "objc2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" +dependencies = [ + "objc2-encode", +] + +[[package]] +name = "objc2-app-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" +dependencies = [ + "bitflags 2.11.0", + "objc2", + "objc2-core-foundation", + "objc2-foundation", + "objc2-quartz-core", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +dependencies = [ + "bitflags 2.11.0", + "dispatch2", + "objc2", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" +dependencies = [ + "bitflags 2.11.0", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-metal" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0125f776a10d00af4152d74616409f0d4a2053a6f57fa5b7d6aa2854ac04794" +dependencies = [ + "bitflags 2.11.0", + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f" +dependencies = [ + "bitflags 2.11.0", + "objc2", + "objc2-core-foundation", + "objc2-foundation", + "objc2-metal", +] + +[[package]] +name = "objc2-ui-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" +dependencies = [ + "bitflags 2.11.0", + "objc2", + "objc2-core-foundation", + "objc2-foundation", + "objc2-quartz-core", +] + +[[package]] +name = "objc_exception" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" +dependencies = [ + "cc", +] + +[[package]] +name = "objc_id" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" +dependencies = [ + "objc", +] + +[[package]] +name = "object" +version = "0.37.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "oo7" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3299dd401feaf1d45afd8fd1c0586f10fcfb22f244bb9afa942cec73503b89d" +dependencies = [ + "aes", + "ashpd 0.12.3", + "async-fs", + "async-io", + "async-lock", + "blocking", + "cbc", + "cipher", + "digest", + "endi", + "futures-lite 2.6.1", + "futures-util", + "getrandom 0.3.4", + "hkdf", + "hmac", + "md-5", + "num", + "num-bigint-dig", + "pbkdf2", + "rand 0.9.2", + "serde", + "sha2", + "subtle", + "zbus", + "zbus_macros", + "zeroize", + "zvariant", +] + +[[package]] +name = "open" +version = "5.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43bb73a7fa3799b198970490a51174027ba0d4ec504b03cd08caf513d40024bc" +dependencies = [ + "is-wsl", + "libc", + "pathdiff", +] + +[[package]] +name = "opencut-desktop" +version = "0.1.0" +dependencies = [ + "gpui", +] + +[[package]] +name = "opencut-wasm" +version = "0.2.10" +dependencies = [ + "bridge", + "compositor", + "console_error_panic_hook", + "effects", + "gpu", + "js-sys", + "masks", + "num-traits", + "serde", + "serde-wasm-bindgen", + "time", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "ordered-float" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7d950ca161dc355eaf28f82b11345ed76c6e1f6eb1f4f4479e0323b9e2fbd0e" +dependencies = [ + "num-traits", +] + +[[package]] +name = "ordered-stream" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" +dependencies = [ + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.5.18", + "smallvec", + "windows-link 0.2.1", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pastey" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec" + +[[package]] +name = "pathdiff" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" + +[[package]] +name = "pathfinder_geometry" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b7e7b4ea703700ce73ebf128e1450eb69c3a8329199ffbfb9b2a0418e5ad3" +dependencies = [ + "log", + "pathfinder_simd", +] + +[[package]] +name = "pathfinder_simd" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf9027960355bf3afff9841918474a81a5f972ac6d226d518060bba758b5ad57" +dependencies = [ + "rustc_version", +] + +[[package]] +name = "pbkdf2" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +dependencies = [ + "digest", + "hmac", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pico-args" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" + +[[package]] +name = "pin-project" +version = "1.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1749c7ed4bcaf4c3d0a3efc28538844fb29bcdd7d2b67b2be7e20ba861ff517" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b20ed30f105399776b9c883e68e536ef602a16ae6f596d2c473591d6ad64c6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "piper" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1" +dependencies = [ + "atomic-waker", + "fastrand 2.3.0", + "futures-io", +] + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" + +[[package]] +name = "png" +version = "0.17.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "png" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" +dependencies = [ + "bitflags 2.11.0", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "polling" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi", + "pin-project-lite", + "rustix 1.1.4", + "windows-sys 0.61.2", +] + +[[package]] +name = "pollster" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5da3b0203fd7ee5720aa0b5e790b591aa5d3f41c3ed2c34a3a393382198af2f7" + +[[package]] +name = "portable-atomic" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" + +[[package]] +name = "portable-atomic-util" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "091397be61a01d4be58e7841595bd4bfedb15f1cd54977d79b8271e94ed799a3" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "postage" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af3fb618632874fb76937c2361a7f22afd393c982a2165595407edc75b06d3c1" +dependencies = [ + "atomic", + "crossbeam-queue", + "futures", + "log", + "parking_lot", + "pin-project", + "pollster", + "static_assertions", + "thiserror 1.0.69", +] + +[[package]] +name = "potential_utf" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +dependencies = [ + "zerovec", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "presser" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa" + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.117", +] + +[[package]] +name = "proc-macro-crate" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" +dependencies = [ + "toml_edit 0.25.8+spec-1.1.0", +] + +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "profiling" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773" +dependencies = [ + "profiling-procmacros", +] + +[[package]] +name = "profiling-procmacros" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b" +dependencies = [ + "quote", + "syn 2.0.117", +] + +[[package]] +name = "psm" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3852766467df634d74f0b2d7819bf8dc483a0eb2e3b0f50f756f9cfe8b0d18d8" +dependencies = [ + "ar_archive_writer", + "cc", +] + +[[package]] +name = "pxfm" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5a041e753da8b807c9255f28de81879c78c876392ff2469cde94799b2896b9d" + +[[package]] +name = "qoi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "quick-error" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + +[[package]] +name = "quick-xml" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eff6510e86862b57b210fd8cbe8ed3f0d7d600b9c2863cd4549a2e033c66e956" +dependencies = [ + "memchr", +] + +[[package]] +name = "quick-xml" +version = "0.39.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "958f21e8e7ceb5a1aa7fa87fab28e7c75976e0bfe7e23ff069e0a260f894067d" +dependencies = [ + "memchr", +] + +[[package]] +name = "quinn" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash 2.1.1", + "rustls", + "socket2", + "thiserror 2.0.18", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" +dependencies = [ + "bytes", + "getrandom 0.3.4", + "lru-slab", + "rand 0.9.2", + "ring", + "rustc-hash 2.1.1", + "rustls", + "rustls-pki-types", + "slab", + "thiserror 2.0.18", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.60.2", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.17", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "range-alloc" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca45419789ae5a7899559e9512e58ca889e41f04f1f2445e9f4b290ceccd1d08" + +[[package]] +name = "rangemap" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "973443cf09a9c8656b574a866ab68dfa19f0867d0340648c7d2f6a71b8a8ea68" + +[[package]] +name = "rav1e" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43b6dd56e85d9483277cde964fd1bdb0428de4fec5ebba7540995639a21cb32b" +dependencies = [ + "aligned-vec", + "arbitrary", + "arg_enum_proc_macro", + "arrayvec", + "av-scenechange", + "av1-grain", + "bitstream-io", + "built", + "cfg-if", + "interpolate_name", + "itertools 0.14.0", + "libc", + "libfuzzer-sys", + "log", + "maybe-rayon", + "new_debug_unreachable", + "noop_proc_macro", + "num-derive", + "num-traits", + "paste", + "profiling", + "rand 0.9.2", + "rand_chacha 0.9.0", + "simd_helpers", + "thiserror 2.0.18", + "v_frame", + "wasm-bindgen", +] + +[[package]] +name = "ravif" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e52310197d971b0f5be7fe6b57530dcd27beb35c1b013f29d66c1ad73fbbcc45" +dependencies = [ + "avif-serialize", + "imgref", + "loop9", + "quick-error", + "rav1e", + "rayon", + "rgb", +] + +[[package]] +name = "raw-window-handle" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" + +[[package]] +name = "raw-window-metal" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76e8caa82e31bb98fee12fa8f051c94a6aa36b07cddb03f0d4fc558988360ff1" +dependencies = [ + "cocoa 0.25.0", + "core-graphics 0.23.2", + "objc", + "raw-window-handle", +] + +[[package]] +name = "raw-window-metal" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40d213455a5f1dc59214213c7330e074ddf8114c9a42411eb890c767357ce135" +dependencies = [ + "objc2", + "objc2-core-foundation", + "objc2-foundation", + "objc2-quartz-core", +] + +[[package]] +name = "rayon" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "read-fonts" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b634fabf032fab15307ffd272149b622260f55974d9fad689292a5d33df02e5" +dependencies = [ + "bytemuck", + "font-types", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags 2.11.0", +] + +[[package]] +name = "redox_syscall" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce70a74e890531977d37e532c34d45e9055d2409ed08ddba14529471ed0be16" +dependencies = [ + "bitflags 2.11.0", +] + +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom 0.2.17", + "libredox", + "thiserror 1.0.69", +] + +[[package]] +name = "ref-cast" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "renderdoc-sys" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" + +[[package]] +name = "resvg" +version = "0.45.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8928798c0a55e03c9ca6c4c6846f76377427d2c1e1f7e6de3c06ae57942df43" +dependencies = [ + "log", + "pico-args", + "rgb", + "svgtypes", + "tiny-skia", + "usvg", +] + +[[package]] +name = "rgb" +version = "0.8.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b34b781b31e5d73e9fbc8689c70551fd1ade9a19e3e28cfec8580a79290cc4" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "roxmltree" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" + +[[package]] +name = "rust-embed" +version = "8.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04113cb9355a377d83f06ef1f0a45b8ab8cd7d8b1288160717d66df5c7988d27" +dependencies = [ + "rust-embed-impl", + "rust-embed-utils", + "walkdir", +] + +[[package]] +name = "rust-embed-impl" +version = "8.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0902e4c7c8e997159ab384e6d0fc91c221375f6894346ae107f47dd0f3ccaa" +dependencies = [ + "proc-macro2", + "quote", + "rust-embed-utils", + "syn 2.0.117", + "walkdir", +] + +[[package]] +name = "rust-embed-utils" +version = "8.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bcdef0be6fe7f6fa333b1073c949729274b05f123a0ad7efcb8efd878e5c3b1" +dependencies = [ + "globset", + "sha2", + "walkdir", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags 2.11.0", + "errno", + "libc", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags 2.11.0", + "errno", + "libc", + "linux-raw-sys 0.12.1", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls" +version = "0.23.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" +dependencies = [ + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" +dependencies = [ + "web-time", + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df33b2b81ac578cabaf06b89b0631153a3f416b0a886e8a7a1707fb51abbd1ef" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "rustybuzz" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfb9cf8877777222e4a3bc7eb247e398b56baba500c38c1c46842431adc8b55c" +dependencies = [ + "bitflags 2.11.0", + "bytemuck", + "libm", + "smallvec", + "ttf-parser 0.21.1", + "unicode-bidi-mirroring 0.2.0", + "unicode-ccc 0.2.0", + "unicode-properties", + "unicode-script", +] + +[[package]] +name = "rustybuzz" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd3c7c96f8a08ee34eff8857b11b49b07d71d1c3f4e88f8a88d4c9e9f90b1702" +dependencies = [ + "bitflags 2.11.0", + "bytemuck", + "core_maths", + "log", + "smallvec", + "ttf-parser 0.25.1", + "unicode-bidi-mirroring 0.4.0", + "unicode-ccc 0.4.0", + "unicode-properties", + "unicode-script", +] + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "schemars" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" +dependencies = [ + "dyn-clone", + "indexmap", + "ref-cast", + "schemars_derive", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d115b50f4aaeea07e79c1912f645c7513d81715d0420f8bc77a18c6260b307f" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 2.0.117", +] + +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "screencapturekit" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5eeeb57ac94960cfe5ff4c402be6585ae4c8d29a2cf41b276048c2e849d64e" +dependencies = [ + "screencapturekit-sys", +] + +[[package]] +name = "screencapturekit-sys" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22411b57f7d49e7fe08025198813ee6fd65e1ee5eff4ebc7880c12c82bde4c60" +dependencies = [ + "block", + "dispatch", + "objc", + "objc-foundation", + "objc_id", + "once_cell", +] + +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + +[[package]] +name = "security-framework" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +dependencies = [ + "bitflags 2.11.0", + "core-foundation 0.10.0", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "self_cell" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b12e76d157a900eb52e81bc6e9f3069344290341720e9178cde2407113ac8d89" + +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde-wasm-bindgen" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b" +dependencies = [ + "js-sys", + "serde", + "wasm-bindgen", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "serde_derive_internals" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "serde_fmt" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e497af288b3b95d067a23a4f749f2861121ffcb2f6d8379310dcda040c345ed" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "indexmap", + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_json_lenient" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e033097bf0d2b59a62b42c18ebbb797503839b26afdda2c4e1415cb6c813540" +dependencies = [ + "indexmap", + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_repr" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_spanned" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876ac351060d4f882bb1032b6369eb0aef79ad9df1ea8bc404874d8cc3d0cd98" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha1_smol" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "simd-adler32" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" + +[[package]] +name = "simd_helpers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6" +dependencies = [ + "quote", +] + +[[package]] +name = "simplecss" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a9c6883ca9c3c7c90e888de77b7a5c849c779d25d74a1269b0218b14e8b136c" +dependencies = [ + "log", +] + +[[package]] +name = "siphasher" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" + +[[package]] +name = "skrifa" +version = "0.40.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fbdfe3d2475fbd7ddd1f3e5cf8288a30eb3e5f95832829570cd88115a7434ac" +dependencies = [ + "bytemuck", + "read-fonts", +] + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "slotmap" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdd58c3c93c3d278ca835519292445cb4b0d4dc59ccfdf7ceadaab3f8aeb4038" +dependencies = [ + "version_check", +] + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "smol" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33bd3e260892199c3ccfc487c88b2da2265080acb316cd920da72fdfd7c599f" +dependencies = [ + "async-channel 2.5.0", + "async-executor", + "async-fs", + "async-io", + "async-lock", + "async-net", + "async-process", + "blocking", + "futures-lite 2.6.1", +] + +[[package]] +name = "smol_str" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead" + +[[package]] +name = "socket2" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spirv" +version = "0.3.0+sdk-1.3.268.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844" +dependencies = [ + "bitflags 2.11.0", +] + +[[package]] +name = "spirv" +version = "0.4.0+sdk-1.4.341.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9571ea910ebd84c86af4b3ed27f9dbdc6ad06f17c5f96146b2b671e2976744f" +dependencies = [ + "bitflags 2.11.0", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "stacker" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d74a23609d509411d10e2176dc2a4346e3b4aea2e7b1869f19fdedbc71c013" +dependencies = [ + "cc", + "cfg-if", + "libc", + "psm", + "windows-sys 0.59.0", +] + +[[package]] +name = "stacksafe" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d9c1172965d317e87ddb6d364a040d958b40a1db82b6ef97da26253a8b3d090" +dependencies = [ + "stacker", + "stacksafe-macro", +] + +[[package]] +name = "stacksafe-macro" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "172175341049678163e979d9107ca3508046d4d2a7c6682bee46ac541b17db69" +dependencies = [ + "proc-macro-error2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strict-num" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" +dependencies = [ + "float-cmp", +] + +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +dependencies = [ + "strum_macros 0.26.4", +] + +[[package]] +name = "strum" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" +dependencies = [ + "strum_macros 0.27.2", +] + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.117", +] + +[[package]] +name = "strum_macros" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "sval" +version = "2.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb9318255ebd817902d7e279d8f8e39b35b1b9954decd5eb9ea0e30e5fd2b6a" + +[[package]] +name = "sval_buffer" +version = "2.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12571299185e653fdb0fbfe36cd7f6529d39d4e747a60b15a3f34574b7b97c61" +dependencies = [ + "sval", + "sval_ref", +] + +[[package]] +name = "sval_dynamic" +version = "2.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39526f24e997706c0de7f03fb7371f7f5638b66a504ded508e20ad173d0a3677" +dependencies = [ + "sval", +] + +[[package]] +name = "sval_fmt" +version = "2.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "933dd3bb26965d682280fcc49400ac2a05036f4ee1e6dbd61bf8402d5a5c3a54" +dependencies = [ + "itoa", + "ryu", + "sval", +] + +[[package]] +name = "sval_json" +version = "2.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0cda08f6d5c9948024a6551077557b1fdcc3880ff2f20ae839667d2ec2d87ed" +dependencies = [ + "itoa", + "ryu", + "sval", +] + +[[package]] +name = "sval_nested" +version = "2.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d49d5e6c1f9fd0e53515819b03a97ca4eb1bff5c8ee097c43391c09ecfb19f" +dependencies = [ + "sval", + "sval_buffer", + "sval_ref", +] + +[[package]] +name = "sval_ref" +version = "2.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14f876c5a78405375b4e19cbb9554407513b59c93dea12dc6a4af4e1d30899ca" +dependencies = [ + "sval", +] + +[[package]] +name = "sval_serde" +version = "2.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f9ccd3b7f7200239a655e517dd3fd48d960b9111ad24bd6a5e055bef17607c7" +dependencies = [ + "serde_core", + "sval", + "sval_nested", +] + +[[package]] +name = "svg_fmt" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0193cc4331cfd2f3d2011ef287590868599a2f33c3e69bc22c1a3d3acf9e02fb" + +[[package]] +name = "svgtypes" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68c7541fff44b35860c1a7a47a7cadf3e4a304c457b58f9870d9706ece028afc" +dependencies = [ + "kurbo", + "siphasher", +] + +[[package]] +name = "swash" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "842f3cd369c2ba38966204f983eaa5e54a8e84a7d7159ed36ade2b6c335aae64" +dependencies = [ + "skrifa", + "yazi", + "zeno", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "sys-locale" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4" +dependencies = [ + "libc", +] + +[[package]] +name = "sysinfo" +version = "0.31.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "355dbe4f8799b304b05e1b0f05fc59b2a18d36645cf169607da45bde2f69a1be" +dependencies = [ + "core-foundation-sys", + "libc", + "memchr", + "ntapi", + "rayon", + "windows 0.57.0", +] + +[[package]] +name = "system-configuration" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +dependencies = [ + "bitflags 2.11.0", + "core-foundation 0.9.4", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "taffy" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a13e5d13f79d558b5d353a98072ca8ca0e99da429467804de959aa8c83c9a004" +dependencies = [ + "arrayvec", + "grid", + "serde", + "slotmap", +] + +[[package]] +name = "take-until" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bdb6fa0dfa67b38c1e66b7041ba9dcf23b99d8121907cd31c807a332f7a0bbb" + +[[package]] +name = "tao-core-video-sys" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271450eb289cb4d8d0720c6ce70c72c8c858c93dd61fc625881616752e6b98f6" +dependencies = [ + "cfg-if", + "core-foundation-sys", + "libc", + "objc", +] + +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand 2.3.0", + "getrandom 0.4.2", + "once_cell", + "rustix 1.1.4", + "windows-sys 0.61.2", +] + +[[package]] +name = "tendril" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" +dependencies = [ + "futf", + "mac", + "utf-8", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl 2.0.18", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "tiff" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b63feaf3343d35b6ca4d50483f94843803b0f51634937cc2ec519fc32232bc52" +dependencies = [ + "fax", + "flate2", + "half", + "quick-error", + "weezl", + "zune-jpeg", +] + +[[package]] +name = "time" +version = "0.1.0" +dependencies = [ + "bridge", + "num-traits", + "serde", + "tsify-next", + "wasm-bindgen", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "tiny-skia" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab" +dependencies = [ + "arrayref", + "arrayvec", + "bytemuck", + "cfg-if", + "log", + "png 0.17.16", + "tiny-skia-path", +] + +[[package]] +name = "tiny-skia-path" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93" +dependencies = [ + "arrayref", + "bytemuck", + "strict-num", +] + +[[package]] +name = "tinystr" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d" +dependencies = [ + "bytes", + "libc", + "mio", + "pin-project-lite", + "socket2", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-socks" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d4770b8024672c1101b3f6733eab95b18007dbe0847a8afe341fcf79e06043f" +dependencies = [ + "either", + "futures-util", + "thiserror 1.0.69", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +dependencies = [ + "serde", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", + "toml_edit 0.22.27", +] + +[[package]] +name = "toml" +version = "0.9.12+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" +dependencies = [ + "indexmap", + "serde_core", + "serde_spanned 1.1.0", + "toml_datetime 0.7.5+spec-1.1.0", + "toml_parser", + "toml_writer", + "winnow 0.7.15", +] + +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_datetime" +version = "0.7.5+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_datetime" +version = "1.1.0+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97251a7c317e03ad83774a8752a7e81fb6067740609f75ea2b585b569a59198f" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.22.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +dependencies = [ + "indexmap", + "serde", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", + "toml_write", + "winnow 0.7.15", +] + +[[package]] +name = "toml_edit" +version = "0.25.8+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16bff38f1d86c47f9ff0647e6838d7bb362522bdf44006c7068c2b1e606f1f3c" +dependencies = [ + "indexmap", + "toml_datetime 1.1.0+spec-1.1.0", + "toml_parser", + "winnow 1.0.0", +] + +[[package]] +name = "toml_parser" +version = "1.1.0+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2334f11ee363607eb04df9b8fc8a13ca1715a72ba8662a26ac285c98aabb4011" +dependencies = [ + "winnow 1.0.0", +] + +[[package]] +name = "toml_write" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" + +[[package]] +name = "toml_writer" +version = "1.1.0+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d282ade6016312faf3e41e57ebbba0c073e4056dab1232ab1cb624199648f8ed" + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "tsify-next" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d0f2208feeb5f7a6edb15a2389c14cd42480ef6417318316bb866da5806a61d" +dependencies = [ + "gloo-utils", + "serde", + "serde-wasm-bindgen", + "serde_json", + "tsify-next-macros", + "wasm-bindgen", +] + +[[package]] +name = "tsify-next-macros" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f81253930d0d388a3ab8fa4ae56da9973ab171ef833d1be2e9080fc3ce502bd6" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 2.0.117", +] + +[[package]] +name = "ttf-parser" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17f77d76d837a7830fe1d4f12b7b4ba4192c1888001c7164257e4bc6d21d96b4" + +[[package]] +name = "ttf-parser" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c591d83f69777866b9126b24c6dd9a18351f177e49d625920d19f989fd31cf8" + +[[package]] +name = "ttf-parser" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" +dependencies = [ + "core_maths", +] + +[[package]] +name = "typeid" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" + +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + +[[package]] +name = "uds_windows" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" +dependencies = [ + "memoffset", + "tempfile", + "windows-sys 0.61.2", +] + +[[package]] +name = "unicase" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" + +[[package]] +name = "unicode-bidi" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" + +[[package]] +name = "unicode-bidi-mirroring" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23cb788ffebc92c5948d0e997106233eeb1d8b9512f93f41651f52b6c5f5af86" + +[[package]] +name = "unicode-bidi-mirroring" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfa6e8c60bb66d49db113e0125ee8711b7647b5579dc7f5f19c42357ed039fe" + +[[package]] +name = "unicode-ccc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df77b101bcc4ea3d78dafc5ad7e4f58ceffe0b2b16bf446aeb50b6cb4157656" + +[[package]] +name = "unicode-ccc" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce61d488bcdc9bc8b5d1772c404828b17fc481c0a582b5581e95fb233aef503e" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-linebreak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" + +[[package]] +name = "unicode-properties" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d" + +[[package]] +name = "unicode-script" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "383ad40bb927465ec0ce7720e033cb4ca06912855fc35db31b5755d0de75b1ee" + +[[package]] +name = "unicode-segmentation" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" + +[[package]] +name = "unicode-vo" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94" + +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", + "serde_derive", +] + +[[package]] +name = "usvg" +version = "0.45.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80be9b06fbae3b8b303400ab20778c80bbaf338f563afe567cf3c9eea17b47ef" +dependencies = [ + "base64", + "data-url", + "flate2", + "fontdb 0.23.0", + "imagesize", + "kurbo", + "log", + "pico-args", + "roxmltree", + "rustybuzz 0.20.1", + "simplecss", + "siphasher", + "strict-num", + "svgtypes", + "tiny-skia-path", + "unicode-bidi", + "unicode-script", + "unicode-vo", + "xmlwriter", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "uuid" +version = "1.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ac8b6f42ead25368cf5b098aeb3dc8a1a2c05a3eee8a9a1a68c640edbfc79d9" +dependencies = [ + "getrandom 0.4.2", + "js-sys", + "serde_core", + "sha1_smol", + "wasm-bindgen", +] + +[[package]] +name = "v_frame" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "666b7727c8875d6ab5db9533418d7c764233ac9c0cff1d469aec8fa127597be2" +dependencies = [ + "aligned-vec", + "num-traits", + "wasm-bindgen", +] + +[[package]] +name = "value-bag" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ba6f5989077681266825251a52748b8c1d8a4ad098cc37e440103d0ea717fc0" +dependencies = [ + "value-bag-serde1", + "value-bag-sval2", +] + +[[package]] +name = "value-bag-serde1" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16530907bfe2999a1773ca5900a65101e092c70f642f25cc23ca0c43573262c5" +dependencies = [ + "erased-serde", + "serde_core", + "serde_fmt", +] + +[[package]] +name = "value-bag-sval2" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d00ae130edd690eaa877e4f40605d534790d1cf1d651e7685bd6a144521b251f" +dependencies = [ + "sval", + "sval_buffer", + "sval_dynamic", + "sval_fmt", + "sval_json", + "sval_ref", + "sval_serde", +] + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "vswhom" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be979b7f07507105799e854203b470ff7c78a1639e330a58f183b5fea574608b" +dependencies = [ + "libc", + "vswhom-sys", +] + +[[package]] +name = "vswhom-sys" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb067e4cbd1ff067d1df46c9194b5de0e98efd2810bbc95c5d5e5f25a3231150" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "waker-fn" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.2+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.116" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dc0882f7b5bb01ae8c5215a1230832694481c1a4be062fd410e12ea3da5b631" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19280959e2844181895ef62f065c63e0ca07ece4771b53d89bfdb967d97cbf05" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.116" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75973d3066e01d035dbedaad2864c398df42f8dd7b1ea057c35b8407c015b537" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.116" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91af5e4be765819e0bcfee7322c14374dc821e35e72fa663a830bbc7dc199eac" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 2.0.117", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.116" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9bf0406a78f02f336bf1e451799cca198e8acde4ffa278f0fb20487b150a633" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasm-streams" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags 2.11.0", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + +[[package]] +name = "wayland-backend" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa75f400b7f719bcd68b3f47cd939ba654cedeef690f486db71331eec4c6a406" +dependencies = [ + "cc", + "downcast-rs", + "rustix 1.1.4", + "scoped-tls", + "smallvec", + "wayland-sys", +] + +[[package]] +name = "wayland-client" +version = "0.31.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab51d9f7c071abeee76007e2b742499e535148035bb835f97aaed1338cf516c3" +dependencies = [ + "bitflags 2.11.0", + "rustix 1.1.4", + "wayland-backend", + "wayland-scanner", +] + +[[package]] +name = "wayland-cursor" +version = "0.31.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b3298683470fbdc6ca40151dfc48c8f2fd4c41a26e13042f801f85002384091" +dependencies = [ + "rustix 1.1.4", + "wayland-client", + "xcursor", +] + +[[package]] +name = "wayland-protocols" +version = "0.31.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f81f365b8b4a97f422ac0e8737c438024b5951734506b0e1d775c73030561f4" +dependencies = [ + "bitflags 2.11.0", + "wayland-backend", + "wayland-client", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols" +version = "0.32.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b23b5df31ceff1328f06ac607591d5ba360cf58f90c8fad4ac8d3a55a3c4aec7" +dependencies = [ + "bitflags 2.11.0", + "wayland-backend", + "wayland-client", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-plasma" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23803551115ff9ea9bce586860c5c5a971e360825a0309264102a9495a5ff479" +dependencies = [ + "bitflags 2.11.0", + "wayland-backend", + "wayland-client", + "wayland-protocols 0.31.2", + "wayland-scanner", +] + +[[package]] +name = "wayland-scanner" +version = "0.31.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c86287151a309799b821ca709b7345a048a2956af05957c89cb824ab919fa4e3" +dependencies = [ + "proc-macro2", + "quick-xml 0.39.2", + "quote", +] + +[[package]] +name = "wayland-sys" +version = "0.31.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "374f6b70e8e0d6bf9461a32988fd553b59ff630964924dad6e4a4eb6bd538d17" +dependencies = [ + "dlib", + "log", + "once_cell", + "pkg-config", +] + +[[package]] +name = "web-sys" +version = "0.3.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "749466a37ee189057f54748b200186b59a03417a117267baf3fd89cecc9fb837" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "weezl" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" + +[[package]] +name = "wgpu" +version = "29.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72c239a9a747bbd379590985bac952c2e53cb19873f7072b3370c6a6a8e06837" +dependencies = [ + "arrayvec", + "bitflags 2.11.0", + "bytemuck", + "cfg-if", + "cfg_aliases", + "document-features", + "hashbrown 0.16.1", + "js-sys", + "log", + "naga 29.0.1", + "parking_lot", + "portable-atomic", + "profiling", + "raw-window-handle", + "smallvec", + "static_assertions", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "wgpu-core", + "wgpu-hal", + "wgpu-types", +] + +[[package]] +name = "wgpu-core" +version = "29.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e80ac6cf1895df6342f87d975162108f9d98772a0d74bc404ab7304ac29469e" +dependencies = [ + "arrayvec", + "bit-set 0.9.1", + "bit-vec 0.9.1", + "bitflags 2.11.0", + "bytemuck", + "cfg_aliases", + "document-features", + "hashbrown 0.16.1", + "indexmap", + "log", + "naga 29.0.1", + "once_cell", + "parking_lot", + "portable-atomic", + "profiling", + "raw-window-handle", + "rustc-hash 1.1.0", + "smallvec", + "thiserror 2.0.18", + "wgpu-core-deps-apple", + "wgpu-core-deps-emscripten", + "wgpu-core-deps-wasm", + "wgpu-core-deps-windows-linux-android", + "wgpu-hal", + "wgpu-naga-bridge", + "wgpu-types", +] + +[[package]] +name = "wgpu-core-deps-apple" +version = "29.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43acd053312501689cd92a01a9638d37f3e41a5fd9534875efa8917ee2d11ac0" +dependencies = [ + "wgpu-hal", +] + +[[package]] +name = "wgpu-core-deps-emscripten" +version = "29.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef043bf135cc68b6f667c55ff4e345ce2b5924d75bad36a47921b0287ca4b24a" +dependencies = [ + "wgpu-hal", +] + +[[package]] +name = "wgpu-core-deps-wasm" +version = "29.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f7b75e72f49035f000dd5262e4126242e92a090a4fd75931ecfe7e60784e6fa" +dependencies = [ + "wgpu-hal", +] + +[[package]] +name = "wgpu-core-deps-windows-linux-android" +version = "29.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "725d5c006a8c02967b6d93ef04f6537ec4593313e330cfe86d9d3f946eb90f28" +dependencies = [ + "wgpu-hal", +] + +[[package]] +name = "wgpu-hal" +version = "29.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89a47aef47636562f3937285af4c44b4b5b404b46577471411cc5313a921da7e" +dependencies = [ + "android_system_properties", + "arrayvec", + "ash", + "bit-set 0.9.1", + "bitflags 2.11.0", + "block2", + "bytemuck", + "cfg-if", + "cfg_aliases", + "glow 0.17.0", + "glutin_wgl_sys", + "gpu-allocator", + "gpu-descriptor", + "hashbrown 0.16.1", + "js-sys", + "khronos-egl", + "libc", + "libloading", + "log", + "naga 29.0.1", + "ndk-sys", + "objc2", + "objc2-core-foundation", + "objc2-foundation", + "objc2-metal", + "objc2-quartz-core", + "once_cell", + "ordered-float", + "parking_lot", + "portable-atomic", + "portable-atomic-util", + "profiling", + "range-alloc", + "raw-window-handle", + "raw-window-metal 1.1.0", + "renderdoc-sys", + "smallvec", + "thiserror 2.0.18", + "wasm-bindgen", + "wayland-sys", + "web-sys", + "wgpu-naga-bridge", + "wgpu-types", + "windows 0.62.2", + "windows-core 0.62.2", +] + +[[package]] +name = "wgpu-naga-bridge" +version = "29.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4684f4410da0cf95a4cb63bb5edaac022461dedb6adf0b64d0d9b5f6890d51" +dependencies = [ + "naga 29.0.1", + "wgpu-types", +] + +[[package]] +name = "wgpu-types" +version = "29.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec2675540fb1a5cfa5ef122d3d5f390e2c75711a0b946410f2d6ac3a0f77d1f6" +dependencies = [ + "bitflags 2.11.0", + "bytemuck", + "js-sys", + "log", + "raw-window-handle", + "web-sys", +] + +[[package]] +name = "which" +version = "6.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ee928febd44d98f2f459a4a79bd4d928591333a494a10a868418ac1b39cf1f" +dependencies = [ + "either", + "home", + "rustix 0.38.44", + "winsafe", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143" +dependencies = [ + "windows-core 0.57.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows" +version = "0.61.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" +dependencies = [ + "windows-collections 0.2.0", + "windows-core 0.61.2", + "windows-future 0.2.1", + "windows-link 0.1.3", + "windows-numerics 0.2.0", +] + +[[package]] +name = "windows" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" +dependencies = [ + "windows-collections 0.3.2", + "windows-core 0.62.2", + "windows-future 0.3.2", + "windows-numerics 0.3.1", +] + +[[package]] +name = "windows-capture" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a4df73e95feddb9ec1a7e9c2ca6323b8c97d5eeeff78d28f1eccdf19c882b24" +dependencies = [ + "parking_lot", + "rayon", + "thiserror 2.0.18", + "windows 0.61.3", + "windows-future 0.2.1", +] + +[[package]] +name = "windows-collections" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" +dependencies = [ + "windows-core 0.61.2", +] + +[[package]] +name = "windows-collections" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" +dependencies = [ + "windows-core 0.62.2", +] + +[[package]] +name = "windows-core" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" +dependencies = [ + "windows-implement 0.57.0", + "windows-interface 0.57.0", + "windows-result 0.1.2", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +dependencies = [ + "windows-implement 0.60.2", + "windows-interface 0.59.3", + "windows-link 0.1.3", + "windows-result 0.3.4", + "windows-strings 0.4.2", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement 0.60.2", + "windows-interface 0.59.3", + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", +] + +[[package]] +name = "windows-future" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" +dependencies = [ + "windows-core 0.61.2", + "windows-link 0.1.3", + "windows-threading 0.1.0", +] + +[[package]] +name = "windows-future" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +dependencies = [ + "windows-core 0.62.2", + "windows-link 0.2.1", + "windows-threading 0.2.1", +] + +[[package]] +name = "windows-implement" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "windows-interface" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-numerics" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" +dependencies = [ + "windows-core 0.61.2", + "windows-link 0.1.3", +] + +[[package]] +name = "windows-numerics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +dependencies = [ + "windows-core 0.62.2", + "windows-link 0.2.1", +] + +[[package]] +name = "windows-registry" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" +dependencies = [ + "windows-result 0.3.4", + "windows-strings 0.3.1", + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-registry" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e" +dependencies = [ + "windows-link 0.1.3", + "windows-result 0.3.4", + "windows-strings 0.4.2", +] + +[[package]] +name = "windows-result" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows-strings" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link 0.2.1", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", +] + +[[package]] +name = "windows-threading" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-threading" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "winnow" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" +dependencies = [ + "memchr", +] + +[[package]] +name = "winnow" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a90e88e4667264a994d34e6d1ab2d26d398dcdca8b7f52bec8668957517fc7d8" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.55.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb5a765337c50e9ec252c2069be9bf91c7df47afb103b642ba3a53bf8101be97" +dependencies = [ + "cfg-if", + "windows-sys 0.59.0", +] + +[[package]] +name = "winsafe" +version = "0.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" + +[[package]] +name = "wio" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5" +dependencies = [ + "winapi", +] + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck 0.5.0", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck 0.5.0", + "indexmap", + "prettyplease", + "syn 2.0.117", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn 2.0.117", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags 2.11.0", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "writeable" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" + +[[package]] +name = "x11" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "x11-clipboard" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "662d74b3d77e396b8e5beb00b9cad6a9eccf40b2ef68cc858784b14c41d535a3" +dependencies = [ + "libc", + "x11rb", +] + +[[package]] +name = "x11rb" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9993aa5be5a26815fe2c3eacfc1fde061fc1a1f094bf1ad2a18bf9c495dd7414" +dependencies = [ + "as-raw-xcb-connection", + "gethostname", + "libc", + "rustix 1.1.4", + "x11rb-protocol", + "xcursor", +] + +[[package]] +name = "x11rb-protocol" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd" + +[[package]] +name = "xattr" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d1526bbe5aaeb5eb06885f4d987bcdfa5e23187055de9b83fe00156a821fabc" +dependencies = [ + "libc", +] + +[[package]] +name = "xcb" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee4c580d8205abb0a5cf4eb7e927bd664e425b6c3263f9c5310583da96970cf6" +dependencies = [ + "bitflags 1.3.2", + "libc", + "quick-xml 0.30.0", + "x11", +] + +[[package]] +name = "xcursor" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec9e4a500ca8864c5b47b8b482a73d62e4237670e5b5f1d6b9e3cae50f28f2b" + +[[package]] +name = "xim-ctext" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ac61a7062c40f3c37b6e82eeeef835d5cc7824b632a72784a89b3963c33284c" +dependencies = [ + "encoding_rs", +] + +[[package]] +name = "xim-parser" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dcee45f89572d5a65180af3a84e7ddb24f5ea690a6d3aa9de231281544dd7b7" +dependencies = [ + "bitflags 2.11.0", +] + +[[package]] +name = "xkbcommon" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d66ca9352cbd4eecbbc40871d8a11b4ac8107cfc528a6e14d7c19c69d0e1ac9" +dependencies = [ + "as-raw-xcb-connection", + "libc", + "memmap2", + "xkeysym", +] + +[[package]] +name = "xkeysym" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" + +[[package]] +name = "xml-rs" +version = "0.8.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f" + +[[package]] +name = "xmlwriter" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" + +[[package]] +name = "y4m" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5a4b21e1a62b67a2970e6831bc091d7b87e119e7f9791aef9702e3bef04448" + +[[package]] +name = "yazi" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01738255b5a16e78bbb83e7fbba0a1e7dd506905cfc53f4622d89015a03fbb5" + +[[package]] +name = "yeslogic-fontconfig-sys" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "503a066b4c037c440169d995b869046827dbc71263f6e8f3be6d77d4f3229dbd" +dependencies = [ + "dlib", + "once_cell", + "pkg-config", +] + +[[package]] +name = "yoke" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", + "synstructure", +] + +[[package]] +name = "zbus" +version = "5.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca82f95dbd3943a40a53cfded6c2d0a2ca26192011846a1810c4256ef92c60bc" +dependencies = [ + "async-broadcast", + "async-executor", + "async-io", + "async-lock", + "async-process", + "async-recursion", + "async-task", + "async-trait", + "blocking", + "enumflags2", + "event-listener 5.4.1", + "futures-core", + "futures-lite 2.6.1", + "hex", + "libc", + "ordered-stream", + "rustix 1.1.4", + "serde", + "serde_repr", + "tracing", + "uds_windows", + "uuid", + "windows-sys 0.61.2", + "winnow 0.7.15", + "zbus_macros", + "zbus_names", + "zvariant", +] + +[[package]] +name = "zbus_macros" +version = "5.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897e79616e84aac4b2c46e9132a4f63b93105d54fe8c0e8f6bffc21fa8d49222" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.117", + "zbus_names", + "zvariant", + "zvariant_utils", +] + +[[package]] +name = "zbus_names" +version = "4.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffd8af6d5b78619bab301ff3c560a5bd22426150253db278f164d6cf3b72c50f" +dependencies = [ + "serde", + "winnow 0.7.15", + "zvariant", +] + +[[package]] +name = "zed-async-tar" +version = "0.5.0-zed" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cf4b5f655e29700e473cb1acd914ab112b37b62f96f7e642d5fc6a0c02eb881" +dependencies = [ + "async-std", + "filetime", + "libc", + "pin-project", + "redox_syscall 0.2.16", + "xattr", +] + +[[package]] +name = "zed-font-kit" +version = "0.14.1-zed" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3898e450f36f852edda72e3f985c34426042c4951790b23b107f93394f9bff5" +dependencies = [ + "bitflags 2.11.0", + "byteorder", + "core-foundation 0.10.0", + "core-graphics 0.24.0", + "core-text", + "dirs 5.0.1", + "dwrote", + "float-ord", + "freetype-sys", + "lazy_static", + "libc", + "log", + "pathfinder_geometry", + "pathfinder_simd", + "walkdir", + "winapi", + "yeslogic-fontconfig-sys", +] + +[[package]] +name = "zed-reqwest" +version = "0.12.15-zed" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac2d05756ff48539950c3282ad7acf3817ad3f08797c205ad1c34a2ce03b9970" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "mime_guess", + "once_cell", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-native-certs", + "rustls-pemfile", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tokio-rustls", + "tokio-socks", + "tokio-util", + "tower", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "windows-registry 0.4.0", +] + +[[package]] +name = "zed-scap" +version = "0.0.8-zed" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6b338d705ae33a43ca00287c11129303a7a0aa57b101b72a1c08c863f698ac8" +dependencies = [ + "anyhow", + "cocoa 0.25.0", + "core-graphics-helmer-fork", + "log", + "objc", + "rand 0.8.5", + "screencapturekit", + "screencapturekit-sys", + "sysinfo", + "tao-core-video-sys", + "windows 0.61.3", + "windows-capture", + "x11", + "xcb", +] + +[[package]] +name = "zed-xim" +version = "0.4.0-zed" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0b46ed118eba34d9ba53d94ddc0b665e0e06a2cf874cfa2dd5dec278148642" +dependencies = [ + "ahash", + "hashbrown 0.14.5", + "log", + "x11rb", + "xim-ctext", + "xim-parser", +] + +[[package]] +name = "zeno" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6df3dc4292935e51816d896edcd52aa30bc297907c26167fec31e2b0c6a32524" + +[[package]] +name = "zerocopy" +version = "0.8.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efbb2a062be311f2ba113ce66f697a4dc589f85e78a4aea276200804cea0ed87" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e8bc7269b54418e7aeeef514aa68f8690b8c0489a06b0136e5f57c4c5ccab89" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "zerotrie" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" + +[[package]] +name = "zune-core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb8a0807f7c01457d0379ba880ba6322660448ddebc890ce29bb64da71fb40f9" + +[[package]] +name = "zune-inflate" +version = "0.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "zune-jpeg" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27bc9d5b815bc103f142aa054f561d9187d191692ec7c2d1e2b4737f8dbd7296" +dependencies = [ + "zune-core", +] + +[[package]] +name = "zvariant" +version = "5.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5708299b21903bbe348e94729f22c49c55d04720a004aa350f1f9c122fd2540b" +dependencies = [ + "endi", + "enumflags2", + "serde", + "url", + "winnow 0.7.15", + "zvariant_derive", + "zvariant_utils", +] + +[[package]] +name = "zvariant_derive" +version = "5.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b59b012ebe9c46656f9cc08d8da8b4c726510aef12559da3e5f1bf72780752c" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.117", + "zvariant_utils", +] + +[[package]] +name = "zvariant_utils" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f75c23a64ef8f40f13a6989991e643554d9bef1d682a281160cf0c1bc389c5e9" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "syn 2.0.117", + "winnow 0.7.15", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 000000000..b00c78e87 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,11 @@ +[workspace] +resolver = "2" +members = [ + "apps/desktop", + "rust/crates/time", + "rust/crates/bridge", + "rust/crates/effects", + "rust/crates/gpu", + "rust/crates/masks", + "rust/wasm", "rust/crates/compositor", +] diff --git a/LICENSE b/LICENSE index e2c5ff28d..3df4f89f5 100644 --- a/LICENSE +++ b/LICENSE @@ -1,7 +1,7 @@ -Copyright 2025 OpenCut +Copyright 2025-2026 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. \ No newline at end of file +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. diff --git a/README.md b/README.md index cacc4555c..af46df565 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ @@ -28,23 +28,12 @@ Thanks to [Vercel](https://vercel.com?utm_source=github-opencut&utm_campaign=oss - **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 +- `apps/web/`: Next.js web application +- `apps/desktop/`: Native desktop app built with GPUI (in progress) +- `rust/`: Platform-agnostic core: GPU compositor, effects, masks, and WASM bindings. We're actively migrating business logic here from TypeScript. +- `docs/`: Architecture and subsystem documentation ## Getting Started @@ -86,6 +75,62 @@ The application will be available at [http://localhost:3000](http://localhost:30 The `.env.example` has sensible defaults that match the Docker Compose config — it should work out of the box. +### Desktop setup + +Desktop is opt-in. If you're only working on the web app, skip this entirely. + +If you want to get ready for `apps/desktop`, see [`apps/desktop/README.md`](apps/desktop/README.md). It's a two-step setup: Rust toolchain first, then desktop native dependencies. + +### Local WASM development + +Only needed if you're editing `rust/wasm` and want the web app to use your local build instead of the published package. + +**Prerequisites** — install these once before anything else: + +```bash +# Rust toolchain +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh + +# build the WASM package +cargo install wasm-pack + +# reruns the build on file changes, used by bun dev:wasm +cargo install cargo-watch +``` + +1. Build the package once from the repo root: + + ```bash + bun run build:wasm + ``` + +2. Register the generated package for linking: + + ```bash + cd rust/wasm/pkg + bun link + ``` + +3. Link `apps/web` to the local package: + + ```bash + cd apps/web + bun link opencut-wasm + ``` + +4. Rebuild on changes while you work: + + ```bash + bun dev:wasm + ``` + +To switch `apps/web` back to the published package, run: + +```bash +cd apps/web +bun add opencut-wasm +``` + ### Self-Hosting with Docker To run everything (including a production build of the app) in Docker: @@ -110,6 +155,7 @@ See our [Contributing Guide](.github/CONTRIBUTING.md) for detailed setup instruc - Fork the repo and clone locally - Follow the setup instructions in CONTRIBUTING.md +- Working on `apps/desktop`? See [`apps/desktop/README.md`](apps/desktop/README.md) for setup - Create a feature branch and submit a PR ## License diff --git a/apps/desktop/Cargo.toml b/apps/desktop/Cargo.toml new file mode 100644 index 000000000..e09443ab3 --- /dev/null +++ b/apps/desktop/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "opencut-desktop" +version = "0.1.0" +edition = "2021" + +[[bin]] +name = "opencut" +path = "src/main.rs" + +[dependencies] +gpui = "0.2.2" diff --git a/apps/desktop/README.md b/apps/desktop/README.md new file mode 100644 index 000000000..9cff04e12 --- /dev/null +++ b/apps/desktop/README.md @@ -0,0 +1,47 @@ +# Desktop + +The native desktop app, built with [GPUI](https://gpui.rs). + +## Getting started + +**1. Install Rust:** + +```bash +# Linux / macOS / WSL +./script/setup-rust +``` + +```powershell +# Windows +powershell -ExecutionPolicy Bypass -File .\script\setup-rust.ps1 +``` + +Both scripts skip installation if Rust is already present. On Linux/macOS/WSL only: after a fresh install, reload your shell with `source "$HOME/.cargo/env"` + +**2. Install native dependencies:** + +```bash +# Linux / macOS / WSL +./apps/desktop/script/setup +``` + +```powershell +# Windows +powershell -ExecutionPolicy Bypass -File .\apps\desktop\script\setup.ps1 +``` + +**3. Run:** + +```bash +cargo run -p opencut-desktop +``` + +## Platform notes + +**Linux:** supports apt (Debian/Ubuntu/Mint), dnf (Fedora/RHEL), and pacman (Arch). + +**macOS:** installs Xcode Command Line Tools if missing. + +**Windows:** the setup script checks for Visual Studio Build Tools. If missing, it prints the install link. + +**WSL:** runs the same scripts as Linux. Window rendering works via WSLg on Windows 11 and Windows 10 22H2+. If you're on an older build, test on the host instead. diff --git a/apps/desktop/script/setup b/apps/desktop/script/setup new file mode 100644 index 000000000..3152a0804 --- /dev/null +++ b/apps/desktop/script/setup @@ -0,0 +1,110 @@ +#!/usr/bin/env bash + +# Installs the native system libraries GPUI needs to compile on Linux/macOS. +# Run script/setup-rust first if you don't have Rust installed yet. + +set -euo pipefail + +if [ "$(id -u)" -eq 0 ]; then + maysudo='' +else + maysudo="$(command -v sudo || command -v doas || true)" +fi + +# Ubuntu, Debian, Mint, Pop!_OS, Kali, Raspbian, etc. +apt=$(command -v apt-get || true) +if [[ -n $apt ]]; then + deps=( + curl + build-essential + pkg-config + gcc + g++ + make + libasound2-dev + libfontconfig-dev + libglib2.0-dev + libssl-dev + libva-dev + libvulkan1 + libwayland-dev + libx11-xcb-dev + libxkbcommon-x11-dev + libzstd-dev + libsqlite3-dev + ) + + $maysudo "$apt" update + $maysudo "$apt" install -y "${deps[@]}" + echo "Desktop native dependencies installed." + exit 0 +fi + +# Fedora, RHEL, CentOS, Alma, etc. +dnf=$(command -v dnf || true) +if [[ -n $dnf ]]; then + deps=( + gcc + gcc-c++ + make + pkg-config + alsa-lib-devel + fontconfig-devel + glib2-devel + openssl-devel + libva-devel + vulkan-loader + wayland-devel + libxcb-devel + libxkbcommon-x11-devel + libzstd-devel + sqlite-devel + curl + ) + + $maysudo "$dnf" install -y "${deps[@]}" + echo "Desktop native dependencies installed." + exit 0 +fi + +# Arch, Manjaro, etc. +pacman=$(command -v pacman || true) +if [[ -n $pacman ]]; then + deps=( + gcc + make + pkgconf + alsa-lib + fontconfig + glib2 + openssl + libva + vulkan-loader + wayland + libxcb + libxkbcommon-x11 + zstd + sqlite + curl + ) + + $maysudo "$pacman" -Syu --needed --noconfirm "${deps[@]}" + echo "Desktop native dependencies installed." + exit 0 +fi + +# macOS +if [[ "$(uname)" == "Darwin" ]]; then + if ! xcode-select -p &> /dev/null; then + echo "Installing Xcode Command Line Tools..." + xcode-select --install + echo "Re-run this script after the Xcode install completes." + exit 0 + fi + + echo "Desktop native dependencies installed." + exit 0 +fi + +echo "Unsupported platform. See apps/desktop/README.md for manual setup instructions." +exit 1 diff --git a/apps/desktop/script/setup.ps1 b/apps/desktop/script/setup.ps1 new file mode 100644 index 000000000..63f7fd1af --- /dev/null +++ b/apps/desktop/script/setup.ps1 @@ -0,0 +1,27 @@ +# Installs the native build tools GPUI needs to compile on Windows. +# Run script/setup-rust.ps1 first if you don't have Rust installed yet. + +$ErrorActionPreference = "Stop" + +function Check-VSBuildTools { + $vswhere = "C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" + + if (-not (Test-Path $vswhere)) { + return $false + } + + $result = & $vswhere -latest -requires Microsoft.VisualStudio.Workload.NativeDesktop -property installationPath + return $result -and $result.Trim().Length -gt 0 +} + +if (-not (Check-VSBuildTools)) { + Write-Output "" + Write-Output "Visual Studio Build Tools with 'Desktop development with C++' not found." + Write-Output "" + Write-Output "Install it from: https://visualstudio.microsoft.com/visual-cpp-build-tools/" + Write-Output "Check the 'Desktop development with C++' workload during setup, then re-run this script." + Write-Output "" + exit 1 +} + +Write-Output "Desktop native dependencies ready." diff --git a/apps/desktop/src/main.rs b/apps/desktop/src/main.rs new file mode 100644 index 000000000..0ba70959c --- /dev/null +++ b/apps/desktop/src/main.rs @@ -0,0 +1,41 @@ +use gpui::{ + div, prelude::*, px, rgb, size, App, Application, Bounds, Context, SharedString, Window, + WindowBounds, WindowOptions, +}; + +struct AppWindow { + title: SharedString, +} + +impl Render for AppWindow { + fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { + div() + .size_full() + .bg(rgb(0x0f0f0f)) + .flex() + .justify_center() + .items_center() + .text_xl() + .text_color(rgb(0xffffff)) + .child(self.title.clone()) + } +} + +fn main() { + Application::new().run(|cx: &mut App| { + let bounds = Bounds::centered(None, size(px(1280.), px(720.)), cx); + cx.open_window( + WindowOptions { + window_bounds: Some(WindowBounds::Windowed(bounds)), + ..Default::default() + }, + |_, cx| { + cx.new(|_| AppWindow { + title: "OpenCut".into(), + }) + }, + ) + .unwrap(); + cx.activate(true); + }); +} diff --git a/apps/web/.env.example b/apps/web/.env.example index e19435716..b15779a5c 100644 --- a/apps/web/.env.example +++ b/apps/web/.env.example @@ -1,28 +1,21 @@ -# 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 \ No newline at end of file +# 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 diff --git a/apps/web/.gitignore b/apps/web/.gitignore index 4b8a9fe9c..2c4edca41 100644 --- a/apps/web/.gitignore +++ b/apps/web/.gitignore @@ -1,10 +1,14 @@ -# Turborepo -.turbo - -# Env vars -.env* -!.env.example - -.next/ - -.font-cache/ \ No newline at end of file +# Turborepo +.turbo + +# Env vars +.env* +!.env.example + +.next/ + +.font-cache/ + +next-env.d.ts + +*.tsbuildinfo \ No newline at end of file diff --git a/apps/web/Dockerfile b/apps/web/Dockerfile index 9f2dfa2c4..a08d9b68f 100644 --- a/apps/web/Dockerfile +++ b/apps/web/Dockerfile @@ -1,70 +1,62 @@ -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"] +FROM oven/bun:alpine AS base + +FROM base AS builder + +WORKDIR /app + +ARG FREESOUND_CLIENT_ID +ARG FREESOUND_API_KEY +ARG NEXT_PUBLIC_MARBLE_API_URL=https://api.marblecms.com +ARG MARBLE_WORKSPACE_KEY=build-placeholder + +COPY package.json package.json +COPY bun.lock bun.lock +COPY turbo.json turbo.json + +COPY apps/web/package.json apps/web/package.json +RUN bun install + +COPY apps/web/ apps/web/ + +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=$NEXT_PUBLIC_MARBLE_API_URL +ENV MARBLE_WORKSPACE_KEY=$MARBLE_WORKSPACE_KEY + +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"] diff --git a/apps/web/components.json b/apps/web/components.json index 5bcedb314..3c84e31aa 100644 --- a/apps/web/components.json +++ b/apps/web/components.json @@ -1,21 +1,21 @@ -{ - "$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" -} +{ + "$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" +} diff --git a/apps/web/content-collections.ts b/apps/web/content-collections.ts index a3455d808..0fdd72346 100644 --- a/apps/web/content-collections.ts +++ b/apps/web/content-collections.ts @@ -1,33 +1,37 @@ -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], -}); +import { defineCollection, defineConfig } from "@content-collections/core"; +import { z } from "zod"; + +const changelog = defineCollection({ + name: "changelog", + directory: "src/lib/changelog/entries", + include: "*.md", + schema: z.object({ + content: z.string(), + version: z.string(), + date: z.string(), + published: z.boolean().default(true), + title: z.string(), + description: z.string().optional(), + summary: z.string().optional(), + changes: z.array( + z.object({ + type: z.string(), + text: z.string(), + }), + ), + }), + transform: async (doc, { collection }) => { + const allDocs = await collection.documents(); + const publishedDocs = allDocs.filter((entry) => entry.published !== false); + const sorted = [...publishedDocs].sort((a, b) => + b.version.localeCompare(a.version, undefined, { numeric: true }), + ); + const isLatest = + doc.published !== false && sorted[0]?.version === doc.version; + return { ...doc, isLatest }; + }, +}); + +export default defineConfig({ + content: [changelog], +}); diff --git a/apps/web/drizzle.config.ts b/apps/web/drizzle.config.ts index be2a59fcb..f3c4b3577 100644 --- a/apps/web/drizzle.config.ts +++ b/apps/web/drizzle.config.ts @@ -1,23 +1,26 @@ 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") { +if (process.env.NODE_ENV === "production") { dotenv.config({ path: ".env.production" }); } else { dotenv.config({ path: ".env.local" }); } +const databaseUrl = process.env.DATABASE_URL; +if (!databaseUrl) { + throw new Error("DATABASE_URL is not set"); +} + export default { - schema: "./src/schema.ts", + schema: "./src/lib/db/schema.ts", dialect: "postgresql", migrations: { table: "drizzle_migrations", }, dbCredentials: { - url: webEnv.DATABASE_URL, + url: databaseUrl, }, out: "./migrations", - strict: webEnv.NODE_ENV === "production", + strict: process.env.NODE_ENV === "production", } satisfies Config; diff --git a/apps/web/migrations/0000_brainy_saracen.sql b/apps/web/migrations/0000_brainy_saracen.sql index 2c6ab2b26..3179d6c51 100644 --- a/apps/web/migrations/0000_brainy_saracen.sql +++ b/apps/web/migrations/0000_brainy_saracen.sql @@ -1,62 +1,62 @@ -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 +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; \ No newline at end of file diff --git a/apps/web/migrations/meta/0000_snapshot.json b/apps/web/migrations/meta/0000_snapshot.json index 2f6c9b30f..42bae1c6d 100644 --- a/apps/web/migrations/meta/0000_snapshot.json +++ b/apps/web/migrations/meta/0000_snapshot.json @@ -1,344 +1,344 @@ -{ - "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": {} - } -} +{ + "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": {} + } +} diff --git a/apps/web/migrations/meta/_journal.json b/apps/web/migrations/meta/_journal.json index a3cb0863f..a91398ed2 100644 --- a/apps/web/migrations/meta/_journal.json +++ b/apps/web/migrations/meta/_journal.json @@ -1,13 +1,13 @@ -{ - "version": "7", - "dialect": "postgresql", - "entries": [ - { - "idx": 0, - "version": "7", - "when": 1750753385927, - "tag": "0000_brainy_saracen", - "breakpoints": true - } - ] -} +{ + "version": "7", + "dialect": "postgresql", + "entries": [ + { + "idx": 0, + "version": "7", + "when": 1750753385927, + "tag": "0000_brainy_saracen", + "breakpoints": true + } + ] +} diff --git a/apps/web/next-env.d.ts b/apps/web/next-env.d.ts deleted file mode 100644 index c4b7818fb..000000000 --- a/apps/web/next-env.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -/// -/// -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. diff --git a/apps/web/next.config.ts b/apps/web/next.config.ts index 2f509d2af..71d96c234 100644 --- a/apps/web/next.config.ts +++ b/apps/web/next.config.ts @@ -1,58 +1,54 @@ -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)); +import type { NextConfig } from "next"; +import { withBotId } from "botid/next/config"; +import { withContentCollections } from "@content-collections/next"; + +const nextConfig: NextConfig = { + 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", + }, + { + protocol: "https", + hostname: "cdn.brandfetch.io", + }, + ], + }, +}; + +export default withContentCollections(withBotId(nextConfig)); diff --git a/apps/web/open-next.config.ts b/apps/web/open-next.config.ts new file mode 100644 index 000000000..36144eb15 --- /dev/null +++ b/apps/web/open-next.config.ts @@ -0,0 +1,3 @@ +import { defineCloudflareConfig } from "@opennextjs/cloudflare"; + +export default defineCloudflareConfig(); \ No newline at end of file diff --git a/apps/web/package.json b/apps/web/package.json index a00c96712..90cd9fb55 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -7,25 +7,22 @@ "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", + "preview": "opennextjs-cloudflare build && opennextjs-cloudflare preview", + "deploy": "opennextjs-cloudflare build && opennextjs-cloudflare deploy", + "lint": "eslint src --ext .ts,.tsx", + "lint:fix": "eslint src --ext .ts,.tsx --fix", + "format": "prettier 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", + "@hugeicons/core-free-icons": "^3.3.0", + "@hugeicons/react": "^1.1.6", "@huggingface/transformers": "^3.8.1", - "@opencut/env": "workspace:*", - "@opencut/ui": "workspace:*", + "@opennextjs/cloudflare": "^1.18.0", "@radix-ui/react-accordion": "^1.2.12", "@radix-ui/react-checkbox": "^1.3.3", "@radix-ui/react-dialog": "^1.1.15", @@ -38,15 +35,12 @@ "@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", + "botid": "^1.5.11", "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", @@ -58,6 +52,7 @@ "nanoid": "^5.1.5", "next": "16.1.3", "next-themes": "^0.4.4", + "opencut-wasm": "^0.2.10", "pg": "^8.16.2", "postgres": "^3.4.5", "radix-ui": "^1.4.3", @@ -67,23 +62,21 @@ "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", + "soundtouchjs": "^0.3.0", "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", + "zod": "4.3.6", "zustand": "^5.0.2" }, "devDependencies": { @@ -101,10 +94,9 @@ "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" + "typescript": "^5.8.3", + "wrangler": "^4.77.0" } } diff --git a/apps/web/postcss.config.mjs b/apps/web/postcss.config.mjs index 6ba14df98..61e36849c 100644 --- a/apps/web/postcss.config.mjs +++ b/apps/web/postcss.config.mjs @@ -1,8 +1,7 @@ -/** @type {import('postcss-load-config').Config} */ const config = { - plugins: { - "@tailwindcss/postcss": {}, - }, + plugins: { + "@tailwindcss/postcss": {}, + }, }; export default config; diff --git a/apps/web/public/_headers b/apps/web/public/_headers new file mode 100644 index 000000000..0a731a130 --- /dev/null +++ b/apps/web/public/_headers @@ -0,0 +1,2 @@ +/_next/static/* + Cache-Control: public,max-age=31536000,immutable diff --git a/apps/web/public/browserconfig.xml b/apps/web/public/browserconfig.xml index 39060b211..3e94aae94 100644 --- a/apps/web/public/browserconfig.xml +++ b/apps/web/public/browserconfig.xml @@ -1,11 +1,11 @@ - - - - - - - - #ffffff - - + + + + + + + + #ffffff + + \ No newline at end of file diff --git a/apps/web/public/countries.json b/apps/web/public/countries.json deleted file mode 100644 index 8d53cfaea..000000000 --- a/apps/web/public/countries.json +++ /dev/null @@ -1,1794 +0,0 @@ -[ - { - "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" - } -] diff --git a/apps/web/public/ffmpeg/ffmpeg-core.js b/apps/web/public/ffmpeg/ffmpeg-core.js deleted file mode 100644 index 405d1dbc0..000000000 --- a/apps/web/public/ffmpeg/ffmpeg-core.js +++ /dev/null @@ -1,18 +0,0 @@ -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; diff --git a/apps/web/public/ffmpeg/ffmpeg-core.wasm b/apps/web/public/ffmpeg/ffmpeg-core.wasm deleted file mode 100644 index 246b0fe22..000000000 Binary files a/apps/web/public/ffmpeg/ffmpeg-core.wasm and /dev/null differ diff --git a/apps/web/public/flags/ad.svg b/apps/web/public/flags/ad.svg new file mode 100644 index 000000000..dd4959534 --- /dev/null +++ b/apps/web/public/flags/ad.svg @@ -0,0 +1,150 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/ae.svg b/apps/web/public/flags/ae.svg new file mode 100644 index 000000000..810de70d7 --- /dev/null +++ b/apps/web/public/flags/ae.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/apps/web/public/flags/af.svg b/apps/web/public/flags/af.svg new file mode 100644 index 000000000..2924328c7 --- /dev/null +++ b/apps/web/public/flags/af.svg @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/ag.svg b/apps/web/public/flags/ag.svg new file mode 100644 index 000000000..39a4c90b0 --- /dev/null +++ b/apps/web/public/flags/ag.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/ai.svg b/apps/web/public/flags/ai.svg new file mode 100644 index 000000000..0fb6032f0 --- /dev/null +++ b/apps/web/public/flags/ai.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/al.svg b/apps/web/public/flags/al.svg new file mode 100644 index 000000000..c1353dd3c --- /dev/null +++ b/apps/web/public/flags/al.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/apps/web/public/flags/am.svg b/apps/web/public/flags/am.svg new file mode 100644 index 000000000..0bcc6caed --- /dev/null +++ b/apps/web/public/flags/am.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/apps/web/public/flags/ao.svg b/apps/web/public/flags/ao.svg new file mode 100644 index 000000000..d37de17b3 --- /dev/null +++ b/apps/web/public/flags/ao.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/apps/web/public/flags/aq.svg b/apps/web/public/flags/aq.svg new file mode 100644 index 000000000..78ce8a5fd --- /dev/null +++ b/apps/web/public/flags/aq.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/apps/web/public/flags/ar.svg b/apps/web/public/flags/ar.svg new file mode 100644 index 000000000..45ac8867e --- /dev/null +++ b/apps/web/public/flags/ar.svg @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/arab.svg b/apps/web/public/flags/arab.svg new file mode 100644 index 000000000..f037ddb9b --- /dev/null +++ b/apps/web/public/flags/arab.svg @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/as.svg b/apps/web/public/flags/as.svg new file mode 100644 index 000000000..f23d9d9b5 --- /dev/null +++ b/apps/web/public/flags/as.svg @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/asean.svg b/apps/web/public/flags/asean.svg new file mode 100644 index 000000000..63f4dcd6a --- /dev/null +++ b/apps/web/public/flags/asean.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/apps/web/public/flags/at.svg b/apps/web/public/flags/at.svg new file mode 100644 index 000000000..76149868d --- /dev/null +++ b/apps/web/public/flags/at.svg @@ -0,0 +1,4 @@ + + + + diff --git a/apps/web/public/flags/au.svg b/apps/web/public/flags/au.svg new file mode 100644 index 000000000..fad9844a9 --- /dev/null +++ b/apps/web/public/flags/au.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/apps/web/public/flags/aw.svg b/apps/web/public/flags/aw.svg new file mode 100644 index 000000000..78a54790a --- /dev/null +++ b/apps/web/public/flags/aw.svg @@ -0,0 +1,186 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/ax.svg b/apps/web/public/flags/ax.svg new file mode 100644 index 000000000..e53cadd80 --- /dev/null +++ b/apps/web/public/flags/ax.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/az.svg b/apps/web/public/flags/az.svg new file mode 100644 index 000000000..9849619d4 --- /dev/null +++ b/apps/web/public/flags/az.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/apps/web/public/flags/ba.svg b/apps/web/public/flags/ba.svg new file mode 100644 index 000000000..db836308c --- /dev/null +++ b/apps/web/public/flags/ba.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/apps/web/public/flags/bb.svg b/apps/web/public/flags/bb.svg new file mode 100644 index 000000000..f68431997 --- /dev/null +++ b/apps/web/public/flags/bb.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/apps/web/public/flags/bd.svg b/apps/web/public/flags/bd.svg new file mode 100644 index 000000000..2d3b50ede --- /dev/null +++ b/apps/web/public/flags/bd.svg @@ -0,0 +1,4 @@ + + + + diff --git a/apps/web/public/flags/be.svg b/apps/web/public/flags/be.svg new file mode 100644 index 000000000..b3754f1b4 --- /dev/null +++ b/apps/web/public/flags/be.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/apps/web/public/flags/bf.svg b/apps/web/public/flags/bf.svg new file mode 100644 index 000000000..8095ee17d --- /dev/null +++ b/apps/web/public/flags/bf.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/apps/web/public/flags/bg.svg b/apps/web/public/flags/bg.svg new file mode 100644 index 000000000..e6668f5ae --- /dev/null +++ b/apps/web/public/flags/bg.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/apps/web/public/flags/bh.svg b/apps/web/public/flags/bh.svg new file mode 100644 index 000000000..0e9b7198e --- /dev/null +++ b/apps/web/public/flags/bh.svg @@ -0,0 +1,4 @@ + + + + diff --git a/apps/web/public/flags/bi.svg b/apps/web/public/flags/bi.svg new file mode 100644 index 000000000..4e832637d --- /dev/null +++ b/apps/web/public/flags/bi.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/bj.svg b/apps/web/public/flags/bj.svg new file mode 100644 index 000000000..ac0d0741c --- /dev/null +++ b/apps/web/public/flags/bj.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/bl.svg b/apps/web/public/flags/bl.svg new file mode 100644 index 000000000..ad89c4caf --- /dev/null +++ b/apps/web/public/flags/bl.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/apps/web/public/flags/bm.svg b/apps/web/public/flags/bm.svg new file mode 100644 index 000000000..98578e412 --- /dev/null +++ b/apps/web/public/flags/bm.svg @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/bn.svg b/apps/web/public/flags/bn.svg new file mode 100644 index 000000000..afb726076 --- /dev/null +++ b/apps/web/public/flags/bn.svg @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/bo.svg b/apps/web/public/flags/bo.svg new file mode 100644 index 000000000..2ebe4df30 --- /dev/null +++ b/apps/web/public/flags/bo.svg @@ -0,0 +1,673 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/bq.svg b/apps/web/public/flags/bq.svg new file mode 100644 index 000000000..10ba3c766 --- /dev/null +++ b/apps/web/public/flags/bq.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/apps/web/public/flags/br.svg b/apps/web/public/flags/br.svg new file mode 100644 index 000000000..56fdbc68e --- /dev/null +++ b/apps/web/public/flags/br.svg @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/bs.svg b/apps/web/public/flags/bs.svg new file mode 100644 index 000000000..3198dcb03 --- /dev/null +++ b/apps/web/public/flags/bs.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/apps/web/public/flags/bt.svg b/apps/web/public/flags/bt.svg new file mode 100644 index 000000000..c707bb43e --- /dev/null +++ b/apps/web/public/flags/bt.svg @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/bv.svg b/apps/web/public/flags/bv.svg new file mode 100644 index 000000000..1e6aeab02 --- /dev/null +++ b/apps/web/public/flags/bv.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/apps/web/public/flags/bw.svg b/apps/web/public/flags/bw.svg new file mode 100644 index 000000000..355181cf1 --- /dev/null +++ b/apps/web/public/flags/bw.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/apps/web/public/flags/by.svg b/apps/web/public/flags/by.svg new file mode 100644 index 000000000..66f2daa9e --- /dev/null +++ b/apps/web/public/flags/by.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/bz.svg b/apps/web/public/flags/bz.svg new file mode 100644 index 000000000..8609d443c --- /dev/null +++ b/apps/web/public/flags/bz.svg @@ -0,0 +1,145 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/ca.svg b/apps/web/public/flags/ca.svg new file mode 100644 index 000000000..8e6f250ef --- /dev/null +++ b/apps/web/public/flags/ca.svg @@ -0,0 +1,4 @@ + + + + diff --git a/apps/web/public/flags/cc.svg b/apps/web/public/flags/cc.svg new file mode 100644 index 000000000..647426e12 --- /dev/null +++ b/apps/web/public/flags/cc.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/cd.svg b/apps/web/public/flags/cd.svg new file mode 100644 index 000000000..a56455502 --- /dev/null +++ b/apps/web/public/flags/cd.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/apps/web/public/flags/cefta.svg b/apps/web/public/flags/cefta.svg new file mode 100644 index 000000000..3cf5884b4 --- /dev/null +++ b/apps/web/public/flags/cefta.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/apps/web/public/flags/cf.svg b/apps/web/public/flags/cf.svg new file mode 100644 index 000000000..34af5daeb --- /dev/null +++ b/apps/web/public/flags/cf.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/cg.svg b/apps/web/public/flags/cg.svg new file mode 100644 index 000000000..cec3b071b --- /dev/null +++ b/apps/web/public/flags/cg.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/apps/web/public/flags/ch.svg b/apps/web/public/flags/ch.svg new file mode 100644 index 000000000..8980960b5 --- /dev/null +++ b/apps/web/public/flags/ch.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/apps/web/public/flags/ci.svg b/apps/web/public/flags/ci.svg new file mode 100644 index 000000000..6e71beef7 --- /dev/null +++ b/apps/web/public/flags/ci.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/apps/web/public/flags/ck.svg b/apps/web/public/flags/ck.svg new file mode 100644 index 000000000..9217b6bef --- /dev/null +++ b/apps/web/public/flags/ck.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/apps/web/public/flags/cl.svg b/apps/web/public/flags/cl.svg new file mode 100644 index 000000000..477fb2208 --- /dev/null +++ b/apps/web/public/flags/cl.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/apps/web/public/flags/cm.svg b/apps/web/public/flags/cm.svg new file mode 100644 index 000000000..fbfd5236c --- /dev/null +++ b/apps/web/public/flags/cm.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/cn.svg b/apps/web/public/flags/cn.svg new file mode 100644 index 000000000..86a6776b4 --- /dev/null +++ b/apps/web/public/flags/cn.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/apps/web/public/flags/co.svg b/apps/web/public/flags/co.svg new file mode 100644 index 000000000..2fa50c0be --- /dev/null +++ b/apps/web/public/flags/co.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/apps/web/public/flags/cp.svg b/apps/web/public/flags/cp.svg new file mode 100644 index 000000000..dd2edf4d0 --- /dev/null +++ b/apps/web/public/flags/cp.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/apps/web/public/flags/cr.svg b/apps/web/public/flags/cr.svg new file mode 100644 index 000000000..ff3bc1d9d --- /dev/null +++ b/apps/web/public/flags/cr.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/apps/web/public/flags/cu.svg b/apps/web/public/flags/cu.svg new file mode 100644 index 000000000..ab740b00c --- /dev/null +++ b/apps/web/public/flags/cu.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/apps/web/public/flags/cv.svg b/apps/web/public/flags/cv.svg new file mode 100644 index 000000000..a1dd7f6b7 --- /dev/null +++ b/apps/web/public/flags/cv.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/apps/web/public/flags/cw.svg b/apps/web/public/flags/cw.svg new file mode 100644 index 000000000..c8bd3bfaa --- /dev/null +++ b/apps/web/public/flags/cw.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/cx.svg b/apps/web/public/flags/cx.svg new file mode 100644 index 000000000..b1b770103 --- /dev/null +++ b/apps/web/public/flags/cx.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/cy.svg b/apps/web/public/flags/cy.svg new file mode 100644 index 000000000..e92a6b2c5 --- /dev/null +++ b/apps/web/public/flags/cy.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/apps/web/public/flags/cz.svg b/apps/web/public/flags/cz.svg new file mode 100644 index 000000000..846f0b987 --- /dev/null +++ b/apps/web/public/flags/cz.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/apps/web/public/flags/de.svg b/apps/web/public/flags/de.svg new file mode 100644 index 000000000..0e357abe5 --- /dev/null +++ b/apps/web/public/flags/de.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/apps/web/public/flags/dg.svg b/apps/web/public/flags/dg.svg new file mode 100644 index 000000000..0feac1f0f --- /dev/null +++ b/apps/web/public/flags/dg.svg @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/dj.svg b/apps/web/public/flags/dj.svg new file mode 100644 index 000000000..50ed6aa64 --- /dev/null +++ b/apps/web/public/flags/dj.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/apps/web/public/flags/dk.svg b/apps/web/public/flags/dk.svg new file mode 100644 index 000000000..f9dbb2d3f --- /dev/null +++ b/apps/web/public/flags/dk.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/apps/web/public/flags/dm.svg b/apps/web/public/flags/dm.svg new file mode 100644 index 000000000..fbe5e29dd --- /dev/null +++ b/apps/web/public/flags/dm.svg @@ -0,0 +1,152 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/do.svg b/apps/web/public/flags/do.svg new file mode 100644 index 000000000..fa1154e1e --- /dev/null +++ b/apps/web/public/flags/do.svg @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/dz.svg b/apps/web/public/flags/dz.svg new file mode 100644 index 000000000..107988f11 --- /dev/null +++ b/apps/web/public/flags/dz.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/apps/web/public/flags/eac.svg b/apps/web/public/flags/eac.svg new file mode 100644 index 000000000..714f9ad21 --- /dev/null +++ b/apps/web/public/flags/eac.svg @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/ec.svg b/apps/web/public/flags/ec.svg new file mode 100644 index 000000000..b133e017d --- /dev/null +++ b/apps/web/public/flags/ec.svg @@ -0,0 +1,138 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/ee.svg b/apps/web/public/flags/ee.svg new file mode 100644 index 000000000..c0ffad85e --- /dev/null +++ b/apps/web/public/flags/ee.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/apps/web/public/flags/eg.svg b/apps/web/public/flags/eg.svg new file mode 100644 index 000000000..fbce4b7e0 --- /dev/null +++ b/apps/web/public/flags/eg.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/eh.svg b/apps/web/public/flags/eh.svg new file mode 100644 index 000000000..a47c534c0 --- /dev/null +++ b/apps/web/public/flags/eh.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/er.svg b/apps/web/public/flags/er.svg new file mode 100644 index 000000000..273dca76e --- /dev/null +++ b/apps/web/public/flags/er.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/apps/web/public/flags/es-ct.svg b/apps/web/public/flags/es-ct.svg new file mode 100644 index 000000000..edfabd674 --- /dev/null +++ b/apps/web/public/flags/es-ct.svg @@ -0,0 +1,4 @@ + + + + diff --git a/apps/web/public/flags/es-ga.svg b/apps/web/public/flags/es-ga.svg new file mode 100644 index 000000000..0153e3026 --- /dev/null +++ b/apps/web/public/flags/es-ga.svg @@ -0,0 +1,187 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/es-pv.svg b/apps/web/public/flags/es-pv.svg new file mode 100644 index 000000000..52b393de9 --- /dev/null +++ b/apps/web/public/flags/es-pv.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/apps/web/public/flags/es.svg b/apps/web/public/flags/es.svg new file mode 100644 index 000000000..2e1932fa2 --- /dev/null +++ b/apps/web/public/flags/es.svg @@ -0,0 +1,544 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/et.svg b/apps/web/public/flags/et.svg new file mode 100644 index 000000000..222536435 --- /dev/null +++ b/apps/web/public/flags/et.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/eu.svg b/apps/web/public/flags/eu.svg new file mode 100644 index 000000000..6081c8905 --- /dev/null +++ b/apps/web/public/flags/eu.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/fi.svg b/apps/web/public/flags/fi.svg new file mode 100644 index 000000000..380352b50 --- /dev/null +++ b/apps/web/public/flags/fi.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/apps/web/public/flags/fj.svg b/apps/web/public/flags/fj.svg new file mode 100644 index 000000000..e0e562d2d --- /dev/null +++ b/apps/web/public/flags/fj.svg @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/fk.svg b/apps/web/public/flags/fk.svg new file mode 100644 index 000000000..f81650eb9 --- /dev/null +++ b/apps/web/public/flags/fk.svg @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/fm.svg b/apps/web/public/flags/fm.svg new file mode 100644 index 000000000..74ac77a8a --- /dev/null +++ b/apps/web/public/flags/fm.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/apps/web/public/flags/fo.svg b/apps/web/public/flags/fo.svg new file mode 100644 index 000000000..8e52069ec --- /dev/null +++ b/apps/web/public/flags/fo.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/apps/web/public/flags/fr.svg b/apps/web/public/flags/fr.svg new file mode 100644 index 000000000..49d4bb306 --- /dev/null +++ b/apps/web/public/flags/fr.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/apps/web/public/flags/ga.svg b/apps/web/public/flags/ga.svg new file mode 100644 index 000000000..7cc67746b --- /dev/null +++ b/apps/web/public/flags/ga.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/apps/web/public/flags/gb-eng.svg b/apps/web/public/flags/gb-eng.svg new file mode 100644 index 000000000..0f4c5d5d4 --- /dev/null +++ b/apps/web/public/flags/gb-eng.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/apps/web/public/flags/gb-nir.svg b/apps/web/public/flags/gb-nir.svg new file mode 100644 index 000000000..685ed17a3 --- /dev/null +++ b/apps/web/public/flags/gb-nir.svg @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/gb-sct.svg b/apps/web/public/flags/gb-sct.svg new file mode 100644 index 000000000..7ec53f4f8 --- /dev/null +++ b/apps/web/public/flags/gb-sct.svg @@ -0,0 +1,4 @@ + + + + diff --git a/apps/web/public/flags/gb-wls.svg b/apps/web/public/flags/gb-wls.svg new file mode 100644 index 000000000..2f3525000 --- /dev/null +++ b/apps/web/public/flags/gb-wls.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/apps/web/public/flags/gb.svg b/apps/web/public/flags/gb.svg new file mode 100644 index 000000000..7e7594d23 --- /dev/null +++ b/apps/web/public/flags/gb.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/apps/web/public/flags/gd.svg b/apps/web/public/flags/gd.svg new file mode 100644 index 000000000..819412641 --- /dev/null +++ b/apps/web/public/flags/gd.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/ge.svg b/apps/web/public/flags/ge.svg new file mode 100644 index 000000000..d0800a985 --- /dev/null +++ b/apps/web/public/flags/ge.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/apps/web/public/flags/gf.svg b/apps/web/public/flags/gf.svg new file mode 100644 index 000000000..0283fdec2 --- /dev/null +++ b/apps/web/public/flags/gf.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/apps/web/public/flags/gg.svg b/apps/web/public/flags/gg.svg new file mode 100644 index 000000000..1b80efe4c --- /dev/null +++ b/apps/web/public/flags/gg.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/apps/web/public/flags/gh.svg b/apps/web/public/flags/gh.svg new file mode 100644 index 000000000..1a9047d7e --- /dev/null +++ b/apps/web/public/flags/gh.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/apps/web/public/flags/gi.svg b/apps/web/public/flags/gi.svg new file mode 100644 index 000000000..3b74d19a3 --- /dev/null +++ b/apps/web/public/flags/gi.svg @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/gl.svg b/apps/web/public/flags/gl.svg new file mode 100644 index 000000000..938d20fab --- /dev/null +++ b/apps/web/public/flags/gl.svg @@ -0,0 +1,4 @@ + + + + diff --git a/apps/web/public/flags/gm.svg b/apps/web/public/flags/gm.svg new file mode 100644 index 000000000..4b9515fde --- /dev/null +++ b/apps/web/public/flags/gm.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/gn.svg b/apps/web/public/flags/gn.svg new file mode 100644 index 000000000..00eca826e --- /dev/null +++ b/apps/web/public/flags/gn.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/apps/web/public/flags/gp.svg b/apps/web/public/flags/gp.svg new file mode 100644 index 000000000..06b67891e --- /dev/null +++ b/apps/web/public/flags/gp.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/apps/web/public/flags/gq.svg b/apps/web/public/flags/gq.svg new file mode 100644 index 000000000..f12157bf8 --- /dev/null +++ b/apps/web/public/flags/gq.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/gr.svg b/apps/web/public/flags/gr.svg new file mode 100644 index 000000000..2bfaa7e00 --- /dev/null +++ b/apps/web/public/flags/gr.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/gs.svg b/apps/web/public/flags/gs.svg new file mode 100644 index 000000000..0d6ff1424 --- /dev/null +++ b/apps/web/public/flags/gs.svg @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/gt.svg b/apps/web/public/flags/gt.svg new file mode 100644 index 000000000..a90ed5bff --- /dev/null +++ b/apps/web/public/flags/gt.svg @@ -0,0 +1,204 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/gu.svg b/apps/web/public/flags/gu.svg new file mode 100644 index 000000000..a1b53b87a --- /dev/null +++ b/apps/web/public/flags/gu.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/gw.svg b/apps/web/public/flags/gw.svg new file mode 100644 index 000000000..8a846ad01 --- /dev/null +++ b/apps/web/public/flags/gw.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/apps/web/public/flags/gy.svg b/apps/web/public/flags/gy.svg new file mode 100644 index 000000000..93d8b27aa --- /dev/null +++ b/apps/web/public/flags/gy.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/apps/web/public/flags/hk.svg b/apps/web/public/flags/hk.svg new file mode 100644 index 000000000..ffa87a231 --- /dev/null +++ b/apps/web/public/flags/hk.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/apps/web/public/flags/hm.svg b/apps/web/public/flags/hm.svg new file mode 100644 index 000000000..6402ed8dd --- /dev/null +++ b/apps/web/public/flags/hm.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/apps/web/public/flags/hn.svg b/apps/web/public/flags/hn.svg new file mode 100644 index 000000000..dbda0dd33 --- /dev/null +++ b/apps/web/public/flags/hn.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/hr.svg b/apps/web/public/flags/hr.svg new file mode 100644 index 000000000..ac129f1f9 --- /dev/null +++ b/apps/web/public/flags/hr.svg @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/ht.svg b/apps/web/public/flags/ht.svg new file mode 100644 index 000000000..3f246da8d --- /dev/null +++ b/apps/web/public/flags/ht.svg @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/hu.svg b/apps/web/public/flags/hu.svg new file mode 100644 index 000000000..32ea446b3 --- /dev/null +++ b/apps/web/public/flags/hu.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/apps/web/public/flags/ic.svg b/apps/web/public/flags/ic.svg new file mode 100644 index 000000000..d45040c2b --- /dev/null +++ b/apps/web/public/flags/ic.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/apps/web/public/flags/id.svg b/apps/web/public/flags/id.svg new file mode 100644 index 000000000..ce55a92f8 --- /dev/null +++ b/apps/web/public/flags/id.svg @@ -0,0 +1,4 @@ + + + + diff --git a/apps/web/public/flags/ie.svg b/apps/web/public/flags/ie.svg new file mode 100644 index 000000000..2b16cf123 --- /dev/null +++ b/apps/web/public/flags/ie.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/apps/web/public/flags/il.svg b/apps/web/public/flags/il.svg new file mode 100644 index 000000000..359d81cd2 --- /dev/null +++ b/apps/web/public/flags/il.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/im.svg b/apps/web/public/flags/im.svg new file mode 100644 index 000000000..23a38f160 --- /dev/null +++ b/apps/web/public/flags/im.svg @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/in.svg b/apps/web/public/flags/in.svg new file mode 100644 index 000000000..5fea2313b --- /dev/null +++ b/apps/web/public/flags/in.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/io.svg b/apps/web/public/flags/io.svg new file mode 100644 index 000000000..db811a4d1 --- /dev/null +++ b/apps/web/public/flags/io.svg @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/iq.svg b/apps/web/public/flags/iq.svg new file mode 100644 index 000000000..6b1346e80 --- /dev/null +++ b/apps/web/public/flags/iq.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/apps/web/public/flags/ir.svg b/apps/web/public/flags/ir.svg new file mode 100644 index 000000000..04ef60b1b --- /dev/null +++ b/apps/web/public/flags/ir.svg @@ -0,0 +1,219 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/is.svg b/apps/web/public/flags/is.svg new file mode 100644 index 000000000..fe08a02ff --- /dev/null +++ b/apps/web/public/flags/is.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/apps/web/public/flags/it.svg b/apps/web/public/flags/it.svg new file mode 100644 index 000000000..88bd45c2b --- /dev/null +++ b/apps/web/public/flags/it.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/apps/web/public/flags/je.svg b/apps/web/public/flags/je.svg new file mode 100644 index 000000000..9acb44508 --- /dev/null +++ b/apps/web/public/flags/je.svg @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/jm.svg b/apps/web/public/flags/jm.svg new file mode 100644 index 000000000..a114f8ce7 --- /dev/null +++ b/apps/web/public/flags/jm.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/apps/web/public/flags/jo.svg b/apps/web/public/flags/jo.svg new file mode 100644 index 000000000..bd34ede2f --- /dev/null +++ b/apps/web/public/flags/jo.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/jp.svg b/apps/web/public/flags/jp.svg new file mode 100644 index 000000000..2ec85aa1f --- /dev/null +++ b/apps/web/public/flags/jp.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/apps/web/public/flags/ke.svg b/apps/web/public/flags/ke.svg new file mode 100644 index 000000000..c8004f294 --- /dev/null +++ b/apps/web/public/flags/ke.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/kg.svg b/apps/web/public/flags/kg.svg new file mode 100644 index 000000000..13ae427b5 --- /dev/null +++ b/apps/web/public/flags/kg.svg @@ -0,0 +1,4 @@ + + + + diff --git a/apps/web/public/flags/kh.svg b/apps/web/public/flags/kh.svg new file mode 100644 index 000000000..7f8f92558 --- /dev/null +++ b/apps/web/public/flags/kh.svg @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/ki.svg b/apps/web/public/flags/ki.svg new file mode 100644 index 000000000..14466d238 --- /dev/null +++ b/apps/web/public/flags/ki.svg @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/km.svg b/apps/web/public/flags/km.svg new file mode 100644 index 000000000..114298432 --- /dev/null +++ b/apps/web/public/flags/km.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/kn.svg b/apps/web/public/flags/kn.svg new file mode 100644 index 000000000..aea5e413c --- /dev/null +++ b/apps/web/public/flags/kn.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/kp.svg b/apps/web/public/flags/kp.svg new file mode 100644 index 000000000..13c2f630b --- /dev/null +++ b/apps/web/public/flags/kp.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/kr.svg b/apps/web/public/flags/kr.svg new file mode 100644 index 000000000..2b215d491 --- /dev/null +++ b/apps/web/public/flags/kr.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/kw.svg b/apps/web/public/flags/kw.svg new file mode 100644 index 000000000..a77b1579a --- /dev/null +++ b/apps/web/public/flags/kw.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/apps/web/public/flags/ky.svg b/apps/web/public/flags/ky.svg new file mode 100644 index 000000000..7817710f2 --- /dev/null +++ b/apps/web/public/flags/ky.svg @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/kz.svg b/apps/web/public/flags/kz.svg new file mode 100644 index 000000000..536fb9ff7 --- /dev/null +++ b/apps/web/public/flags/kz.svg @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/la.svg b/apps/web/public/flags/la.svg new file mode 100644 index 000000000..788f096ff --- /dev/null +++ b/apps/web/public/flags/la.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/apps/web/public/flags/lb.svg b/apps/web/public/flags/lb.svg new file mode 100644 index 000000000..953592501 --- /dev/null +++ b/apps/web/public/flags/lb.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/lc.svg b/apps/web/public/flags/lc.svg new file mode 100644 index 000000000..b4de54250 --- /dev/null +++ b/apps/web/public/flags/lc.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/apps/web/public/flags/li.svg b/apps/web/public/flags/li.svg new file mode 100644 index 000000000..50afac078 --- /dev/null +++ b/apps/web/public/flags/li.svg @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/lk.svg b/apps/web/public/flags/lk.svg new file mode 100644 index 000000000..3f9b597da --- /dev/null +++ b/apps/web/public/flags/lk.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/lr.svg b/apps/web/public/flags/lr.svg new file mode 100644 index 000000000..a2f6a6614 --- /dev/null +++ b/apps/web/public/flags/lr.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/ls.svg b/apps/web/public/flags/ls.svg new file mode 100644 index 000000000..d90a8c492 --- /dev/null +++ b/apps/web/public/flags/ls.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/apps/web/public/flags/lt.svg b/apps/web/public/flags/lt.svg new file mode 100644 index 000000000..e42ba5038 --- /dev/null +++ b/apps/web/public/flags/lt.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/apps/web/public/flags/lu.svg b/apps/web/public/flags/lu.svg new file mode 100644 index 000000000..62cbef978 --- /dev/null +++ b/apps/web/public/flags/lu.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/apps/web/public/flags/lv.svg b/apps/web/public/flags/lv.svg new file mode 100644 index 000000000..fdcc9cacf --- /dev/null +++ b/apps/web/public/flags/lv.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/apps/web/public/flags/ly.svg b/apps/web/public/flags/ly.svg new file mode 100644 index 000000000..e90bb0c03 --- /dev/null +++ b/apps/web/public/flags/ly.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/apps/web/public/flags/ma.svg b/apps/web/public/flags/ma.svg new file mode 100644 index 000000000..d390ee6c9 --- /dev/null +++ b/apps/web/public/flags/ma.svg @@ -0,0 +1,4 @@ + + + + diff --git a/apps/web/public/flags/mc.svg b/apps/web/public/flags/mc.svg new file mode 100644 index 000000000..2cfc89a1b --- /dev/null +++ b/apps/web/public/flags/mc.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/apps/web/public/flags/md.svg b/apps/web/public/flags/md.svg new file mode 100644 index 000000000..12356e93e --- /dev/null +++ b/apps/web/public/flags/md.svg @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/me.svg b/apps/web/public/flags/me.svg new file mode 100644 index 000000000..3a8853ed7 --- /dev/null +++ b/apps/web/public/flags/me.svg @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/mf.svg b/apps/web/public/flags/mf.svg new file mode 100644 index 000000000..a06c440f3 --- /dev/null +++ b/apps/web/public/flags/mf.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/apps/web/public/flags/mg.svg b/apps/web/public/flags/mg.svg new file mode 100644 index 000000000..a22961c45 --- /dev/null +++ b/apps/web/public/flags/mg.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/apps/web/public/flags/mh.svg b/apps/web/public/flags/mh.svg new file mode 100644 index 000000000..a451e4915 --- /dev/null +++ b/apps/web/public/flags/mh.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/apps/web/public/flags/mk.svg b/apps/web/public/flags/mk.svg new file mode 100644 index 000000000..9e102bd00 --- /dev/null +++ b/apps/web/public/flags/mk.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/apps/web/public/flags/ml.svg b/apps/web/public/flags/ml.svg new file mode 100644 index 000000000..91cf9fa48 --- /dev/null +++ b/apps/web/public/flags/ml.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/apps/web/public/flags/mm.svg b/apps/web/public/flags/mm.svg new file mode 100644 index 000000000..6c307abfe --- /dev/null +++ b/apps/web/public/flags/mm.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/apps/web/public/flags/mn.svg b/apps/web/public/flags/mn.svg new file mode 100644 index 000000000..3425a98fc --- /dev/null +++ b/apps/web/public/flags/mn.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/mo.svg b/apps/web/public/flags/mo.svg new file mode 100644 index 000000000..5e39a7f30 --- /dev/null +++ b/apps/web/public/flags/mo.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/apps/web/public/flags/mp.svg b/apps/web/public/flags/mp.svg new file mode 100644 index 000000000..c30636287 --- /dev/null +++ b/apps/web/public/flags/mp.svg @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/mq.svg b/apps/web/public/flags/mq.svg new file mode 100644 index 000000000..43ce9df6e --- /dev/null +++ b/apps/web/public/flags/mq.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/apps/web/public/flags/mr.svg b/apps/web/public/flags/mr.svg new file mode 100644 index 000000000..0c55e5d25 --- /dev/null +++ b/apps/web/public/flags/mr.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/apps/web/public/flags/ms.svg b/apps/web/public/flags/ms.svg new file mode 100644 index 000000000..1ea744ea2 --- /dev/null +++ b/apps/web/public/flags/ms.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/mt.svg b/apps/web/public/flags/mt.svg new file mode 100644 index 000000000..145e0d042 --- /dev/null +++ b/apps/web/public/flags/mt.svg @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/mu.svg b/apps/web/public/flags/mu.svg new file mode 100644 index 000000000..359f35556 --- /dev/null +++ b/apps/web/public/flags/mu.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/apps/web/public/flags/mv.svg b/apps/web/public/flags/mv.svg new file mode 100644 index 000000000..46e636eee --- /dev/null +++ b/apps/web/public/flags/mv.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/apps/web/public/flags/mw.svg b/apps/web/public/flags/mw.svg new file mode 100644 index 000000000..fa3d3caa2 --- /dev/null +++ b/apps/web/public/flags/mw.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/apps/web/public/flags/mx.svg b/apps/web/public/flags/mx.svg new file mode 100644 index 000000000..3af0a0236 --- /dev/null +++ b/apps/web/public/flags/mx.svg @@ -0,0 +1,382 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/my.svg b/apps/web/public/flags/my.svg new file mode 100644 index 000000000..df2ec3bc2 --- /dev/null +++ b/apps/web/public/flags/my.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/mz.svg b/apps/web/public/flags/mz.svg new file mode 100644 index 000000000..768e348f2 --- /dev/null +++ b/apps/web/public/flags/mz.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/na.svg b/apps/web/public/flags/na.svg new file mode 100644 index 000000000..01580344c --- /dev/null +++ b/apps/web/public/flags/na.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/nc.svg b/apps/web/public/flags/nc.svg new file mode 100644 index 000000000..44d417168 --- /dev/null +++ b/apps/web/public/flags/nc.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/apps/web/public/flags/ne.svg b/apps/web/public/flags/ne.svg new file mode 100644 index 000000000..456ff2d2b --- /dev/null +++ b/apps/web/public/flags/ne.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/apps/web/public/flags/nf.svg b/apps/web/public/flags/nf.svg new file mode 100644 index 000000000..95c4ee1d4 --- /dev/null +++ b/apps/web/public/flags/nf.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/apps/web/public/flags/ng.svg b/apps/web/public/flags/ng.svg new file mode 100644 index 000000000..bf166c424 --- /dev/null +++ b/apps/web/public/flags/ng.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/apps/web/public/flags/ni.svg b/apps/web/public/flags/ni.svg new file mode 100644 index 000000000..45416d45d --- /dev/null +++ b/apps/web/public/flags/ni.svg @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/nl.svg b/apps/web/public/flags/nl.svg new file mode 100644 index 000000000..70e0c160b --- /dev/null +++ b/apps/web/public/flags/nl.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/apps/web/public/flags/no.svg b/apps/web/public/flags/no.svg new file mode 100644 index 000000000..c57515f72 --- /dev/null +++ b/apps/web/public/flags/no.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/apps/web/public/flags/np.svg b/apps/web/public/flags/np.svg new file mode 100644 index 000000000..c2b1b113c --- /dev/null +++ b/apps/web/public/flags/np.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/apps/web/public/flags/nr.svg b/apps/web/public/flags/nr.svg new file mode 100644 index 000000000..9a64f7aa5 --- /dev/null +++ b/apps/web/public/flags/nr.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/apps/web/public/flags/nu.svg b/apps/web/public/flags/nu.svg new file mode 100644 index 000000000..f59d1d95a --- /dev/null +++ b/apps/web/public/flags/nu.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/apps/web/public/flags/nz.svg b/apps/web/public/flags/nz.svg new file mode 100644 index 000000000..8aa7ab8b9 --- /dev/null +++ b/apps/web/public/flags/nz.svg @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/om.svg b/apps/web/public/flags/om.svg new file mode 100644 index 000000000..eb87ba1a0 --- /dev/null +++ b/apps/web/public/flags/om.svg @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/pa.svg b/apps/web/public/flags/pa.svg new file mode 100644 index 000000000..ffbb92569 --- /dev/null +++ b/apps/web/public/flags/pa.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/pc.svg b/apps/web/public/flags/pc.svg new file mode 100644 index 000000000..039e8ccd3 --- /dev/null +++ b/apps/web/public/flags/pc.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/pe.svg b/apps/web/public/flags/pe.svg new file mode 100644 index 000000000..0b6dc9984 --- /dev/null +++ b/apps/web/public/flags/pe.svg @@ -0,0 +1,4 @@ + + + + diff --git a/apps/web/public/flags/pf.svg b/apps/web/public/flags/pf.svg new file mode 100644 index 000000000..1a86e062c --- /dev/null +++ b/apps/web/public/flags/pf.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/pg.svg b/apps/web/public/flags/pg.svg new file mode 100644 index 000000000..507d32ffc --- /dev/null +++ b/apps/web/public/flags/pg.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/apps/web/public/flags/ph.svg b/apps/web/public/flags/ph.svg new file mode 100644 index 000000000..13b8a0482 --- /dev/null +++ b/apps/web/public/flags/ph.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/apps/web/public/flags/pk.svg b/apps/web/public/flags/pk.svg new file mode 100644 index 000000000..48c4f16c4 --- /dev/null +++ b/apps/web/public/flags/pk.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/pl.svg b/apps/web/public/flags/pl.svg new file mode 100644 index 000000000..560fe336a --- /dev/null +++ b/apps/web/public/flags/pl.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/apps/web/public/flags/pm.svg b/apps/web/public/flags/pm.svg new file mode 100644 index 000000000..d12838d86 --- /dev/null +++ b/apps/web/public/flags/pm.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/apps/web/public/flags/pn.svg b/apps/web/public/flags/pn.svg new file mode 100644 index 000000000..abd815504 --- /dev/null +++ b/apps/web/public/flags/pn.svg @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/pr.svg b/apps/web/public/flags/pr.svg new file mode 100644 index 000000000..57b554c4a --- /dev/null +++ b/apps/web/public/flags/pr.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/apps/web/public/flags/ps.svg b/apps/web/public/flags/ps.svg new file mode 100644 index 000000000..adc546c49 --- /dev/null +++ b/apps/web/public/flags/ps.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/apps/web/public/flags/pt.svg b/apps/web/public/flags/pt.svg new file mode 100644 index 000000000..1df104188 --- /dev/null +++ b/apps/web/public/flags/pt.svg @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/pw.svg b/apps/web/public/flags/pw.svg new file mode 100644 index 000000000..ea1fafc5b --- /dev/null +++ b/apps/web/public/flags/pw.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/apps/web/public/flags/py.svg b/apps/web/public/flags/py.svg new file mode 100644 index 000000000..17aef52d0 --- /dev/null +++ b/apps/web/public/flags/py.svg @@ -0,0 +1,157 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/qa.svg b/apps/web/public/flags/qa.svg new file mode 100644 index 000000000..3efa0dc47 --- /dev/null +++ b/apps/web/public/flags/qa.svg @@ -0,0 +1,4 @@ + + + + diff --git a/apps/web/public/flags/re.svg b/apps/web/public/flags/re.svg new file mode 100644 index 000000000..fd514f223 --- /dev/null +++ b/apps/web/public/flags/re.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/apps/web/public/flags/ro.svg b/apps/web/public/flags/ro.svg new file mode 100644 index 000000000..a33731a1d --- /dev/null +++ b/apps/web/public/flags/ro.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/apps/web/public/flags/rs.svg b/apps/web/public/flags/rs.svg new file mode 100644 index 000000000..8f8b830e6 --- /dev/null +++ b/apps/web/public/flags/rs.svg @@ -0,0 +1,292 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/ru.svg b/apps/web/public/flags/ru.svg new file mode 100644 index 000000000..46758444c --- /dev/null +++ b/apps/web/public/flags/ru.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/apps/web/public/flags/rw.svg b/apps/web/public/flags/rw.svg new file mode 100644 index 000000000..b54e64a16 --- /dev/null +++ b/apps/web/public/flags/rw.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/apps/web/public/flags/sa.svg b/apps/web/public/flags/sa.svg new file mode 100644 index 000000000..c84e29c19 --- /dev/null +++ b/apps/web/public/flags/sa.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/sb.svg b/apps/web/public/flags/sb.svg new file mode 100644 index 000000000..c74e6ad2f --- /dev/null +++ b/apps/web/public/flags/sb.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/apps/web/public/flags/sc.svg b/apps/web/public/flags/sc.svg new file mode 100644 index 000000000..245e04783 --- /dev/null +++ b/apps/web/public/flags/sc.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/apps/web/public/flags/sd.svg b/apps/web/public/flags/sd.svg new file mode 100644 index 000000000..5805b7990 --- /dev/null +++ b/apps/web/public/flags/sd.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/apps/web/public/flags/se.svg b/apps/web/public/flags/se.svg new file mode 100644 index 000000000..d527fec41 --- /dev/null +++ b/apps/web/public/flags/se.svg @@ -0,0 +1,4 @@ + + + + diff --git a/apps/web/public/flags/sg.svg b/apps/web/public/flags/sg.svg new file mode 100644 index 000000000..489a4a043 --- /dev/null +++ b/apps/web/public/flags/sg.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/apps/web/public/flags/sh-ac.svg b/apps/web/public/flags/sh-ac.svg new file mode 100644 index 000000000..6713c99dd --- /dev/null +++ b/apps/web/public/flags/sh-ac.svg @@ -0,0 +1,689 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/sh-hl.svg b/apps/web/public/flags/sh-hl.svg new file mode 100644 index 000000000..289a2da37 --- /dev/null +++ b/apps/web/public/flags/sh-hl.svg @@ -0,0 +1,164 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/sh-ta.svg b/apps/web/public/flags/sh-ta.svg new file mode 100644 index 000000000..e4f9f0b3c --- /dev/null +++ b/apps/web/public/flags/sh-ta.svg @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/sh.svg b/apps/web/public/flags/sh.svg new file mode 100644 index 000000000..9912b237e --- /dev/null +++ b/apps/web/public/flags/sh.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/apps/web/public/flags/si.svg b/apps/web/public/flags/si.svg new file mode 100644 index 000000000..c9a59f614 --- /dev/null +++ b/apps/web/public/flags/si.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/sj.svg b/apps/web/public/flags/sj.svg new file mode 100644 index 000000000..5caa28029 --- /dev/null +++ b/apps/web/public/flags/sj.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/apps/web/public/flags/sk.svg b/apps/web/public/flags/sk.svg new file mode 100644 index 000000000..8c0b5e3f7 --- /dev/null +++ b/apps/web/public/flags/sk.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/apps/web/public/flags/sl.svg b/apps/web/public/flags/sl.svg new file mode 100644 index 000000000..2869a5858 --- /dev/null +++ b/apps/web/public/flags/sl.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/apps/web/public/flags/sm.svg b/apps/web/public/flags/sm.svg new file mode 100644 index 000000000..b055ce726 --- /dev/null +++ b/apps/web/public/flags/sm.svg @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/sn.svg b/apps/web/public/flags/sn.svg new file mode 100644 index 000000000..f27a86909 --- /dev/null +++ b/apps/web/public/flags/sn.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/apps/web/public/flags/so.svg b/apps/web/public/flags/so.svg new file mode 100644 index 000000000..86877ffdf --- /dev/null +++ b/apps/web/public/flags/so.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/apps/web/public/flags/sr.svg b/apps/web/public/flags/sr.svg new file mode 100644 index 000000000..c1e71b5d9 --- /dev/null +++ b/apps/web/public/flags/sr.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/apps/web/public/flags/ss.svg b/apps/web/public/flags/ss.svg new file mode 100644 index 000000000..16dcbb9aa --- /dev/null +++ b/apps/web/public/flags/ss.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/apps/web/public/flags/st.svg b/apps/web/public/flags/st.svg new file mode 100644 index 000000000..29a8e3758 --- /dev/null +++ b/apps/web/public/flags/st.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/sv.svg b/apps/web/public/flags/sv.svg new file mode 100644 index 000000000..c4b63fe2f --- /dev/null +++ b/apps/web/public/flags/sv.svg @@ -0,0 +1,593 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/sx.svg b/apps/web/public/flags/sx.svg new file mode 100644 index 000000000..d37cd8e60 --- /dev/null +++ b/apps/web/public/flags/sx.svg @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/sy.svg b/apps/web/public/flags/sy.svg new file mode 100644 index 000000000..1896a9cd8 --- /dev/null +++ b/apps/web/public/flags/sy.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/apps/web/public/flags/sz.svg b/apps/web/public/flags/sz.svg new file mode 100644 index 000000000..05c14040b --- /dev/null +++ b/apps/web/public/flags/sz.svg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/tc.svg b/apps/web/public/flags/tc.svg new file mode 100644 index 000000000..cb453fd15 --- /dev/null +++ b/apps/web/public/flags/tc.svg @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/td.svg b/apps/web/public/flags/td.svg new file mode 100644 index 000000000..605217ee6 --- /dev/null +++ b/apps/web/public/flags/td.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/apps/web/public/flags/tf.svg b/apps/web/public/flags/tf.svg new file mode 100644 index 000000000..735c059ca --- /dev/null +++ b/apps/web/public/flags/tf.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/tg.svg b/apps/web/public/flags/tg.svg new file mode 100644 index 000000000..dbc017506 --- /dev/null +++ b/apps/web/public/flags/tg.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/th.svg b/apps/web/public/flags/th.svg new file mode 100644 index 000000000..c4404d2d6 --- /dev/null +++ b/apps/web/public/flags/th.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/apps/web/public/flags/tj.svg b/apps/web/public/flags/tj.svg new file mode 100644 index 000000000..d8b6ad90c --- /dev/null +++ b/apps/web/public/flags/tj.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/tk.svg b/apps/web/public/flags/tk.svg new file mode 100644 index 000000000..568d4a8f7 --- /dev/null +++ b/apps/web/public/flags/tk.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/apps/web/public/flags/tl.svg b/apps/web/public/flags/tl.svg new file mode 100644 index 000000000..2052311a3 --- /dev/null +++ b/apps/web/public/flags/tl.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/apps/web/public/flags/tm.svg b/apps/web/public/flags/tm.svg new file mode 100644 index 000000000..2b96db0f7 --- /dev/null +++ b/apps/web/public/flags/tm.svg @@ -0,0 +1,204 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/tn.svg b/apps/web/public/flags/tn.svg new file mode 100644 index 000000000..31c61a954 --- /dev/null +++ b/apps/web/public/flags/tn.svg @@ -0,0 +1,4 @@ + + + + diff --git a/apps/web/public/flags/to.svg b/apps/web/public/flags/to.svg new file mode 100644 index 000000000..ad429dc42 --- /dev/null +++ b/apps/web/public/flags/to.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/apps/web/public/flags/tr.svg b/apps/web/public/flags/tr.svg new file mode 100644 index 000000000..1d9e85269 --- /dev/null +++ b/apps/web/public/flags/tr.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/apps/web/public/flags/tt.svg b/apps/web/public/flags/tt.svg new file mode 100644 index 000000000..cbb0339f0 --- /dev/null +++ b/apps/web/public/flags/tt.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/apps/web/public/flags/tv.svg b/apps/web/public/flags/tv.svg new file mode 100644 index 000000000..eaa1e5273 --- /dev/null +++ b/apps/web/public/flags/tv.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/apps/web/public/flags/tw.svg b/apps/web/public/flags/tw.svg new file mode 100644 index 000000000..4cf6bd8a6 --- /dev/null +++ b/apps/web/public/flags/tw.svg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/tz.svg b/apps/web/public/flags/tz.svg new file mode 100644 index 000000000..ea2c233c5 --- /dev/null +++ b/apps/web/public/flags/tz.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/apps/web/public/flags/ua.svg b/apps/web/public/flags/ua.svg new file mode 100644 index 000000000..4ce79e780 --- /dev/null +++ b/apps/web/public/flags/ua.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/apps/web/public/flags/ug.svg b/apps/web/public/flags/ug.svg new file mode 100644 index 000000000..5630cc7c6 --- /dev/null +++ b/apps/web/public/flags/ug.svg @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/um.svg b/apps/web/public/flags/um.svg new file mode 100644 index 000000000..01236d11b --- /dev/null +++ b/apps/web/public/flags/um.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/apps/web/public/flags/un.svg b/apps/web/public/flags/un.svg new file mode 100644 index 000000000..638cb022b --- /dev/null +++ b/apps/web/public/flags/un.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/us.svg b/apps/web/public/flags/us.svg new file mode 100644 index 000000000..47f54aa53 --- /dev/null +++ b/apps/web/public/flags/us.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/apps/web/public/flags/uy.svg b/apps/web/public/flags/uy.svg new file mode 100644 index 000000000..4f484bcf9 --- /dev/null +++ b/apps/web/public/flags/uy.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/uz.svg b/apps/web/public/flags/uz.svg new file mode 100644 index 000000000..a8d53bff3 --- /dev/null +++ b/apps/web/public/flags/uz.svg @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/va.svg b/apps/web/public/flags/va.svg new file mode 100644 index 000000000..7eaf5d3f7 --- /dev/null +++ b/apps/web/public/flags/va.svg @@ -0,0 +1,190 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/vc.svg b/apps/web/public/flags/vc.svg new file mode 100644 index 000000000..e6091b9d4 --- /dev/null +++ b/apps/web/public/flags/vc.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/apps/web/public/flags/ve.svg b/apps/web/public/flags/ve.svg new file mode 100644 index 000000000..81d317b83 --- /dev/null +++ b/apps/web/public/flags/ve.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/vg.svg b/apps/web/public/flags/vg.svg new file mode 100644 index 000000000..4e89e5cf2 --- /dev/null +++ b/apps/web/public/flags/vg.svg @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/vi.svg b/apps/web/public/flags/vi.svg new file mode 100644 index 000000000..a8ed75536 --- /dev/null +++ b/apps/web/public/flags/vi.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/vn.svg b/apps/web/public/flags/vn.svg new file mode 100644 index 000000000..0450271b9 --- /dev/null +++ b/apps/web/public/flags/vn.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/apps/web/public/flags/vu.svg b/apps/web/public/flags/vu.svg new file mode 100644 index 000000000..da7bbc13c --- /dev/null +++ b/apps/web/public/flags/vu.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/wf.svg b/apps/web/public/flags/wf.svg new file mode 100644 index 000000000..ca0ade00a --- /dev/null +++ b/apps/web/public/flags/wf.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/apps/web/public/flags/ws.svg b/apps/web/public/flags/ws.svg new file mode 100644 index 000000000..8655cdc20 --- /dev/null +++ b/apps/web/public/flags/ws.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/apps/web/public/flags/xk.svg b/apps/web/public/flags/xk.svg new file mode 100644 index 000000000..d5b5ba7df --- /dev/null +++ b/apps/web/public/flags/xk.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/apps/web/public/flags/xx.svg b/apps/web/public/flags/xx.svg new file mode 100644 index 000000000..0baee7805 --- /dev/null +++ b/apps/web/public/flags/xx.svg @@ -0,0 +1,4 @@ + + + + diff --git a/apps/web/public/flags/ye.svg b/apps/web/public/flags/ye.svg new file mode 100644 index 000000000..83886232f --- /dev/null +++ b/apps/web/public/flags/ye.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/apps/web/public/flags/yt.svg b/apps/web/public/flags/yt.svg new file mode 100644 index 000000000..75b580ea2 --- /dev/null +++ b/apps/web/public/flags/yt.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/apps/web/public/flags/za.svg b/apps/web/public/flags/za.svg new file mode 100644 index 000000000..6aeb743e7 --- /dev/null +++ b/apps/web/public/flags/za.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/zm.svg b/apps/web/public/flags/zm.svg new file mode 100644 index 000000000..f7f72c722 --- /dev/null +++ b/apps/web/public/flags/zm.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/flags/zw.svg b/apps/web/public/flags/zw.svg new file mode 100644 index 000000000..0d481a945 --- /dev/null +++ b/apps/web/public/flags/zw.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/fonts/font-atlas.json b/apps/web/public/fonts/font-atlas.json index deb0eb918..a5c2b7f67 100644 --- a/apps/web/public/fonts/font-atlas.json +++ b/apps/web/public/fonts/font-atlas.json @@ -1,11348 +1,11348 @@ -{ - "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"] - } - } -} +{ + "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"] + } + } +} diff --git a/apps/web/public/logos/opencut/icon.svg b/apps/web/public/logos/opencut/icon.svg index b6da29799..753ebb1d4 100644 --- a/apps/web/public/logos/opencut/icon.svg +++ b/apps/web/public/logos/opencut/icon.svg @@ -1,11 +1,11 @@ - - - - - - - - - - - + + + + + + + + + + + diff --git a/apps/web/public/logos/opencut/logo-light.svg b/apps/web/public/logos/opencut/logo-light.svg index 84c2ceb5c..3dea5278e 100644 --- a/apps/web/public/logos/opencut/logo-light.svg +++ b/apps/web/public/logos/opencut/logo-light.svg @@ -1,4 +1,4 @@ - - - - + + + + diff --git a/apps/web/public/logos/opencut/logo.svg b/apps/web/public/logos/opencut/logo.svg index a691b2617..179db068c 100644 --- a/apps/web/public/logos/opencut/logo.svg +++ b/apps/web/public/logos/opencut/logo.svg @@ -1,4 +1,4 @@ - - - - + + + + diff --git a/apps/web/public/logos/opencut/svg/logo.svg b/apps/web/public/logos/opencut/svg/logo.svg index f4079a5ee..47c195669 100644 --- a/apps/web/public/logos/opencut/svg/logo.svg +++ b/apps/web/public/logos/opencut/svg/logo.svg @@ -1,10 +1,10 @@ - - - - - - - - - - + + + + + + + + + + diff --git a/apps/web/public/logos/opencut/symbol-light.svg b/apps/web/public/logos/opencut/symbol-light.svg index b590c442e..9d43da689 100644 --- a/apps/web/public/logos/opencut/symbol-light.svg +++ b/apps/web/public/logos/opencut/symbol-light.svg @@ -1,15 +1,15 @@ - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + diff --git a/apps/web/public/logos/opencut/symbol.svg b/apps/web/public/logos/opencut/symbol.svg index e821060b9..4b9f2b843 100644 --- a/apps/web/public/logos/opencut/symbol.svg +++ b/apps/web/public/logos/opencut/symbol.svg @@ -1,15 +1,15 @@ - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + diff --git a/apps/web/public/logos/opencut/text-light.svg b/apps/web/public/logos/opencut/text-light.svg index daec6eec6..d30e5128e 100644 --- a/apps/web/public/logos/opencut/text-light.svg +++ b/apps/web/public/logos/opencut/text-light.svg @@ -1,10 +1,10 @@ - - - - - - - - - - + + + + + + + + + + diff --git a/apps/web/public/logos/opencut/text.svg b/apps/web/public/logos/opencut/text.svg index 1b419cf5c..d5013ea85 100644 --- a/apps/web/public/logos/opencut/text.svg +++ b/apps/web/public/logos/opencut/text.svg @@ -1,3 +1,3 @@ - - - + + + diff --git a/apps/web/public/logos/others/fal.svg b/apps/web/public/logos/others/fal.svg index abc565134..6562afd53 100644 --- a/apps/web/public/logos/others/fal.svg +++ b/apps/web/public/logos/others/fal.svg @@ -1,3 +1,3 @@ - - - + + + diff --git a/apps/web/public/logos/others/vercel.svg b/apps/web/public/logos/others/vercel.svg index 824018a39..483c970d8 100644 --- a/apps/web/public/logos/others/vercel.svg +++ b/apps/web/public/logos/others/vercel.svg @@ -1,3 +1,3 @@ - - - + + + diff --git a/apps/web/public/manifest.json b/apps/web/public/manifest.json index 2f00e1e9f..3b05a1259 100644 --- a/apps/web/public/manifest.json +++ b/apps/web/public/manifest.json @@ -1,44 +1,44 @@ -{ - "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" - } - ] -} +{ + "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" + } + ] +} diff --git a/apps/web/public/shapes/circle.svg b/apps/web/public/shapes/circle.svg index adb837cf9..2de141cd3 100644 --- a/apps/web/public/shapes/circle.svg +++ b/apps/web/public/shapes/circle.svg @@ -1,3 +1,3 @@ - + diff --git a/apps/web/public/shapes/diamond.svg b/apps/web/public/shapes/diamond.svg index 4a039b5cb..a0a31aa8e 100644 --- a/apps/web/public/shapes/diamond.svg +++ b/apps/web/public/shapes/diamond.svg @@ -1,3 +1,3 @@ - + diff --git a/apps/web/public/shapes/hexagon.svg b/apps/web/public/shapes/hexagon.svg index e1eb087ec..e50cd91f9 100644 --- a/apps/web/public/shapes/hexagon.svg +++ b/apps/web/public/shapes/hexagon.svg @@ -1,3 +1,6 @@ - + diff --git a/apps/web/public/shapes/square.svg b/apps/web/public/shapes/square.svg index ed9b790fd..58a0f6adc 100644 --- a/apps/web/public/shapes/square.svg +++ b/apps/web/public/shapes/square.svg @@ -1,3 +1,3 @@ - + diff --git a/apps/web/public/shapes/star.svg b/apps/web/public/shapes/star.svg index c49e0c53d..f646764b1 100644 --- a/apps/web/public/shapes/star.svg +++ b/apps/web/public/shapes/star.svg @@ -1,3 +1,6 @@ - + diff --git a/apps/web/public/shapes/triangle.svg b/apps/web/public/shapes/triangle.svg index a1bd6b0ca..9c1c5a41e 100644 --- a/apps/web/public/shapes/triangle.svg +++ b/apps/web/public/shapes/triangle.svg @@ -1,3 +1,3 @@ - + diff --git a/apps/web/scripts/generate-font-sprites.ts b/apps/web/scripts/generate-font-sprites.ts index 144d68ecc..f0d98bc8a 100644 --- a/apps/web/scripts/generate-font-sprites.ts +++ b/apps/web/scripts/generate-font-sprites.ts @@ -1,281 +1,281 @@ -/** - * 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); -}); +/** + * 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); +}); diff --git a/apps/web/src/components/editor/dialogs/shortcuts-dialog.tsx b/apps/web/src/actions/components/shortcuts-dialog.tsx similarity index 89% rename from apps/web/src/components/editor/dialogs/shortcuts-dialog.tsx rename to apps/web/src/actions/components/shortcuts-dialog.tsx index 565fe705b..7f7d8a878 100644 --- a/apps/web/src/components/editor/dialogs/shortcuts-dialog.tsx +++ b/apps/web/src/actions/components/shortcuts-dialog.tsx @@ -1,229 +1,232 @@ -"use client"; - -import { useEffect, useState } from "react"; -import { toast } from "sonner"; -import { - type KeyboardShortcut, - useKeyboardShortcutsHelp, -} from "@/hooks/use-keyboard-shortcuts-help"; -import { useKeybindingsStore } from "@/stores/keybindings-store"; -import { Button } from "@/components/ui/button"; -import { - Dialog, - DialogBody, - DialogContent, - DialogFooter, - DialogHeader, - DialogTitle, -} from "@/components/ui/dialog"; - -export function ShortcutsDialog({ - isOpen, - onOpenChange, -}: { - isOpen: boolean; - onOpenChange: (open: boolean) => void; -}) { - const [recordingShortcut, setRecordingShortcut] = - useState(null); - - const { - updateKeybinding, - removeKeybinding, - getKeybindingString, - validateKeybinding, - getKeybindingsForAction, - setIsRecording, - resetToDefaults, - isRecording, - } = useKeybindingsStore(); - - const { shortcuts } = useKeyboardShortcutsHelp(); - - const categories = Array.from(new Set(shortcuts.map((s) => s.category))); - - useEffect(() => { - if (!isRecording || !recordingShortcut) return; - - const handleKeyDown = (e: KeyboardEvent) => { - e.preventDefault(); - e.stopPropagation(); - - const keyString = getKeybindingString(e); - if (keyString) { - const conflict = validateKeybinding( - keyString, - recordingShortcut.action, - ); - if (conflict) { - toast.error( - `Key "${keyString}" is already bound to "${conflict.existingAction}"`, - ); - setRecordingShortcut(null); - return; - } - - const oldKeys = getKeybindingsForAction(recordingShortcut.action); - for (const key of oldKeys) { - removeKeybinding(key); - } - - updateKeybinding(keyString, recordingShortcut.action); - - setIsRecording(false); - setRecordingShortcut(null); - } - }; - - const handleClickOutside = () => { - setRecordingShortcut(null); - setIsRecording(false); - }; - - document.addEventListener("keydown", handleKeyDown); - document.addEventListener("click", handleClickOutside); - - return () => { - document.removeEventListener("keydown", handleKeyDown); - document.removeEventListener("click", handleClickOutside); - }; - }, [ - recordingShortcut, - getKeybindingString, - updateKeybinding, - removeKeybinding, - validateKeybinding, - getKeybindingsForAction, - setIsRecording, - isRecording, - ]); - - const handleStartRecording = (shortcut: KeyboardShortcut) => { - setRecordingShortcut(shortcut); - setIsRecording(true); - }; - - return ( - - - - Keyboard shortcuts - - - -
- {categories.map((category) => ( -
-

- {category} -

-
- {shortcuts - .filter((shortcut) => shortcut.category === category) - .map((shortcut) => ( - handleStartRecording(shortcut)} - /> - ))} -
-
- ))} -
-
- - - -
-
- ); -} - -function ShortcutItem({ - shortcut, - isRecording, - onStartRecording, -}: { - shortcut: KeyboardShortcut; - isRecording: boolean; - onStartRecording: (params: { shortcut: KeyboardShortcut }) => void; -}) { - const displayKeys = shortcut.keys.filter((key: string) => { - if ( - key.includes("Cmd") && - shortcut.keys.includes(key.replace("Cmd", "Ctrl")) - ) - return false; - - return true; - }); - - return ( -
-
- {shortcut.icon && ( -
{shortcut.icon}
- )} - {shortcut.description} -
-
- {displayKeys.map((key: string, index: number) => ( -
-
- {key.split("+").map((keyPart: string, partIndex: number) => { - const keyId = `${shortcut.id}-${index}-${partIndex}`; - return ( - onStartRecording({ shortcut })} - > - {keyPart} - - ); - })} -
- {index < displayKeys.length - 1 && ( - or - )} -
- ))} -
-
- ); -} - -function EditableShortcutKey({ - children, - isRecording, - onStartRecording, -}: { - children: React.ReactNode; - isRecording: boolean; - onStartRecording: () => void; -}) { - const handleClick = (e: React.MouseEvent) => { - e.preventDefault(); - e.stopPropagation(); - onStartRecording(); - }; - - return ( - - ); -} +"use client"; + +import { useEffect, useState } from "react"; +import { toast } from "sonner"; +import { + type KeyboardShortcut, + useKeyboardShortcutsHelp, +} from "@/actions/use-keyboard-shortcuts-help"; +import { useKeybindingsStore } from "@/actions/keybindings-store"; +import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogBody, + DialogContent, + DialogFooter, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; + +export function ShortcutsDialog({ + isOpen, + onOpenChange, +}: { + isOpen: boolean; + onOpenChange: (open: boolean) => void; +}) { + const [recordingShortcut, setRecordingShortcut] = + useState(null); + + const { + updateKeybinding, + removeKeybinding, + getKeybindingString, + validateKeybinding, + getKeybindingsForAction, + setIsRecording, + resetToDefaults, + isRecording, + } = useKeybindingsStore(); + + const { shortcuts } = useKeyboardShortcutsHelp(); + + const categories = Array.from(new Set(shortcuts.map((s) => s.category))); + + useEffect(() => { + if (!isRecording || !recordingShortcut) return; + + const handleKeyDown = (e: KeyboardEvent) => { + e.preventDefault(); + e.stopPropagation(); + + const keyString = getKeybindingString(e); + if (keyString) { + const conflict = validateKeybinding({ + key: keyString, + action: recordingShortcut.action, + }); + if (conflict) { + toast.error( + `Key "${keyString}" is already bound to "${conflict.existingAction}"`, + ); + setRecordingShortcut(null); + return; + } + + const oldKeys = getKeybindingsForAction(recordingShortcut.action); + for (const key of oldKeys) { + removeKeybinding(key); + } + + updateKeybinding({ + key: keyString, + action: recordingShortcut.action, + }); + + setIsRecording(false); + setRecordingShortcut(null); + } + }; + + const handleClickOutside = () => { + setRecordingShortcut(null); + setIsRecording(false); + }; + + document.addEventListener("keydown", handleKeyDown); + document.addEventListener("click", handleClickOutside); + + return () => { + document.removeEventListener("keydown", handleKeyDown); + document.removeEventListener("click", handleClickOutside); + }; + }, [ + recordingShortcut, + getKeybindingString, + updateKeybinding, + removeKeybinding, + validateKeybinding, + getKeybindingsForAction, + setIsRecording, + isRecording, + ]); + + const handleStartRecording = (shortcut: KeyboardShortcut) => { + setRecordingShortcut(shortcut); + setIsRecording(true); + }; + + return ( + + + + Keyboard shortcuts + + + +
+ {categories.map((category) => ( +
+

+ {category} +

+
+ {shortcuts + .filter((shortcut) => shortcut.category === category) + .map((shortcut) => ( + handleStartRecording(shortcut)} + /> + ))} +
+
+ ))} +
+
+ + + +
+
+ ); +} + +function ShortcutItem({ + shortcut, + isRecording, + onStartRecording, +}: { + shortcut: KeyboardShortcut; + isRecording: boolean; + onStartRecording: (params: { shortcut: KeyboardShortcut }) => void; +}) { + const displayKeys = shortcut.keys.filter((key: string) => { + if ( + key.includes("Cmd") && + shortcut.keys.includes(key.replace("Cmd", "Ctrl")) + ) + return false; + + return true; + }); + + return ( +
+
+ {shortcut.icon && ( +
{shortcut.icon}
+ )} + {shortcut.description} +
+
+ {displayKeys.map((key: string, index: number) => ( +
+
+ {key.split("+").map((keyPart: string, partIndex: number) => { + const keyId = `${shortcut.id}-${index}-${partIndex}`; + return ( + onStartRecording({ shortcut })} + > + {keyPart} + + ); + })} +
+ {index < displayKeys.length - 1 && ( + or + )} +
+ ))} +
+
+ ); +} + +function EditableShortcutKey({ + children, + isRecording, + onStartRecording, +}: { + children: React.ReactNode; + isRecording: boolean; + onStartRecording: () => void; +}) { + const handleClick = (e: React.MouseEvent) => { + e.preventDefault(); + e.stopPropagation(); + onStartRecording(); + }; + + return ( + + ); +} diff --git a/apps/web/src/actions/definitions.ts b/apps/web/src/actions/definitions.ts new file mode 100644 index 000000000..986510ec4 --- /dev/null +++ b/apps/web/src/actions/definitions.ts @@ -0,0 +1,210 @@ +import type { ShortcutKey } from "@/actions/keybinding"; +import type { TActionWithOptionalArgs } from "./types"; + +export type TActionCategory = + | "playback" + | "navigation" + | "editing" + | "selection" + | "history" + | "timeline" + | "controls" + | "assets"; + +export interface TActionBaseDefinition { + description: string; + category: TActionCategory; + args?: Record; +} + +export interface TActionDefinition extends TActionBaseDefinition { + defaultShortcuts?: readonly ShortcutKey[]; +} + +export const ACTIONS = { + "toggle-play": { + description: "Play/Pause", + category: "playback", + }, + "stop-playback": { + description: "Stop playback", + category: "playback", + }, + "seek-forward": { + description: "Seek forward 1 second", + category: "playback", + args: { seconds: "number" }, + }, + "seek-backward": { + description: "Seek backward 1 second", + category: "playback", + args: { seconds: "number" }, + }, + "frame-step-forward": { + description: "Frame step forward", + category: "navigation", + }, + "frame-step-backward": { + description: "Frame step backward", + category: "navigation", + }, + "jump-forward": { + description: "Jump forward 5 seconds", + category: "navigation", + args: { seconds: "number" }, + }, + "jump-backward": { + description: "Jump backward 5 seconds", + category: "navigation", + args: { seconds: "number" }, + }, + "goto-start": { + description: "Go to timeline start", + category: "navigation", + }, + "goto-end": { + description: "Go to timeline end", + category: "navigation", + }, + split: { + description: "Split elements at playhead", + category: "editing", + }, + "split-left": { + description: "Split and remove left", + category: "editing", + }, + "split-right": { + description: "Split and remove right", + category: "editing", + }, + "delete-selected": { + description: "Delete current selection", + category: "editing", + }, + "copy-selected": { + description: "Copy selected elements", + category: "editing", + }, + "paste-copied": { + description: "Paste elements at playhead", + category: "editing", + }, + "toggle-snapping": { + description: "Toggle snapping", + category: "editing", + }, + "toggle-ripple-editing": { + description: "Toggle ripple editing", + category: "editing", + }, + "toggle-source-audio": { + description: "Extract or recover source audio", + category: "editing", + }, + "select-all": { + description: "Select all elements", + category: "selection", + }, + "cancel-interaction": { + description: "Cancel current interaction", + category: "controls", + }, + "deselect-all": { + description: "Deselect all elements", + category: "selection", + }, + "duplicate-selected": { + description: "Duplicate selected element", + category: "selection", + }, + "toggle-elements-muted-selected": { + description: "Mute/unmute selected elements", + category: "selection", + }, + "toggle-elements-visibility-selected": { + description: "Show/hide selected elements", + category: "selection", + }, + "toggle-bookmark": { + description: "Toggle bookmark at playhead", + category: "timeline", + }, + undo: { + description: "Undo", + category: "history", + }, + redo: { + description: "Redo", + category: "history", + }, + "remove-media-asset": { + description: "Remove media asset", + category: "assets", + args: { projectId: "string", assetId: "string" }, + }, + "remove-media-assets": { + description: "Remove media assets", + category: "assets", + args: { projectId: "string", assetIds: "string[]" }, + }, +} as const satisfies Record; + +export type TAction = keyof typeof ACTIONS; + +const ACTION_DEFAULT_SHORTCUTS = [ + ["toggle-play", ["space", "k"]], + ["seek-forward", ["l"]], + ["seek-backward", ["j"]], + ["frame-step-forward", ["right"]], + ["frame-step-backward", ["left"]], + ["jump-forward", ["shift+right"]], + ["jump-backward", ["shift+left"]], + ["goto-start", ["home", "enter"]], + ["goto-end", ["end"]], + ["split", ["s"]], + ["split-left", ["q"]], + ["split-right", ["w"]], + ["delete-selected", ["backspace", "delete"]], + ["copy-selected", ["ctrl+c"]], + ["paste-copied", ["ctrl+v"]], + ["toggle-snapping", ["n"]], + ["select-all", ["ctrl+a"]], + ["cancel-interaction", ["escape"]], + ["duplicate-selected", ["ctrl+d"]], + ["undo", ["ctrl+z"]], + ["redo", ["ctrl+shift+z", "ctrl+y"]], +] as const satisfies ReadonlyArray< + readonly [TActionWithOptionalArgs, readonly ShortcutKey[]] +>; + +const ACTION_DEFAULT_SHORTCUTS_BY_ACTION = new Map< + TAction, + readonly ShortcutKey[] +>(ACTION_DEFAULT_SHORTCUTS); + +export function getActionDefinition({ + action, +}: { + action: TAction; +}): TActionDefinition { + return { + ...ACTIONS[action], + defaultShortcuts: ACTION_DEFAULT_SHORTCUTS_BY_ACTION.get(action), + }; +} + +export function getDefaultShortcuts(): Map< + ShortcutKey, + TActionWithOptionalArgs +> { + const shortcuts = new Map(); + + for (const [action, defaultShortcuts] of ACTION_DEFAULT_SHORTCUTS) { + for (const shortcut of defaultShortcuts) { + shortcuts.set(shortcut, action); + } + } + + return shortcuts; +} diff --git a/apps/web/src/lib/actions/index.ts b/apps/web/src/actions/index.ts similarity index 100% rename from apps/web/src/lib/actions/index.ts rename to apps/web/src/actions/index.ts diff --git a/apps/web/src/actions/keybinding.ts b/apps/web/src/actions/keybinding.ts new file mode 100644 index 000000000..aeec2fd6d --- /dev/null +++ b/apps/web/src/actions/keybinding.ts @@ -0,0 +1,43 @@ +import type { TActionWithOptionalArgs } from "./types"; + +/** + * Alt is also regarded as macOS OPTION (⌥) key + * Ctrl is also regarded as macOS COMMAND (⌘) key (NOTE: this differs from HTML Keyboard spec where COMMAND is Meta key!) + */ +export type ModifierKeys = + | "ctrl" + | "alt" + | "shift" + | "ctrl+shift" + | "alt+shift" + | "ctrl+alt" + | "ctrl+alt+shift"; + +const KEYS = [ + "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", + "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", + "u", "v", "w", "x", "y", "z", + "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", + "up", "down", "left", "right", + "/", "?", ".", + "enter", "tab", "space", "escape", "esc", + "backspace", "delete", "home", "end", +] as const; + +export type Key = (typeof KEYS)[number]; + +const KEY_SET: ReadonlySet = new Set(KEYS); + +export function isKey(value: string): value is Key { + return KEY_SET.has(value); +} + +export type ModifierBasedShortcutKey = `${ModifierKeys}+${Key}`; +// Singular keybindings (these will be disabled when an input-ish area has been focused) +export type SingleCharacterShortcutKey = `${Key}`; + +export type ShortcutKey = ModifierBasedShortcutKey | SingleCharacterShortcutKey; + +export type KeybindingConfig = { + [key in ShortcutKey]?: TActionWithOptionalArgs; +}; diff --git a/apps/web/src/actions/keybindings-store.ts b/apps/web/src/actions/keybindings-store.ts new file mode 100644 index 000000000..8f3444873 --- /dev/null +++ b/apps/web/src/actions/keybindings-store.ts @@ -0,0 +1,274 @@ +"use client"; + +import { create } from "zustand"; +import { persist } from "zustand/middleware"; +import type { TActionWithOptionalArgs } from "@/actions"; +import { getDefaultShortcuts } from "@/actions"; +import { isTypableDOMElement } from "@/utils/browser"; +import { isAppleDevice } from "@/utils/platform"; +import type { + Key, + KeybindingConfig, + ModifierKeys, + ShortcutKey, +} from "@/actions/keybinding"; +import { isKey } from "@/actions/keybinding"; +import { runMigrations, CURRENT_VERSION } from "./keybindings/migrations"; + +export interface KeybindingConflict { + key: ShortcutKey; + existingAction: TActionWithOptionalArgs; + newAction: TActionWithOptionalArgs; +} + +interface KeybindingsState { + keybindings: Map; + isCustomized: boolean; + overlayDepth: number; + openOverlayIds: string[]; + isLoadingProject: boolean; + isRecording: boolean; + + updateKeybinding: (params: { + key: ShortcutKey; + action: TActionWithOptionalArgs; + }) => void; + removeKeybinding: (key: ShortcutKey) => void; + resetToDefaults: () => void; + importKeybindings: (config: KeybindingConfig) => void; + exportKeybindings: () => Record; + openOverlay: (overlayId: string) => void; + closeOverlay: (overlayId: string) => void; + setLoadingProject: (loading: boolean) => void; + setIsRecording: (isRecording: boolean) => void; + validateKeybinding: (params: { + key: ShortcutKey; + action: TActionWithOptionalArgs; + }) => KeybindingConflict | null; + getKeybindingsForAction: (action: TActionWithOptionalArgs) => ShortcutKey[]; + getKeybindingString: (ev: KeyboardEvent) => ShortcutKey | null; +} + +type PersistedState = { + keybindings: Record; + isCustomized: boolean; +}; + +function isDOMElement(element: EventTarget | null): element is HTMLElement { + return element instanceof HTMLElement; +} + +function isPersistedState(value: unknown): value is PersistedState { + if (!value || typeof value !== "object") return false; + if (!("keybindings" in value) || !("isCustomized" in value)) return false; + const { keybindings, isCustomized } = value; + return ( + typeof keybindings === "object" && + keybindings !== null && + typeof isCustomized === "boolean" + ); +} + +export const useKeybindingsStore = create()( + persist( + (set, get) => ({ + keybindings: getDefaultShortcuts(), + isCustomized: false, + overlayDepth: 0, + openOverlayIds: [], + isLoadingProject: false, + isRecording: false, + + openOverlay: (overlayId) => + set((s) => { + const openOverlayIds = s.openOverlayIds.includes(overlayId) + ? s.openOverlayIds + : [...s.openOverlayIds, overlayId]; + return { + openOverlayIds, + overlayDepth: openOverlayIds.length, + }; + }), + closeOverlay: (overlayId) => + set((s) => { + const openOverlayIds = s.openOverlayIds.filter( + (id) => id !== overlayId, + ); + return { + openOverlayIds, + overlayDepth: openOverlayIds.length, + }; + }), + setLoadingProject: (loading) => { + set({ isLoadingProject: loading }); + }, + + updateKeybinding: ({ key, action }) => { + set((state) => { + const next = new Map(state.keybindings); + next.set(key, action); + return { + keybindings: next, + isCustomized: true, + }; + }); + }, + + removeKeybinding: (key) => { + set((state) => { + const next = new Map(state.keybindings); + next.delete(key); + return { + keybindings: next, + isCustomized: true, + }; + }); + }, + + resetToDefaults: () => { + set({ + keybindings: getDefaultShortcuts(), + isCustomized: false, + }); + }, + + importKeybindings: (config) => { + const next = new Map(); + for (const [key, action] of Object.entries(config)) { + if (typeof key !== "string" || key.length === 0) { + throw new Error(`Invalid key format: ${key}`); + } + if (action !== undefined) { + // Public type's keys are `ShortcutKey`; trust the caller's typing. + // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion + next.set(key as ShortcutKey, action); + } + } + set({ + keybindings: next, + isCustomized: true, + }); + }, + + exportKeybindings: () => { + return Object.fromEntries(get().keybindings); + }, + + validateKeybinding: ({ key, action }) => { + const existingAction = get().keybindings.get(key); + if (existingAction && existingAction !== action) { + return { + key, + existingAction, + newAction: action, + }; + } + return null; + }, + setIsRecording: (isRecording) => { + set({ isRecording }); + }, + + getKeybindingsForAction: (action) => { + const result: ShortcutKey[] = []; + for (const [key, mapped] of get().keybindings) { + if (mapped === action) result.push(key); + } + return result; + }, + + getKeybindingString: (ev) => generateKeybindingString(ev), + }), + { + name: "opencut-keybindings", + version: CURRENT_VERSION, + partialize: (state): PersistedState => ({ + keybindings: Object.fromEntries(state.keybindings), + isCustomized: state.isCustomized, + }), + migrate: (persisted, version) => + runMigrations({ state: persisted, fromVersion: version }), + merge: (persisted, current) => { + if (!isPersistedState(persisted)) return current; + const entries = Object.entries(persisted.keybindings); + // Persistence boundary: keys are normalized by the migration chain. + // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion + const typedEntries = entries as Array< + [ShortcutKey, TActionWithOptionalArgs] + >; + return { + ...current, + keybindings: new Map(typedEntries), + isCustomized: persisted.isCustomized, + }; + }, + }, + ), +); + +function generateKeybindingString(ev: KeyboardEvent): ShortcutKey | null { + const target = ev.target; + const modifierKey = getActiveModifier(ev); + const key = getPressedKey(ev); + if (!key) return null; + + if (modifierKey) { + if ( + modifierKey === "shift" && + isDOMElement(target) && + isTypableDOMElement({ element: target }) + ) { + return null; + } + + return `${modifierKey}+${key}`; + } + + if (isDOMElement(target) && isTypableDOMElement({ element: target })) { + return null; + } + + return key; +} + +function getPressedKey(ev: KeyboardEvent): Key | null { + const raw = (ev.key ?? "").toLowerCase(); + const code = ev.code ?? ""; + + if (code === "Space" || raw === " " || raw === "spacebar" || raw === "space") + return "space"; + + if (raw === "arrowup") return "up"; + if (raw === "arrowdown") return "down"; + if (raw === "arrowleft") return "left"; + if (raw === "arrowright") return "right"; + + if (code.startsWith("Key")) { + const letter = code.slice(3).toLowerCase(); + if (isKey(letter)) return letter; + } + + // Use physical key position for AZERTY and other non-QWERTY layouts. + if (code.startsWith("Digit")) { + const digit = code.slice(5); + if (isKey(digit)) return digit; + } + + if (isKey(raw)) return raw; + return null; +} + +function getActiveModifier(ev: KeyboardEvent): ModifierKeys | null { + const ctrl = isAppleDevice() ? ev.metaKey : ev.ctrlKey; + const alt = ev.altKey; + const shift = ev.shiftKey; + + if (ctrl && alt && shift) return "ctrl+alt+shift"; + if (ctrl && alt) return "ctrl+alt"; + if (ctrl && shift) return "ctrl+shift"; + if (alt && shift) return "alt+shift"; + if (ctrl) return "ctrl"; + if (alt) return "alt"; + if (shift) return "shift"; + return null; +} diff --git a/apps/web/src/actions/keybindings/__tests__/persistence.test.ts b/apps/web/src/actions/keybindings/__tests__/persistence.test.ts new file mode 100644 index 000000000..bcccc6a49 --- /dev/null +++ b/apps/web/src/actions/keybindings/__tests__/persistence.test.ts @@ -0,0 +1,144 @@ +import { + afterEach, + beforeEach, + describe, + expect, + mock, + test, +} from "bun:test"; +import { + decodePersistedKeybindingsState, + migratePersistedKeybindingsState, + parseImportedKeybindings, + serializeKeybindingsState, +} from "../persistence"; + +describe("keybinding persistence", () => { + let warnSpy: ReturnType; + let originalWarn: typeof console.warn; + + beforeEach(() => { + originalWarn = console.warn; + warnSpy = mock(() => {}); + console.warn = warnSpy; + }); + + afterEach(() => { + console.warn = originalWarn; + }); + + test("migrates legacy persisted keybindings before decoding them", () => { + const migrated = migratePersistedKeybindingsState({ + state: { + keybindings: { + s: "split-selected", + "ctrl+v": "paste-selected", + }, + isCustomized: true, + }, + fromVersion: 2, + }); + + const decoded = decodePersistedKeybindingsState({ state: migrated }); + expect(decoded).not.toBeNull(); + if (!decoded) throw new Error("Expected migrated keybindings to decode"); + + expect(decoded.isCustomized).toBe(true); + expect(decoded.keybindings.get("s")).toBe("split"); + expect(decoded.keybindings.get("ctrl+v")).toBe("paste-copied"); + expect(decoded.keybindings.get("escape")).toBe("cancel-interaction"); + expect(warnSpy).not.toHaveBeenCalled(); + }); + + test("filters invalid persisted entries at the boundary and warns", () => { + const decoded = decodePersistedKeybindingsState({ + state: { + keybindings: { + space: "toggle-play", + "shift+bogus": "toggle-play", + "ctrl+v": "not-an-action", + }, + isCustomized: false, + }, + }); + + expect(decoded).not.toBeNull(); + if (!decoded) throw new Error("Expected persisted keybindings to decode"); + + expect(Array.from(decoded.keybindings.entries())).toEqual([ + ["space", "toggle-play"], + ]); + expect(warnSpy).toHaveBeenCalledTimes(1); + }); + + test("returns null and warns when persisted shape is unrecognizable", () => { + const decoded = decodePersistedKeybindingsState({ state: "garbage" }); + expect(decoded).toBeNull(); + expect(warnSpy).toHaveBeenCalledTimes(1); + }); + + test("round-trips actions that have no default shortcut", () => { + // `stop-playback` is a valid `TActionWithOptionalArgs` but is not in the + // defaults table; the validator must still accept it. + const serialized = serializeKeybindingsState({ + keybindings: new Map([ + ["space", "toggle-play"], + ["x", "stop-playback"], + ["b", "toggle-bookmark"], + ]), + isCustomized: true, + }); + + const decoded = decodePersistedKeybindingsState({ state: serialized }); + expect(decoded).not.toBeNull(); + if (!decoded) throw new Error("Expected round-tripped keybindings"); + + expect(decoded.keybindings.get("space")).toBe("toggle-play"); + expect(decoded.keybindings.get("x")).toBe("stop-playback"); + expect(decoded.keybindings.get("b")).toBe("toggle-bookmark"); + expect(warnSpy).not.toHaveBeenCalled(); + }); +}); + +describe("parseImportedKeybindings", () => { + test("accepts a valid configuration", () => { + const result = parseImportedKeybindings({ + config: { + space: "toggle-play", + x: "stop-playback", + }, + }); + + expect(result.get("space")).toBe("toggle-play"); + expect(result.get("x")).toBe("stop-playback"); + }); + + test("throws on non-object input", () => { + expect(() => parseImportedKeybindings({ config: null })).toThrow( + /JSON object/, + ); + expect(() => parseImportedKeybindings({ config: [] })).toThrow( + /JSON object/, + ); + }); + + test("throws on non-string action values", () => { + expect(() => + parseImportedKeybindings({ config: { space: 42 } }), + ).toThrow(/expected string/); + }); + + test("throws on invalid shortcut keys", () => { + expect(() => + parseImportedKeybindings({ + config: { "shift+bogus": "toggle-play" }, + }), + ).toThrow(/shift\+bogus/); + }); + + test("throws on invalid actions", () => { + expect(() => + parseImportedKeybindings({ config: { space: "not-an-action" } }), + ).toThrow(/not-an-action/); + }); +}); diff --git a/apps/web/src/actions/keybindings/migrations/index.ts b/apps/web/src/actions/keybindings/migrations/index.ts new file mode 100644 index 000000000..71fd81a61 --- /dev/null +++ b/apps/web/src/actions/keybindings/migrations/index.ts @@ -0,0 +1,32 @@ +import { v2ToV3 } from "./v2-to-v3"; +import { v3ToV4 } from "./v3-to-v4"; +import { v4ToV5 } from "./v4-to-v5"; +import { v5ToV6 } from "./v5-to-v6"; +import { v6ToV7 } from "./v6-to-v7"; + +type MigrationFn = ({ state }: { state: unknown }) => unknown; + +const migrations: Record = { + 2: v2ToV3, + 3: v3ToV4, + 4: v4ToV5, + 5: v5ToV6, + 6: v6ToV7, +}; + +export const CURRENT_VERSION = 7; + +export function runMigrations({ + state, + fromVersion, +}: { + state: unknown; + fromVersion: number; +}): unknown { + let current = state; + for (let version = fromVersion; version < CURRENT_VERSION; version++) { + const migrate = migrations[version]; + if (migrate) current = migrate({ state: current }); + } + return current; +} diff --git a/apps/web/src/actions/keybindings/migrations/v2-to-v3.ts b/apps/web/src/actions/keybindings/migrations/v2-to-v3.ts new file mode 100644 index 000000000..81fd86a79 --- /dev/null +++ b/apps/web/src/actions/keybindings/migrations/v2-to-v3.ts @@ -0,0 +1,22 @@ +import { getPersistedKeybindingsState } from "../persisted-state"; + +export function v2ToV3({ state }: { state: unknown }): unknown { + const v2 = getPersistedKeybindingsState({ state }); + if (!v2) return state; + + const renames: Record = { + "split-selected": "split", + "split-selected-left": "split-left", + "split-selected-right": "split-right", + }; + + const migrated = { ...v2.keybindings }; + for (const [key, action] of Object.entries(migrated)) { + const renamedAction = action ? renames[action] : undefined; + if (renamedAction) { + migrated[key] = renamedAction; + } + } + + return { ...v2, keybindings: migrated }; +} diff --git a/apps/web/src/actions/keybindings/migrations/v3-to-v4.ts b/apps/web/src/actions/keybindings/migrations/v3-to-v4.ts new file mode 100644 index 000000000..3724f61c1 --- /dev/null +++ b/apps/web/src/actions/keybindings/migrations/v3-to-v4.ts @@ -0,0 +1,20 @@ +import { getPersistedKeybindingsState } from "../persisted-state"; + +export function v3ToV4({ state }: { state: unknown }): unknown { + const v3 = getPersistedKeybindingsState({ state }); + if (!v3) return state; + + const renames: Record = { + "paste-selected": "paste-copied", + }; + + const migrated = { ...v3.keybindings }; + for (const [key, action] of Object.entries(migrated)) { + const renamedAction = action ? renames[action] : undefined; + if (renamedAction) { + migrated[key] = renamedAction; + } + } + + return { ...v3, keybindings: migrated }; +} diff --git a/apps/web/src/actions/keybindings/migrations/v4-to-v5.ts b/apps/web/src/actions/keybindings/migrations/v4-to-v5.ts new file mode 100644 index 000000000..bb60b044a --- /dev/null +++ b/apps/web/src/actions/keybindings/migrations/v4-to-v5.ts @@ -0,0 +1,13 @@ +import { getPersistedKeybindingsState } from "../persisted-state"; + +export function v4ToV5({ state }: { state: unknown }): unknown { + const v4 = getPersistedKeybindingsState({ state }); + if (!v4) return state; + const keybindings = { ...v4.keybindings }; + + if (!keybindings.escape) { + keybindings.escape = "deselect-all"; + } + + return { ...v4, keybindings }; +} diff --git a/apps/web/src/actions/keybindings/migrations/v5-to-v6.ts b/apps/web/src/actions/keybindings/migrations/v5-to-v6.ts new file mode 100644 index 000000000..cb572aac6 --- /dev/null +++ b/apps/web/src/actions/keybindings/migrations/v5-to-v6.ts @@ -0,0 +1,13 @@ +import { getPersistedKeybindingsState } from "../persisted-state"; + +export function v5ToV6({ state }: { state: unknown }): unknown { + const v5 = getPersistedKeybindingsState({ state }); + if (!v5) return state; + const keybindings = { ...v5.keybindings }; + + if (keybindings.escape === "deselect-all") { + keybindings.escape = "cancel-interaction"; + } + + return { ...v5, keybindings }; +} diff --git a/apps/web/src/actions/keybindings/migrations/v6-to-v7.ts b/apps/web/src/actions/keybindings/migrations/v6-to-v7.ts new file mode 100644 index 000000000..9c05db232 --- /dev/null +++ b/apps/web/src/actions/keybindings/migrations/v6-to-v7.ts @@ -0,0 +1,15 @@ +import { getPersistedKeybindingsState } from "../persisted-state"; + +export function v6ToV7({ state }: { state: unknown }): unknown { + const v6 = getPersistedKeybindingsState({ state }); + if (!v6) return state; + const keybindings = { ...v6.keybindings }; + + for (const [key, action] of Object.entries(keybindings)) { + if (action === "split-element") { + keybindings[key] = "split"; + } + } + + return { ...v6, keybindings }; +} diff --git a/apps/web/src/actions/keybindings/persisted-state.ts b/apps/web/src/actions/keybindings/persisted-state.ts new file mode 100644 index 000000000..57d83f6d4 --- /dev/null +++ b/apps/web/src/actions/keybindings/persisted-state.ts @@ -0,0 +1,37 @@ +export type PersistedKeybindingConfig = Record; + +export interface PersistedKeybindingsState { + keybindings: PersistedKeybindingConfig; + isCustomized: boolean; +} + +function isRecord(value: unknown): value is Record { + return typeof value === "object" && value !== null && !Array.isArray(value); +} + +export function getPersistedKeybindingsState({ + state, +}: { + state: unknown; +}): PersistedKeybindingsState | null { + if (!isRecord(state)) return null; + + const { keybindings, isCustomized } = state; + if (!isRecord(keybindings) || typeof isCustomized !== "boolean") { + return null; + } + + const normalizedKeybindings: PersistedKeybindingConfig = {}; + for (const [key, action] of Object.entries(keybindings)) { + if (action !== undefined && typeof action !== "string") { + return null; + } + + normalizedKeybindings[key] = action; + } + + return { + keybindings: normalizedKeybindings, + isCustomized, + }; +} diff --git a/apps/web/src/actions/keybindings/persistence.ts b/apps/web/src/actions/keybindings/persistence.ts new file mode 100644 index 000000000..ced44bd0a --- /dev/null +++ b/apps/web/src/actions/keybindings/persistence.ts @@ -0,0 +1,119 @@ +import type { ShortcutKey } from "@/actions/keybinding"; +import { isShortcutKey } from "@/actions/keybinding"; +import type { TActionWithOptionalArgs } from "@/actions"; +import { isActionWithOptionalArgs } from "@/actions"; +import { runMigrations } from "./migrations"; +import { + getPersistedKeybindingsState, + type PersistedKeybindingsState, +} from "./persisted-state"; + +export interface DecodedKeybindingsState { + keybindings: Map; + isCustomized: boolean; +} + +export function serializeKeybindingsState({ + keybindings, + isCustomized, +}: DecodedKeybindingsState): PersistedKeybindingsState { + return { + keybindings: Object.fromEntries(keybindings), + isCustomized, + }; +} + +export function migratePersistedKeybindingsState({ + state, + fromVersion, +}: { + state: unknown; + fromVersion: number; +}): unknown { + return runMigrations({ state, fromVersion }); +} + +/** + * Decode a persisted/migrated keybindings blob into the in-memory shape. + * + * Lossy by design: invalid entries are dropped and a warning is emitted, so the + * user falls back to (mostly) sensible defaults instead of a broken store. + * Returns `null` if the top-level shape is unrecognizable, in which case the + * caller should keep its current state. + */ +export function decodePersistedKeybindingsState({ + state, +}: { + state: unknown; +}): DecodedKeybindingsState | null { + const persisted = getPersistedKeybindingsState({ state }); + if (!persisted) { + console.warn( + "[keybindings] Persisted state has unexpected shape; keeping current keybindings.", + state, + ); + return null; + } + + const keybindings = new Map(); + const dropped: Array<{ key: string; action: string | undefined }> = []; + for (const [key, action] of Object.entries(persisted.keybindings)) { + if (action === undefined) continue; + if (!isShortcutKey(key) || !isActionWithOptionalArgs(action)) { + dropped.push({ key, action }); + continue; + } + + keybindings.set(key, action); + } + + if (dropped.length > 0) { + console.warn( + "[keybindings] Dropped invalid persisted entries:", + dropped, + ); + } + + return { + keybindings, + isCustomized: persisted.isCustomized, + }; +} + +/** + * Parse a user-supplied keybindings configuration (typically the output of + * `JSON.parse` on an imported file). + * + * Strict by design: throws on the first invalid entry so the caller can surface + * the failure to the user instead of silently producing a half-applied import. + * Accepts `unknown` because the input has already crossed a trust boundary. + */ +export function parseImportedKeybindings({ + config, +}: { + config: unknown; +}): Map { + if (typeof config !== "object" || config === null || Array.isArray(config)) { + throw new Error("Imported keybindings must be a JSON object"); + } + + const result = new Map(); + for (const [key, action] of Object.entries(config)) { + if (action === undefined) continue; + if (typeof action !== "string") { + throw new Error( + `Invalid action for "${key}": expected string, got ${typeof action}`, + ); + } + if (!isShortcutKey(key)) { + throw new Error(`Invalid shortcut key: ${JSON.stringify(key)}`); + } + if (!isActionWithOptionalArgs(action)) { + throw new Error( + `Invalid action for "${key}": ${JSON.stringify(action)}`, + ); + } + result.set(key, action); + } + return result; +} diff --git a/apps/web/src/actions/registry.ts b/apps/web/src/actions/registry.ts new file mode 100644 index 000000000..ece14eaf6 --- /dev/null +++ b/apps/web/src/actions/registry.ts @@ -0,0 +1,66 @@ +import type { + TAction, + TActionFunc, + TActionWithArgs, + TActionWithOptionalArgs, + TActionArgsMap, + TArgOfAction, + TInvocationTrigger, +} from "./types"; + +type ActionHandler = (arg: unknown, trigger?: TInvocationTrigger) => void; +const boundActions: Partial> = {}; + +// eslint-disable-next-line opencut/prefer-object-params -- action registries read best as (action, handler). +export function bindAction( + action: A, + handler: TActionFunc, +) { + const handlers = boundActions[action]; + const typedHandler = handler as ActionHandler; + if (handlers) { + handlers.push(typedHandler); + } else { + boundActions[action] = [typedHandler]; + } +} + +// eslint-disable-next-line opencut/prefer-object-params -- action registries read best as (action, handler). +export function unbindAction( + action: A, + handler: TActionFunc, +) { + const handlers = boundActions[action]; + if (!handlers) return; + + const typedHandler = handler as ActionHandler; + boundActions[action] = handlers.filter((h) => h !== typedHandler); + + if (boundActions[action]?.length === 0) { + delete boundActions[action]; + } +} + +type InvokeActionFunc = { + ( + action: TActionWithOptionalArgs, + args?: undefined, + trigger?: TInvocationTrigger, + ): void; + ( + action: A, + args: TActionArgsMap[A], + trigger?: TInvocationTrigger, + ): void; +}; + +// eslint-disable-next-line opencut/prefer-object-params -- dispatchers conventionally separate action, payload, and trigger. +export const invokeAction: InvokeActionFunc = ( + action: A, + args?: TArgOfAction, + trigger?: TInvocationTrigger, +) => { + boundActions[action]?.forEach((handler) => { + handler(args, trigger); + }); +}; diff --git a/apps/web/src/actions/types.ts b/apps/web/src/actions/types.ts new file mode 100644 index 000000000..38627b957 --- /dev/null +++ b/apps/web/src/actions/types.ts @@ -0,0 +1,44 @@ +import type { MutableRefObject } from "react"; +import type { TAction } from "./definitions"; + +export type { TAction }; + +export type TActionArgsMap = { + "seek-forward": { seconds: number } | undefined; + "seek-backward": { seconds: number } | undefined; + "jump-forward": { seconds: number } | undefined; + "jump-backward": { seconds: number } | undefined; + "remove-media-asset": { projectId: string; assetId: string }; + "remove-media-assets": { projectId: string; assetIds: string[] }; +}; + +type TKeysWithValueUndefined = { + [K in keyof T]: undefined extends T[K] ? K : never; +}[keyof T]; + +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 + : (_?: undefined, trigger?: TInvocationTrigger) => void; + +export type TInvocationTrigger = "keypress" | "mouseclick"; + +export type TBoundActionList = { + [A in TAction]?: Array>; +}; + +export type TActionHandlerOptions = + | MutableRefObject + | boolean + | undefined; diff --git a/apps/web/src/hooks/actions/use-action-handler.ts b/apps/web/src/actions/use-action-handler.ts similarity index 86% rename from apps/web/src/hooks/actions/use-action-handler.ts rename to apps/web/src/actions/use-action-handler.ts index 3f6dbd215..d4a37b50a 100644 --- a/apps/web/src/hooks/actions/use-action-handler.ts +++ b/apps/web/src/actions/use-action-handler.ts @@ -5,9 +5,10 @@ import type { TActionHandlerOptions, TArgOfAction, TInvocationTrigger, -} from "@/lib/actions"; -import { bindAction, unbindAction } from "@/lib/actions"; +} from "@/actions"; +import { bindAction, unbindAction } from "@/actions"; +// eslint-disable-next-line opencut/prefer-object-params -- action subscriptions read best as (action, handler, isActive). export function useActionHandler( action: A, handler: TActionFunc, diff --git a/apps/web/src/actions/use-editor-actions.ts b/apps/web/src/actions/use-editor-actions.ts new file mode 100644 index 000000000..e5d3a23b5 --- /dev/null +++ b/apps/web/src/actions/use-editor-actions.ts @@ -0,0 +1,520 @@ +"use client"; + +import { useEffect, useState } from "react"; +import { useTimelineStore } from "@/timeline/timeline-store"; +import { useActionHandler } from "@/actions/use-action-handler"; +import { useEditor } from "@/editor/use-editor"; +import { useElementSelection } from "@/timeline/hooks/element/use-element-selection"; +import { + addMediaTime, + maxMediaTime, + mediaTime, + mediaTimeFromSeconds, + minMediaTime, + subMediaTime, + TICKS_PER_SECOND, + ZERO_MEDIA_TIME, +} from "@/wasm"; +import { useKeyframeSelection } from "@/timeline/hooks/element/use-keyframe-selection"; +import { getElementsAtTime, hasMediaId } from "@/timeline"; +import { cancelInteraction } from "@/editor/cancel-interaction"; +import { invokeAction } from "@/actions"; +import { canToggleSourceAudio } from "@/timeline/audio-separation"; +import { + activateScope, + clearActiveScope, + type ScopeEntry, +} from "@/selection/scope"; +import { useCommittedRef } from "@/hooks/use-committed-ref"; + +export function useEditorActions() { + const editor = useEditor(); + const { selectedElements, setElementSelection } = useElementSelection(); + const { selectedKeyframes, clearKeyframeSelection } = useKeyframeSelection(); + const selectedMaskPointSelection = useEditor((e) => + e.selection.getSelectedMaskPointSelection(), + ); + const toggleSnapping = useTimelineStore((s) => s.toggleSnapping); + const rippleEditingEnabled = useTimelineStore((s) => s.rippleEditingEnabled); + const toggleRippleEditing = useTimelineStore((s) => s.toggleRippleEditing); + const hasTimelineSelection = + selectedElements.length > 0 || + selectedKeyframes.length > 0 || + selectedMaskPointSelection !== null; + const hasTimelineSelectionRef = useCommittedRef(hasTimelineSelection); + const clearTimelineSelectionRef = useCommittedRef(() => { + editor.selection.clearSelection(); + }); + const clearTimelineActiveSelectionRef = useCommittedRef(() => { + editor.selection.clearMostSpecificSelection(); + }); + const [timelineScope] = useState(() => ({ + hasSelection: () => hasTimelineSelectionRef.current, + clear: () => { + clearTimelineSelectionRef.current(); + }, + clearActive: () => { + clearTimelineActiveSelectionRef.current(); + }, + })); + + useEffect(() => { + if (!hasTimelineSelection) { + return; + } + + return activateScope({ entry: timelineScope }); + }, [hasTimelineSelection, timelineScope]); + + useActionHandler( + "toggle-play", + () => { + editor.playback.toggle(); + }, + undefined, + ); + + useActionHandler( + "stop-playback", + () => { + if (editor.playback.getIsPlaying()) { + editor.playback.toggle(); + } + editor.playback.seek({ time: ZERO_MEDIA_TIME }); + }, + undefined, + ); + + useActionHandler( + "seek-forward", + (args) => { + const seconds = args?.seconds ?? 1; + const delta = mediaTimeFromSeconds({ seconds }); + editor.playback.seek({ + time: minMediaTime({ + a: editor.timeline.getTotalDuration(), + b: addMediaTime({ + a: editor.playback.getCurrentTime(), + b: delta, + }), + }), + }); + }, + undefined, + ); + + useActionHandler( + "seek-backward", + (args) => { + const seconds = args?.seconds ?? 1; + const delta = mediaTimeFromSeconds({ seconds }); + editor.playback.seek({ + time: maxMediaTime({ + a: ZERO_MEDIA_TIME, + b: subMediaTime({ + a: editor.playback.getCurrentTime(), + b: delta, + }), + }), + }); + }, + undefined, + ); + + useActionHandler( + "frame-step-forward", + () => { + const fps = editor.project.getActive().settings.fps; + const ticksPerFrame = mediaTime({ + ticks: Math.round( + (TICKS_PER_SECOND * fps.denominator) / fps.numerator, + ), + }); + editor.playback.seek({ + time: minMediaTime({ + a: editor.timeline.getTotalDuration(), + b: addMediaTime({ + a: editor.playback.getCurrentTime(), + b: ticksPerFrame, + }), + }), + }); + }, + undefined, + ); + + useActionHandler( + "frame-step-backward", + () => { + const fps = editor.project.getActive().settings.fps; + const ticksPerFrame = mediaTime({ + ticks: Math.round( + (TICKS_PER_SECOND * fps.denominator) / fps.numerator, + ), + }); + editor.playback.seek({ + time: maxMediaTime({ + a: ZERO_MEDIA_TIME, + b: subMediaTime({ + a: editor.playback.getCurrentTime(), + b: ticksPerFrame, + }), + }), + }); + }, + undefined, + ); + + useActionHandler( + "jump-forward", + (args) => { + const seconds = args?.seconds ?? 5; + const delta = mediaTimeFromSeconds({ seconds }); + editor.playback.seek({ + time: minMediaTime({ + a: editor.timeline.getTotalDuration(), + b: addMediaTime({ + a: editor.playback.getCurrentTime(), + b: delta, + }), + }), + }); + }, + undefined, + ); + + useActionHandler( + "jump-backward", + (args) => { + const seconds = args?.seconds ?? 5; + const delta = mediaTimeFromSeconds({ seconds }); + editor.playback.seek({ + time: maxMediaTime({ + a: ZERO_MEDIA_TIME, + b: subMediaTime({ + a: editor.playback.getCurrentTime(), + b: delta, + }), + }), + }); + }, + undefined, + ); + + useActionHandler( + "goto-start", + () => { + editor.playback.seek({ time: ZERO_MEDIA_TIME }); + }, + undefined, + ); + + useActionHandler( + "goto-end", + () => { + editor.playback.seek({ time: editor.timeline.getTotalDuration() }); + }, + undefined, + ); + + useActionHandler( + "split", + () => { + const currentTime = editor.playback.getCurrentTime(); + const tracks = editor.scenes.getActiveScene().tracks; + const elementsToSplit = + selectedElements.length > 0 + ? selectedElements + : getElementsAtTime({ + tracks, + time: currentTime, + }); + + if (elementsToSplit.length === 0) return; + + editor.timeline.splitElements({ + elements: elementsToSplit, + splitTime: currentTime, + }); + }, + undefined, + ); + + useActionHandler( + "split-left", + () => { + const currentTime = editor.playback.getCurrentTime(); + const tracks = editor.scenes.getActiveScene().tracks; + const elementsToSplit = + selectedElements.length > 0 + ? selectedElements + : getElementsAtTime({ + tracks, + time: currentTime, + }); + + if (elementsToSplit.length === 0) return; + + const rightSideElements = editor.timeline.splitElements({ + elements: elementsToSplit, + splitTime: currentTime, + retainSide: "right", + }); + + if (rippleEditingEnabled && rightSideElements.length > 0) { + const firstRightElement = editor.timeline.getElementsWithTracks({ + elements: [rightSideElements[0]], + })[0]; + if (firstRightElement) { + editor.playback.seek({ time: firstRightElement.element.startTime }); + } + } + }, + undefined, + ); + + useActionHandler( + "split-right", + () => { + const currentTime = editor.playback.getCurrentTime(); + const tracks = editor.scenes.getActiveScene().tracks; + const elementsToSplit = + selectedElements.length > 0 + ? selectedElements + : getElementsAtTime({ + tracks, + time: currentTime, + }); + + if (elementsToSplit.length === 0) return; + + editor.timeline.splitElements({ + elements: elementsToSplit, + splitTime: currentTime, + retainSide: "left", + }); + }, + undefined, + ); + + useActionHandler( + "delete-selected", + () => { + switch (editor.selection.getActiveSelectionKind()) { + case "mask-points": + if (!selectedMaskPointSelection) { + return; + } + editor.timeline.deleteFreeformPathMaskPoints({ + trackId: selectedMaskPointSelection.trackId, + elementId: selectedMaskPointSelection.elementId, + maskId: selectedMaskPointSelection.maskId, + pointIds: selectedMaskPointSelection.pointIds, + }); + return; + case "keyframes": + if (selectedKeyframes.length === 0) { + return; + } + editor.timeline.removeKeyframes({ keyframes: selectedKeyframes }); + clearKeyframeSelection(); + return; + case "elements": + if (selectedElements.length === 0) { + return; + } + editor.timeline.deleteElements({ + elements: selectedElements, + }); + return; + default: + return; + } + }, + undefined, + ); + + useActionHandler( + "toggle-source-audio", + () => { + if (selectedElements.length !== 1) { + return; + } + + const selectedElement = editor.timeline.getElementsWithTracks({ + elements: selectedElements, + })[0]; + if (!selectedElement) { + return; + } + + const mediaAsset = (() => { + const { element } = selectedElement; + if (!hasMediaId(element)) { + return null; + } + + return ( + editor.media + .getAssets() + .find((asset) => asset.id === element.mediaId) ?? null + ); + })(); + if (!canToggleSourceAudio(selectedElement.element, mediaAsset)) { + return; + } + + editor.timeline.toggleSourceAudioSeparation({ + trackId: selectedElement.track.id, + elementId: selectedElement.element.id, + }); + }, + undefined, + ); + + useActionHandler( + "select-all", + () => { + const scene = editor.scenes.getActiveScene(); + const allElements = [ + ...scene.tracks.overlay, + scene.tracks.main, + ...scene.tracks.audio, + ].flatMap((track) => + track.elements.map((element) => ({ + trackId: track.id, + elementId: element.id, + })), + ); + setElementSelection({ elements: allElements }); + }, + undefined, + ); + + useActionHandler( + "cancel-interaction", + () => { + if (!cancelInteraction()) { + invokeAction("deselect-all"); + } + }, + undefined, + ); + + useActionHandler( + "deselect-all", + () => { + if (!clearActiveScope()) { + editor.selection.clearMostSpecificSelection(); + } + }, + undefined, + ); + + useActionHandler( + "duplicate-selected", + () => { + editor.timeline.duplicateElements({ + elements: selectedElements, + }); + }, + undefined, + ); + + useActionHandler( + "toggle-elements-muted-selected", + () => { + editor.timeline.toggleElementsMuted({ elements: selectedElements }); + }, + undefined, + ); + + useActionHandler( + "toggle-elements-visibility-selected", + () => { + editor.timeline.toggleElementsVisibility({ elements: selectedElements }); + }, + undefined, + ); + + useActionHandler( + "toggle-bookmark", + () => { + editor.scenes.toggleBookmark({ time: editor.playback.getCurrentTime() }); + }, + undefined, + ); + + useActionHandler( + "copy-selected", + () => { + editor.clipboard.copy(); + }, + undefined, + ); + + useActionHandler( + "paste-copied", + () => { + editor.clipboard.paste(); + }, + undefined, + ); + + useActionHandler( + "toggle-snapping", + () => { + toggleSnapping(); + }, + undefined, + ); + + useActionHandler( + "toggle-ripple-editing", + () => { + toggleRippleEditing(); + }, + undefined, + ); + + useActionHandler( + "undo", + () => { + editor.command.undo(); + }, + undefined, + ); + + useActionHandler( + "redo", + () => { + editor.command.redo(); + }, + undefined, + ); + + // todo: potnetially unify these two actions: + useActionHandler( + "remove-media-asset", + (args) => { + if (!args) return; + editor.media.removeMediaAsset({ + projectId: args.projectId, + id: args.assetId, + }); + }, + undefined, + ); + + useActionHandler( + "remove-media-assets", + (args) => { + if (!args) return; + editor.media.removeMediaAssets({ + projectId: args.projectId, + ids: args.assetIds, + }); + }, + undefined, + ); +} diff --git a/apps/web/src/actions/use-keybindings.ts b/apps/web/src/actions/use-keybindings.ts new file mode 100644 index 000000000..e9d78982f --- /dev/null +++ b/apps/web/src/actions/use-keybindings.ts @@ -0,0 +1,87 @@ +import { useEffect } from "react"; +import { invokeAction } from "@/actions"; +import { useEditor } from "@/editor/use-editor"; +import { useKeybindingsStore } from "@/actions/keybindings-store"; +import { isTypableDOMElement } from "@/utils/browser"; + +/** + * a composable that hooks to the caller component's + * lifecycle and hooks to the keyboard events to fire + * the appropriate actions based on keybindings + */ +export function useKeybindingsListener() { + const editor = useEditor(); + const { + keybindings, + getKeybindingString, + overlayDepth, + isLoadingProject, + isRecording, + } = useKeybindingsStore(); + + useEffect(() => { + const eventOptions: AddEventListenerOptions = { capture: true }; + const handleKeyDown = (ev: KeyboardEvent) => { + const normalizedKey = (ev.key ?? "").toLowerCase(); + + if (overlayDepth > 0 || isLoadingProject || isRecording) { + return; + } + + const binding = getKeybindingString(ev); + const activeElement = document.activeElement; + const isTextInput = + activeElement instanceof HTMLElement && + isTypableDOMElement({ element: activeElement }); + const boundAction = binding ? keybindings.get(binding) : undefined; + + if (normalizedKey === "escape" && isTextInput) { + activeElement.blur(); + return; + } + + if (!binding) return; + if (!boundAction) return; + + if (isTextInput) return; + if (boundAction === "paste-copied") { + if (!editor.clipboard.hasEntry()) return; + ev.preventDefault(); + invokeAction("paste-copied", undefined, "keypress"); + return; + } + + ev.preventDefault(); + + switch (boundAction) { + case "seek-forward": + invokeAction("seek-forward", { seconds: 1 }, "keypress"); + break; + case "seek-backward": + invokeAction("seek-backward", { seconds: 1 }, "keypress"); + break; + case "jump-forward": + invokeAction("jump-forward", { seconds: 5 }, "keypress"); + break; + case "jump-backward": + invokeAction("jump-backward", { seconds: 5 }, "keypress"); + break; + default: + invokeAction(boundAction, undefined, "keypress"); + } + }; + + document.addEventListener("keydown", handleKeyDown, eventOptions); + + return () => { + document.removeEventListener("keydown", handleKeyDown, eventOptions); + }; + }, [ + keybindings, + getKeybindingString, + overlayDepth, + isLoadingProject, + isRecording, + editor, + ]); +} diff --git a/apps/web/src/actions/use-keyboard-shortcuts-help.ts b/apps/web/src/actions/use-keyboard-shortcuts-help.ts new file mode 100644 index 000000000..89a178872 --- /dev/null +++ b/apps/web/src/actions/use-keyboard-shortcuts-help.ts @@ -0,0 +1,77 @@ +"use client"; + +import { useMemo } from "react"; +import { useKeybindingsStore } from "@/actions/keybindings-store"; +import { ACTIONS, type TActionWithOptionalArgs } from "@/actions"; +import { + getPlatformAlternateKey, + getPlatformSpecialKey, +} from "@/utils/platform"; + +export interface KeyboardShortcut { + id: string; + keys: string[]; + description: string; + category: string; + action: TActionWithOptionalArgs; + icon?: React.ReactNode; +} + +function formatKey({ key }: { key: string }): string { + return key + .replace("ctrl", getPlatformSpecialKey()) + .replace("alt", getPlatformAlternateKey()) + .replace("shift", "Shift") + .replace("left", "←") + .replace("right", "→") + .replace("up", "↑") + .replace("down", "↓") + .replace("space", "Space") + .replace("home", "Home") + .replace("enter", "Enter") + .replace("end", "End") + .replace("delete", "Delete") + .replace("backspace", "Backspace") + .replace("-", "+"); +} + +export function useKeyboardShortcutsHelp() { + const { keybindings } = useKeybindingsStore(); + + const shortcuts = useMemo(() => { + const actionToKeys = new Map(); + + for (const [key, action] of keybindings) { + const existing = actionToKeys.get(action); + if (existing) { + existing.push(formatKey({ key })); + } else { + actionToKeys.set(action, [formatKey({ key })]); + } + } + + const result: KeyboardShortcut[] = []; + for (const [action, keys] of actionToKeys) { + const actionDef = ACTIONS[action]; + if (!actionDef) continue; + result.push({ + id: action, + keys, + description: actionDef.description, + category: actionDef.category, + action, + }); + } + + return result.sort((a, b) => { + if (a.category !== b.category) { + return a.category.localeCompare(b.category); + } + return a.description.localeCompare(b.description); + }); + }, [keybindings]); + + return { + shortcuts, + }; +} diff --git a/apps/web/src/animation/__tests__/animated-params.test.ts b/apps/web/src/animation/__tests__/animated-params.test.ts new file mode 100644 index 000000000..a47b03826 --- /dev/null +++ b/apps/web/src/animation/__tests__/animated-params.test.ts @@ -0,0 +1,241 @@ +import { describe, expect, test } from "bun:test"; +import { + coerceParamValue, + getParamDefaultInterpolation, + getParamNumericRange, + getParamValueKind, +} from "@/params"; + +describe("animated params", () => { + test("snaps and clamps number params", () => { + expect( + coerceParamValue({ + param: { + key: "intensity", + label: "Intensity", + type: "number", + default: 0, + min: 0, + max: 1, + step: 0.25, + }, + value: 0.62, + }), + ).toBe(0.5); + + expect( + coerceParamValue({ + param: { + key: "intensity", + label: "Intensity", + type: "number", + default: 0, + min: 0, + max: 1, + step: 0.25, + }, + value: 1.2, + }), + ).toBe(1); + }); + + test("rejects NaN and non-number values for number params", () => { + const param = { + key: "intensity", + label: "Intensity", + type: "number" as const, + default: 0, + min: 0, + max: 1, + step: 0.25, + }; + expect(coerceParamValue({ param, value: Number.NaN })).toBeNull(); + expect(coerceParamValue({ param, value: "0.5" })).toBeNull(); + expect(coerceParamValue({ param, value: true })).toBeNull(); + }); + + test("passthrough with step <= 0 guard", () => { + expect( + coerceParamValue({ + param: { + key: "x", + label: "X", + type: "number", + default: 0, + min: 0, + step: 0, + }, + value: 0.123, + }), + ).toBe(0.123); + }); + + test("accepts valid select values", () => { + const param = { + key: "blend", + label: "Blend", + type: "select" as const, + default: "normal", + options: [ + { value: "normal", label: "Normal" }, + { value: "multiply", label: "Multiply" }, + ], + }; + expect(coerceParamValue({ param, value: "normal" })).toBe("normal"); + expect(coerceParamValue({ param, value: "multiply" })).toBe("multiply"); + }); + + test("rejects select values outside the allowed options", () => { + expect( + coerceParamValue({ + param: { + key: "blend", + label: "Blend", + type: "select", + default: "normal", + options: [ + { value: "normal", label: "Normal" }, + { value: "multiply", label: "Multiply" }, + ], + }, + value: "screen", + }), + ).toBeNull(); + }); + + test("rejects non-string select values", () => { + const param = { + key: "blend", + label: "Blend", + type: "select" as const, + default: "normal", + options: [{ value: "normal", label: "Normal" }], + }; + expect(coerceParamValue({ param, value: 42 })).toBeNull(); + expect(coerceParamValue({ param, value: null })).toBeNull(); + expect(coerceParamValue({ param, value: undefined })).toBeNull(); + }); + + test("boolean params accept booleans and reject other types", () => { + const param = { + key: "visible", + label: "Visible", + type: "boolean" as const, + default: true, + }; + expect(coerceParamValue({ param, value: true })).toBe(true); + expect(coerceParamValue({ param, value: false })).toBe(false); + expect(coerceParamValue({ param, value: 1 })).toBeNull(); + expect(coerceParamValue({ param, value: "true" })).toBeNull(); + }); + + test("color params accept strings and reject other types", () => { + const param = { + key: "fill", + label: "Fill", + type: "color" as const, + default: "#ffffff", + }; + expect(coerceParamValue({ param, value: "#ff0000" })).toBe("#ff0000"); + expect(coerceParamValue({ param, value: 0xff0000 })).toBeNull(); + expect(coerceParamValue({ param, value: null })).toBeNull(); + }); + + test("getAnimationParamValueKind maps param type to binding kind", () => { + expect( + getParamValueKind({ + param: { + key: "n", + label: "N", + type: "number", + default: 0, + min: 0, + step: 1, + }, + }), + ).toBe("number"); + expect( + getParamValueKind({ + param: { key: "c", label: "C", type: "color", default: "#fff" }, + }), + ).toBe("color"); + expect( + getParamValueKind({ + param: { key: "b", label: "B", type: "boolean", default: false }, + }), + ).toBe("discrete"); + expect( + getParamValueKind({ + param: { + key: "s", + label: "S", + type: "select", + default: "a", + options: [{ value: "a", label: "A" }], + }, + }), + ).toBe("discrete"); + }); + + test("getAnimationParamDefaultInterpolation is linear for continuous, hold for discrete", () => { + expect( + getParamDefaultInterpolation({ + param: { + key: "n", + label: "N", + type: "number", + default: 0, + min: 0, + step: 1, + }, + }), + ).toBe("linear"); + expect( + getParamDefaultInterpolation({ + param: { key: "c", label: "C", type: "color", default: "#fff" }, + }), + ).toBe("linear"); + expect( + getParamDefaultInterpolation({ + param: { key: "b", label: "B", type: "boolean", default: false }, + }), + ).toBe("hold"); + expect( + getParamDefaultInterpolation({ + param: { + key: "s", + label: "S", + type: "select", + default: "a", + options: [{ value: "a", label: "A" }], + }, + }), + ).toBe("hold"); + }); + + test("getAnimationParamNumericRange returns spec for number params, undefined otherwise", () => { + expect( + getParamNumericRange({ + param: { + key: "intensity", + label: "Intensity", + type: "number", + default: 0.5, + min: 0, + max: 1, + step: 0.1, + }, + }), + ).toEqual({ min: 0, max: 1, step: 0.1 }); + expect( + getParamNumericRange({ + param: { key: "c", label: "C", type: "color", default: "#fff" }, + }), + ).toBeUndefined(); + expect( + getParamNumericRange({ + param: { key: "b", label: "B", type: "boolean", default: false }, + }), + ).toBeUndefined(); + }); +}); diff --git a/apps/web/src/animation/bezier.ts b/apps/web/src/animation/bezier.ts new file mode 100644 index 000000000..6f9a1d182 --- /dev/null +++ b/apps/web/src/animation/bezier.ts @@ -0,0 +1,90 @@ +import type { ScalarAnimationKey } from "@/animation/types"; + +const BEZIER_SOLVE_ITERATIONS = 20; + +export function getBezierPoint({ + progress, + p0, + p1, + p2, + p3, +}: { + progress: number; + p0: number; + p1: number; + p2: number; + p3: number; +}) { + const mt = 1 - progress; + return ( + mt * mt * mt * p0 + + 3 * mt * mt * progress * p1 + + 3 * mt * progress * progress * p2 + + progress * progress * progress * p3 + ); +} + +export function getDefaultRightHandle({ + leftKey, + rightKey, +}: { + leftKey: ScalarAnimationKey; + rightKey: ScalarAnimationKey; +}) { + const span = rightKey.time - leftKey.time; + const valueDelta = rightKey.value - leftKey.value; + return { + dt: span / 3, + dv: valueDelta / 3, + }; +} + +export function getDefaultLeftHandle({ + leftKey, + rightKey, +}: { + leftKey: ScalarAnimationKey; + rightKey: ScalarAnimationKey; +}) { + const span = rightKey.time - leftKey.time; + const valueDelta = rightKey.value - leftKey.value; + return { + dt: -span / 3, + dv: -valueDelta / 3, + }; +} + +export function solveBezierProgressForTime({ + time, + leftKey, + rightKey, +}: { + time: number; + leftKey: ScalarAnimationKey; + rightKey: ScalarAnimationKey; +}) { + let lower = 0; + let upper = 1; + const rightHandle = + leftKey.rightHandle ?? getDefaultRightHandle({ leftKey, rightKey }); + const leftHandle = + rightKey.leftHandle ?? getDefaultLeftHandle({ leftKey, rightKey }); + + for (let iteration = 0; iteration < BEZIER_SOLVE_ITERATIONS; iteration++) { + const mid = (lower + upper) / 2; + const estimate = getBezierPoint({ + progress: mid, + p0: leftKey.time, + p1: leftKey.time + rightHandle.dt, + p2: rightKey.time + leftHandle.dt, + p3: rightKey.time, + }); + if (estimate < time) { + lower = mid; + } else { + upper = mid; + } + } + + return (lower + upper) / 2; +} diff --git a/apps/web/src/animation/channel-data.ts b/apps/web/src/animation/channel-data.ts new file mode 100644 index 000000000..2fc08b5c0 --- /dev/null +++ b/apps/web/src/animation/channel-data.ts @@ -0,0 +1,57 @@ +import type { + AnimationChannel, + ChannelData, + CompositeChannelData, +} from "@/animation/types"; + +const LEGACY_ANIMATION_STORAGE_KEYS = new Set(["bindings", "channels"]); + +function isRecord(value: unknown): value is Record { + return typeof value === "object" && value !== null; +} + +export function isLeafChannelData( + data: ChannelData | undefined, +): data is AnimationChannel { + return isRecord(data) && Array.isArray(data.keys); +} + +export function isCompositeChannelData( + data: ChannelData | undefined, +): data is CompositeChannelData { + return isRecord(data) && !Array.isArray(data.keys); +} + +export function getChannelsFromData({ + data, +}: { + data: ChannelData | undefined; +}): AnimationChannel[] { + if (isLeafChannelData(data)) { + return [data]; + } + if (!isCompositeChannelData(data)) { + return []; + } + return Object.values(data).filter(isLeafChannelData); +} + +export function getChannelEntriesFromData({ + data, +}: { + data: ChannelData | undefined; +}): Array<[string, AnimationChannel]> { + if (isLeafChannelData(data)) { + return [["value", data]]; + } + if (!isCompositeChannelData(data)) { + return []; + } + return Object.entries(data).flatMap(([componentKey, channel]) => + isLeafChannelData(channel) ? [[componentKey, channel]] : [], + ); +} + +export function isAnimationStorageKey({ key }: { key: string }): boolean { + return !LEGACY_ANIMATION_STORAGE_KEYS.has(key); +} diff --git a/apps/web/src/animation/curve-bridge.ts b/apps/web/src/animation/curve-bridge.ts new file mode 100644 index 000000000..c947e7dce --- /dev/null +++ b/apps/web/src/animation/curve-bridge.ts @@ -0,0 +1,96 @@ +import { + getDefaultLeftHandle, + getDefaultRightHandle, +} from "@/animation/bezier"; +import type { + CurveHandle, + NormalizedCubicBezier, + ScalarAnimationKey, +} from "@/animation/types"; +import { roundMediaTime } from "@/wasm"; + +const VALUE_EPSILON = 1e-6; + +function clamp01({ value }: { value: number }): number { + return Math.max(0, Math.min(1, value)); +} + +export function getNormalizedCubicBezierForScalarSegment({ + leftKey, + rightKey, + referenceSpanValue, +}: { + leftKey: ScalarAnimationKey; + rightKey: ScalarAnimationKey; + /** Fallback Y-axis scale used when the segment is flat (spanValue ≈ 0). */ + referenceSpanValue?: number; +}): NormalizedCubicBezier | null { + const spanTime = rightKey.time - leftKey.time; + const spanValue = rightKey.value - leftKey.value; + const effectiveSpanValue = + Math.abs(spanValue) > VALUE_EPSILON + ? spanValue + : referenceSpanValue !== undefined && Math.abs(referenceSpanValue) > VALUE_EPSILON + ? referenceSpanValue + : null; + + if (spanTime === 0 || effectiveSpanValue === null) { + return null; + } + + const rightHandle = + leftKey.rightHandle ?? getDefaultRightHandle({ leftKey, rightKey }); + const leftHandle = + rightKey.leftHandle ?? getDefaultLeftHandle({ leftKey, rightKey }); + + return [ + clamp01({ value: rightHandle.dt / spanTime }), + rightHandle.dv / effectiveSpanValue, + clamp01({ value: 1 + leftHandle.dt / spanTime }), + 1 + leftHandle.dv / effectiveSpanValue, + ]; +} + +export function getCurveHandlesForNormalizedCubicBezier({ + leftKey, + rightKey, + cubicBezier, + referenceSpanValue, +}: { + leftKey: ScalarAnimationKey; + rightKey: ScalarAnimationKey; + cubicBezier: NormalizedCubicBezier; + /** Fallback Y-axis scale used when the segment is flat (spanValue ≈ 0). */ + referenceSpanValue?: number; +}): { + rightHandle: CurveHandle; + leftHandle: CurveHandle; +} | null { + const spanTime = rightKey.time - leftKey.time; + const spanValue = rightKey.value - leftKey.value; + const effectiveSpanValue = + Math.abs(spanValue) > VALUE_EPSILON + ? spanValue + : referenceSpanValue !== undefined && Math.abs(referenceSpanValue) > VALUE_EPSILON + ? referenceSpanValue + : null; + + if (spanTime === 0 || effectiveSpanValue === null) { + return null; + } + + const [rawX1, y1, rawX2, y2] = cubicBezier; + const x1 = clamp01({ value: rawX1 }); + const x2 = clamp01({ value: rawX2 }); + + return { + rightHandle: { + dt: roundMediaTime({ time: spanTime * x1 }), + dv: effectiveSpanValue * y1, + }, + leftHandle: { + dt: roundMediaTime({ time: spanTime * (x2 - 1) }), + dv: effectiveSpanValue * (y2 - 1), + }, + }; +} diff --git a/apps/web/src/animation/effect-param-channel.ts b/apps/web/src/animation/effect-param-channel.ts new file mode 100644 index 000000000..3def21091 --- /dev/null +++ b/apps/web/src/animation/effect-param-channel.ts @@ -0,0 +1,102 @@ +import type { + ElementAnimations, + EffectParamPath, +} from "@/animation/types"; +import type { ParamValues } from "@/params"; +import { removeElementKeyframe } from "./keyframes"; +import { resolveAnimationPathValueAtTime } from "./resolve"; + +export const EFFECT_PARAM_PATH_PREFIX = "effects."; +export const EFFECT_PARAM_PATH_SUFFIX = ".params."; + +export function buildEffectParamPath({ + effectId, + paramKey, +}: { + effectId: string; + paramKey: string; +}): EffectParamPath { + return `${EFFECT_PARAM_PATH_PREFIX}${effectId}${EFFECT_PARAM_PATH_SUFFIX}${paramKey}`; +} + +export function isEffectParamPath( + propertyPath: string, +): propertyPath is EffectParamPath { + return ( + propertyPath.startsWith(EFFECT_PARAM_PATH_PREFIX) && + propertyPath.includes(EFFECT_PARAM_PATH_SUFFIX) + ); +} + +export function parseEffectParamPath({ + propertyPath, +}: { + propertyPath: string; +}): { effectId: string; paramKey: string } | null { + if (!isEffectParamPath(propertyPath)) { + return null; + } + + const withoutPrefix = propertyPath.slice(EFFECT_PARAM_PATH_PREFIX.length); + const separatorIndex = withoutPrefix.indexOf(EFFECT_PARAM_PATH_SUFFIX); + if (separatorIndex <= 0) { + return null; + } + + const effectId = withoutPrefix.slice(0, separatorIndex); + const paramKey = withoutPrefix.slice( + separatorIndex + EFFECT_PARAM_PATH_SUFFIX.length, + ); + if (!effectId || !paramKey) { + return null; + } + + return { effectId, paramKey }; +} + +export function resolveEffectParamsAtTime({ + effectId, + params, + animations, + localTime, +}: { + effectId: string; + params: ParamValues; + animations: ElementAnimations | undefined; + localTime: number; +}): ParamValues { + const safeLocalTime = Math.max(0, localTime); + const resolved: ParamValues = {}; + + for (const [paramKey, staticValue] of Object.entries(params)) { + const path = buildEffectParamPath({ effectId, paramKey }); + resolved[paramKey] = animations?.[path] + ? resolveAnimationPathValueAtTime({ + animations, + propertyPath: path, + localTime: safeLocalTime, + fallbackValue: staticValue, + }) + : staticValue; + } + + return resolved; +} + +export function removeEffectParamKeyframe({ + animations, + effectId, + paramKey, + keyframeId, +}: { + animations: ElementAnimations | undefined; + effectId: string; + paramKey: string; + keyframeId: string; +}): ElementAnimations | undefined { + return removeElementKeyframe({ + animations, + propertyPath: buildEffectParamPath({ effectId, paramKey }), + keyframeId, + }); +} diff --git a/apps/web/src/animation/graph-channels.ts b/apps/web/src/animation/graph-channels.ts new file mode 100644 index 000000000..cef1397a9 --- /dev/null +++ b/apps/web/src/animation/graph-channels.ts @@ -0,0 +1,114 @@ +import type { + AnimationPath, + ElementAnimations, + ChannelData, + ScalarAnimationChannel, + ScalarGraphChannel, + ScalarGraphKeyframeContext, +} from "@/animation/types"; +import type { ChannelEasingMode } from "@/params"; +import { isCompositeChannelData, isLeafChannelData } from "./channel-data"; +import { isScalarChannel } from "./interpolation"; + +export interface EditableScalarChannels { + easingMode: ChannelEasingMode; + channels: ScalarGraphChannel[]; +} + +function isScalarAnimationChannel( + channel: ChannelData | undefined, +): channel is ScalarAnimationChannel { + return isLeafChannelData(channel) && isScalarChannel(channel); +} + +function getEasingModeForChannelData({ + data, +}: { + data: ChannelData | undefined; +}): ChannelEasingMode { + return isCompositeChannelData(data) && + ["r", "g", "b", "a"].every((componentKey) => componentKey in data) + ? "shared" + : "independent"; +} + +export function getEditableScalarChannels({ + animations, + propertyPath, +}: { + animations: ElementAnimations | undefined; + propertyPath: AnimationPath; +}): EditableScalarChannels | null { + const data = animations?.[propertyPath]; + if (!data) { + return null; + } + + const channelEntries = isLeafChannelData(data) + ? [["value", data] as const] + : Object.entries(data); + const channels = channelEntries.flatMap(([componentKey, channel]) => { + if (!isScalarAnimationChannel(channel)) { + return []; + } + + return [ + { + propertyPath, + componentKey, + channel, + } satisfies ScalarGraphChannel, + ]; + }); + + return { easingMode: getEasingModeForChannelData({ data }), channels }; +} + +export function getEditableScalarChannel({ + animations, + propertyPath, + componentKey, +}: { + animations: ElementAnimations | undefined; + propertyPath: AnimationPath; + componentKey: string; +}): ScalarGraphChannel | null { + const result = getEditableScalarChannels({ animations, propertyPath }); + return result?.channels.find((channel) => channel.componentKey === componentKey) ?? null; +} + +export function getScalarKeyframeContext({ + animations, + propertyPath, + componentKey, + keyframeId, +}: { + animations: ElementAnimations | undefined; + propertyPath: AnimationPath; + componentKey: string; + keyframeId: string; +}): ScalarGraphKeyframeContext | null { + const scalarChannel = getEditableScalarChannel({ + animations, + propertyPath, + componentKey, + }); + if (!scalarChannel) { + return null; + } + + const keyframeIndex = scalarChannel.channel.keys.findIndex( + (keyframe) => keyframe.id === keyframeId, + ); + if (keyframeIndex < 0) { + return null; + } + + return { + ...scalarChannel, + keyframe: scalarChannel.channel.keys[keyframeIndex], + keyframeIndex, + previousKey: scalarChannel.channel.keys[keyframeIndex - 1] ?? null, + nextKey: scalarChannel.channel.keys[keyframeIndex + 1] ?? null, + }; +} diff --git a/apps/web/src/animation/graphic-param-channel.ts b/apps/web/src/animation/graphic-param-channel.ts new file mode 100644 index 000000000..32f84714f --- /dev/null +++ b/apps/web/src/animation/graphic-param-channel.ts @@ -0,0 +1,65 @@ +import type { + ElementAnimations, + GraphicParamPath, +} from "@/animation/types"; +import type { ParamDefinition, ParamValues } from "@/params"; +import { resolveAnimationPathValueAtTime } from "./resolve"; + +export const GRAPHIC_PARAM_PATH_PREFIX = "params."; + +export function buildGraphicParamPath({ + paramKey, +}: { + paramKey: string; +}): GraphicParamPath { + return `${GRAPHIC_PARAM_PATH_PREFIX}${paramKey}`; +} + +export function isGraphicParamPath( + propertyPath: string, +): propertyPath is GraphicParamPath { + return propertyPath.startsWith(GRAPHIC_PARAM_PATH_PREFIX); +} + +export function parseGraphicParamPath({ + propertyPath, +}: { + propertyPath: string; +}): { paramKey: string } | null { + if (!isGraphicParamPath(propertyPath)) { + return null; + } + + const paramKey = propertyPath.slice(GRAPHIC_PARAM_PATH_PREFIX.length); + return paramKey.length > 0 ? { paramKey } : null; +} + +export function resolveGraphicParamsAtTime({ + params, + definitions, + animations, + localTime, +}: { + params: ParamValues; + definitions: ParamDefinition[]; + animations?: ElementAnimations; + localTime: number; +}): ParamValues { + const resolved: ParamValues = { ...params }; + + for (const param of definitions) { + const path = buildGraphicParamPath({ paramKey: param.key }); + if (!animations?.[path]) { + continue; + } + + resolved[param.key] = resolveAnimationPathValueAtTime({ + animations, + propertyPath: path, + localTime: Math.max(0, localTime), + fallbackValue: params[param.key] ?? param.default, + }); + } + + return resolved; +} diff --git a/apps/web/src/animation/index.ts b/apps/web/src/animation/index.ts new file mode 100644 index 000000000..f3d8aca49 --- /dev/null +++ b/apps/web/src/animation/index.ts @@ -0,0 +1,72 @@ +export { + getChannelValueAtTime, + getDiscreteChannelValueAtTime, + getScalarChannelValueAtTime, + getScalarSegmentInterpolation, + normalizeChannel, +} from "./interpolation"; + +export { + clampAnimationsToDuration, + cloneAnimations, + getChannel, + removeElementKeyframe, + retimeElementKeyframe, + setBindingComponentChannel, + setChannel, + splitAnimationsAtTime, + updateScalarKeyframeCurve, + upsertPathKeyframe, +} from "./keyframes"; + +export { + getElementLocalTime, + resolveAnimationPathValueAtTime, +} from "./resolve"; + +export { + getElementKeyframes, + getKeyframeById, + getKeyframeAtTime, + hasKeyframesForPath, +} from "./keyframe-query"; + +export { + type EditableScalarChannels, + getEditableScalarChannel, + getEditableScalarChannels, + getScalarKeyframeContext, +} from "./graph-channels"; + +export { + getCurveHandlesForNormalizedCubicBezier, + getNormalizedCubicBezierForScalarSegment, +} from "./curve-bridge"; + +export { + buildGraphicParamPath, + isGraphicParamPath, + parseGraphicParamPath, + resolveGraphicParamsAtTime, +} from "./graphic-param-channel"; + +export { + buildEffectParamPath, + isEffectParamPath, + parseEffectParamPath, + removeEffectParamKeyframe, + resolveEffectParamsAtTime, +} from "./effect-param-channel"; + +export { + getGroupKeyframesAtTime, + hasGroupKeyframeAtTime, + type GroupKeyframeRef, +} from "./property-groups"; + +export { + isAnimationPath, + isAnimationPropertyPath, +} from "./path"; + +export type { NumericSpec } from "./types"; diff --git a/apps/web/src/animation/interpolation.ts b/apps/web/src/animation/interpolation.ts new file mode 100644 index 000000000..cdb8bcc98 --- /dev/null +++ b/apps/web/src/animation/interpolation.ts @@ -0,0 +1,425 @@ +import type { + AnimationChannel, + AnimationInterpolation, + Channel, + DiscreteAnimationChannel, + DiscreteValue, + ScalarAnimationChannel, + ScalarAnimationKey, + ScalarSegmentType, +} from "@/animation/types"; +import type { ParamValue } from "@/params"; +import { mediaTime } from "@/wasm"; +import { + getBezierPoint, + getDefaultLeftHandle, + getDefaultRightHandle, + solveBezierProgressForTime, +} from "./bezier"; +import { clamp } from "@/utils/math"; + +function byTimeAscending({ + leftTime, + rightTime, +}: { + leftTime: number; + rightTime: number; +}): number { + return leftTime - rightTime; +} + +function isWithinTimePair({ + time, + leftTime, + rightTime, +}: { + time: number; + leftTime: number; + rightTime: number; +}): boolean { + return time >= leftTime && time <= rightTime; +} + +function lerpNumber({ + leftValue, + rightValue, + progress, +}: { + leftValue: number; + rightValue: number; + progress: number; +}): number { + return leftValue + (rightValue - leftValue) * progress; +} + +function normalizeRightHandle({ + handle, + leftKey, + rightKey, +}: { + handle: ScalarAnimationKey["rightHandle"]; + leftKey: ScalarAnimationKey; + rightKey: ScalarAnimationKey; +}) { + if (!handle) { + return undefined; + } + + const span = mediaTime({ + ticks: Math.max(1, rightKey.time - leftKey.time), + }); + return { + dt: mediaTime({ + ticks: Math.min(span, Math.max(0, handle.dt)), + }), + dv: handle.dv, + }; +} + +function normalizeLeftHandle({ + handle, + leftKey, + rightKey, +}: { + handle: ScalarAnimationKey["leftHandle"]; + leftKey: ScalarAnimationKey; + rightKey: ScalarAnimationKey; +}) { + if (!handle) { + return undefined; + } + + const span = mediaTime({ + ticks: Math.max(1, rightKey.time - leftKey.time), + }); + return { + dt: mediaTime({ + ticks: Math.max(-span, Math.min(0, handle.dt)), + }), + dv: handle.dv, + }; +} + +function normalizeScalarKey({ + key, +}: { + key: ScalarAnimationKey; +}): ScalarAnimationKey { + return { + ...key, + tangentMode: key.tangentMode ?? "flat", + segmentToNext: key.segmentToNext ?? "linear", + }; +} + +export function normalizeScalarChannel({ + channel, +}: { + channel: ScalarAnimationChannel; +}): ScalarAnimationChannel { + const sortedKeys = [...channel.keys] + .map((key) => normalizeScalarKey({ key })) + .sort((leftKey, rightKey) => + byTimeAscending({ + leftTime: leftKey.time, + rightTime: rightKey.time, + }), + ); + const nextKeys = sortedKeys.map((key, index) => { + const previousKey = sortedKeys[index - 1]; + const nextKey = sortedKeys[index + 1]; + return { + ...key, + leftHandle: + previousKey != null + ? normalizeLeftHandle({ + handle: key.leftHandle, + leftKey: previousKey, + rightKey: key, + }) + : undefined, + rightHandle: + nextKey != null + ? normalizeRightHandle({ + handle: key.rightHandle, + leftKey: key, + rightKey: nextKey, + }) + : undefined, + }; + }); + + return { + ...channel, + keys: nextKeys, + }; +} + +export function normalizeDiscreteChannel({ + channel, +}: { + channel: DiscreteAnimationChannel; +}): DiscreteAnimationChannel { + return { + ...channel, + keys: [...channel.keys].sort((leftKeyframe, rightKeyframe) => + byTimeAscending({ + leftTime: leftKeyframe.time, + rightTime: rightKeyframe.time, + }), + ), + }; +} + +export function isScalarChannel(channel: AnimationChannel): channel is ScalarAnimationChannel { + return ( + "extrapolation" in channel || + channel.keys.some((keyframe) => "segmentToNext" in keyframe) + ); +} + +export function normalizeChannel({ + channel, +}: { + channel: ScalarAnimationChannel; +}): ScalarAnimationChannel; +export function normalizeChannel({ + channel, +}: { + channel: DiscreteAnimationChannel; +}): DiscreteAnimationChannel; +export function normalizeChannel({ + channel, +}: { + channel: AnimationChannel; +}): AnimationChannel; +export function normalizeChannel({ + channel, +}: { + channel: AnimationChannel; +}): AnimationChannel { + return isScalarChannel(channel) + ? normalizeScalarChannel({ channel }) + : normalizeDiscreteChannel({ channel }); +} + +function extrapolateScalarEdge({ + mode, + edgeKey, + neighborKey, + time, +}: { + mode: "hold" | "linear"; + edgeKey: ScalarAnimationKey; + neighborKey: ScalarAnimationKey | undefined; + time: number; +}) { + if (mode === "hold" || !neighborKey) { + return edgeKey.value; + } + + const span = neighborKey.time - edgeKey.time; + if (span === 0) { + return edgeKey.value; + } + + return edgeKey.value + ((time - edgeKey.time) / span) * (neighborKey.value - edgeKey.value); +} + +export function getScalarSegmentInterpolation({ + segment, +}: { + segment: ScalarSegmentType; +}): AnimationInterpolation { + if (segment === "step") { + return "hold"; + } + + return segment === "bezier" ? "bezier" : "linear"; +} + +export function getScalarChannelValueAtTime({ + channel, + time, + fallbackValue, +}: { + channel: Channel | undefined; + time: number; + fallbackValue: number; +}): number { + if (!channel || channel.keys.length === 0) { + return fallbackValue; + } + + const normalizedChannel = normalizeScalarChannel({ channel }); + const firstKey = normalizedChannel.keys[0]; + const lastKey = normalizedChannel.keys[normalizedChannel.keys.length - 1]; + if (!firstKey || !lastKey) { + return fallbackValue; + } + + if (time <= firstKey.time) { + if (time < firstKey.time) { + return extrapolateScalarEdge({ + mode: normalizedChannel.extrapolation?.before ?? "hold", + edgeKey: firstKey, + neighborKey: normalizedChannel.keys[1], + time, + }); + } + + return firstKey.value; + } + + if (time >= lastKey.time) { + if (time > lastKey.time) { + return extrapolateScalarEdge({ + mode: normalizedChannel.extrapolation?.after ?? "hold", + edgeKey: lastKey, + neighborKey: normalizedChannel.keys[normalizedChannel.keys.length - 2], + time, + }); + } + + return lastKey.value; + } + + for ( + let keyIndex = 0; + keyIndex < normalizedChannel.keys.length - 1; + keyIndex++ + ) { + const leftKey = normalizedChannel.keys[keyIndex]; + const rightKey = normalizedChannel.keys[keyIndex + 1]; + if (time === rightKey.time) { + return rightKey.value; + } + + if ( + !isWithinTimePair({ + time, + leftTime: leftKey.time, + rightTime: rightKey.time, + }) + ) { + continue; + } + + if (leftKey.segmentToNext === "step") { + return leftKey.value; + } + + const span = rightKey.time - leftKey.time; + if (span === 0) { + return rightKey.value; + } + + const progress = clamp({ + value: (time - leftKey.time) / span, + min: 0, + max: 1, + }); + if (leftKey.segmentToNext === "linear") { + return lerpNumber({ + leftValue: leftKey.value, + rightValue: rightKey.value, + progress, + }); + } + + const curveProgress = solveBezierProgressForTime({ + time, + leftKey, + rightKey, + }); + const rightHandle = + leftKey.rightHandle ?? getDefaultRightHandle({ leftKey, rightKey }); + const leftHandle = + rightKey.leftHandle ?? getDefaultLeftHandle({ leftKey, rightKey }); + return getBezierPoint({ + progress: curveProgress, + p0: leftKey.value, + p1: leftKey.value + rightHandle.dv, + p2: rightKey.value + leftHandle.dv, + p3: rightKey.value, + }); + } + + return lastKey.value; +} + +export function getDiscreteChannelValueAtTime({ + channel, + time, + fallbackValue, +}: { + channel: Channel | undefined; + time: number; + fallbackValue: DiscreteValue; +}): DiscreteValue { + if (!channel || channel.keys.length === 0) { + return fallbackValue; + } + + const normalizedChannel = normalizeDiscreteChannel({ channel }); + let currentValue = fallbackValue; + for (const key of normalizedChannel.keys) { + if (time < key.time) { + break; + } + currentValue = key.value; + } + return currentValue; +} + +export function getChannelValueAtTime({ + channel, + time, + fallbackValue, +}: { + channel: Channel | undefined; + time: number; + fallbackValue: number; +}): number; +export function getChannelValueAtTime({ + channel, + time, + fallbackValue, +}: { + channel: DiscreteAnimationChannel | undefined; + time: number; + fallbackValue: TValue; +}): TValue; +export function getChannelValueAtTime({ + channel, + time, + fallbackValue, +}: { + channel: AnimationChannel | undefined; + time: number; + fallbackValue: ParamValue; +}): ParamValue { + if (!channel || channel.keys.length === 0) { + return fallbackValue; + } + + if (typeof fallbackValue === "number") { + return isScalarChannel(channel) + ? getScalarChannelValueAtTime({ + channel, + time, + fallbackValue, + }) + : fallbackValue; + } + + if (typeof fallbackValue !== "string" && typeof fallbackValue !== "boolean") { + return fallbackValue; + } + + return getDiscreteChannelValueAtTime({ + channel: isScalarChannel(channel) ? undefined : channel, + time, + fallbackValue, + }); +} diff --git a/apps/web/src/animation/keyframe-query.ts b/apps/web/src/animation/keyframe-query.ts new file mode 100644 index 000000000..95f7fcf00 --- /dev/null +++ b/apps/web/src/animation/keyframe-query.ts @@ -0,0 +1,314 @@ +import type { + AnimationChannel, + AnimationPath, + ChannelData, + ElementAnimations, + ElementKeyframe, +} from "@/animation/types"; +import { formatLinearRgba } from "@/params"; +import { + getChannelEntriesFromData, + isAnimationStorageKey, +} from "./channel-data"; +import { + getChannelValueAtTime, + getScalarSegmentInterpolation, + isScalarChannel, +} from "./interpolation"; +import { isAnimationPath } from "./path"; + +function getChannelFallbackValue({ + channel, +}: { + channel: AnimationChannel; +}) { + if (channel.keys.length === 0) { + return isScalarChannel(channel) ? 0 : false; + } + + return channel.keys[0].value; +} + +interface ChannelKeyframeMatch { + componentKey: string; + componentIndex: number; + channel: AnimationChannel; + keyframe: AnimationChannel["keys"][number]; +} + +function getChannelKeyframeMatches({ + data, +}: { + data: ChannelData | undefined; +}): ChannelKeyframeMatch[] { + return getChannelEntriesFromData({ data }).flatMap( + ([componentKey, channel], componentIndex) => { + if (channel.keys.length === 0) { + return []; + } + + return channel.keys.map((keyframe) => ({ + componentKey, + componentIndex, + channel, + keyframe, + })); + }, + ); +} + +function getUniqueChannelKeyframeMatches({ + data, +}: { + data: ChannelData | undefined; +}): ChannelKeyframeMatch[] { + const sortedMatches = getChannelKeyframeMatches({ + data, + }).sort( + (leftMatch, rightMatch) => + leftMatch.keyframe.time - rightMatch.keyframe.time || + leftMatch.componentIndex - rightMatch.componentIndex, + ); + const uniqueMatches: ChannelKeyframeMatch[] = []; + + for (const match of sortedMatches) { + const previousMatch = uniqueMatches[uniqueMatches.length - 1]; + if ( + !previousMatch || + previousMatch.keyframe.time !== match.keyframe.time + ) { + uniqueMatches.push(match); + continue; + } + + if ( + previousMatch.componentIndex !== 0 && + match.componentIndex === 0 + ) { + uniqueMatches[uniqueMatches.length - 1] = match; + } + } + + return uniqueMatches; +} + +function getPreferredChannelKeyframeMatch({ + matches, +}: { + matches: ChannelKeyframeMatch[]; +}): ChannelKeyframeMatch | null { + return ( + matches.find((match) => match.componentIndex === 0) ?? + matches[0] ?? + null + ); +} + +function getChannelValue({ + channel, + time, +}: { + channel: AnimationChannel; + time: number; +}) { + const fallbackValue = getChannelFallbackValue({ channel }); + if (typeof fallbackValue === "number") { + return getChannelValueAtTime({ + channel: isScalarChannel(channel) ? channel : undefined, + time, + fallbackValue, + }); + } + return getChannelValueAtTime({ + channel: !isScalarChannel(channel) ? channel : undefined, + time, + fallbackValue, + }); +} + +function getComposedChannelDataValueAtTime({ + data, + time, +}: { + data: ChannelData | undefined; + time: number; +}) { + const entries = getChannelEntriesFromData({ data }); + if (entries.length === 0) { + return null; + } + if (entries.length === 1 && entries[0]?.[0] === "value") { + return getChannelValue({ channel: entries[0][1], time }); + } + + const componentValues = Object.fromEntries( + entries.map(([componentKey, channel]) => [ + componentKey, + getChannelValue({ channel, time }), + ]), + ); + if ( + typeof componentValues.r === "number" && + typeof componentValues.g === "number" && + typeof componentValues.b === "number" && + typeof componentValues.a === "number" + ) { + return formatLinearRgba({ + color: { + r: componentValues.r, + g: componentValues.g, + b: componentValues.b, + a: componentValues.a, + }, + }); + } + return null; +} + +function getKeyframeInterpolation({ + channel, + keyframe, +}: { + channel: AnimationChannel; + keyframe: AnimationChannel["keys"][number]; +}) { + return isScalarChannel(channel) && "segmentToNext" in keyframe + ? getScalarSegmentInterpolation({ segment: keyframe.segmentToNext }) + : "hold"; +} + +function toElementKeyframe({ + data, + propertyPath, + keyframeMatch, +}: { + data: ChannelData | undefined; + propertyPath: AnimationPath; + keyframeMatch: ChannelKeyframeMatch; +}): ElementKeyframe | null { + const value = getComposedChannelDataValueAtTime({ + data, + time: keyframeMatch.keyframe.time, + }); + if (value === null) { + return null; + } + + return { + propertyPath, + id: keyframeMatch.keyframe.id, + time: keyframeMatch.keyframe.time, + value, + interpolation: getKeyframeInterpolation({ + channel: keyframeMatch.channel, + keyframe: keyframeMatch.keyframe, + }), + }; +} + +export function getElementKeyframes({ + animations, +}: { + animations: ElementAnimations | undefined; +}): ElementKeyframe[] { + if (!animations) { + return []; + } + + return Object.entries(animations).filter(([key]) => + isAnimationStorageKey({ key }), + ).flatMap( + ([propertyPath, data]) => { + if (!data || !isAnimationPath(propertyPath)) { + return []; + } + + return getUniqueChannelKeyframeMatches({ + data, + }).flatMap((keyframeMatch) => { + const keyframe = toElementKeyframe({ + data, + propertyPath, + keyframeMatch, + }); + if (!keyframe) { + return []; + } + + return [keyframe]; + }); + }, + ); +} + +export function hasKeyframesForPath({ + animations, + propertyPath, +}: { + animations: ElementAnimations | undefined; + propertyPath: AnimationPath; +}): boolean { + return getChannelEntriesFromData({ data: animations?.[propertyPath] }).some( + ([, channel]) => channel.keys.length > 0, + ); +} + +export function getKeyframeAtTime({ + animations, + propertyPath, + time, +}: { + animations: ElementAnimations | undefined; + propertyPath: AnimationPath; + time: number; +}): ElementKeyframe | null { + const data = animations?.[propertyPath]; + if (!data) { + return null; + } + + const keyframeMatch = getPreferredChannelKeyframeMatch({ + matches: getChannelKeyframeMatches({ data }).filter( + ({ keyframe }) => keyframe.time === time, + ), + }); + if (!keyframeMatch) { + return null; + } + + return toElementKeyframe({ + data, + propertyPath, + keyframeMatch, + }); +} + +export function getKeyframeById({ + animations, + propertyPath, + keyframeId, +}: { + animations: ElementAnimations | undefined; + propertyPath: AnimationPath; + keyframeId: string; +}): ElementKeyframe | null { + const data = animations?.[propertyPath]; + if (!data) { + return null; + } + + const keyframeMatch = getPreferredChannelKeyframeMatch({ + matches: getChannelKeyframeMatches({ data }).filter( + ({ keyframe }) => keyframe.id === keyframeId, + ), + }); + if (!keyframeMatch) { + return null; + } + + return toElementKeyframe({ + data, + propertyPath, + keyframeMatch, + }); +} diff --git a/apps/web/src/animation/keyframes.ts b/apps/web/src/animation/keyframes.ts new file mode 100644 index 000000000..c3fd82028 --- /dev/null +++ b/apps/web/src/animation/keyframes.ts @@ -0,0 +1,1361 @@ +import type { + AnimationChannel, + ChannelData, + AnimationInterpolation, + AnimationPath, + DiscreteAnimationChannel, + DiscreteAnimationKey, + ElementAnimations, + ScalarAnimationChannel, + ScalarAnimationKey, + ScalarCurveKeyframePatch, + ScalarSegmentType, +} from "@/animation/types"; +import type { + ChannelComponentDefinition, + ParamChannelLayout, + ParamValue, +} from "@/params"; +import { + getChannelsFromData, + isCompositeChannelData, + isAnimationStorageKey, + isLeafChannelData, +} from "./channel-data"; +import { + getDefaultLeftHandle, + getDefaultRightHandle, + solveBezierProgressForTime, +} from "./bezier"; +import { + getChannelValueAtTime, + getScalarSegmentInterpolation, + isScalarChannel, + normalizeChannel, + normalizeDiscreteChannel, + normalizeScalarChannel, +} from "./interpolation"; +import { + type MediaTime, + roundMediaTime, + subMediaTime, + ZERO_MEDIA_TIME, +} from "@/wasm"; +import { generateUUID } from "@/utils/id"; + +function isNearlySameTime({ + leftTime, + rightTime, +}: { + leftTime: number; + rightTime: number; +}): boolean { + return leftTime === rightTime; +} + +function hasChannelKeys({ + channel, +}: { + channel: AnimationChannel | undefined; +}): boolean { + return Boolean(channel && channel.keys.length > 0); +} + +function hasChannelData({ data }: { data: ChannelData | undefined }): boolean { + return getChannelsFromData({ data }).some((channel) => + hasChannelKeys({ channel }), + ); +} + +function toAnimation({ + animations, +}: { + animations: ElementAnimations; +}): ElementAnimations | undefined { + const nextAnimations = Object.fromEntries( + Object.entries(animations).filter( + ([key, data]) => isAnimationStorageKey({ key }) && hasChannelData({ data }), + ), + ); + if (Object.keys(nextAnimations).length === 0) { + return undefined; + } + + return nextAnimations; +} + +function cloneAnimationsState({ + animations, +}: { + animations: ElementAnimations | undefined; +}): ElementAnimations { + return { ...(animations ?? {}) }; +} + +function getChannelFromData({ + data, + componentKey, +}: { + data: ChannelData | undefined; + componentKey: string; +}): AnimationChannel | undefined { + if (isLeafChannelData(data)) { + return componentKey === "value" ? data : undefined; + } + if (isCompositeChannelData(data)) { + return data[componentKey]; + } + return undefined; +} + +type LayoutComponent = ChannelComponentDefinition; + +function getLayoutComponents({ + channelLayout, +}: { + channelLayout: ParamChannelLayout; +}): LayoutComponent[] { + return channelLayout.kind === "leaf" + ? [channelLayout.component] + : channelLayout.components; +} + +function getPrimaryComponentKey({ + channelLayout, +}: { + channelLayout: ParamChannelLayout; +}): string { + return getLayoutComponents({ channelLayout })[0]?.key ?? "value"; +} + +function getPrimaryChannelFromData({ + data, + channelLayout, +}: { + data: ChannelData | undefined; + channelLayout: ParamChannelLayout; +}): AnimationChannel | undefined { + return getChannelFromData({ + data, + componentKey: getPrimaryComponentKey({ channelLayout }), + }); +} + +function setChannelInData({ + data, + componentKey, + channel, +}: { + data: ChannelData | undefined; + componentKey: string; + channel: AnimationChannel | undefined; +}): ChannelData | undefined { + if (componentKey === "value") { + return channel; + } + const components = isCompositeChannelData(data) ? { ...data } : {}; + if (channel && hasChannelKeys({ channel })) { + components[componentKey] = channel; + } else { + delete components[componentKey]; + } + return Object.keys(components).length > 0 ? components : undefined; +} + +function getChannelDataEntries({ + data, +}: { + data: ChannelData | undefined; +}): Array<[string, AnimationChannel | undefined]> { + if (isLeafChannelData(data)) { + return [["value", data]]; + } + return isCompositeChannelData(data) ? Object.entries(data) : []; +} + +function getScalarSegmentType({ + interpolation, +}: { + interpolation: AnimationInterpolation; +}): ScalarSegmentType { + if (interpolation === "hold") { + return "step"; + } + return interpolation === "bezier" ? "bezier" : "linear"; +} + +function getInterpolationForComponent({ + component, + interpolation, +}: { + component: LayoutComponent; + interpolation: AnimationInterpolation | undefined; +}): AnimationInterpolation { + if (component.valueKind === "discrete") { + return "hold"; + } + + if ( + interpolation === "linear" || + interpolation === "hold" || + interpolation === "bezier" + ) { + return interpolation; + } + + return component.defaultInterpolation; +} + +function decomposeChannelLayoutValue({ + channelLayout, + value, +}: { + channelLayout: ParamChannelLayout; + value: ParamValue; +}): Record | null { + if (channelLayout.kind === "leaf") { + return { [channelLayout.component.key]: value }; + } + if (typeof value !== "string") { + return null; + } + + const components = channelLayout.decompose(value); + return components ? { ...components } : null; +} + +function createScalarKey({ + id, + time, + value, + interpolation, + previousKey, +}: { + id: string; + time: MediaTime; + value: number; + interpolation?: AnimationInterpolation; + previousKey?: ScalarAnimationKey; +}): ScalarAnimationKey { + return { + id, + time, + value, + leftHandle: previousKey?.leftHandle, + rightHandle: previousKey?.rightHandle, + segmentToNext: + previousKey?.segmentToNext ?? + getScalarSegmentType({ interpolation: interpolation ?? "linear" }), + tangentMode: previousKey?.tangentMode ?? "flat", + }; +} + +function createDiscreteKey({ + id, + time, + value, +}: { + id: string; + time: MediaTime; + value: string | boolean; +}): DiscreteAnimationKey { + return { + id, + time, + value, + }; +} + +function isDiscreteChannel( + channel: AnimationChannel | undefined, +): channel is DiscreteAnimationChannel { + return channel != null && !isScalarChannel(channel); +} + +function getChannelData({ + animations, + propertyPath, +}: { + animations: ElementAnimations | undefined; + propertyPath: AnimationPath; +}): ChannelData | undefined { + return animations?.[propertyPath]; +} + +function getTargetKeyMetadata({ + channel, + time, + keyframeId, +}: { + channel: AnimationChannel | undefined; + time: MediaTime; + keyframeId?: string; +}) { + const normalizedChannel = + channel != null ? normalizeChannel({ channel }) : undefined; + const keys = normalizedChannel?.keys ?? []; + if (keyframeId) { + const keyById = keys.find((key) => key.id === keyframeId); + if (keyById) { + return { + id: keyById.id, + time, + }; + } + } + + const keyAtTime = keys.find((key) => + isNearlySameTime({ leftTime: key.time, rightTime: time }), + ); + if (keyAtTime) { + return { + id: keyAtTime.id, + time: keyAtTime.time, + }; + } + + return { + id: keyframeId ?? generateUUID(), + time, + }; +} + +function upsertDiscreteChannelKey({ + channel, + time, + value, + keyframeId, +}: { + channel: DiscreteAnimationChannel | undefined; + time: MediaTime; + value: string | boolean; + keyframeId?: string; +}): DiscreteAnimationChannel { + const normalizedChannel = normalizeDiscreteChannel({ + channel: channel ?? { keys: [] }, + }); + const keys = [...normalizedChannel.keys]; + if (keyframeId) { + const existingIndex = keys.findIndex((key) => key.id === keyframeId); + if (existingIndex >= 0) { + keys[existingIndex] = createDiscreteKey({ + id: keys[existingIndex].id, + time, + value, + }); + return normalizeDiscreteChannel({ + channel: { keys }, + }); + } + } + + const existingAtTimeIndex = keys.findIndex((key) => + isNearlySameTime({ leftTime: key.time, rightTime: time }), + ); + if (existingAtTimeIndex >= 0) { + keys[existingAtTimeIndex] = createDiscreteKey({ + id: keys[existingAtTimeIndex].id, + time: keys[existingAtTimeIndex].time, + value, + }); + return normalizeDiscreteChannel({ + channel: { keys }, + }); + } + + keys.push( + createDiscreteKey({ + id: keyframeId ?? generateUUID(), + time, + value, + }), + ); + return normalizeDiscreteChannel({ + channel: { keys }, + }); +} + +function upsertScalarChannelKey({ + channel, + time, + value, + interpolation, + defaultInterpolation, + keyframeId, +}: { + channel: ScalarAnimationChannel | undefined; + time: MediaTime; + value: number; + interpolation?: AnimationInterpolation; + defaultInterpolation?: AnimationInterpolation; + keyframeId?: string; +}): ScalarAnimationChannel { + const normalizedChannel = normalizeScalarChannel({ + channel: channel ?? { keys: [] }, + }); + const keys = [...normalizedChannel.keys]; + if (keyframeId) { + const existingIndex = keys.findIndex((key) => key.id === keyframeId); + if (existingIndex >= 0) { + keys[existingIndex] = createScalarKey({ + id: keys[existingIndex].id, + time, + value, + interpolation, + previousKey: + interpolation != null + ? { + ...keys[existingIndex], + segmentToNext: getScalarSegmentType({ interpolation }), + } + : keys[existingIndex], + }); + return normalizeScalarChannel({ + channel: { + keys, + extrapolation: normalizedChannel.extrapolation, + }, + }); + } + } + + const existingAtTimeIndex = keys.findIndex((key) => + isNearlySameTime({ leftTime: key.time, rightTime: time }), + ); + if (existingAtTimeIndex >= 0) { + keys[existingAtTimeIndex] = createScalarKey({ + id: keys[existingAtTimeIndex].id, + time: keys[existingAtTimeIndex].time, + value, + interpolation, + previousKey: + interpolation != null + ? { + ...keys[existingAtTimeIndex], + segmentToNext: getScalarSegmentType({ interpolation }), + } + : keys[existingAtTimeIndex], + }); + return normalizeScalarChannel({ + channel: { + keys, + extrapolation: normalizedChannel.extrapolation, + }, + }); + } + + keys.push( + createScalarKey({ + id: keyframeId ?? generateUUID(), + time, + value, + interpolation: interpolation ?? defaultInterpolation, + }), + ); + return normalizeScalarChannel({ + channel: { + keys, + extrapolation: normalizedChannel.extrapolation, + }, + }); +} + +export function getChannel({ + animations, + propertyPath, +}: { + animations: ElementAnimations | undefined; + propertyPath: AnimationPath; +}): AnimationChannel | undefined { + const data = getChannelData({ animations, propertyPath }); + if (isLeafChannelData(data)) { + return data; + } + return getChannelsFromData({ data })[0]; +} + +export function upsertPathKeyframe({ + animations, + propertyPath, + time, + value, + interpolation, + keyframeId, + channelLayout, + coerceValue, +}: { + animations: ElementAnimations | undefined; + propertyPath: AnimationPath; + time: MediaTime; + value: ParamValue; + interpolation?: AnimationInterpolation; + keyframeId?: string; + channelLayout: ParamChannelLayout; + coerceValue: ({ value }: { value: ParamValue }) => ParamValue | null; +}): ElementAnimations | undefined { + const coercedValue = coerceValue({ value }); + if (coercedValue === null) { + return animations; + } + + const nextAnimations = cloneAnimationsState({ animations }); + const currentData = getChannelData({ animations, propertyPath }); + const primaryChannel = getPrimaryChannelFromData({ + data: currentData, + channelLayout, + }); + const targetKey = getTargetKeyMetadata({ + channel: primaryChannel, + time, + keyframeId, + }); + const componentValues = decomposeChannelLayoutValue({ + channelLayout, + value: coercedValue, + }); + if (!componentValues) { + return animations; + } + + let nextData: ChannelData | undefined = currentData; + for (const component of getLayoutComponents({ channelLayout })) { + const componentKey = component.key; + const nextValue = componentValues[componentKey]; + if (nextValue == null) { + continue; + } + + const currentChannel = getChannelFromData({ + data: currentData, + componentKey, + }); + if (component.valueKind === "discrete") { + if (typeof nextValue !== "string" && typeof nextValue !== "boolean") { + continue; + } + const nextChannel = upsertDiscreteChannelKey({ + channel: isDiscreteChannel(currentChannel) ? currentChannel : undefined, + time: targetKey.time, + value: nextValue, + keyframeId: targetKey.id, + }); + nextData = setChannelInData({ + data: nextData, + componentKey, + channel: nextChannel, + }); + continue; + } + + if (typeof nextValue !== "number") { + continue; + } + const nextChannel = upsertScalarChannelKey({ + channel: + currentChannel != null && isScalarChannel(currentChannel) + ? currentChannel + : undefined, + time: targetKey.time, + value: nextValue, + interpolation: + interpolation != null + ? getInterpolationForComponent({ component, interpolation }) + : undefined, + defaultInterpolation: component.defaultInterpolation, + keyframeId: targetKey.id, + }); + nextData = setChannelInData({ + data: nextData, + componentKey, + channel: nextChannel, + }); + } + nextAnimations[propertyPath] = nextData; + + return toAnimation({ + animations: nextAnimations, + }); +} + +export function upsertKeyframe({ + channel, + time, + value, + interpolation, + keyframeId, +}: { + channel: AnimationChannel | undefined; + time: MediaTime; + value: ParamValue; + interpolation?: AnimationInterpolation; + keyframeId?: string; +}): AnimationChannel | undefined { + if (!channel) { + return undefined; + } + + if (typeof value === "string" || typeof value === "boolean") { + return upsertDiscreteChannelKey({ + channel: isDiscreteChannel(channel) ? channel : undefined, + time, + value, + keyframeId, + }); + } + + if (typeof value !== "number") { + return channel; + } + + return upsertScalarChannelKey({ + channel: isScalarChannel(channel) ? channel : undefined, + time, + value, + interpolation, + keyframeId, + }); +} + +export function removeKeyframe({ + channel, + keyframeId, +}: { + channel: AnimationChannel | undefined; + keyframeId: string; +}): AnimationChannel | undefined { + if (!channel) { + return undefined; + } + + if (isScalarChannel(channel)) { + const nextKeys = channel.keys.filter((keyframe) => keyframe.id !== keyframeId); + if (nextKeys.length === 0) { + return undefined; + } + + return normalizeScalarChannel({ + channel: { + ...channel, + keys: nextKeys, + }, + }); + } + + const nextKeys = channel.keys.filter((keyframe) => keyframe.id !== keyframeId); + if (nextKeys.length === 0) { + return undefined; + } + + return normalizeDiscreteChannel({ + channel: { + ...channel, + keys: nextKeys, + }, + }); +} + +export function retimeKeyframe({ + channel, + keyframeId, + time, +}: { + channel: AnimationChannel | undefined; + keyframeId: string; + time: MediaTime; +}): AnimationChannel | undefined { + if (!channel) { + return undefined; + } + + if (isScalarChannel(channel)) { + const keyframeByIdIndex = channel.keys.findIndex( + (keyframe) => keyframe.id === keyframeId, + ); + if (keyframeByIdIndex < 0) { + return channel; + } + + const nextKeys = [...channel.keys]; + nextKeys[keyframeByIdIndex] = { + ...nextKeys[keyframeByIdIndex], + time, + }; + + return normalizeScalarChannel({ + channel: { + ...channel, + keys: nextKeys, + }, + }); + } + + const keyframeByIdIndex = channel.keys.findIndex( + (keyframe) => keyframe.id === keyframeId, + ); + if (keyframeByIdIndex < 0) { + return channel; + } + + const nextKeys = [...channel.keys]; + nextKeys[keyframeByIdIndex] = { + ...nextKeys[keyframeByIdIndex], + time, + }; + + return normalizeDiscreteChannel({ + channel: { + ...channel, + keys: nextKeys, + }, + }); +} + +export function setChannel({ + animations, + propertyPath, + channel, +}: { + animations: ElementAnimations | undefined; + propertyPath: AnimationPath; + channel: AnimationChannel | undefined; +}): ElementAnimations | undefined { + return setBindingComponentChannel({ + animations, + propertyPath, + componentKey: "value", + channel, + }); +} + +export function setBindingComponentChannel({ + animations, + propertyPath, + componentKey, + channel, +}: { + animations: ElementAnimations | undefined; + propertyPath: AnimationPath; + componentKey: string; + channel: AnimationChannel | undefined; +}): ElementAnimations | undefined { + const nextAnimations = cloneAnimationsState({ animations }); + nextAnimations[propertyPath] = setChannelInData({ + data: nextAnimations[propertyPath], + componentKey, + channel: + channel && hasChannelKeys({ channel }) + ? normalizeChannel({ channel }) + : undefined, + }); + return toAnimation({ + animations: nextAnimations, + }); +} + +export function updateScalarKeyframeCurve({ + animations, + propertyPath, + componentKey, + keyframeId, + patch, +}: { + animations: ElementAnimations | undefined; + propertyPath: AnimationPath; + componentKey: string; + keyframeId: string; + patch: ScalarCurveKeyframePatch; +}): ElementAnimations | undefined { + const channel = getChannelFromData({ + data: getChannelData({ animations, propertyPath }), + componentKey, + }); + if (!channel || !isScalarChannel(channel)) { + return animations; + } + + const keyframeIndex = channel.keys.findIndex((keyframe) => keyframe.id === keyframeId); + if (keyframeIndex < 0) { + return animations; + } + + const nextKeys = [...channel.keys]; + const currentKey = nextKeys[keyframeIndex]; + nextKeys[keyframeIndex] = { + ...currentKey, + leftHandle: + patch.leftHandle === undefined + ? currentKey.leftHandle + : patch.leftHandle ?? undefined, + rightHandle: + patch.rightHandle === undefined + ? currentKey.rightHandle + : patch.rightHandle ?? undefined, + segmentToNext: patch.segmentToNext ?? currentKey.segmentToNext, + tangentMode: patch.tangentMode ?? currentKey.tangentMode, + }; + + return setBindingComponentChannel({ + animations, + propertyPath, + componentKey, + channel: { + keys: nextKeys, + extrapolation: channel.extrapolation, + }, + }); +} + +function cloneChannelWithKeyIds({ + channel, + keyIdMap, +}: { + channel: AnimationChannel; + keyIdMap: Map; +}): AnimationChannel { + return isScalarChannel(channel) + ? normalizeScalarChannel({ + channel: { + ...channel, + keys: channel.keys.map((key) => ({ + ...key, + id: keyIdMap.get(key.id) ?? key.id, + })), + }, + }) + : normalizeDiscreteChannel({ + channel: { + ...channel, + keys: channel.keys.map((key) => ({ + ...key, + id: keyIdMap.get(key.id) ?? key.id, + })), + }, + }); +} + +export function cloneAnimations({ + animations, + shouldRegenerateKeyframeIds = false, +}: { + animations: ElementAnimations | undefined; + shouldRegenerateKeyframeIds?: boolean; +}): ElementAnimations | undefined { + if (!animations) { + return undefined; + } + + const nextAnimations = cloneAnimationsState({ animations }); + for (const [propertyPath, data] of Object.entries(animations).filter(([key]) => + isAnimationStorageKey({ key }), + )) { + const channels = getChannelsFromData({ data }); + const primaryChannel = channels[0]; + const keyIdMap = new Map(); + if (primaryChannel) { + for (const key of primaryChannel.keys) { + keyIdMap.set( + key.id, + shouldRegenerateKeyframeIds ? generateUUID() : key.id, + ); + } + } + + if (isLeafChannelData(data)) { + nextAnimations[propertyPath] = cloneChannelWithKeyIds({ + channel: data, + keyIdMap, + }); + continue; + } + if (isCompositeChannelData(data)) { + nextAnimations[propertyPath] = Object.fromEntries( + Object.entries(data).map(([componentKey, channel]) => [ + componentKey, + channel + ? cloneChannelWithKeyIds({ channel, keyIdMap }) + : undefined, + ]), + ); + } + } + + return toAnimation({ + animations: nextAnimations, + }); +} + +export function clampAnimationsToDuration({ + animations, + duration, +}: { + animations: ElementAnimations | undefined; + duration: MediaTime; +}): ElementAnimations | undefined { + if (!animations || duration <= 0) { + return undefined; + } + + return splitAnimationsAtTime({ + animations, + splitTime: duration, + shouldIncludeSplitBoundary: true, + }).leftAnimations; +} + +function lerpPoint({ + left, + right, + progress, +}: { + left: { x: number; y: number }; + right: { x: number; y: number }; + progress: number; +}) { + return { + x: left.x + (right.x - left.x) * progress, + y: left.y + (right.y - left.y) * progress, + }; +} + +function splitDiscreteChannelAtTime({ + channel, + splitTime, + leftBoundaryId, + rightBoundaryId, + shouldIncludeSplitBoundary, +}: { + channel: DiscreteAnimationChannel | undefined; + splitTime: MediaTime; + leftBoundaryId: string; + rightBoundaryId: string; + shouldIncludeSplitBoundary: boolean; +}) { + if (!channel || channel.keys.length === 0) { + return { + leftChannel: undefined, + rightChannel: undefined, + }; + } + + const normalizedChannel = normalizeChannel({ channel }); + let leftKeys = normalizedChannel.keys.filter((key) => key.time <= splitTime); + let rightKeys = normalizedChannel.keys + .filter((key) => key.time >= splitTime) + .map((key) => ({ + ...key, + time: subMediaTime({ a: key.time, b: splitTime }), + })); + + if (shouldIncludeSplitBoundary) { + const hasBoundaryOnLeft = leftKeys.some((key) => + isNearlySameTime({ leftTime: key.time, rightTime: splitTime }), + ); + const hasBoundaryOnRight = rightKeys.some((key) => + isNearlySameTime({ leftTime: key.time, rightTime: 0 }), + ); + const boundaryValue = getChannelValueAtTime({ + channel: normalizedChannel, + time: splitTime, + fallbackValue: normalizedChannel.keys[0].value, + }); + if (!hasBoundaryOnLeft) { + leftKeys = [ + ...leftKeys, + createDiscreteKey({ + id: leftBoundaryId, + time: splitTime, + value: boundaryValue, + }), + ]; + } + if (!hasBoundaryOnRight) { + rightKeys = [ + createDiscreteKey({ + id: rightBoundaryId, + time: ZERO_MEDIA_TIME, + value: boundaryValue, + }), + ...rightKeys, + ]; + } + } + + return { + leftChannel: leftKeys.length + ? normalizeChannel({ channel: { keys: leftKeys } }) + : undefined, + rightChannel: rightKeys.length + ? normalizeChannel({ channel: { keys: rightKeys } }) + : undefined, + }; +} + +function splitScalarChannelAtTime({ + channel, + splitTime, + leftBoundaryId, + rightBoundaryId, + shouldIncludeSplitBoundary, +}: { + channel: ScalarAnimationChannel | undefined; + splitTime: MediaTime; + leftBoundaryId: string; + rightBoundaryId: string; + shouldIncludeSplitBoundary: boolean; +}) { + if (!channel || channel.keys.length === 0) { + return { + leftChannel: undefined, + rightChannel: undefined, + }; + } + + const normalizedChannel = normalizeChannel({ channel }); + let leftKeys = normalizedChannel.keys.filter((key) => key.time <= splitTime); + let rightKeys = normalizedChannel.keys + .filter((key) => key.time >= splitTime) + .map((key) => ({ + ...key, + time: subMediaTime({ a: key.time, b: splitTime }), + })); + + const hasBoundaryOnLeft = leftKeys.some((key) => + isNearlySameTime({ leftTime: key.time, rightTime: splitTime }), + ); + const hasBoundaryOnRight = rightKeys.some((key) => + isNearlySameTime({ leftTime: key.time, rightTime: 0 }), + ); + if (!shouldIncludeSplitBoundary || (hasBoundaryOnLeft && hasBoundaryOnRight)) { + return { + leftChannel: leftKeys.length + ? normalizeChannel({ + channel: { + keys: leftKeys, + extrapolation: normalizedChannel.extrapolation, + }, + }) + : undefined, + rightChannel: rightKeys.length + ? normalizeChannel({ + channel: { + keys: rightKeys, + extrapolation: normalizedChannel.extrapolation, + }, + }) + : undefined, + }; + } + + for (let keyIndex = 0; keyIndex < normalizedChannel.keys.length - 1; keyIndex++) { + const leftKey = normalizedChannel.keys[keyIndex]; + const rightKey = normalizedChannel.keys[keyIndex + 1]; + if ( + !( + splitTime > leftKey.time && + splitTime < rightKey.time + ) + ) { + continue; + } + + const boundaryValue = getChannelValueAtTime({ + channel: normalizedChannel, + time: splitTime, + fallbackValue: leftKey.value, + }); + + if (leftKey.segmentToNext === "bezier") { + const rightHandle = + leftKey.rightHandle ?? getDefaultRightHandle({ leftKey, rightKey }); + const leftHandle = + rightKey.leftHandle ?? getDefaultLeftHandle({ leftKey, rightKey }); + const progress = solveBezierProgressForTime({ + time: splitTime, + leftKey, + rightKey, + }); + const p0 = { x: leftKey.time, y: leftKey.value }; + const p1 = { + x: leftKey.time + rightHandle.dt, + y: leftKey.value + rightHandle.dv, + }; + const p2 = { + x: rightKey.time + leftHandle.dt, + y: rightKey.value + leftHandle.dv, + }; + const p3 = { x: rightKey.time, y: rightKey.value }; + const q0 = lerpPoint({ left: p0, right: p1, progress }); + const q1 = lerpPoint({ left: p1, right: p2, progress }); + const q2 = lerpPoint({ left: p2, right: p3, progress }); + const r0 = lerpPoint({ left: q0, right: q1, progress }); + const r1 = lerpPoint({ left: q1, right: q2, progress }); + const splitPoint = lerpPoint({ left: r0, right: r1, progress }); + leftKeys = [ + ...normalizedChannel.keys.filter((key) => key.time < splitTime), + { + ...leftKey, + rightHandle: { + dt: roundMediaTime({ time: q0.x - p0.x }), + dv: q0.y - p0.y, + }, + }, + { + id: leftBoundaryId, + time: splitTime, + value: boundaryValue, + leftHandle: { + dt: roundMediaTime({ time: r0.x - splitPoint.x }), + dv: r0.y - splitPoint.y, + }, + segmentToNext: leftKey.segmentToNext, + tangentMode: leftKey.tangentMode, + }, + ]; + rightKeys = [ + { + id: rightBoundaryId, + time: ZERO_MEDIA_TIME, + value: boundaryValue, + rightHandle: { + dt: roundMediaTime({ time: r1.x - splitPoint.x }), + dv: r1.y - splitPoint.y, + }, + segmentToNext: "bezier", + tangentMode: leftKey.tangentMode, + }, + { + ...rightKey, + time: subMediaTime({ a: rightKey.time, b: splitTime }), + leftHandle: { + dt: roundMediaTime({ time: q2.x - p3.x }), + dv: q2.y - p3.y, + }, + }, + ...normalizedChannel.keys + .filter((key) => key.time > rightKey.time) + .map((key) => ({ + ...key, + time: subMediaTime({ a: key.time, b: splitTime }), + })), + ]; + } else { + leftKeys = [ + ...leftKeys, + createScalarKey({ + id: leftBoundaryId, + time: splitTime, + value: boundaryValue, + interpolation: "linear", + }), + ]; + rightKeys = [ + createScalarKey({ + id: rightBoundaryId, + time: ZERO_MEDIA_TIME, + value: boundaryValue, + interpolation: getScalarSegmentInterpolation({ + segment: leftKey.segmentToNext, + }), + }), + ...rightKeys, + ]; + } + + return { + leftChannel: normalizeChannel({ + channel: { + keys: leftKeys, + extrapolation: normalizedChannel.extrapolation, + }, + }), + rightChannel: normalizeChannel({ + channel: { + keys: rightKeys, + extrapolation: normalizedChannel.extrapolation, + }, + }), + }; + } + + return { + leftChannel: leftKeys.length + ? normalizeChannel({ + channel: { + keys: leftKeys, + extrapolation: normalizedChannel.extrapolation, + }, + }) + : undefined, + rightChannel: rightKeys.length + ? normalizeChannel({ + channel: { + keys: rightKeys, + extrapolation: normalizedChannel.extrapolation, + }, + }) + : undefined, + }; +} + +function splitChannelAtTime({ + channel, + splitTime, + leftBoundaryId, + rightBoundaryId, + shouldIncludeSplitBoundary, +}: { + channel: AnimationChannel | undefined; + splitTime: MediaTime; + leftBoundaryId: string; + rightBoundaryId: string; + shouldIncludeSplitBoundary: boolean; +}) { + return channel != null && !isScalarChannel(channel) + ? splitDiscreteChannelAtTime({ + channel, + splitTime, + leftBoundaryId, + rightBoundaryId, + shouldIncludeSplitBoundary, + }) + : splitScalarChannelAtTime({ + channel: + channel != null && isScalarChannel(channel) ? channel : undefined, + splitTime, + leftBoundaryId, + rightBoundaryId, + shouldIncludeSplitBoundary, + }); +} + +export function splitAnimationsAtTime({ + animations, + splitTime, + shouldIncludeSplitBoundary = true, +}: { + animations: ElementAnimations | undefined; + splitTime: MediaTime; + shouldIncludeSplitBoundary?: boolean; +}): { + leftAnimations: ElementAnimations | undefined; + rightAnimations: ElementAnimations | undefined; +} { + if (!animations) { + return { leftAnimations: undefined, rightAnimations: undefined }; + } + + const leftAnimations = cloneAnimationsState({ animations: undefined }); + const rightAnimations = cloneAnimationsState({ animations: undefined }); + + for (const [propertyPath, data] of Object.entries(animations).filter(([key]) => + isAnimationStorageKey({ key }), + )) { + if (!data) { + continue; + } + + const leftBoundaryId = generateUUID(); + const rightBoundaryId = generateUUID(); + + for (const [componentKey, channel] of getChannelDataEntries({ data })) { + const splitResult = splitChannelAtTime({ + channel, + splitTime, + leftBoundaryId, + rightBoundaryId, + shouldIncludeSplitBoundary, + }); + if (splitResult.leftChannel) { + leftAnimations[propertyPath] = setChannelInData({ + data: leftAnimations[propertyPath], + componentKey, + channel: splitResult.leftChannel, + }); + } + if (splitResult.rightChannel) { + rightAnimations[propertyPath] = setChannelInData({ + data: rightAnimations[propertyPath], + componentKey, + channel: splitResult.rightChannel, + }); + } + } + } + + return { + leftAnimations: toAnimation({ animations: leftAnimations }), + rightAnimations: toAnimation({ animations: rightAnimations }), + }; +} + +export function removeElementKeyframe({ + animations, + propertyPath, + keyframeId, +}: { + animations: ElementAnimations | undefined; + propertyPath: AnimationPath; + keyframeId: string; +}): ElementAnimations | undefined { + const data = getChannelData({ animations, propertyPath }); + if (!data) { + return animations; + } + + const nextAnimations = cloneAnimationsState({ animations }); + if (isLeafChannelData(data)) { + nextAnimations[propertyPath] = removeKeyframe({ channel: data, keyframeId }); + } else if (isCompositeChannelData(data)) { + let nextData: ChannelData | undefined = data; + for (const [componentKey, channel] of Object.entries(data)) { + nextData = setChannelInData({ + data: nextData, + componentKey, + channel: removeKeyframe({ channel, keyframeId }), + }); + } + nextAnimations[propertyPath] = nextData; + } + return toAnimation({ + animations: nextAnimations, + }); +} + +export function retimeElementKeyframe({ + animations, + propertyPath, + keyframeId, + time, +}: { + animations: ElementAnimations | undefined; + propertyPath: AnimationPath; + keyframeId: string; + time: MediaTime; +}): ElementAnimations | undefined { + const data = getChannelData({ animations, propertyPath }); + if (!data) { + return animations; + } + + const nextAnimations = cloneAnimationsState({ animations }); + if (isLeafChannelData(data)) { + nextAnimations[propertyPath] = retimeKeyframe({ + channel: data, + keyframeId, + time, + }); + } else if (isCompositeChannelData(data)) { + let nextData: ChannelData | undefined = data; + for (const [componentKey, channel] of Object.entries(data)) { + nextData = setChannelInData({ + data: nextData, + componentKey, + channel: retimeKeyframe({ channel, keyframeId, time }), + }); + } + nextAnimations[propertyPath] = nextData; + } + return toAnimation({ + animations: nextAnimations, + }); +} diff --git a/apps/web/src/animation/path.ts b/apps/web/src/animation/path.ts new file mode 100644 index 000000000..69e5e228c --- /dev/null +++ b/apps/web/src/animation/path.ts @@ -0,0 +1,22 @@ +import type { AnimationPath, AnimationPropertyPath } from "@/animation/types"; +import { ANIMATION_PROPERTY_PATHS } from "./types"; +import { isEffectParamPath } from "./effect-param-channel"; +import { isGraphicParamPath } from "./graphic-param-channel"; + +const ANIMATION_PROPERTY_PATH_SET = new Set(ANIMATION_PROPERTY_PATHS); + +export function isAnimationPropertyPath( + propertyPath: string, +): propertyPath is AnimationPropertyPath { + return ANIMATION_PROPERTY_PATH_SET.has(propertyPath); +} + +export function isAnimationPath( + propertyPath: string, +): propertyPath is AnimationPath { + return ( + isAnimationPropertyPath(propertyPath) || + isGraphicParamPath(propertyPath) || + isEffectParamPath(propertyPath) + ); +} diff --git a/apps/web/src/animation/property-groups.ts b/apps/web/src/animation/property-groups.ts new file mode 100644 index 000000000..7b71af159 --- /dev/null +++ b/apps/web/src/animation/property-groups.ts @@ -0,0 +1,39 @@ +import type { + AnimationPropertyGroup, + AnimationPropertyPath, + ElementAnimations, +} from "@/animation/types"; +import { ANIMATION_PROPERTY_GROUPS } from "@/animation/types"; +import { getKeyframeAtTime } from "./keyframe-query"; + +export interface GroupKeyframeRef { + propertyPath: AnimationPropertyPath; + keyframeId: string; +} + +export function getGroupKeyframesAtTime({ + animations, + group, + time, +}: { + animations: ElementAnimations | undefined; + group: AnimationPropertyGroup; + time: number; +}): GroupKeyframeRef[] { + return ANIMATION_PROPERTY_GROUPS[group].flatMap((propertyPath) => { + const keyframe = getKeyframeAtTime({ animations, propertyPath, time }); + return keyframe ? [{ propertyPath, keyframeId: keyframe.id }] : []; + }); +} + +export function hasGroupKeyframeAtTime({ + animations, + group, + time, +}: { + animations: ElementAnimations | undefined; + group: AnimationPropertyGroup; + time: number; +}): boolean { + return getGroupKeyframesAtTime({ animations, group, time }).length > 0; +} diff --git a/apps/web/src/animation/resolve.ts b/apps/web/src/animation/resolve.ts new file mode 100644 index 000000000..331dcbaa1 --- /dev/null +++ b/apps/web/src/animation/resolve.ts @@ -0,0 +1,153 @@ +import type { + AnimationPath, + ElementAnimations, +} from "@/animation/types"; +import { formatLinearRgba, parseColorToLinearRgba } from "@/params"; +import type { ParamValue } from "@/params"; +import { isCompositeChannelData, isLeafChannelData } from "./channel-data"; +import { + getChannelValueAtTime, + isScalarChannel, +} from "./interpolation"; + +export function getElementLocalTime({ + timelineTime, + elementStartTime, + elementDuration, +}: { + timelineTime: number; + elementStartTime: number; + elementDuration: number; +}): number { + const localTime = timelineTime - elementStartTime; + if (localTime <= 0) { + return 0; + } + + if (localTime >= elementDuration) { + return elementDuration; + } + + return localTime; +} + +export function resolveAnimationPathValueAtTime({ + animations, + propertyPath, + localTime, + fallbackValue, +}: { + animations: ElementAnimations | undefined; + propertyPath: AnimationPath; + localTime: number; + fallbackValue: number; +}): number; +export function resolveAnimationPathValueAtTime({ + animations, + propertyPath, + localTime, + fallbackValue, +}: { + animations: ElementAnimations | undefined; + propertyPath: AnimationPath; + localTime: number; + fallbackValue: string; +}): string; +export function resolveAnimationPathValueAtTime({ + animations, + propertyPath, + localTime, + fallbackValue, +}: { + animations: ElementAnimations | undefined; + propertyPath: AnimationPath; + localTime: number; + fallbackValue: boolean; +}): boolean; +export function resolveAnimationPathValueAtTime({ + animations, + propertyPath, + localTime, + fallbackValue, +}: { + animations: ElementAnimations | undefined; + propertyPath: AnimationPath; + localTime: number; + fallbackValue: ParamValue; +}): ParamValue; +export function resolveAnimationPathValueAtTime({ + animations, + propertyPath, + localTime, + fallbackValue, +}: { + animations: ElementAnimations | undefined; + propertyPath: AnimationPath; + localTime: number; + fallbackValue: ParamValue; +}): ParamValue { + const data = animations?.[propertyPath]; + if (!data) { + return fallbackValue; + } + if (isLeafChannelData(data)) { + if (typeof fallbackValue === "number") { + return getChannelValueAtTime({ + channel: isScalarChannel(data) ? data : undefined, + time: localTime, + fallbackValue, + }); + } + if (typeof fallbackValue === "string" || typeof fallbackValue === "boolean") { + return getChannelValueAtTime({ + channel: !isScalarChannel(data) ? data : undefined, + time: localTime, + fallbackValue, + }); + } + return fallbackValue; + } + if (!isCompositeChannelData(data)) { + return fallbackValue; + } + + if ( + typeof fallbackValue !== "string" || + !("r" in data) || + !("g" in data) || + !("b" in data) || + !("a" in data) + ) { + return fallbackValue; + } + + const fallbackComponents = parseColorToLinearRgba({ color: fallbackValue }); + if (fallbackComponents === null) { + return fallbackValue; + } + + return formatLinearRgba({ + color: { + r: getChannelValueAtTime({ + channel: data.r && isScalarChannel(data.r) ? data.r : undefined, + time: localTime, + fallbackValue: fallbackComponents.r, + }), + g: getChannelValueAtTime({ + channel: data.g && isScalarChannel(data.g) ? data.g : undefined, + time: localTime, + fallbackValue: fallbackComponents.g, + }), + b: getChannelValueAtTime({ + channel: data.b && isScalarChannel(data.b) ? data.b : undefined, + time: localTime, + fallbackValue: fallbackComponents.b, + }), + a: getChannelValueAtTime({ + channel: data.a && isScalarChannel(data.a) ? data.a : undefined, + time: localTime, + fallbackValue: fallbackComponents.a, + }), + }, + }); +} diff --git a/apps/web/src/animation/transform.ts b/apps/web/src/animation/transform.ts new file mode 100644 index 000000000..ab932ea2f --- /dev/null +++ b/apps/web/src/animation/transform.ts @@ -0,0 +1 @@ +export const MIN_TRANSFORM_SCALE = 0.01; diff --git a/apps/web/src/animation/types.ts b/apps/web/src/animation/types.ts new file mode 100644 index 000000000..9b5bd0c74 --- /dev/null +++ b/apps/web/src/animation/types.ts @@ -0,0 +1,157 @@ +import type { MediaTime } from "@/wasm"; +import type { ParamValue } from "@/params"; + +export const ANIMATION_PROPERTY_PATHS = [ + "transform.positionX", + "transform.positionY", + "transform.scaleX", + "transform.scaleY", + "transform.rotate", + "opacity", + "volume", + "color", + "background.color", + "background.paddingX", + "background.paddingY", + "background.offsetX", + "background.offsetY", + "background.cornerRadius", +] as const; + +export type AnimationPropertyPath = (typeof ANIMATION_PROPERTY_PATHS)[number]; +export type GraphicParamPath = `params.${string}`; +export type EffectParamPath = `effects.${string}.params.${string}`; +export type AnimationPath = string; + +export const ANIMATION_PROPERTY_GROUPS = { + "transform.scale": ["transform.scaleX", "transform.scaleY"], +} as const satisfies Record>; + +export type AnimationPropertyGroup = keyof typeof ANIMATION_PROPERTY_GROUPS; + +export type DiscreteValue = boolean | string; + +export interface NumericSpec { + min?: number; + max?: number; + step?: number; +} +export type AnimationColorPropertyPath = Extract< + AnimationPropertyPath, + "color" | "background.color" +>; +export type AnimationNumericPropertyPath = Exclude< + AnimationPropertyPath, + AnimationColorPropertyPath +>; + +export type ContinuousKeyframeInterpolation = "linear" | "hold" | "bezier"; +export type DiscreteKeyframeInterpolation = "hold"; +export type AnimationInterpolation = + | ContinuousKeyframeInterpolation + | DiscreteKeyframeInterpolation; + +export type ScalarSegmentType = "step" | "linear" | "bezier"; +export type TangentMode = "auto" | "aligned" | "broken" | "flat"; +export type ChannelExtrapolationMode = "hold" | "linear"; + +export interface CurveHandle { + dt: MediaTime; + dv: number; +} + +interface BaseAnimationKeyframe { + id: string; + time: MediaTime; // relative to element start time + value: TValue; +} + +export interface ScalarAnimationKey extends BaseAnimationKeyframe { + leftHandle?: CurveHandle; + rightHandle?: CurveHandle; + segmentToNext: ScalarSegmentType; + tangentMode: TangentMode; +} + +export type DiscreteAnimationKey = BaseAnimationKeyframe; + +export type Keyframe = + TValue extends number + ? ScalarAnimationKey + : TValue extends DiscreteValue + ? DiscreteAnimationKey + : never; + +export interface ScalarChannel { + keys: ScalarAnimationKey[]; + extrapolation?: { + before: ChannelExtrapolationMode; + after: ChannelExtrapolationMode; + }; +} + +export interface DiscreteChannel { + keys: DiscreteAnimationKey[]; +} + +export type Channel = + TValue extends number + ? ScalarChannel + : TValue extends DiscreteValue + ? DiscreteChannel + : never; + +export type ScalarAnimationChannel = Channel; +export type DiscreteAnimationChannel = Channel; +export type AnimationChannel = Channel; + +export type CompositeChannelData = Record; +export type ChannelData = AnimationChannel | CompositeChannelData; + +export interface ElementAnimations { + [propertyPath: AnimationPath]: ChannelData | undefined; +} + +export type NormalizedCubicBezier = [number, number, number, number]; + +export interface ScalarGraphChannelTarget { + propertyPath: AnimationPath; + componentKey: string; +} + +export interface ScalarGraphChannel extends ScalarGraphChannelTarget { + channel: ScalarAnimationChannel; +} + +export interface ScalarGraphKeyframeRef extends ScalarGraphChannelTarget { + keyframeId: string; +} + +export interface ScalarGraphKeyframeContext extends ScalarGraphChannel { + keyframe: ScalarAnimationKey; + keyframeIndex: number; + previousKey: ScalarAnimationKey | null; + nextKey: ScalarAnimationKey | null; +} + +export interface ScalarCurveKeyframePatch { + leftHandle?: CurveHandle | null; + rightHandle?: CurveHandle | null; + segmentToNext?: ScalarSegmentType; + tangentMode?: TangentMode; +} + +export interface ElementKeyframe { + propertyPath: AnimationPath; + id: string; + time: MediaTime; + value: ParamValue; + interpolation: AnimationInterpolation; +} + +export interface SelectedKeyframeRef { + trackId: string; + elementId: string; + propertyPath: AnimationPath; + keyframeId: string; +} diff --git a/apps/web/src/animation/values.ts b/apps/web/src/animation/values.ts new file mode 100644 index 000000000..7733ec4ce --- /dev/null +++ b/apps/web/src/animation/values.ts @@ -0,0 +1,61 @@ +import type { + AnimationColorPropertyPath, + AnimationNumericPropertyPath, + ElementAnimations, +} from "./types"; +import { resolveAnimationPathValueAtTime } from "./resolve"; + +export function resolveOpacityAtTime({ + baseOpacity, + animations, + localTime, +}: { + baseOpacity: number; + animations: ElementAnimations | undefined; + localTime: number; +}): number { + return resolveAnimationPathValueAtTime({ + animations, + propertyPath: "opacity", + localTime: Math.max(0, localTime), + fallbackValue: baseOpacity, + }); +} + +export function resolveNumberAtTime({ + baseValue, + animations, + propertyPath, + localTime, +}: { + baseValue: number; + animations: ElementAnimations | undefined; + propertyPath: AnimationNumericPropertyPath; + localTime: number; +}): number { + return resolveAnimationPathValueAtTime({ + animations, + propertyPath, + localTime: Math.max(0, localTime), + fallbackValue: baseValue, + }); +} + +export function resolveColorAtTime({ + baseColor, + animations, + propertyPath, + localTime, +}: { + baseColor: string; + animations: ElementAnimations | undefined; + propertyPath: AnimationColorPropertyPath; + localTime: number; +}): string { + return resolveAnimationPathValueAtTime({ + animations, + propertyPath, + localTime: Math.max(0, localTime), + fallbackValue: baseColor, + }); +} diff --git a/apps/web/src/app/api/auth/[...all]/route.ts b/apps/web/src/app/api/auth/[...all]/route.ts index e17d0cce3..edf0d0c22 100644 --- a/apps/web/src/app/api/auth/[...all]/route.ts +++ b/apps/web/src/app/api/auth/[...all]/route.ts @@ -1,4 +1,4 @@ -import { auth } from "@/lib/auth/server"; -import { toNextJsHandler } from "better-auth/next-js"; - -export const { POST, GET } = toNextJsHandler(auth); +import { auth } from "@/auth/server"; +import { toNextJsHandler } from "better-auth/next-js"; + +export const { POST, GET } = toNextJsHandler(auth); diff --git a/apps/web/src/app/api/feedback/route.ts b/apps/web/src/app/api/feedback/route.ts new file mode 100644 index 000000000..cd853eeae --- /dev/null +++ b/apps/web/src/app/api/feedback/route.ts @@ -0,0 +1,31 @@ +import { type NextRequest, NextResponse } from "next/server"; +import { z } from "zod"; +import { checkRateLimit } from "@/auth/rate-limit"; +import { submitFeedback, MAX_MESSAGE_LENGTH } from "@/feedback"; + +const submitSchema = z.object({ + message: z + .string() + .min(1, "Message is required") + .max(MAX_MESSAGE_LENGTH, "Message too long"), +}); + +export async function POST(request: NextRequest) { + const { limited } = await checkRateLimit({ request }); + if (limited) { + return NextResponse.json({ error: "Too many requests" }, { status: 429 }); + } + + const body = await request.json(); + const result = submitSchema.safeParse(body); + + if (!result.success) { + return NextResponse.json( + { error: "Invalid input", details: result.error.flatten().fieldErrors }, + { status: 400 }, + ); + } + + const entry = await submitFeedback(result.data); + return NextResponse.json({ entry }, { status: 201 }); +} diff --git a/apps/web/src/app/api/health/route.ts b/apps/web/src/app/api/health/route.ts index edb40a0dd..dbe6fdb16 100644 --- a/apps/web/src/app/api/health/route.ts +++ b/apps/web/src/app/api/health/route.ts @@ -1,3 +1,3 @@ -export async function GET() { - return new Response("OK", { status: 200 }); -} +export async function GET() { + return new Response("OK", { status: 200 }); +} diff --git a/apps/web/src/app/api/sounds/search/route.ts b/apps/web/src/app/api/sounds/search/route.ts index 339a54daa..aa7a1b607 100644 --- a/apps/web/src/app/api/sounds/search/route.ts +++ b/apps/web/src/app/api/sounds/search/route.ts @@ -1,7 +1,7 @@ -import { webEnv } from "@opencut/env/web"; +import { webEnv } from "@/env/web"; import { type NextRequest, NextResponse } from "next/server"; import { z } from "zod"; -import { checkRateLimit } from "@/lib/rate-limit"; +import { checkRateLimit } from "@/auth/rate-limit"; const searchParamsSchema = z.object({ q: z.string().max(500, "Query too long").optional(), diff --git a/apps/web/src/app/base-page.tsx b/apps/web/src/app/base-page.tsx index 1841a1ee7..787685816 100644 --- a/apps/web/src/app/base-page.tsx +++ b/apps/web/src/app/base-page.tsx @@ -1,56 +1,56 @@ -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} -
-
-
- ); -} +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} +
+
+
+ ); +} diff --git a/apps/web/src/app/blog/[slug]/page.tsx b/apps/web/src/app/blog/[slug]/page.tsx index e1afc3a80..dba9b5042 100644 --- a/apps/web/src/app/blog/[slug]/page.tsx +++ b/apps/web/src/app/blog/[slug]/page.tsx @@ -1,135 +1,135 @@ -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 ( -
- -
- ); -} - -function PostMeta({ date, publishedAt }: { date: string; publishedAt: Date }) { - return ( -
- -
- ); -} - -function PostTitle({ title }: { title: string }) { - return ( -

- {title} -

- ); -} - -function PostContent({ html }: { html: string }) { - return ( -
- -
- ); -} +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 "@/blog/query"; +import type { Author, Post } from "@/blog/types"; + +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 ( +
+ +
+ ); +} + +function PostMeta({ date, publishedAt }: { date: string; publishedAt: Date }) { + return ( +
+ +
+ ); +} + +function PostTitle({ title }: { title: string }) { + return ( +

+ {title} +

+ ); +} + +function PostContent({ html }: { html: string }) { + return ( +
+ +
+ ); +} diff --git a/apps/web/src/app/blog/page.tsx b/apps/web/src/app/blog/page.tsx index 7a2b5f974..b40bc2fc2 100644 --- a/apps/web/src/app/blog/page.tsx +++ b/apps/web/src/app/blog/page.tsx @@ -1,52 +1,52 @@ -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}

-
-
- - ); -} +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 "@/blog/query"; +import type { Post } from "@/blog/types"; + +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().catch(() => null); + if (!data || !data.posts) return
No posts yet
; + + return ( + +
+ {data.posts.map((post) => ( +
+ + +
+ ))} +
+
+ ); +} + +function BlogPostItem({ post }: { post: Post }) { + return ( + +
+
+

{post.title}

+

{post.description}

+
+
+ + ); +} diff --git a/apps/web/src/app/brand/page.tsx b/apps/web/src/app/brand/page.tsx index 94e3f3e10..9728cbc7c 100644 --- a/apps/web/src/app/brand/page.tsx +++ b/apps/web/src/app/brand/page.tsx @@ -190,7 +190,7 @@ export default function BrandPage() {
-

What's not allowed

+

What's not allowed

    {[ "Using OpenCut in the name of your product, service, or domain.", diff --git a/apps/web/src/app/changelog/[version]/page.tsx b/apps/web/src/app/changelog/[version]/page.tsx index af382822f..6c529ce13 100644 --- a/apps/web/src/app/changelog/[version]/page.tsx +++ b/apps/web/src/app/changelog/[version]/page.tsx @@ -1,105 +1,102 @@ -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} - )} -
    - -
    - - -
    -
    - ); -} +import type { Metadata } from "next"; +import Link from "next/link"; +import { notFound } from "next/navigation"; +import { BasePage } from "@/app/base-page"; +import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react"; +import { getReleaseByVersion, getSortedReleases } from "@/changelog/utils"; +import { + ReleaseArticle, + ReleaseMeta, + ReleaseTitle, + ReleaseDescription, + ReleaseChanges, +} from "@/changelog/components/release"; +import { CopyMarkdownButton } from "@/changelog/components/copy-markdown-button"; + +type Props = { params: Promise<{ version: string }> }; + +export async function generateStaticParams() { + return getSortedReleases().map((release) => ({ version: release.version })); +} + +export async function generateMetadata({ params }: Props): Promise { + const { version } = await params; + const release = getReleaseByVersion({ 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((entry) => entry.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} + )} +
    + +
    + + +
    +
    + ); +} diff --git a/apps/web/src/app/changelog/components/copy-markdown-button.tsx b/apps/web/src/app/changelog/components/copy-markdown-button.tsx deleted file mode 100644 index 2366550a9..000000000 --- a/apps/web/src/app/changelog/components/copy-markdown-button.tsx +++ /dev/null @@ -1,68 +0,0 @@ -"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 ( - - ); -} diff --git a/apps/web/src/app/changelog/components/release.tsx b/apps/web/src/app/changelog/components/release.tsx deleted file mode 100644 index eb04c1f07..000000000 --- a/apps/web/src/app/changelog/components/release.tsx +++ /dev/null @@ -1,106 +0,0 @@ -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} -
    • - ))} -
    -
    - ))} -
    - ); -} diff --git a/apps/web/src/app/changelog/page.tsx b/apps/web/src/app/changelog/page.tsx index 20d599a61..25ae25db6 100644 --- a/apps/web/src/app/changelog/page.tsx +++ b/apps/web/src/app/changelog/page.tsx @@ -1,14 +1,17 @@ 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 { + type Release as ReleaseType, + getSortedReleases, +} from "@/changelog/utils"; import { ReleaseArticle, ReleaseMeta, ReleaseTitle, ReleaseDescription, ReleaseChanges, -} from "./components/release"; +} from "@/changelog/components/release"; export const metadata: Metadata = { title: "Changelog - OpenCut", diff --git a/apps/web/src/app/changelog/utils.ts b/apps/web/src/app/changelog/utils.ts deleted file mode 100644 index 163207c07..000000000 --- a/apps/web/src/app/changelog/utils.ts +++ /dev/null @@ -1,43 +0,0 @@ -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 }), - ); -} diff --git a/apps/web/src/app/contributors/page.tsx b/apps/web/src/app/contributors/page.tsx index 217038523..f4d13732f 100644 --- a/apps/web/src/app/contributors/page.tsx +++ b/apps/web/src/app/contributors/page.tsx @@ -1,242 +1,242 @@ -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} -

    -
    -
    -
    - - ))} -
    -
    - ); -} +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 "@/site/external-tools"; +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} +

    +
    +
    +
    + + ))} +
    +
    + ); +} diff --git a/apps/web/src/app/editor/[project_id]/page.tsx b/apps/web/src/app/editor/[project_id]/page.tsx index 3ec6ddb8d..680dfff4e 100644 --- a/apps/web/src/app/editor/[project_id]/page.tsx +++ b/apps/web/src/app/editor/[project_id]/page.tsx @@ -8,15 +8,32 @@ import { } 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 { Timeline } from "@/timeline/components"; +import { PreviewPanel } from "@/preview/components"; 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 { MigrationDialog } from "@/project/components/migration-dialog"; +import { usePanelStore } from "@/editor/panel-store"; +import { usePasteMedia } from "@/media/use-paste-media"; import { MobileGate } from "@/components/editor/mobile-gate"; +import { useMemo, useState } from "react"; +import { useEditor } from "@/editor/use-editor"; +import { Cancel01Icon } from "@hugeicons/core-free-icons"; +import { HugeiconsIcon } from "@hugeicons/react"; +import { Button } from "@/components/ui/button"; +import { ChangelogNotification } from "@/changelog/components/changelog-notification"; +import { + createPreviewOverlayControl, + isPreviewOverlayVisible, + mergePreviewOverlaySources, +} from "@/preview/overlays"; +import { usePreviewStore } from "@/preview/preview-store"; +import { getGuidePreviewOverlaySource } from "@/guides"; +import { + bookmarkNotesPreviewOverlay, + getBookmarkPreviewOverlaySource, +} from "@/timeline/bookmarks/index"; export default function Editor() { const params = useParams(); @@ -26,29 +43,101 @@ export default function Editor() {
    +
    +
    ); } +function DegradedRendererBanner() { + const isDegraded = useEditor((e) => e.renderer.isDegraded); + const [dismissed, setDismissed] = useState(false); + if (!isDegraded || dismissed) return null; + + return ( +
    + For the best experience, open OpenCut in Chrome. + +
    + ); +} + function EditorLayout() { usePasteMedia(); const { panels, setPanel } = usePanelStore(); + const activeScene = useEditor((editor) => + editor.scenes.getActiveSceneOrNull(), + ); + const currentTime = useEditor((editor) => editor.playback.getCurrentTime()); + const activeGuide = usePreviewStore((state) => state.activeGuide); + const overlays = usePreviewStore((state) => state.overlays); + const setOverlayVisibility = usePreviewStore( + (state) => state.setOverlayVisibility, + ); + const showBookmarkNotes = isPreviewOverlayVisible({ + overlay: bookmarkNotesPreviewOverlay, + overlays, + }); + + const overlaySource = useMemo( + () => + mergePreviewOverlaySources({ + sources: [ + getGuidePreviewOverlaySource({ + guideId: activeGuide, + }), + activeScene + ? getBookmarkPreviewOverlaySource({ + bookmarks: activeScene.bookmarks, + time: currentTime, + isVisible: showBookmarkNotes, + }) + : { + definitions: [bookmarkNotesPreviewOverlay], + instances: [], + }, + ], + }), + [activeGuide, activeScene, currentTime, showBookmarkNotes], + ); + + const overlayControls = useMemo( + () => + overlaySource.definitions.map((overlay) => + createPreviewOverlayControl({ overlay, overlays }), + ), + [overlaySource.definitions, overlays], + ); return ( { - setPanel("mainContent", sizes[0] ?? panels.mainContent); - setPanel("timeline", sizes[1] ?? panels.timeline); + setPanel({ + panel: "mainContent", + size: sizes[0] ?? panels.mainContent, + }); + setPanel({ + panel: "timeline", + size: sizes[1] ?? panels.timeline, + }); }} > { - setPanel("tools", sizes[0] ?? panels.tools); - setPanel("preview", sizes[1] ?? panels.preview); - setPanel("properties", sizes[2] ?? panels.properties); + setPanel({ panel: "tools", size: sizes[0] ?? panels.tools }); + setPanel({ panel: "preview", size: sizes[1] ?? panels.preview }); + setPanel({ + panel: "properties", + size: sizes[2] ?? panels.properties, + }); }} > - + diff --git a/apps/web/src/app/globals.css b/apps/web/src/app/globals.css index 6d3e984f5..36417e158 100644 --- a/apps/web/src/app/globals.css +++ b/apps/web/src/app/globals.css @@ -8,111 +8,115 @@ @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%); + --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: hsl(200, 90%, 52%); + --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%); + --caution: hsl(38, 92%, 50%); + --caution-foreground: hsl(0, 0%, 10%); + --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%); + --background: hsl(210, 20%, 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% 87%); + --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%); + --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%, 13%); + --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%); + --caution: hsl(38, 92%, 60%); + --caution-foreground: hsl(0, 0%, 10%); + --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%); + --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. @@ -120,165 +124,168 @@ 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; - } + *, + ::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; - } - } + /* 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-caution: var(--caution); + --color-caution-foreground: var(--caution-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; - } + -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; - } + -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; - } + -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); - } + &::-webkit-scrollbar { + width: 7px; + height: 7px; + } + &::-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; - } + * { + @apply border-border outline-ring/50; + } + body { + @apply bg-background text-foreground; + } } diff --git a/apps/web/src/app/layout.tsx b/apps/web/src/app/layout.tsx index c2608bd9d..558ae5380 100644 --- a/apps/web/src/app/layout.tsx +++ b/apps/web/src/app/layout.tsx @@ -2,10 +2,11 @@ import { ThemeProvider } from "next-themes"; import Script from "next/script"; import "./globals.css"; import { Toaster } from "../components/ui/sonner"; +import { ChangelogNotification } from "@/changelog/components/changelog-notification"; import { TooltipProvider } from "../components/ui/tooltip"; import { baseMetaData } from "./metadata"; import { BotIdClient } from "botid/client"; -import { webEnv } from "@opencut/env/web"; +import { webEnv } from "@/env/web"; import { Inter } from "next/font/google"; const siteFont = Inter({ subsets: ["latin"] }); @@ -29,11 +30,13 @@ export default function RootLayout({ {process.env.NODE_ENV === "development" && ( -
- OpenCut Logo + OpenCut Logo -

OpenCut

+

OpenCut

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