From 832a2c8baeeb6bdd7d7316faf646762cbae58467 Mon Sep 17 00:00:00 2001 From: webadderall <131426131+webadderall@users.noreply.github.com> Date: Sun, 20 Sep 2026 18:52:27 +1000 Subject: [PATCH] docs(design): add HeroUI component catalogs and implementation notes --- design-app-catalog.html | 7 + design-capture.html | 5 + design-extra-catalog.html | 1 + design-hud-branches.html | 5 + design-inspector-catalog.html | 8 + design-library.html | 1 + design-preview-menus.html | 1 + design-timeline-catalog.html | 1 + design-timeline-details.html | 5 + design-window-capture.html | 27 ++ design-window-catalog.html | 5 + docs/figma-component-coverage.md | 590 +++++++++++++++++++++++ docs/ui-redundancy-audit.md | 65 +++ src/design-app-catalog.tsx | 799 +++++++++++++++++++++++++++++++ src/design-extra-catalog.tsx | 322 +++++++++++++ src/design-hud-branches.tsx | 190 ++++++++ src/design-inspector-catalog.tsx | 290 +++++++++++ src/design-library.tsx | 34 ++ src/design-preview-menus.tsx | 212 ++++++++ src/design-timeline-catalog.tsx | 272 +++++++++++ src/design-timeline-details.tsx | 87 ++++ src/design-window-catalog.tsx | 195 ++++++++ 22 files changed, 3122 insertions(+) create mode 100644 design-app-catalog.html create mode 100644 design-capture.html create mode 100644 design-extra-catalog.html create mode 100644 design-hud-branches.html create mode 100644 design-inspector-catalog.html create mode 100644 design-library.html create mode 100644 design-preview-menus.html create mode 100644 design-timeline-catalog.html create mode 100644 design-timeline-details.html create mode 100644 design-window-capture.html create mode 100644 design-window-catalog.html create mode 100644 docs/figma-component-coverage.md create mode 100644 docs/ui-redundancy-audit.md create mode 100644 src/design-app-catalog.tsx create mode 100644 src/design-extra-catalog.tsx create mode 100644 src/design-hud-branches.tsx create mode 100644 src/design-inspector-catalog.tsx create mode 100644 src/design-library.tsx create mode 100644 src/design-preview-menus.tsx create mode 100644 src/design-timeline-catalog.tsx create mode 100644 src/design-timeline-details.tsx create mode 100644 src/design-window-catalog.tsx diff --git a/design-app-catalog.html b/design-app-catalog.html new file mode 100644 index 00000000..bd137609 --- /dev/null +++ b/design-app-catalog.html @@ -0,0 +1,7 @@ +Recordly app component catalog
diff --git a/design-capture.html b/design-capture.html new file mode 100644 index 00000000..c88860da --- /dev/null +++ b/design-capture.html @@ -0,0 +1,5 @@ +Recordly design transfer
diff --git a/design-extra-catalog.html b/design-extra-catalog.html new file mode 100644 index 00000000..e466487b --- /dev/null +++ b/design-extra-catalog.html @@ -0,0 +1 @@ +Recordly Export and Popup Catalog
diff --git a/design-hud-branches.html b/design-hud-branches.html new file mode 100644 index 00000000..eb3fbfb0 --- /dev/null +++ b/design-hud-branches.html @@ -0,0 +1,5 @@ +Recordly HUD branch catalog
\ No newline at end of file diff --git a/design-inspector-catalog.html b/design-inspector-catalog.html new file mode 100644 index 00000000..013a532e --- /dev/null +++ b/design-inspector-catalog.html @@ -0,0 +1,8 @@ + +Recordly inspector catalog + +
diff --git a/design-library.html b/design-library.html new file mode 100644 index 00000000..d6037287 --- /dev/null +++ b/design-library.html @@ -0,0 +1 @@ +Recordly Component Library
diff --git a/design-preview-menus.html b/design-preview-menus.html new file mode 100644 index 00000000..8c80cb47 --- /dev/null +++ b/design-preview-menus.html @@ -0,0 +1 @@ +Recordly Preview Menus
diff --git a/design-timeline-catalog.html b/design-timeline-catalog.html new file mode 100644 index 00000000..8fab5ab9 --- /dev/null +++ b/design-timeline-catalog.html @@ -0,0 +1 @@ +Recordly Timeline Component Library
diff --git a/design-timeline-details.html b/design-timeline-details.html new file mode 100644 index 00000000..db02cc9c --- /dev/null +++ b/design-timeline-details.html @@ -0,0 +1,5 @@ +Recordly timeline detail components
diff --git a/design-window-capture.html b/design-window-capture.html new file mode 100644 index 00000000..74774936 --- /dev/null +++ b/design-window-capture.html @@ -0,0 +1,27 @@ + +Recordly window states +
diff --git a/design-window-catalog.html b/design-window-catalog.html new file mode 100644 index 00000000..bcae4a0e --- /dev/null +++ b/design-window-catalog.html @@ -0,0 +1,5 @@ +Recordly window catalog
diff --git a/docs/figma-component-coverage.md b/docs/figma-component-coverage.md new file mode 100644 index 00000000..6df3cc63 --- /dev/null +++ b/docs/figma-component-coverage.md @@ -0,0 +1,590 @@ +# Recordly HeroUI — Figma component coverage + +[Open the editable Figma file](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm) + +Audited 20 September 2026 against the `recordly-heroui` renderer. The file contains **254 native components**, **6,986 editable component layers**, **126 variables**, an assembled editor and two structural wireframes. Variable-alias validation found no broken references. + +## Start here + +- [Assembled desktop editor](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=32-3) +- [Editor wireframe](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=33-2) +- [Recorder wireframe](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=33-13) +- [Foundations](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=9-4) +- [Getting started](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=9-3) + +Components are organized by controls, inspectors, timeline, recording, windows and popup families. Edit a main component to update its Figma instances. The full component/node index below links directly to each definition. This is an editable design handoff; Figma changes do not automatically rewrite the React project. + +## Scope and fidelity + +- Every one of the 91 original renderer TSX modules is accounted for below: 85 visual modules and 6 providers/bootstrap modules without standalone UI. +- Includes basic and advanced inspectors, four background tabs, selected/unselected timeline blocks, annotations, waveform and keyframe details, project dialogs, export stages and errors, presets, announcement layouts, all recorder menus, recording/finalizing states, floating webcam preview, countdown, update notifications, and tooltips. +- Controls, text, icons and waveform shapes are editable Figma layers. Filmstrip/preview footage and image assets retain bitmap media fills. These are fixture media, not editable video sequences. +- The source uses SF Pro; the matching Figma faces are used. Menlo was unavailable in Figma and its small monospaced samples use JetBrains Mono Medium. +- Context providers, event hooks, native operating-system file/device permission dialogs and runtime behavior have no independent Figma visual. Project-owned dialogs and device-selection menus are included. +- Frame and Crop inspector identifiers currently resolve to Scene in the source. Their captured compatibility states reflect that behavior. Extensions are retained as a labelled legacy component, despite removal from active navigation. +- Capture-only containers make otherwise hidden dialogs visible and expand inspector scroll areas. Preview menus and recording-only HUD branches preserve source JSX and original CSS with inert handlers. No capture controls are added to the shipping editor. + +## Source module coverage + +| Source | Figma representation | +| --- | --- | +| [src/App.tsx](../src/App.tsx) | [Editor screen](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=32-3), [App / Startup](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4977) | +| [src/components/announcements/AnnouncementDialog.tsx](../src/components/announcements/AnnouncementDialog.tsx) | [Popup/announcement-popup](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4683), [Announcement cover](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=51-37) | +| [src/components/announcements/EditorAnnouncementBanner.tsx](../src/components/announcements/EditorAnnouncementBanner.tsx) | [Popup/announcement-banner](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4701) | +| [src/components/announcements/LiveAnnouncementNotifications.tsx](../src/components/announcements/LiveAnnouncementNotifications.tsx) | [Popup/announcement-notification](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4713) | +| [src/components/countdown/CountdownOverlay.tsx](../src/components/countdown/CountdownOverlay.tsx) | [CountdownOverlay / 3 seconds](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4916) | +| [src/components/launch/HudWindow.tsx](../src/components/launch/HudWindow.tsx) | [HudWindow / Idle](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4968), [HUD — recording states](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=46-2) | +| [src/components/launch/LaunchWindow.tsx](../src/components/launch/LaunchWindow.tsx) | [HudWindow / Idle](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4968), [HUD — recording states](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=46-2), [Popups — Recording](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=38-2) | +| [src/components/launch/MarqueeText.tsx](../src/components/launch/MarqueeText.tsx) | [MarqueeText](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=35-1306) | +| [src/components/launch/RecordingControls.tsx](../src/components/launch/RecordingControls.tsx) | [RecordingControls / Recording](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=35-1191), [RecordingControls / Paused](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=35-1222) | +| [src/components/launch/SourceSelector.tsx](../src/components/launch/SourceSelector.tsx) | [SourceSelectorContent / Sources](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=35-1255), [Popup/launch-sources](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=38-255) | +| [src/components/launch/UpdateToastWindow.tsx](../src/components/launch/UpdateToastWindow.tsx) | [Windows & tooltips](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4759) | +| [src/components/launch/contexts/HudInteractionContext.tsx](../src/components/launch/contexts/HudInteractionContext.tsx) | Provider/bootstrap only; represented through its rendered children. | +| [src/components/launch/popovers/CountdownPopover.tsx](../src/components/launch/popovers/CountdownPopover.tsx) | [Popup/launch-countdown](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=38-97) | +| [src/components/launch/popovers/LaunchPopoverCoordinator.tsx](../src/components/launch/popovers/LaunchPopoverCoordinator.tsx) | Provider/bootstrap only; represented through its rendered children. | +| [src/components/launch/popovers/MicPopover.tsx](../src/components/launch/popovers/MicPopover.tsx) | [Popup/launch-mic](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=38-113), [Popup/launch-mic-devices](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=38-37) | +| [src/components/launch/popovers/MorePopover.tsx](../src/components/launch/popovers/MorePopover.tsx) | [Popup/launch-more](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=38-236) | +| [src/components/launch/popovers/PopoverScaffold.tsx](../src/components/launch/popovers/PopoverScaffold.tsx) | [DropdownItem / Selected](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=35-1280), [MicDeviceRow / Selected and unselected](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=35-1301), [Popups — Recording](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=38-2) | +| [src/components/launch/popovers/ProjectPopover.tsx](../src/components/launch/popovers/ProjectPopover.tsx) | [Popup/launch-projects](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=38-279) | +| [src/components/launch/popovers/SourcePopover.tsx](../src/components/launch/popovers/SourcePopover.tsx) | [Popup/launch-sources](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=38-255) | +| [src/components/launch/popovers/WebcamPopover.tsx](../src/components/launch/popovers/WebcamPopover.tsx) | [Popup/launch-webcam](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=38-122), [Popup/launch-webcam-devices](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=38-67) | +| [src/components/ui/accordion.tsx](../src/components/ui/accordion.tsx) | [Accordion](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=30-15) | +| [src/components/ui/audio-level-meter.tsx](../src/components/ui/audio-level-meter.tsx) | [App — Shared utilities](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=35-348) | +| [src/components/ui/button.tsx](../src/components/ui/button.tsx) | [Button](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=18-2) | +| [src/components/ui/card.tsx](../src/components/ui/card.tsx) | [Card](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=30-2) | +| [src/components/ui/choice-group.tsx](../src/components/ui/choice-group.tsx) | [ChoiceGroup](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=28-36) | +| [src/components/ui/color-picker.tsx](../src/components/ui/color-picker.tsx) | [ColorPalette](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=35-368), [ColorControl](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=35-379), [Popup/color](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=38-886) | +| [src/components/ui/content-clamp.tsx](../src/components/ui/content-clamp.tsx) | [ContentClamp](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=35-383), [Tooltip / Content clamp](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4981) | +| [src/components/ui/dialog.tsx](../src/components/ui/dialog.tsx) | [Popup/dialog](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=38-573) | +| [src/components/ui/dropdown-menu.tsx](../src/components/ui/dropdown-menu.tsx) | [Popup/dropdown](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=38-783) | +| [src/components/ui/input.tsx](../src/components/ui/input.tsx) | [Input](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=22-2), [TextArea](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=22-27) | +| [src/components/ui/item-content.tsx](../src/components/ui/item-content.tsx) | [ItemContent](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=35-390) | +| [src/components/ui/label.tsx](../src/components/ui/label.tsx) | [Label](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=30-47) | +| [src/components/ui/popover.tsx](../src/components/ui/popover.tsx) | [Popup/popover](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=38-769) | +| [src/components/ui/select.tsx](../src/components/ui/select.tsx) | [Select](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=28-58), [Popup/select](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=38-759) | +| [src/components/ui/separator.tsx](../src/components/ui/separator.tsx) | [Separator](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=30-51) | +| [src/components/ui/skeleton.tsx](../src/components/ui/skeleton.tsx) | [Skeleton](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=30-54) | +| [src/components/ui/slider.tsx](../src/components/ui/slider.tsx) | [Slider](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=24-2) | +| [src/components/ui/switch.tsx](../src/components/ui/switch.tsx) | [Switch](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=23-2) | +| [src/components/ui/tabs.tsx](../src/components/ui/tabs.tsx) | [Tabs](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=28-45) | +| [src/components/ui/toast.tsx](../src/components/ui/toast.tsx) | [Popup/toast](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=38-794) | +| [src/components/ui/toggle-group.tsx](../src/components/ui/toggle-group.tsx) | [ToggleGroup](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=28-27) | +| [src/components/ui/toggle.tsx](../src/components/ui/toggle.tsx) | [Toggle](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=28-2) | +| [src/components/video-editor/AddCustomFontDialog.tsx](../src/components/video-editor/AddCustomFontDialog.tsx) | [Popup/font](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=38-605) | +| [src/components/video-editor/AnnotationOverlay.tsx](../src/components/video-editor/AnnotationOverlay.tsx) | [Timeline — AnnotationOverlay](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4199) | +| [src/components/video-editor/AnnotationSettingsPanel.tsx](../src/components/video-editor/AnnotationSettingsPanel.tsx) | [App — Annotation controls](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=35-750) | +| [src/components/video-editor/ArrowSvgs.tsx](../src/components/video-editor/ArrowSvgs.tsx) | [AnnotationSettingsPanel / figure](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=35-986) — all eight editable direction vectors | +| [src/components/video-editor/CaptionListPanel.tsx](../src/components/video-editor/CaptionListPanel.tsx) | [CaptionListPanel / Editing](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=35-748) | +| [src/components/video-editor/CropControl.tsx](../src/components/video-editor/CropControl.tsx) | [CropControl](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=35-690) | +| [src/components/video-editor/EditorWindow.tsx](../src/components/video-editor/EditorWindow.tsx) | [Assembled editor](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=32-3) | +| [src/components/video-editor/ExportSettingsMenu.tsx](../src/components/video-editor/ExportSettingsMenu.tsx) | [ExportSettingsMenu / MP4](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=35-586), [ExportSettingsMenu / GIF](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=35-642) | +| [src/components/video-editor/ExtensionManager.tsx](../src/components/video-editor/ExtensionManager.tsx) | [ExtensionManager / Legacy](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=35-454) | +| [src/components/video-editor/FormatSelector.tsx](../src/components/video-editor/FormatSelector.tsx) | [FormatSelector / MP4](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=35-472), [FormatSelector / GIF](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=35-489) | +| [src/components/video-editor/GifOptionsPanel.tsx](../src/components/video-editor/GifOptionsPanel.tsx) | [GifOptionsPanel](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=35-519) | +| [src/components/video-editor/KeyboardShortcutsHelp.tsx](../src/components/video-editor/KeyboardShortcutsHelp.tsx) | [Popup/keyboard-help](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=38-853) | +| [src/components/video-editor/PlaybackControls.tsx](../src/components/video-editor/PlaybackControls.tsx) | [Timeline — PlaybackControls](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4126) | +| [src/components/video-editor/ProjectBrowserDialog.tsx](../src/components/video-editor/ProjectBrowserDialog.tsx) | [App — Projects](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=35-407) | +| [src/components/video-editor/SettingsPanel.tsx](../src/components/video-editor/SettingsPanel.tsx) | [Inspectors — all sections](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-2) | +| [src/components/video-editor/ShortcutsConfigDialog.tsx](../src/components/video-editor/ShortcutsConfigDialog.tsx) | [Popup/shortcuts](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=38-701) | +| [src/components/video-editor/SliderControl.tsx](../src/components/video-editor/SliderControl.tsx) | [SliderControl](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=35-655) | +| [src/components/video-editor/TutorialHelp.tsx](../src/components/video-editor/TutorialHelp.tsx) | [Popup/tutorial](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=38-556), [Popup/feedback](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=38-415), [Popup/keyboard-dialog](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=38-493), [Editor/Header](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=32-391) | +| [src/components/video-editor/VideoEditor.tsx](../src/components/video-editor/VideoEditor.tsx) | [Assembled editor](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=32-3) | +| [src/components/video-editor/VideoPlayback.tsx](../src/components/video-editor/VideoPlayback.tsx) | [Editor/Preview](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=32-887), [Timeline — AnnotationOverlay](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4199) | +| [src/components/video-editor/WallpaperGrid.tsx](../src/components/video-editor/WallpaperGrid.tsx) | [WallpaperGrid](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=35-713) | +| [src/components/video-editor/WebcamCropControl.tsx](../src/components/video-editor/WebcamCropControl.tsx) | [WebcamCropControl](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=35-674) | +| [src/components/video-editor/layout/CropEditorDialog.tsx](../src/components/video-editor/layout/CropEditorDialog.tsx) | [Popup/crop-dialog](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=38-731) | +| [src/components/video-editor/layout/EditorDialogs.tsx](../src/components/video-editor/layout/EditorDialogs.tsx) | [Popups — Dialogs](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=38-281) | +| [src/components/video-editor/layout/EditorExportMenu.tsx](../src/components/video-editor/layout/EditorExportMenu.tsx) | [Popups — Export](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4276) | +| [src/components/video-editor/layout/EditorHeader.tsx](../src/components/video-editor/layout/EditorHeader.tsx) | [Editor/Header](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=32-391) | +| [src/components/video-editor/layout/EditorLoadingSkeleton.tsx](../src/components/video-editor/layout/EditorLoadingSkeleton.tsx) | [Popup/loading](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=38-957) | +| [src/components/video-editor/layout/EditorPresetMenu.tsx](../src/components/video-editor/layout/EditorPresetMenu.tsx) | [Popups — Presets & announcements](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4592) | +| [src/components/video-editor/layout/EditorPreviewPanel.tsx](../src/components/video-editor/layout/EditorPreviewPanel.tsx) | [Editor/Preview](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=32-887), [Popups — Preview](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4988) | +| [src/components/video-editor/layout/EditorShell.tsx](../src/components/video-editor/layout/EditorShell.tsx) | [Assembled editor](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=32-3) | +| [src/components/video-editor/layout/EditorSidebar.tsx](../src/components/video-editor/layout/EditorSidebar.tsx) | [Editor/Sidebar / Scene](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=32-630), [Inspectors — all sections](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-2), [Tooltip / Sidebar placement](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4985) | +| [src/components/video-editor/layout/EditorTimelinePanel.tsx](../src/components/video-editor/layout/EditorTimelinePanel.tsx) | [Editor/Timeline](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=32-986), [Timeline — TimelineEditor](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-3613) | +| [src/components/video-editor/layout/EditorVideoPreview.tsx](../src/components/video-editor/layout/EditorVideoPreview.tsx) | [Editor/Preview](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=32-887) | +| [src/components/video-editor/timeline/Item.tsx](../src/components/video-editor/timeline/Item.tsx) | [Timeline — TimelineEditor](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-3613) — clip, zoom, annotation, caption and audio blocks | +| [src/components/video-editor/timeline/Row.tsx](../src/components/video-editor/timeline/Row.tsx) | [Timeline — TimelineEditor](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-3613) — nested row layers | +| [src/components/video-editor/timeline/TimelineEditor.tsx](../src/components/video-editor/timeline/TimelineEditor.tsx) | [Timeline — TimelineEditor](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-3613) | +| [src/components/video-editor/timeline/components/filmstrip/ClipFilmstrip.tsx](../src/components/video-editor/timeline/components/filmstrip/ClipFilmstrip.tsx) | [Timeline — TimelineEditor](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-3613) — nested filmstrip media frames | +| [src/components/video-editor/timeline/components/markers/KeyframeMarkers.tsx](../src/components/video-editor/timeline/components/markers/KeyframeMarkers.tsx) | [Default marker](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=52-7), [Selected marker](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=52-9) | +| [src/components/video-editor/timeline/components/playhead/PlaybackCursor.tsx](../src/components/video-editor/timeline/components/playhead/PlaybackCursor.tsx) | [Timeline — TimelineEditor](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-3613) — nested playhead layers | +| [src/components/video-editor/timeline/components/viewport/TimelineCanvas.tsx](../src/components/video-editor/timeline/components/viewport/TimelineCanvas.tsx) | [Timeline — TimelineEditor](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-3613) — nested track viewport layers | +| [src/components/video-editor/timeline/components/waveform/AudioWaveform.tsx](../src/components/video-editor/timeline/components/waveform/AudioWaveform.tsx) | [AudioWaveform / Normal](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=55-6), [AudioWaveform / Normalized](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=55-9) | +| [src/components/video-editor/timeline/components/wrapper/TimelineWrapper.tsx](../src/components/video-editor/timeline/components/wrapper/TimelineWrapper.tsx) | [Timeline — TimelineEditor](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-3613) — composition/context wrapper | +| [src/contexts/I18nContext.tsx](../src/contexts/I18nContext.tsx) | Provider/bootstrap only; represented through its rendered children. | +| [src/contexts/ShortcutsContext.tsx](../src/contexts/ShortcutsContext.tsx) | Provider/bootstrap only; represented through its rendered children. | +| [src/contexts/ThemeContext.tsx](../src/contexts/ThemeContext.tsx) | Provider/bootstrap only; represented through its rendered children. | +| [src/main.tsx](../src/main.tsx) | Provider/bootstrap only; represented through its rendered children. | + +## Native component index + +Each row is a Figma component definition. Button names include their containing style family to distinguish repeated size/state names. + +### Icons + +| Component | Node | +| --- | --- | +| [Icon / Plus](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=16-5) | `16:5` | +| [Icon / Trash](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=16-8) | `16:8` | +| [Icon / Play](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=16-11) | `16:11` | +| [Icon / Pause](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=16-14) | `16:14` | +| [Icon / Scissors](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=16-17) | `16:17` | +| [Icon / Volume2](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=16-20) | `16:20` | +| [Icon / Undo2](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=16-23) | `16:23` | +| [Icon / Redo2](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=16-26) | `16:26` | +| [Icon / ChevronDown](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=16-29) | `16:29` | +| [Icon / Settings2](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=16-32) | `16:32` | +| [Icon / MousePointer](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=16-35) | `16:35` | +| [Icon / Type](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=16-38) | `16:38` | +| [Icon / Camera](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=16-41) | `16:41` | +| [Icon / Image](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=16-44) | `16:44` | +| [Icon / Download](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=16-47) | `16:47` | + +### Button + +| Component | Node | +| --- | --- | +| [default / Size=sm, State=Default](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=19-20) | `19:20` | +| [default / Size=sm, State=Disabled](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=19-23) | `19:23` | +| [default / Size=default, State=Default](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=19-26) | `19:26` | +| [default / Size=default, State=Disabled](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=19-29) | `19:29` | +| [default / Size=lg, State=Default](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=19-32) | `19:32` | +| [default / Size=lg, State=Disabled](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=19-35) | `19:35` | +| [default / Size=icon, State=Default](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=19-40) | `19:40` | +| [default / Size=icon, State=Disabled](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=19-45) | `19:45` | +| [secondary / Size=sm, State=Default](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=19-48) | `19:48` | +| [secondary / Size=sm, State=Disabled](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=19-51) | `19:51` | +| [secondary / Size=default, State=Default](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=19-54) | `19:54` | +| [secondary / Size=default, State=Disabled](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=19-57) | `19:57` | +| [secondary / Size=lg, State=Default](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=19-60) | `19:60` | +| [secondary / Size=lg, State=Disabled](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=19-63) | `19:63` | +| [secondary / Size=icon, State=Default](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=19-68) | `19:68` | +| [secondary / Size=icon, State=Disabled](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=19-73) | `19:73` | +| [outline / Size=sm, State=Default](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=19-76) | `19:76` | +| [outline / Size=sm, State=Disabled](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=19-79) | `19:79` | +| [outline / Size=default, State=Default](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=19-82) | `19:82` | +| [outline / Size=default, State=Disabled](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=19-85) | `19:85` | +| [outline / Size=lg, State=Default](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=19-88) | `19:88` | +| [outline / Size=lg, State=Disabled](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=19-91) | `19:91` | +| [outline / Size=icon, State=Default](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=19-96) | `19:96` | +| [outline / Size=icon, State=Disabled](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=19-101) | `19:101` | +| [ghost / Size=sm, State=Default](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=19-104) | `19:104` | +| [ghost / Size=sm, State=Disabled](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=19-107) | `19:107` | +| [ghost / Size=default, State=Default](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=19-110) | `19:110` | +| [ghost / Size=default, State=Disabled](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=19-113) | `19:113` | +| [ghost / Size=lg, State=Default](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=19-116) | `19:116` | +| [ghost / Size=lg, State=Disabled](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=19-119) | `19:119` | +| [ghost / Size=icon, State=Default](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=19-124) | `19:124` | +| [ghost / Size=icon, State=Disabled](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=19-129) | `19:129` | +| [destructive / Size=sm, State=Default](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=19-132) | `19:132` | +| [destructive / Size=sm, State=Disabled](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=19-135) | `19:135` | +| [destructive / Size=default, State=Default](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=19-138) | `19:138` | +| [destructive / Size=default, State=Disabled](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=19-141) | `19:141` | +| [destructive / Size=lg, State=Default](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=19-144) | `19:144` | +| [destructive / Size=lg, State=Disabled](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=19-147) | `19:147` | +| [destructive / Size=icon, State=Default](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=19-152) | `19:152` | +| [destructive / Size=icon, State=Disabled](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=19-157) | `19:157` | +| [destructive-soft / Size=sm, State=Default](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=19-160) | `19:160` | +| [destructive-soft / Size=sm, State=Disabled](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=19-163) | `19:163` | +| [destructive-soft / Size=default, State=Default](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=19-166) | `19:166` | +| [destructive-soft / Size=default, State=Disabled](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=19-169) | `19:169` | +| [destructive-soft / Size=lg, State=Default](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=19-172) | `19:172` | +| [destructive-soft / Size=lg, State=Disabled](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=19-175) | `19:175` | +| [destructive-soft / Size=icon, State=Default](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=19-180) | `19:180` | +| [destructive-soft / Size=icon, State=Disabled](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=19-185) | `19:185` | +| [link / Size=sm, State=Default](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=19-188) | `19:188` | +| [link / Size=sm, State=Disabled](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=19-191) | `19:191` | +| [link / Size=default, State=Default](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=19-194) | `19:194` | +| [link / Size=default, State=Disabled](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=19-197) | `19:197` | +| [link / Size=lg, State=Default](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=19-200) | `19:200` | +| [link / Size=lg, State=Disabled](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=19-203) | `19:203` | +| [link / Size=icon, State=Default](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=19-208) | `19:208` | +| [link / Size=icon, State=Disabled](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=19-213) | `19:213` | + +### Input + +| Component | Node | +| --- | --- | +| [State=Default](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=22-5) | `22:5` | +| [State=Disabled](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=22-8) | `22:8` | +| [State=Filled](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=22-11) | `22:11` | + +### TextArea + +| Component | Node | +| --- | --- | +| [TextArea](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=22-30) | `22:30` | + +### Switch + +| Component | Node | +| --- | --- | +| [State=Off](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=23-8) | `23:8` | +| [State=On](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=23-14) | `23:14` | +| [State=Disabled](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=23-20) | `23:20` | + +### Slider + +| Component | Node | +| --- | --- | +| [State=Single](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=24-8) | `24:8` | +| [State=Range](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=24-16) | `24:16` | +| [State=Disabled](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=24-22) | `24:22` | + +### Toggle + +| Component | Node | +| --- | --- | +| [State=Default](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=28-5) | `28:5` | +| [State=Selected](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=28-8) | `28:8` | +| [State=Disabled](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=28-11) | `28:11` | + +### ToggleGroup + +| Component | Node | +| --- | --- | +| [ToggleGroup](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=28-35) | `28:35` | + +### ChoiceGroup + +| Component | Node | +| --- | --- | +| [ChoiceGroup](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=28-44) | `28:44` | + +### Tabs + +| Component | Node | +| --- | --- | +| [Tabs](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=28-57) | `28:57` | + +### Select + +| Component | Node | +| --- | --- | +| [Select](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=28-64) | `28:64` | + +### Card + +| Component | Node | +| --- | --- | +| [Card](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=30-14) | `30:14` | + +### Accordion + +| Component | Node | +| --- | --- | +| [State=Expanded](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=30-25) | `30:25` | +| [State=Collapsed](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=30-31) | `30:31` | + +### Label + +| Component | Node | +| --- | --- | +| [Label](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=30-50) | `30:50` | + +### Separator + +| Component | Node | +| --- | --- | +| [Separator](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=30-53) | `30:53` | + +### Skeleton + +| Component | Node | +| --- | --- | +| [Skeleton](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=30-57) | `30:57` | + +### App — Shared utilities + +| Component | Node | +| --- | --- | +| [ColorPalette](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=35-368) | `35:368` | +| [ColorControl](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=35-379) | `35:379` | +| [ContentClamp](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=35-383) | `35:383` | +| [ItemContent](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=35-390) | `35:390` | +| [AudioLevelMeter / 12](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=35-395) | `35:395` | +| [AudioLevelMeter / 72](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=35-400) | `35:400` | +| [AudioLevelMeter / 94](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=35-405) | `35:405` | + +### App — Projects + +| Component | Node | +| --- | --- | +| [ProjectBrowserDialog / Populated](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=35-432) | `35:432` | +| [ProjectBrowserDialog / Empty](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=35-443) | `35:443` | +| [ExtensionManager / Legacy](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=35-454) | `35:454` | + +### App — Export controls + +| Component | Node | +| --- | --- | +| [FormatSelector / MP4](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=35-472) | `35:472` | +| [FormatSelector / GIF](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=35-489) | `35:489` | +| [GifOptionsPanel](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=35-519) | `35:519` | +| [ExportSettingsMenu / MP4](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=35-586) | `35:586` | +| [ExportSettingsMenu / GIF](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=35-642) | `35:642` | + +### App — Preview controls + +| Component | Node | +| --- | --- | +| [SliderControl](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=35-655) | `35:655` | +| [WebcamCropControl](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=35-674) | `35:674` | +| [CropControl](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=35-690) | `35:690` | +| [WallpaperGrid](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=35-713) | `35:713` | +| [CaptionListPanel / Editing](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=35-748) | `35:748` | + +### App — Annotation controls + +| Component | Node | +| --- | --- | +| [AnnotationSettingsPanel / text](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=35-879) | `35:879` | +| [AnnotationSettingsPanel / figure](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=35-986) | `35:986` | +| [AnnotationSettingsPanel / blur](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=35-1098) | `35:1098` | +| [AnnotationSettingsPanel / image](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=35-1159) | `35:1159` | + +### App — Recording & sources + +| Component | Node | +| --- | --- | +| [RecordingControls / Recording](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=35-1191) | `35:1191` | +| [RecordingControls / Paused](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=35-1222) | `35:1222` | +| [SourceSelectorContent / Sources](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=35-1255) | `35:1255` | +| [SourceSelectorContent / Empty](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=35-1260) | `35:1260` | +| [SourceSelectorContent / Loading](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=35-1265) | `35:1265` | +| [DropdownItem / Selected](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=35-1280) | `35:1280` | +| [MicDeviceRow / Selected and unselected](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=35-1301) | `35:1301` | +| [MarqueeText](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=35-1306) | `35:1306` | + +### Popups — Recording + +| Component | Node | +| --- | --- | +| [Popup/launch-mic-devices](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=38-37) | `38:37` | +| [Popup/launch-webcam-devices](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=38-67) | `38:67` | +| [Popup/launch-countdown](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=38-97) | `38:97` | +| [Popup/launch-mic](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=38-113) | `38:113` | +| [Popup/launch-webcam](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=38-122) | `38:122` | +| [Popup/launch-more](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=38-236) | `38:236` | +| [Popup/launch-sources](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=38-255) | `38:255` | +| [Popup/launch-projects](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=38-279) | `38:279` | + +### Popups — Dialogs + +| Component | Node | +| --- | --- | +| [Popup/project-save](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=38-307) | `38:307` | +| [Popup/project-saving](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=38-334) | `38:334` | +| [Popup/unsaved-changes](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=38-353) | `38:353` | +| [Popup/native-capture-unavailable](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=38-368) | `38:368` | +| [Popup/feedback](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=38-415) | `38:415` | +| [Popup/keyboard-dialog](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=38-493) | `38:493` | +| [Popup/tutorial](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=38-556) | `38:556` | +| [Popup/dialog](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=38-573) | `38:573` | +| [Popup/font](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=38-605) | `38:605` | +| [Popup/shortcuts](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=38-701) | `38:701` | +| [Popup/crop-dialog](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=38-731) | `38:731` | + +### Popups — Controls & feedback + +| Component | Node | +| --- | --- | +| [Popup/select](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=38-759) | `38:759` | +| [Popup/popover](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=38-769) | `38:769` | +| [Popup/dropdown](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=38-783) | `38:783` | +| [Popup/toast](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=38-794) | `38:794` | +| [Popup/keyboard-help](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=38-853) | `38:853` | +| [Popup/color](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=38-886) | `38:886` | +| [Popup/loading](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=38-957) | `38:957` | + +### Inspectors — all sections + +| Component | Node | +| --- | --- | +| [Inspector/scene / Basic](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-116) | `41:116` | +| [Inspector/scene / Advanced](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-283) | `41:283` | +| [Inspector/frame / Basic](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-398) | `41:398` | +| [Inspector/frame / Advanced](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-565) | `41:565` | +| [Inspector/crop / Basic](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-680) | `41:680` | +| [Inspector/crop / Advanced](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-847) | `41:847` | +| [Inspector/cursor / Basic](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-932) | `41:932` | +| [Inspector/cursor / Advanced](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-1115) | `41:1115` | +| [Inspector/captions / Basic](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-1177) | `41:1177` | +| [Inspector/captions / Advanced](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-1300) | `41:1300` | +| [Inspector/caption / Basic](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-1337) | `41:1337` | +| [Inspector/caption / Advanced](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-1374) | `41:1374` | +| [Inspector/webcam / Basic](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-1451) | `41:1451` | +| [Inspector/webcam / Advanced](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-1600) | `41:1600` | +| [Inspector/settings / Basic](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-1668) | `41:1668` | +| [Inspector/settings / Advanced](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-1839) | `41:1839` | +| [Inspector/zoom / Basic](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-1878) | `41:1878` | +| [Inspector/zoom / Advanced](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-1930) | `41:1930` | +| [Inspector/clip / Basic](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-1957) | `41:1957` | +| [Inspector/clip / Advanced](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-1984) | `41:1984` | +| [Inspector/audio / Basic](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-2016) | `41:2016` | +| [Inspector/audio / Advanced](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-2048) | `41:2048` | +| [Inspector/extensions / Basic](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-2163) | `41:2163` | +| [Inspector/extensions / Advanced](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-2330) | `41:2330` | +| [Inspector/Background / Image](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-2444) | `41:2444` | +| [Inspector/Background / Video](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-2497) | `41:2497` | +| [Inspector/Background / Color](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-2575) | `41:2575` | +| [Inspector/Background / Gradient](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-2661) | `41:2661` | +| [Inspector/Zoom / Manual](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-2713) | `41:2713` | +| [Inspector/Clip / Muted and speed override](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-2742) | `41:2742` | +| [Inspector/Captions / Model unavailable](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-2804) | `41:2804` | +| [Inspector/Captions / Downloading model](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-2868) | `41:2868` | +| [Inspector/Captions / Model ready](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-2930) | `41:2930` | +| [Inspector/Captions / Generating](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-2998) | `41:2998` | +| [Inspector/Captions / Download error](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-3060) | `41:3060` | +| [Inspector/Webcam / Empty](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-3131) | `41:3131` | +| [Inspector/Cursor / Hidden](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-3216) | `41:3216` | +| [Inspector/Zoom / No selection](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-3220) | `41:3220` | +| [Inspector/Clip / No selection](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-3240) | `41:3240` | +| [Inspector/Audio / No selection](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-3265) | `41:3265` | + +### Timeline — TimelineEditor + +| Component | Node | +| --- | --- | +| [TimelineEditor / clip selected](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-3712) | `41:3712` | +| [TimelineEditor / zoom selected](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-3812) | `41:3812` | +| [TimelineEditor / annotation selected](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-3912) | `41:3912` | +| [TimelineEditor / audio selected](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4012) | `41:4012` | +| [TimelineEditor / caption selected](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4112) | `41:4112` | +| [TimelineEditor / Empty](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4124) | `41:4124` | + +### Timeline — PlaybackControls + +| Component | Node | +| --- | --- | +| [PlaybackControls / Paused](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4149) | `41:4149` | +| [PlaybackControls / Playing](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4173) | `41:4173` | +| [PlaybackControls / Muted](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4197) | `41:4197` | + +### Timeline — AnnotationOverlay + +| Component | Node | +| --- | --- | +| [AnnotationOverlay / text / Default](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4205) | `41:4205` | +| [AnnotationOverlay / text / Selected](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4219) | `41:4219` | +| [AnnotationOverlay / figure / Default](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4226) | `41:4226` | +| [AnnotationOverlay / figure / Selected](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4240) | `41:4240` | +| [AnnotationOverlay / image / Default](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4245) | `41:4245` | +| [AnnotationOverlay / image / Selected](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4257) | `41:4257` | +| [AnnotationOverlay / blur / Default](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4262) | `41:4262` | +| [AnnotationOverlay / blur / Selected](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4274) | `41:4274` | + +### Popups — Export + +| Component | Node | +| --- | --- | +| [Popup/export-settings-mp4](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4357) | `41:4357` | +| [Popup/export-settings-gif](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4413) | `41:4413` | +| [Popup/export-preparing](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4431) | `41:4431` | +| [Popup/export-rendering](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4452) | `41:4452` | +| [Popup/export-audio](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4473) | `41:4473` | +| [Popup/export-finalizing](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4491) | `41:4491` | +| [Popup/export-muxing](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4509) | `41:4509` | +| [Popup/export-saving](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4527) | `41:4527` | +| [Popup/export-success](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4541) | `41:4541` | +| [Popup/export-error](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4553) | `41:4553` | +| [Popup/export-pending-save](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4567) | `41:4567` | +| [Popup/export-legacy](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4590) | `41:4590` | + +### Popups — Presets & announcements + +| Component | Node | +| --- | --- | +| [Popup/presets-empty](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4611) | `41:4611` | +| [Popup/presets-saved](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4649) | `41:4649` | +| [Popup/announcement-popup](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4683) | `41:4683` | +| [Popup/announcement-banner](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4701) | `41:4701` | +| [Popup/announcement-notification](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4713) | `41:4713` | +| [Popup/toolbar-aspect-ratio](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4757) | `41:4757` | +| [Popup/announcement-cover](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=51-37) | `51:37` | + +### Windows & tooltips + +| Component | Node | +| --- | --- | +| [UpdateToastWindow / Available](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4778) | `41:4778` | +| [UpdateToastWindow / Downloading](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4803) | `41:4803` | +| [UpdateToastWindow / Ready](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4823) | `41:4823` | +| [UpdateToastWindow / Check error](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4843) | `41:4843` | +| [UpdateToastWindow / Download error](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4863) | `41:4863` | +| [UpdateToastWindow / Experimental](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4886) | `41:4886` | +| [UpdateToastWindow / Preview](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4909) | `41:4909` | +| [CountdownOverlay / 3 seconds](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4916) | `41:4916` | +| [HudWindow / Idle](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4968) | `41:4968` | +| [App / Startup](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4977) | `41:4977` | +| [Tooltip / Content clamp](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4981) | `41:4981` | +| [Tooltip / Sidebar placement](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-4985) | `41:4985` | + +### Popups — Preview + +| Component | Node | +| --- | --- | +| [Popup/aspect-ratio](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-5017) | `41:5017` | +| [Popup/add-layer](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-5025) | `41:5025` | +| [Popup/preview-volume](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=41-5034) | `41:5034` | + +### HUD — recording states + +| Component | Node | +| --- | --- | +| [HUD / Finalizing recording](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=46-18) | `46:18` | +| [HUD / Floating webcam preview](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=46-23) | `46:23` | +| [HUD / Recording](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=46-61) | `46:61` | + +### Timeline — KeyframeMarkers + +| Component | Node | +| --- | --- | +| [State=Default](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=52-7) | `52:7` | +| [State=Selected](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=52-9) | `52:9` | + +### Timeline — AudioWaveform + +| Component | Node | +| --- | --- | +| [AudioWaveform / Normal](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=55-6) | `55:6` | +| [AudioWaveform / Normalized](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=55-9) | `55:9` | + +### Editor — shell & screen + +| Component | Node | +| --- | --- | +| [Editor/Header](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=32-391) | `32:391` | +| [Editor/Sidebar / Scene](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=32-630) | `32:630` | +| [Editor/Preview](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=32-887) | `32:887` | +| [Editor/Timeline](https://www.figma.com/design/bE2Ew2nF1Zd0ixci9SwJDm?node-id=32-986) | `32:986` | + +## Reproducible capture fixtures + +| Entry | Coverage | +| --- | --- | +| `design-library.html` | Primitive controls and variants | +| `design-capture.html?windowType=editor` | Actual assembled renderer | +| `design-app-catalog.html` | Shared app controls, source lists and annotation inspectors | +| `design-app-catalog.html?view=all-states` | 26 popup and dialog states | +| `design-inspector-catalog.html` | 40 inspector/background states | +| `design-timeline-catalog.html` | Timeline, playback and annotation overlays | +| `design-timeline-details.html` | Explicit waveform peaks and keyframe markers | +| `design-extra-catalog.html` | Export, preset and announcement states; legacy toolbar | +| `design-extra-catalog.html?view=announcement-cover` | Cover-media announcement | +| `design-preview-menus.html` | Aspect ratio, Add Layer and volume menus | +| `design-window-catalog.html` | Seven update states, countdown, idle HUD, startup and tooltips | +| `design-hud-branches.html` | Finalizing recorder and floating webcam/recording HUD | +| `design-window-capture.html` | Individual window state verification through the live bridge path | + +The capture entry points load the existing test bridge and fixture media. The renderer-only development server must be running to use them. They are separate from the production `index.html` entry. + +## Validation + +- TypeScript and targeted lint pass for source changes and all capture fixtures. +- Timeline/deletion changes: 135 focused unit tests and 13 browser regressions passed. +- Controlled and live update-window paths were rendered successfully; the window/detail galleries reported no JavaScript errors. +- Figma audit checks native component/layer counts, variable aliases and fonts. Visual QA corrected capture artifacts in slider borders, tooltip sizing and popup envelopes. Raw and failed intermediate capture frames were removed after conversion. + +Compatibility toolbar, unmounted ruler and marker-overlay source files were removed on 20 September 2026. Their earlier Figma captures are historical, not current app surfaces. diff --git a/docs/ui-redundancy-audit.md b/docs/ui-redundancy-audit.md new file mode 100644 index 00000000..0cdd8fca --- /dev/null +++ b/docs/ui-redundancy-audit.md @@ -0,0 +1,65 @@ +# HeroUI editor redundancy audit + +Reviewed 19 September 2026 in the `recordly-heroui` redesign. Findings use the current renderer and source, including the existing uncommitted redesign work. + +## Cleaned up + +| Area | Problem | Change | +| --- | --- | --- | +| Zoom inspector | Two Delete Zoom buttons for the same selected block: one in the scrolling content, another in the fixed footer. | Kept the fixed footer action. | +| Extensions navigation | A permanent tool tab leads only to “Extensions are no longer available.” | Removed the navigation entry; retained the existing compatibility component. | +| Account navigation | A permanent button only shows “Account coming soon.” | Removed the placeholder button. | +| Clip and Caption inspectors | The panel title is repeated immediately as a section heading without introducing a subsection. | Kept the panel title and removed the repeated heading. | +| Basic Zoom inspector | Animation heading, Reset button and directions to Settings appear without an animation control. The actual control is Advanced-only. | Grouped these with Classic Animation under Advanced. | + +## Further consolidation opportunities + +These are recommendations, not additional changes in this patch. + +1. **Crop has two separate editing surfaces.** The toolbar opens the visual Crop Video dialog; Scene → Advanced offers four inset sliders for the same crop state. Put precise inset inputs and reset inside the crop dialog so there is one place to edit cropping. Preserve numeric precision when consolidating. +2. **Zoom controls are scattered across two inspectors.** The block inspector contains Mode, Amount and Classic Animation; Settings contains Connect neighboring zooms, automatic suggestions and Motion Presets. Group motion settings in a clearly labelled project-wide subsection alongside Zoom controls, instead of a message directing users elsewhere. Some motion presets also affect the cursor, so their scope must stay explicit. +3. **Adding content is split between Add Layer and adjacent buttons.** Add Layer offers annotation/audio, while Add Zoom is a separate icon. A single Add menu could enumerate all supported insertions; keep Split separate as an editing operation. Hover-to-add is useful direct manipulation and need not be removed. +4. **Support commands occupy permanent editing-toolbar space.** Discord and Feedback sit beside project opening and Undo/Redo. A single Help menu could contain both plus shortcut help. This is lower-priority toolbar clutter, not two identical functions. +5. **Audio repeats its inspector title.** An “Audio” heading inside the Audio inspector mainly provides a home for Reset. Move Reset to the inspector header when standardizing section actions. +6. **Saved Presets and Motion Presets need clearer scope.** The header stores whole-editor presets; Settings applies motion presets. These are different functions, but the repeated naming obscures that distinction. Label the former “Style presets” and the latter “Motion” or explain their scope. + +Removed the unused compatibility timeline toolbar and its design-catalog fixture, plus the unmounted TimelineAxis and ClipMarkerOverlay components. Removed ignored SliderControl props and their call-site values. + +## Interaction defects fixed + +- Connected `handleClipDelete` to `TimelineEditor.onClipDelete`. Previously Backspace reached a no-op because the callback was absent. +- Routed Delete, Backspace and the configured delete shortcut consistently for every selected block type before the timeline-focus check. Inspecting a block no longer makes it undeletable. Form controls, editable text, composition events and overlay controls retain their own keyboard behavior. +- Removed the clip-only Backspace special case, which bypassed shared selection priority. +- Preserved a selected caption fragment when the playhead leaves its time span. Deletion still maps the fragment to its source caption cue. +- Removed unnecessary requirements for optional selection callbacks from clip, annotation and audio deletion. +- Cleared stale keyframe selection when selecting a timeline block. +- Cleared block selection when navigating to a global sidebar tool. A selected annotation previously overrode every tool's inspector, making Cursor, Scene, Webcam and Settings appear to show the same content. + +## Validation + +Passed: TypeScript checking, 72 focused unit tests, 14 browser tests, targeted lint, and diff whitespace checks. + +Regression coverage includes keyboard deletion for each block type, inspector deletion, clip undo, caption deletion after seeking, text-entry protection, and switching away from an annotation inspector. Unit coverage includes both delete keys, configured shortcuts, selection priority, editable/overlay guards and composition/modifier handling. Browser tests use the real renderer with a mocked Electron bridge and fixture media; packaged Electron is not exercised here. + +## Videos library and HUD follow-up + +- HUD source/device rows and source/region triggers use ghost buttons. Project cards fit the 300px HUD popup, scroll vertically with thin scrollbars, and no longer truncate the library at 24 entries. +- Caption generation skips session re-registration when the source is unchanged, preserving active companion-audio approvals. Local media URLs resolve the current server port; preview retries a failed local load once through the existing approved-media API. +- Videos uses a secondary header button at the far left and replaces the existing left inspector with lightweight file cards (no embedded video players). It lists files in the configured recordings directory. Removal sends the selected recordings and their capture companions to system Trash. Session undo restores from a temporary recovery copy without overwriting newer files. This replaces the earlier hide-only behavior. Automatic recording age/count pruning is removed. +- Timeline drops insert at a clip boundary. Since preview/export share a single source, imports prepare an immutable combined source in `.recordly-media`. New footage fits the current source canvas/frame rate; subsequent imports copy existing prepared video, with lossless system/mic companions and shifted cursor telemetry. Preparation takes time for large recordings. Clip source bounds prevent trimming into the next recording. Timeline history can undo/redo insertions because existing source offsets are retained. +- The existing FFmpeg metadata parser is now shared by import and export, rather than duplicating it or depending on the incompatible FFprobe binary found in this development install. + +Validation: real FFmpeg integration covers differing dimensions, audio routing, repeated inserts, and original preservation. Browser tests cover library removal/undo, drag insertion/undo/redo, caption generation without session reset, preview recovery, and 30 projects in the HUD popup. Caption visibility at 1x/2x/4x and gap snapping remain covered. + + +## Clips, overlays, and cloud integration + +- The Videos inspector uses shared search, checkbox, menu, and button controls. File rows support selection, double-click/+ insertion, and drag-to-timeline; bulk Trash is in the actions menu. No library video elements are loaded. +- Webcam imports normalize and concatenate a separate webcam track, account for capture start offsets, and preserve source-time visibility ranges. Preview and both renderer paths hide the bubble in screen-only spans. Saved projects preserve these ranges; style presets omit them and preserve the current source's ranges when applied. +- Imported click, mouse-up, right-click, and cursor-shape samples retain event types, shifted timestamps, and letterbox-adjusted coordinates. +- Annotation drag bounds now use the complete canvas. Position percentages may extend outside the recording rectangle and survive save/reload; export uses the same coordinates. Webcam and caption overlays already use their own canvas layout. +- Zoom blocks show the icon with the full label when space allows; narrow blocks hide the icon and mode while retaining the numeric multiplier. +- Cloud integration was ported from the cloud worktree, including its current service/viewer edits, desktop authentication, multipart uploads, and preparation of the current edit for sharing. Account lives at the bottom of the tool rail; Create share link lives in Export. Nothing was deployed or uploaded. Live sign-in requires the public Supabase environment settings described in authentication.md. + + +Thumbnail and sign-in refinement: Videos uses lazily requested 160×90 JPEG stills, cached by source path, modification time, and size. It displays unmodified filenames, including extensions, with ellipsis and the full hover title. Sign-in uses HeroUI Modal, Form, TextField, Input, Button, and Alert components with shared styling. diff --git a/src/design-app-catalog.tsx b/src/design-app-catalog.tsx new file mode 100644 index 00000000..3fae98dd --- /dev/null +++ b/src/design-app-catalog.tsx @@ -0,0 +1,799 @@ +import { FolderOpen, Microphone, Timer } from "@phosphor-icons/react"; +import { type ReactNode, useEffect, useRef, useState } from "react"; +import { UNSAFE_PortalProvider } from "react-aria"; +import { createRoot } from "react-dom/client"; +import "./index.css"; +import "./components/launch/launchTheme.css"; +import { HudInteractionContext } from "./components/launch/contexts/HudInteractionContext"; +import { MarqueeText } from "./components/launch/MarqueeText"; +import { CountdownPopover } from "./components/launch/popovers/CountdownPopover"; +import { + LaunchPopoverCoordinatorProvider, + useLaunchPopoverCoordinator, +} from "./components/launch/popovers/LaunchPopoverCoordinator"; +import { MicPopover } from "./components/launch/popovers/MicPopover"; +import { MorePopover } from "./components/launch/popovers/MorePopover"; +import { DropdownItem, MicDeviceRow } from "./components/launch/popovers/PopoverScaffold"; +import { ProjectPopover } from "./components/launch/popovers/ProjectPopover"; +import { SourcePopover } from "./components/launch/popovers/SourcePopover"; +import { WebcamPopover } from "./components/launch/popovers/WebcamPopover"; +import { RecordingControls } from "./components/launch/RecordingControls"; +import { SourceSelectorContent } from "./components/launch/SourceSelector"; +import { + Select, + SelectTrigger, + SelectValue, + SelectContent, + SelectItem, +} from "./components/ui/select"; +import { AudioLevelMeter } from "./components/ui/audio-level-meter"; +import { Button } from "./components/ui/button"; +import { ColorControl, ColorPalette } from "./components/ui/color-picker"; +import { ContentClamp } from "./components/ui/content-clamp"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from "./components/ui/dialog"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuSeparator, + DropdownMenuShortcut, + DropdownMenuTrigger, +} from "./components/ui/dropdown-menu"; +import ItemContent from "./components/ui/item-content"; +import { Popover, PopoverContent, PopoverTrigger } from "./components/ui/popover"; +import { Toaster, toast } from "./components/ui/toast"; +import { AddCustomFontDialog } from "./components/video-editor/AddCustomFontDialog"; +import { AnnotationSettingsPanel } from "./components/video-editor/AnnotationSettingsPanel"; +import CaptionListPanel from "./components/video-editor/CaptionListPanel"; +import { CropControl } from "./components/video-editor/CropControl"; +import { ExportSettingsMenu } from "./components/video-editor/ExportSettingsMenu"; +import ExtensionManager from "./components/video-editor/ExtensionManager"; +import { FormatSelector } from "./components/video-editor/FormatSelector"; +import { GifOptionsPanel } from "./components/video-editor/GifOptionsPanel"; +import { KeyboardShortcutsHelp } from "./components/video-editor/KeyboardShortcutsHelp"; +import { CropEditorDialog } from "./components/video-editor/layout/CropEditorDialog"; +import { EditorDialogs } from "./components/video-editor/layout/EditorDialogs"; +import { + FeedbackDialog, + KeyboardShortcutsDialog, + TutorialHelp, +} from "./components/video-editor/TutorialHelp"; +import { EditorLoadingSkeleton } from "./components/video-editor/layout/EditorLoadingSkeleton"; +import ProjectBrowserDialog from "./components/video-editor/ProjectBrowserDialog"; +import { ShortcutsConfigDialog } from "./components/video-editor/ShortcutsConfigDialog"; +import { SliderControl } from "./components/video-editor/SliderControl"; +import { + type AnnotationType, + DEFAULT_ANNOTATION_POSITION, + DEFAULT_ANNOTATION_SIZE, + DEFAULT_ANNOTATION_STYLE, + DEFAULT_FIGURE_DATA, +} from "./components/video-editor/types"; +import { WallpaperGrid } from "./components/video-editor/WallpaperGrid"; +import { WebcamCropControl } from "./components/video-editor/WebcamCropControl"; +import { I18nProvider, useI18n } from "./contexts/I18nContext"; +import { ShortcutsProvider, useShortcuts } from "./contexts/ShortcutsContext"; +import { ThemeProvider } from "./contexts/ThemeContext"; + +const noop = () => undefined; +const view = new URLSearchParams(location.search).get("view") ?? "gallery"; +const palette = ["#2563eb", "#a855f7", "#f43f5e", "#f59e0b", "#10b981", "#ffffff", "#171717"]; +const entries = [ + { + path: "/design-fixtures/product-demo.recordly", + name: "Product demo", + updatedAt: 1789800000000, + thumbnailPath: null, + isCurrent: true, + isInProjectsDirectory: true, + }, + { + path: "/design-fixtures/onboarding.recordly", + name: "Onboarding walkthrough", + updatedAt: 1789710000000, + thumbnailPath: null, + isCurrent: false, + isInProjectsDirectory: true, + }, +]; +const screenSources = [ + { + id: "screen:1:0", + name: "Built-in Display", + thumbnail: null, + display_id: "1", + appIcon: null, + sourceType: "screen" as const, + }, +]; +const windowSources = [ + { + id: "window:1:0", + name: "Product walkthrough", + thumbnail: null, + display_id: "1", + appIcon: null, + sourceType: "window" as const, + appName: "Browser", + }, +]; + +function Sample({ + name, + children, + width = 396, +}: { + name: string; + children: ReactNode; + width?: number; +}) { + return ( +
+

{name}

+
{children}
+
+ ); +} +function Section({ name, children }: { name: string; children: ReactNode }) { + return ( +
+

{name}

+
+ {children} +
+
+ ); +} +function ExportSample({ format }: { format: "mp4" | "gif" }) { + return ( + + ); +} +function AnnotationSample({ type }: { type: AnnotationType }) { + return ( + + ); +} +function Gallery() { + return ( +
+

Recordly — App component catalog

+
+ + + + +
+ + +
+
+ + + A deliberately long recording title that truncates while preserving its full + text in the tooltip + + + + + + Product demo recording + + + {[12, 72, 94].map((level) => ( + + + + ))} +
+
+ {[false, true].map((paused) => ( + +
+ "01:23"} + /> +
+
+ ))} + + + + + + + + + + + } onClick={noop}> + 3s countdown + + } onClick={noop}> + Microphone + + + + + + + + + +
+
+ + + + + + + + + +
+
+ + + + + + + + + + + + + + + +
+
+ + `${v}px`} + /> + + + + + + + + + ({ + key: String(i), + value: String(i), + previewUrl: `/wallpapers/wallpaper${i}.jpg`, + label: `Wallpaper ${i}`, + removable: i === 4, + }))} + addLabel="Add wallpaper" + onAdd={noop} + onSelect={noop} + onRemove={noop} + isSelected={(value) => value === "1"} + /> + + + + +
+
+ {(["text", "figure", "blur", "image"] as const).map((type) => ( + + + + ))} +
+
+ ); +} +function OpenLaunchPopover({ mode }: { mode: string }) { + const { requestOpen } = useLaunchPopoverCoordinator(); + useEffect(() => { + requestOpen(mode.replace("launch-", "").replace("-devices", "")); + }, [requestOpen, mode]); + const trigger = ; + return ( +
+ {mode === "launch-countdown" && ( + + )} + {(mode === "launch-mic" || mode === "launch-mic-devices") && ( + + )} + {(mode === "launch-webcam" || mode === "launch-webcam-devices") && ( + + )} + {mode === "launch-more" && ( + + )} + {mode === "launch-sources" && ( + + )} + {mode === "launch-projects" && ( + + )} +
+ ); +} +function ProjectDialogState({ mode }: { mode: string }) { + const { t } = useI18n(); + const inputRef = useRef(null); + const anchorRef = useRef(null); + return ( + undefined} + unsavedChangesDialogOpen={mode === "unsaved-changes"} + setUnsavedChangesDialogOpen={noop} + unsavedChangesDialogActionLabel="open another project" + resolveUnsavedChangesDialog={noop} + projectBrowserOpen={false} + setProjectBrowserOpen={noop} + projectLibraryEntries={entries} + projectBrowserAnchorRef={anchorRef} + handleImportMediaOrProject={async () => undefined} + handleOpenProjectFromLibrary={async () => undefined} + nativeCaptureUnavailableModalOpen={mode === "native-capture-unavailable"} + setNativeCaptureUnavailableModalOpen={noop} + /> + ); +} +function OverlayViews({ mode }: { mode: string }) { + const triggerRoot = useRef(null); + const { t } = useI18n(); + const { openConfig } = useShortcuts(); + useEffect(() => { + if (mode === "shortcuts") openConfig(); + if (mode === "toast") { + toast.success("Export complete", { + id: "catalog-export", + description: "Your recording is ready to share.", + duration: Infinity, + action: { label: "Show file", onClick: noop }, + }); + } + const timer = setTimeout(() => { + if ( + [ + "font", + "keyboard-help", + "color", + "feedback", + "keyboard-dialog", + "tutorial", + ].includes(mode) + ) + triggerRoot.current + ?.querySelector("[data-catalog-trigger] button") + ?.click(); + }, 500); + return () => clearTimeout(timer); + }, [openConfig, mode]); + if (mode.startsWith("launch-")) return ; + if (mode === "loading") return ; + return ( +
+

Recordly · {mode}

+ {mode === "dialog" && ( + + + + Save your project + + Keep your recording and edits together so you can continue later. + + + + + + + + + )} + {mode === "popover" && ( + + + + + +

Preview options

+

Adjust the recording preview.

+ +
+
+ )} + {mode === "dropdown" && ( + + + + + + + Save project⌘S + + Duplicate project + + + Delete project + + + + )} + {mode === "select" && ( + + )} + {mode === "toast" && } + {mode === "font" && ( +
+ +
+ )} + {mode === "keyboard-help" && ( +
+ +
+ )} + {mode === "color" && ( +
+ +
+ )} + {mode === "feedback" && ( +
+ +
+ )} + {mode === "keyboard-dialog" && ( +
+ +
+ )} + {mode === "tutorial" && ( +
+ +
+ )} + {[ + "project-save", + "project-saving", + "unsaved-changes", + "native-capture-unavailable", + ].includes(mode) && } + {mode === "shortcuts" && } + {mode === "crop-dialog" && ( + + )} +
+ ); +} +const overlayModes = [ + "launch-mic-devices", + "launch-webcam-devices", + "select", + "project-save", + "project-saving", + "unsaved-changes", + "native-capture-unavailable", + "feedback", + "keyboard-dialog", + "tutorial", + "dialog", + "popover", + "dropdown", + "toast", + "font", + "keyboard-help", + "color", + "shortcuts", + "crop-dialog", + "loading", + "launch-countdown", + "launch-mic", + "launch-webcam", + "launch-more", + "launch-sources", + "launch-projects", +]; + +/** Keep real portals inside their own preview canvas without duplicating React roots. */ +function ScopedState({ mode }: { mode: string }) { + const [container, setContainer] = useState(null); + return ( +
+

{mode}

+
+ {container && ( + container}> + + + + + + + )} +
+
+ ); +} +function AllStates() { + return ( +
+ {overlayModes.map((mode) => ( + + ))} +
+ ); +} +createRoot(document.getElementById("root")!).render( + + + + + + {view === "gallery" ? ( + + ) : view === "all-states" ? ( + + ) : ( + + )} + + + + + , +); diff --git a/src/design-extra-catalog.tsx b/src/design-extra-catalog.tsx new file mode 100644 index 00000000..f99d29ec --- /dev/null +++ b/src/design-extra-catalog.tsx @@ -0,0 +1,322 @@ +import { type ComponentProps, useState } from "react"; +import { createRoot } from "react-dom/client"; +import { UNSAFE_PortalProvider } from "react-aria"; +import "./index.css"; +import { ThemeProvider } from "./contexts/ThemeContext"; +import { I18nProvider, useI18n } from "./contexts/I18nContext"; +import { AnnouncementDialog } from "./components/announcements/AnnouncementDialog"; +import { EditorAnnouncementBanner } from "./components/announcements/EditorAnnouncementBanner"; +import { LiveAnnouncementNotifications } from "./components/announcements/LiveAnnouncementNotifications"; +import { Toaster } from "./components/ui/toast"; +import { EditorExportMenu } from "./components/video-editor/layout/EditorExportMenu"; +import { EditorPresetMenu } from "./components/video-editor/layout/EditorPresetMenu"; +import { useExportSettings } from "./components/video-editor/export/useExportSettings"; +import { useExportSession } from "./components/video-editor/export/useExportSession"; +import { useExportStatusViewModel } from "./components/video-editor/export/useExportStatusViewModel"; +import { + DEFAULT_EDITOR_PREFERENCES, + type EditorPresetSnapshot, +} from "./components/video-editor/editorPreferences"; +import { + DEFAULT_AUTO_CAPTION_SETTINGS, + DEFAULT_CROP_REGION, +} from "./components/video-editor/types"; +import type { ExportProgress } from "./lib/exporter"; +import type { AnnouncementFeed } from "./lib/announcements"; + +const noop = () => undefined; +const query = new URLSearchParams(location.search); +const view = query.get("view"); +const memory = new Map(); +const feed: AnnouncementFeed = { + settings: { aspectRatio: "4:3" }, + announcements: [ + { + id: "catalog-popup", + title: "A clearer way to tell your story", + body: "Explore the redesigned editor, with precise timeline cuts and a simpler workspace.", + presentation: "popup", + audience: "editor", + priority: 20, + mediaMode: view === "announcement-cover" ? "cover" : "banner", + media: { type: "image", url: "/wallpapers/bluerays.jpeg", alt: "Blue rays" }, + action: { label: "Explore the editor", section: "scene" }, + }, + { + id: "catalog-popup-next", + title: "Create with confidence", + body: "Keep your favorite settings together in a reusable preset.", + presentation: "popup", + audience: "editor", + priority: 10, + action: { label: "Open settings", section: "settings" }, + }, + { + id: "catalog-banner", + title: "A new Recordly update is available", + body: "See what has changed in the latest release.", + presentation: "banner", + audience: "editor", + priority: 20, + action: { label: "Learn more", url: "https://recordly.dev" }, + }, + { + id: "catalog-notification", + title: "Your workspace is ready", + body: "Browse the refreshed controls and save your favorite setup as a preset.", + presentation: "notification", + audience: "editor", + priority: 20, + displayDurationSeconds: 3600, + action: { label: "View settings", section: "settings" }, + }, + ], +}; +Object.assign(window, { + electronAPI: { + getAppSetting: (key: string) => + key === "recordly.theme" + ? query.get("theme") === "dark" + ? "dark" + : "light" + : (memory.get(key) ?? null), + setAppSetting: (key: string, value: unknown) => { + memory.set(key, value); + return true; + }, + getPlatform: async () => "darwin", + getAppVersion: async () => "1.4.0", + getAnnouncements: async () => feed, + openExternal: async () => ({ success: true }), + }, +}); +const modes = [ + "export-settings-mp4", + "export-settings-gif", + "export-preparing", + "export-rendering", + "export-audio", + "export-finalizing", + "export-muxing", + "export-saving", + "export-success", + "export-error", + "export-pending-save", + "export-legacy", + "presets-empty", + "presets-saved", + "announcement-popup", + "announcement-banner", + "announcement-notification", +]; + +function ExportState({ mode }: { mode: string }) { + const { t } = useI18n(); + const baseSettings = useExportSettings(DEFAULT_EDITOR_PREFERENCES, [], []); + const baseSession = useExportSession(); + const exportSettings = { + ...baseSettings, + exportFormat: mode === "export-settings-gif" ? ("gif" as const) : ("mp4" as const), + exportPipelineModel: mode === "export-legacy" ? ("legacy" as const) : ("modern" as const), + }; + const exporting = [ + "export-preparing", + "export-rendering", + "export-audio", + "export-finalizing", + "export-muxing", + "export-saving", + "export-legacy", + ].includes(mode); + const progress: ExportProgress = { + currentFrame: 84, + totalFrames: 180, + percentage: 47, + estimatedTimeRemaining: 3, + renderFps: 96.2, + renderBackend: "webgpu", + encodeBackend: "webcodecs", + ...(mode === "export-preparing" ? { phase: "preparing" as const } : {}), + ...(mode === "export-audio" ? { phase: "finalizing" as const, audioProgress: 0.62 } : {}), + ...(mode === "export-finalizing" + ? { phase: "finalizing" as const, renderProgress: 76 } + : {}), + ...(mode === "export-muxing" ? { phase: "finalizing" as const, renderProgress: 100 } : {}), + ...(mode === "export-saving" ? { phase: "saving" as const } : {}), + }; + const exportSession = { + ...baseSession, + showExportDropdown: true, + isExporting: exporting, + exportProgress: exporting ? progress : null, + exportError: + mode === "export-error" + ? "The video encoder could not finish this export. Try a different export mode." + : mode === "export-pending-save" + ? "Your video was rendered, but the destination could not be saved." + : null, + hasPendingExportSave: mode === "export-pending-save", + exportedFilePath: mode === "export-success" ? "/Movies/Product walkthrough.mp4" : undefined, + }; + const exportStatus = useExportStatusViewModel({ + t, + settings: exportSettings, + session: exportSession, + }); + const exportDimensions = { + gifOutputDimensions: { width: 960, height: 540 }, + mp4OutputDimensions: { + medium: { width: 1280, height: 720 }, + good: { width: 1920, height: 1080 }, + high: { width: 2560, height: 1440 }, + source: { width: 1920, height: 1080 }, + }, + supportedMp4SourceDimensions: { + width: 1920, + height: 1080, + capped: false, + encoderPath: null, + }, + ensureSupportedMp4SourceDimensions: async () => ({ + width: 1920, + height: 1080, + capped: false, + encoderPath: null, + }), + } satisfies ComponentProps["exportDimensions"]; + return ( + undefined} + onRequestShareSignIn={() => {}} + shareRequestNonce={0} + t={t} + exportSettings={exportSettings} + exportSession={exportSession} + exportDimensions={exportDimensions} + exportStatus={exportStatus} + hasCaptionsForSidecar + nvidiaCudaExportAvailable + experimentalNvidiaCudaExport={false} + setExperimentalNvidiaCudaExport={noop} + handleOpenExportDropdown={noop} + handleExportDropdownClose={noop} + handleCancelExport={noop} + handleRetrySaveExport={noop} + handleStartExportFromDropdown={noop} + revealExportedFile={noop} + exportMessage={null} + /> + ); +} +function PresetState({ empty }: { empty: boolean }) { + const { t } = useI18n(); + const snapshot: EditorPresetSnapshot = { + ...DEFAULT_EDITOR_PREFERENCES, + cropRegion: DEFAULT_CROP_REGION, + autoCaptionSettings: DEFAULT_AUTO_CAPTION_SETTINGS, + }; + const presets = [ + { + id: "clean", + name: "Clean product demo", + createdAt: "2026-09-19T00:00:00Z", + updatedAt: "2026-09-19T00:00:00Z", + snapshot, + }, + { + id: "social", + name: "Social portrait", + createdAt: "2026-09-19T00:00:00Z", + updatedAt: "2026-09-19T00:00:00Z", + snapshot, + }, + ]; + return ( + + ); +} +function State({ mode }: { mode: string }) { + if (mode.startsWith("export-")) return ; + if (mode.startsWith("presets-")) return ; + if (mode === "announcement-popup" || mode === "announcement-cover") + return ; + if (mode === "announcement-banner") + return ( +
+ +
+ ); + if (mode === "announcement-notification") + return ( + <> + + + + ); + return null; +} +function ScopedState({ mode }: { mode: string }) { + const [container, setContainer] = useState(null); + return ( +
+

{mode}

+
+ {container && ( + container}> +
+ +
+
+ )} +
+
+ ); +} +function Catalog() { + return ( +
+ {(view ? [view] : modes).map((mode) => ( + + ))} +
+ ); +} +createRoot(document.getElementById("root")!).render( + + + + + , +); diff --git a/src/design-hud-branches.tsx b/src/design-hud-branches.tsx new file mode 100644 index 00000000..e35ef99e --- /dev/null +++ b/src/design-hud-branches.tsx @@ -0,0 +1,190 @@ +/** + * Capture-only fixtures for otherwise hook-local LaunchWindow branches. + * Source JSX copied verbatim from LaunchWindow.tsx: RecordingControls binding, + * finalizingControls and complete returned HUD shell, including floating webcam. + * Imports the original LaunchWindow.module.css and original RecordingControls. + * Only recorder state, handlers and camera stream are replaced with inert fixtures. + */ +import { useRef } from "react"; +import { createRoot } from "react-dom/client"; +import { AnimatePresence, motion } from "motion/react"; +import { ArrowClockwiseIcon } from "@phosphor-icons/react"; +import { RxDragHandleDots2 } from "react-icons/rx"; +import { I18nProvider, useScopedT } from "./contexts/I18nContext"; +import { ThemeProvider } from "./contexts/ThemeContext"; +import { HudInteractionContext } from "./components/launch/contexts/HudInteractionContext"; +import { RecordingControls } from "./components/launch/RecordingControls"; +import styles from "./components/launch/LaunchWindow.module.css"; +import "./index.css"; +import "./components/launch/launchTheme.css"; + +const noop = () => undefined; +function attachFixtureVideo(node: HTMLVideoElement | null) { + if (!node) return; + node.addEventListener("loadeddata", () => { node.currentTime = 2; }, { once: true }); + node.src = "/tests/ui/fixtures/filmstrip.mp4"; +} +function HudBranch({ finalizing }: { finalizing: boolean }) { + const t = useScopedT("launch"); + const recording = !finalizing; + const paused = false; + const microphoneEnabled = true; + const elapsed = 83; + const formatTime = () => "01:23"; + const setMicrophoneEnabled = (_enabled: boolean) => undefined; + const resumeRecording = noop, pauseRecording = noop, toggleRecording = noop, cancelRecording = noop; + const hudContentRef = useRef(null); + const hudBarTransformRef = useRef(null); + const hudBarRef = useRef(null); + const recordingWebcamPreviewContainerRef = useRef(null); + const setRecordingWebcamPreviewNode = attachFixtureVideo; + const recordingHudOffset = {x:0,y:0}, webcamPreviewOffset = {x:0,y:0}; + const showRecordingWebcamPreview = !finalizing; + const useNativeHudBarDrag = false, shouldAnimateHudLayout = false; + const handleHudMouseEnter = noop, handleHudMouseLeave = noop; + const handleHudBarPointerDown = noop, handleHudBarPointerMove = noop, handleHudBarPointerUp = noop; + const handleWebcamPreviewPointerDown = noop, handleWebcamPreviewPointerMove = noop, handleWebcamPreviewPointerUp = noop; + const hudStateTransition = {duration:0.24,ease:[0.22,1,0.36,1] as const}; + const hudMode = finalizing ? "finalizing" : "recording"; + const idleControls = null; + const recordingControls = ( + setMicrophoneEnabled(!microphoneEnabled)} + onPauseResume={paused ? resumeRecording : pauseRecording} + onStopRecording={toggleRecording} + onHideHud={() => window.electronAPI?.hudOverlayHide?.()} + onCancelRecording={cancelRecording} + formatTime={formatTime} + /> + ); + const finalizingControls = ( +
+ +
+ {t("recording.preparing", "Preparing recording")} + {t("recording.preparingSubtitle", "Opening the editor in a moment")} +
+
+ ); + + return ( + +
+
+
+
+ +
+ +
+ +
+ + + {finalizing + ? finalizingControls + : recording + ? recordingControls + : idleControls} + + +
+
+
+ {showRecordingWebcamPreview && ( +
+
+ )} +
+
+
+
+ ); +} +function HudBranchGallery() { + return
+ {[{name:"HUD / Finalizing recording",finalizing:true},{name:"HUD / Floating webcam preview",finalizing:false}].map(state =>
+

{state.name}

+
+
)} +
; +} +createRoot(document.getElementById("root")!).render(); diff --git a/src/design-inspector-catalog.tsx b/src/design-inspector-catalog.tsx new file mode 100644 index 00000000..3283a54c --- /dev/null +++ b/src/design-inspector-catalog.tsx @@ -0,0 +1,290 @@ +import { type ComponentProps } from "react"; +import { createRoot } from "react-dom/client"; +import "./index.css"; +import { SettingsPanel } from "./components/video-editor/SettingsPanel"; +import { + DEFAULT_AUTO_CAPTION_SETTINGS, + DEFAULT_WEBCAM_OVERLAY, + type EditorEffectSection, +} from "./components/video-editor/types"; +import { I18nProvider } from "./contexts/I18nContext"; +import { ShortcutsProvider } from "./contexts/ShortcutsContext"; +import { ThemeProvider } from "./contexts/ThemeContext"; + +type PanelProps = ComponentProps; +const noop = () => undefined; +const base: PanelProps = { + selected: "#2563eb", + onWallpaperChange: noop, + aspectRatio: "16:9", + showCursor: true, + selectedZoomDepth: 3, + selectedZoomMode: "auto", + selectedClipSpeed: 1, + selectedClipMuted: false, + selectedAudioVolume: 0.8, + selectedAudioNormalize: true, + webcam: { + ...DEFAULT_WEBCAM_OVERLAY, + enabled: true, + sourcePath: "/tests/ui/fixtures/filmstrip.mp4", + }, + webcamPreviewSrc: "/tests/ui/fixtures/filmstrip.mp4", + cropRegion: { x: 0, y: 0, width: 1, height: 1 }, + autoCaptionSettings: { ...DEFAULT_AUTO_CAPTION_SETTINGS, enabled: true }, + autoCaptions: [ + { id: "caption-1", startMs: 0, endMs: 2800, text: "Create something worth sharing." }, + { id: "caption-2", startMs: 2800, endMs: 6000, text: "Make every moment clear." }, + ], + captionCurrentTimeMs: 1000, +}; + +const sections: EditorEffectSection[] = [ + "scene", + "frame", + "crop", + "cursor", + "captions", + "caption", + "webcam", + "settings", + "zoom", + "clip", + "audio", + "extensions", +]; +const variants: { name: string; props: Partial }[] = sections.flatMap((section) => + [false, true].map((advanced) => ({ + name: `${section} / ${advanced ? "Advanced" : "Basic"}`, + props: { + activeEffectSection: section, + advanced, + selectedZoomId: section === "zoom" ? "zoom-1" : null, + selectedClipId: section === "clip" ? "clip-1" : null, + selectedAudioId: section === "audio" ? "audio-1" : null, + selectedCaptionId: section === "caption" ? "caption-1" : null, + }, + })), +); +variants.push( + { + name: "Background / Image", + props: { panelMode: "background", selected: "/wallpapers/wallpaper1.jpg" }, + }, + { + name: "Background / Video", + props: { panelMode: "background", selected: "/tests/ui/fixtures/filmstrip.mp4" }, + }, + { name: "Background / Color", props: { panelMode: "background", selected: "#2563eb" } }, + { + name: "Background / Gradient", + props: { panelMode: "background", selected: "linear-gradient(315deg, #EC0101, #5044A9)" }, + }, + { + name: "Zoom / Manual", + props: { + activeEffectSection: "zoom", + advanced: true, + selectedZoomId: "zoom-1", + selectedZoomMode: "manual", + }, + }, + { + name: "Clip / Muted and speed override", + props: { + activeEffectSection: "clip", + advanced: true, + selectedClipId: "clip-1", + selectedClipSpeed: 2, + selectedClipMuted: true, + hasClipAudioOverrides: true, + }, + }, + { + name: "Captions / Model unavailable", + props: { + activeEffectSection: "captions", + autoCaptions: [], + autoCaptionSettings: DEFAULT_AUTO_CAPTION_SETTINGS, + }, + }, + { + name: "Captions / Downloading model", + props: { + activeEffectSection: "captions", + whisperModelDownloadStatus: "downloading", + whisperModelDownloadProgress: 42, + }, + }, + { + name: "Captions / Model ready", + props: { + activeEffectSection: "captions", + whisperModelPath: "/models/ggml-small.bin", + whisperModelDownloadStatus: "downloaded", + }, + }, + { + name: "Captions / Generating", + props: { + activeEffectSection: "captions", + whisperModelPath: "/models/ggml-small.bin", + isGeneratingCaptions: true, + }, + }, + { + name: "Captions / Download error", + props: { activeEffectSection: "captions", whisperModelDownloadStatus: "error" }, + }, + { + name: "Webcam / Empty", + props: { + activeEffectSection: "webcam", + webcam: DEFAULT_WEBCAM_OVERLAY, + webcamPreviewSrc: null, + }, + }, + { name: "Cursor / Hidden", props: { activeEffectSection: "cursor", showCursor: false } }, + { name: "Zoom / No selection", props: { activeEffectSection: "zoom", selectedZoomId: null } }, + { name: "Clip / No selection", props: { activeEffectSection: "clip", selectedClipId: null } }, + { + name: "Audio / No selection", + props: { activeEffectSection: "audio", selectedAudioId: null }, + }, +); + +function Catalog() { + return ( +
+

+ Inspectors and background panels +

+

+ Actual SettingsPanel components, including basic, advanced, empty and loading + states. +

+
+ {variants.map(({ name, props }) => ( +
+

+ {name} +

+
+ +
+
+ ))} +
+
+ ); +} + +// Keep every optional source control visible without mutating an editor project. +const callbacks = { + onWallpaperChange: noop, + onZoomDepthChange: noop, + onZoomModeChange: noop, + onZoomDelete: noop, + onResetClipAudio: noop, + onClipSpeedChange: noop, + onClipMutedChange: noop, + onClipDelete: noop, + onAudioVolumeChange: noop, + onAudioNormalizeChange: noop, + onAudioDelete: noop, + onShadowChange: noop, + onBackgroundBlurChange: noop, + onConnectZoomsChange: noop, + onAutoApplyFreshRecordingAutoZoomsChange: noop, + onZoomInDurationMsChange: noop, + onZoomInOverlapMsChange: noop, + onZoomOutDurationMsChange: noop, + onConnectedZoomGapMsChange: noop, + onConnectedZoomDurationMsChange: noop, + onZoomInEasingChange: noop, + onZoomOutEasingChange: noop, + onConnectedZoomEasingChange: noop, + onShowCursorChange: noop, + onLoopCursorChange: noop, + onCursorStyleChange: noop, + onCursorSizeChange: noop, + onCursorSmoothingChange: noop, + onCursorSpringStiffnessMultiplierChange: noop, + onCursorSpringDampingMultiplierChange: noop, + onCursorSpringMassMultiplierChange: noop, + onCameraSpringStiffnessMultiplierChange: noop, + onCameraSpringDampingMultiplierChange: noop, + onCameraSpringMassMultiplierChange: noop, + onZoomClassicModeChange: noop, + onCursorClickEffectChange: noop, + onCursorClickEffectColorChange: noop, + onCursorClickEffectScaleChange: noop, + onCursorClickEffectOpacityChange: noop, + onCursorClickEffectDurationMsChange: noop, + onCursorClickBounceChange: noop, + onCursorClickBounceDurationChange: noop, + onCursorSwayChange: noop, + onBorderRadiusChange: noop, + onWebcamChange: noop, + onUploadWebcam: noop, + onClearWebcam: noop, + onPaddingChange: noop, + onCropChange: noop, + onAspectRatioChange: noop, + onAnnotationContentChange: noop, + onAnnotationTypeChange: noop, + onAnnotationStyleChange: noop, + onAnnotationFigureDataChange: noop, + onAnnotationBlurIntensityChange: noop, + onAnnotationBlurColorChange: noop, + onAnnotationDelete: noop, + onAutoCaptionSettingsChange: noop, + onPickWhisperExecutable: noop, + onPickWhisperModel: noop, + onGenerateAutoCaptions: noop, + onClearAutoCaptions: noop, + onDownloadWhisperSmallModel: noop, + onDeleteWhisperSmallModel: noop, + onBeginCaptionEdit: noop, + onCaptionTextEdit: noop, + onCaptionRetime: noop, + onCaptionSplit: noop, + onCaptionMerge: noop, + onCaptionDelete: noop, + onOpenNativeCaptureUnavailableModal: noop, +}; + +createRoot(document.getElementById("root")!).render( + + + + + + + , +); diff --git a/src/design-library.tsx b/src/design-library.tsx new file mode 100644 index 00000000..05b9dd22 --- /dev/null +++ b/src/design-library.tsx @@ -0,0 +1,34 @@ +import React from 'react'; +import {createRoot} from 'react-dom/client'; +import './index.css'; +import {Button} from './components/ui/button'; +import {Input,TextArea} from './components/ui/input'; +import {Switch} from './components/ui/switch'; +import {Slider} from './components/ui/slider'; +import {Toggle} from './components/ui/toggle'; +import {ToggleGroup,ToggleGroupItem} from './components/ui/toggle-group'; +import {ChoiceGroup,ChoiceItem} from './components/ui/choice-group'; +import {Tabs,TabsList,TabsTrigger,TabsContent} from './components/ui/tabs'; +import {Select,SelectTrigger,SelectValue,SelectContent,SelectItem} from './components/ui/select'; +import {Card,CardHeader,CardTitle,CardDescription,CardContent,CardFooter} from './components/ui/card'; +import {Accordion,AccordionItem,AccordionTrigger,AccordionContent} from './components/ui/accordion'; +import {Label} from './components/ui/label'; +import {Separator} from './components/ui/separator'; +import {Skeleton} from './components/ui/skeleton'; +import {Plus,Trash,Play,Pause,Scissors,SpeakerHigh as Volume2,ArrowCounterClockwise as Undo2,ArrowClockwise as Redo2,CaretDown as ChevronDown,SlidersHorizontal as Settings2,Cursor as MousePointer,TextT as Type,Camera,Image,Download} from '@phosphor-icons/react'; +const Sample=({name,children}:{name:string,children:React.ReactNode})=>

{name}

{children}
; +const Section=({name,children}:{name:string,children:React.ReactNode})=>

{name}

{children}
; +function Library(){return

Recordly — Source components

+
{(['default','secondary','outline','ghost','destructive','destructive-soft','link'] as const).map(variant=>
{(['sm','default','lg'] as const).map(size=>)}
)}
+
{[false,true].map(disabled=>)}