Add a top-level publish config in electron-builder.json5 pointing to
GitHub Releases. This embeds the update information URL in the AppImage
header, enabling tools like AppImageUpdate, AppImageLauncher, and
AppManager to perform delta updates instead of full re-downloads.
Also update the Linux build workflow to upload the generated .zsync file
alongside the .AppImage artifact.
Fixes#219
Expand the arrow key guard to also skip elements with
role="separator" (PanelResizeHandle), role="slider", and
role="spinbutton" so keyboard panel resizing is not intercepted.
- Read currentTime directly from the video element instead of the React
ref so rapid arrow key presses each advance by exactly one frame
- Add JSDoc docstrings to frameStep.ts exports
- Add HTMLSelectElement and contentEditable to the arrow key input guard
to prevent intercepting native keyboard behavior on form controls
- Add i18nKey field to FixedShortcut interface and wire up i18n lookups
in ShortcutsConfigDialog and KeyboardShortcutsHelp so fixed shortcut
labels are properly localized
Contains the zoom region configuration used in the PR demo video:
two auto-follow zoom regions and one manual zoom region.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- LaunchWindow: render sr-only <select> when webcamExpanded but
cameraDevices.length === 0 (loading/error/empty), so keyboard users
always have a focusable element even in no-camera states
- useCameraDevices.test: add error-branch test asserting error message,
empty devices array and isLoading=false when enumerateDevices rejects
- useCameraDevices: remove getUserMedia label probe to avoid conflict with
useScreenRecorder acquiring the real stream; use enumerateDevices only and
fall back to 'Camera <id>' for unlabeled devices; gate effect on enabled flag
- LaunchWindow: fix selectedCameraLabel to reflect loading/error/empty states
in the collapsed view (was always showing 'Default Camera')
- LaunchWindow: unify webcam <select> to a single always-mounted element
(sr-only when unavailable); mirrors the mic selector pattern
- useCameraDevices.test.ts: re-seed mockGetUserMedia in beforeEach after
vi.resetAllMocks(); update permission test to assert fallback label behavior
- LaunchWindow: expose isLoading/error from useCameraDevices; show
'Searching...' only while enumeration is in flight, 'Camera unavailable'
on error, 'No camera found' when list is empty (fixes perpetual loading state)
- LaunchWindow: keep <select> always mounted (sr-only when collapsed) and
expand panel on focus as well as hover; fixes keyboard inaccessibility for
both mic and webcam selectors
- i18n: add webcam.noneFound and webcam.unavailable to en/es/zh-CN locales
- useCameraDevices: remove selectedDeviceId from useEffect deps (use ref instead)
- useCameraDevices: fall back to first available device when selected device is unplugged
- i18n: add missing keys (audio.defaultMicrophone, webcam.defaultCamera, webcam.searching) to en/es/zh-CN
- LaunchWindow: replace hardcoded strings with t() i18n calls
- tests: add afterEach(vi.resetAllMocks()), improve permission test assertions, add stale device fallback test