Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
dcdc297
feat(shortcuts): make transport bindings canonical
lightninglu10 Aug 27, 2026
fe2bdef
fix(host): guard shortcut settings ownership epochs
lightninglu10 Aug 27, 2026
7ebce1a
fix: make shortcut labels reactive in editor menus
lightninglu10 Aug 27, 2026
172f11c
fix: resolve duplicate shortcut bindings
lightninglu10 Aug 27, 2026
830fd03
fix(shortcuts): guard dialog control key events
lightninglu10 Aug 27, 2026
850e390
fix(shortcuts): align reactive authority and guards
lightninglu10 Aug 27, 2026
ebcad18
fix(host): cancel deferred shortcut ownership
lightninglu10 Aug 27, 2026
d7a400c
test(shortcuts): enforce unique imported bindings
lightninglu10 Aug 27, 2026
d514f19
refactor(shortcuts): keep conflict resolution bounded
lightninglu10 Aug 27, 2026
541085c
fix(host): surface shortcut conflict notices
lightninglu10 Aug 27, 2026
0f5602d
fix(shortcuts): reconcile runtime ownership
lightninglu10 Aug 27, 2026
0e71126
fix(shortcuts): retry host writes and alias collisions
lightninglu10 Aug 27, 2026
93d57a2
fix global runtime hotkey ownership
lightninglu10 Aug 27, 2026
8388082
Fix atomic runtime hotkey ownership
lightninglu10 Aug 27, 2026
dc40a83
fix hotkey registration gates
lightninglu10 Aug 27, 2026
026c62d
fix hotkey proxy and import boundary
lightninglu10 Aug 27, 2026
0eda4ba
fix(qa): resolve hotkey imports lexically
lightninglu10 Aug 27, 2026
3a69802
fix(qa): match Rolldown lexical import folding
lightninglu10 Aug 27, 2026
62e6c1d
fix(shortcuts): repair runtime hotkey boundary parity
lightninglu10 Aug 27, 2026
25f751d
fix(qa): track mutable and erased runtime bindings
lightninglu10 Aug 27, 2026
3726994
fix(hotkeys): match Rolldown control-flow folding
lightninglu10 Aug 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions packages/freecut-editor/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ provider details, URLs, paths, and media bytes remain host-owned.
The same 0.3.0 surface retains the host-backed caption tracks, bounded cues,
caption styles, and display toggles from 0.2.0.

Hosts can also provide the optional `EditorHost.shortcuts` port. Its versioned
`HostShortcutSettings` payload carries the same override map used by FreeCut's
shortcut editor, including J/K/L transport. UI changes call `setSettings`, and
host or agent changes can flow back through `subscribe`, so embedded shortcut
configuration never becomes a UI-only setting.

This package is built from a specific FreeCut commit. To create the local
consumer artifact from a clean checkout, run:

Expand Down
18 changes: 18 additions & 0 deletions packages/freecut-editor/consumer-smoke.test.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
// @vitest-environment jsdom
/// <reference path="./consumer-smoke-style.d.ts" />

import '@testing-library/jest-dom'
import '@quantfive/freecut-editor-surface/style.css'
import { render, screen, waitFor } from '@testing-library/react'
import { beforeAll, describe, expect, it, vi } from 'vite-plus/test'
import {
FreeCutEditorSurface,
HOTKEYS,
capabilityForCommand,
createHostShortcutSettings,
isHostCapabilityEnabled,
type EditorHost,
type EmbeddedEditorSnapshot,
Expand Down Expand Up @@ -44,6 +47,15 @@ function fakeHost(): EditorHost {
submitEdit: vi.fn(() => {
throw new Error('consumer smoke does not submit an edit')
}),
shortcuts: {
getSettings: () =>
createHostShortcutSettings({
SHUTTLE_REVERSE: 'q',
SHUTTLE_PAUSE: 'w',
SHUTTLE_FORWARD: 'e',
}),
setSettings: vi.fn(),
},
}
}

Expand Down Expand Up @@ -89,6 +101,12 @@ describe('published FreeCut browser entry', () => {

expect(screen.getByTestId('properties-clip-panel-host')).toBeInTheDocument()
expect(await screen.findByTestId('caption-editor')).toBeInTheDocument()
expect(HOTKEYS).toMatchObject({
SHUTTLE_REVERSE: 'j',
SHUTTLE_PAUSE: 'k',
SHUTTLE_FORWARD: 'l',
EDIT_KEYFRAME_ADD: 'shift+k',
})
expect(host.load).toHaveBeenCalledTimes(1)
expect(capabilityForCommand('move_item')).toBe('timeline.move')
expect(capabilityForCommand('set_caption_style')).toBe('timeline.caption')
Expand Down
97 changes: 97 additions & 0 deletions packages/freecut-editor/src/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,82 @@
import type { ComponentType, ReactNode } from 'react'

export type HotkeyKey =
| 'PLAY_PAUSE'
| 'SHUTTLE_REVERSE'
| 'SHUTTLE_PAUSE'
| 'SHUTTLE_FORWARD'
| 'PREVIOUS_FRAME'
| 'NEXT_FRAME'
| 'GO_TO_START'
| 'GO_TO_END'
| 'NEXT_SNAP_POINT'
| 'PREVIOUS_SNAP_POINT'
| 'SPLIT_AT_PLAYHEAD'
| 'SPLIT_AT_PLAYHEAD_ALT'
| 'JOIN_ITEMS'
| 'DELETE_SELECTED'
| 'DELETE_SELECTED_ALT'
| 'RIPPLE_DELETE'
| 'RIPPLE_DELETE_ALT'
| 'FREEZE_FRAME'
| 'LINK_AUDIO_VIDEO'
| 'UNLINK_AUDIO_VIDEO'
| 'TOGGLE_LINKED_SELECTION'
| 'NUDGE_LEFT'
| 'NUDGE_RIGHT'
| 'NUDGE_UP'
| 'NUDGE_DOWN'
| 'NUDGE_LEFT_LARGE'
| 'NUDGE_RIGHT_LARGE'
| 'NUDGE_UP_LARGE'
| 'NUDGE_DOWN_LARGE'
| 'UNDO'
| 'REDO'
| 'ZOOM_IN'
| 'ZOOM_OUT'
| 'ZOOM_TO_FIT'
| 'ZOOM_TO_100'
| 'ZOOM_TO_100_ALT'
| 'COPY'
| 'CUT'
| 'PASTE'
| 'SELECTION_TOOL'
| 'TRIM_EDIT_TOOL'
| 'RAZOR_TOOL'
| 'RATE_STRETCH_TOOL'
| 'SLIP_TOOL'
| 'SLIDE_TOOL'
| 'SAVE'
| 'EXPORT'
| 'TOGGLE_SNAP'
| 'TOGGLE_CANVAS_SNAP'
| 'OPEN_SCENE_BROWSER'
| 'WORKSPACE_EDIT'
| 'WORKSPACE_COLOR'
| 'WORKSPACE_ANIMATE'
| 'ADD_MARKER'
| 'REMOVE_MARKER'
| 'PREVIOUS_MARKER'
| 'NEXT_MARKER'
| 'CLEAR_KEYFRAMES'
| 'KEYFRAME_EDITOR_GRAPH'
| 'KEYFRAME_EDITOR_DOPESHEET'
| 'KEYFRAME_EDITOR_SPLIT'
| 'EDIT_KEYFRAME_ADD'
| 'KEYFRAME_PREVIOUS'
| 'KEYFRAME_NEXT'
| 'KEYFRAME_TOGGLE_AUTO'
| 'KEYFRAME_FIT'
| 'MARK_IN'
| 'MARK_OUT'
| 'CLEAR_IN_OUT'
| 'INSERT_EDIT'
| 'OVERWRITE_EDIT'

export type HotkeyOverrideMap = Partial<Record<HotkeyKey, string>>

export declare const HOTKEYS: Readonly<Record<HotkeyKey, string>>

export type EditorCapability =
| 'project.navigate'
| 'project.save'
Expand Down Expand Up @@ -333,13 +410,33 @@ export interface EditorHostNavigation {
back(): void
}

export declare const HOST_SHORTCUTS_SCHEMA: 'freecut-host-shortcuts'
export declare const HOST_SHORTCUTS_VERSION: 1

export interface HostShortcutSettings {
schema: typeof HOST_SHORTCUTS_SCHEMA
version: typeof HOST_SHORTCUTS_VERSION
overrides: HotkeyOverrideMap
}

export interface EditorShortcutPort {
getSettings(): Promise<HostShortcutSettings> | HostShortcutSettings
setSettings(settings: HostShortcutSettings): Promise<void> | void
subscribe?(listener: (settings: HostShortcutSettings) => void): () => void
}

export declare function createHostShortcutSettings(
overrides?: HotkeyOverrideMap,
): HostShortcutSettings

export interface EditorHost {
readonly capabilities: EditorCapabilityMap
load(): Promise<EmbeddedEditorSnapshot> | EmbeddedEditorSnapshot
resolveMedia(
locator: MediaLocator,
): Promise<ResolvedMediaLocator | null> | ResolvedMediaLocator | null
submitEdit(batch: EditCommandBatch): Promise<HostEditResult> | HostEditResult
shortcuts?: EditorShortcutPort
transcript?: EditorTranscriptPort
navigation?: EditorHostNavigation
notify?(notice: HostNotice): void
Expand Down
7 changes: 7 additions & 0 deletions packages/freecut-editor/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
export { FreeCutEditorSurface } from '@/features/editor/host/editor-surface'
export { EditorHostProvider } from '@/features/editor/host/context-provider'
export { HOTKEYS } from '@/config/hotkeys'
export type { HotkeyKey, HotkeyOverrideMap } from '@/config/hotkeys'
export {
DEFAULT_HOST_CAPABILITIES,
HOST_SHORTCUTS_SCHEMA,
HOST_SHORTCUTS_VERSION,
MAX_TRANSCRIPT_COMMAND_TEXT_BYTES,
MAX_TRANSCRIPT_CURSOR_LENGTH,
MAX_TRANSCRIPT_DURATION_US,
Expand All @@ -11,6 +15,7 @@ export {
MAX_TRANSCRIPT_SELECTIONS,
SUPPORTED_HOST_COMMANDS,
capabilityForCommand,
createHostShortcutSettings,
createLocalEditorHost,
isHostCapabilityEnabled,
} from '@/features/editor/host/contract'
Expand All @@ -21,6 +26,7 @@ export type {
EditorCapabilityMap,
EditorHost,
EditorHostNavigation,
EditorShortcutPort,
EmbeddedEditorAsset,
EmbeddedEditorProject,
EmbeddedEditorSnapshot,
Expand All @@ -30,6 +36,7 @@ export type {
HostEditResult,
HostMediaKind,
HostNotice,
HostShortcutSettings,
HostTranscriptCommandAction,
HostTranscriptCommandPreview,
HostTranscriptCommandPreviewRequest,
Expand Down
19 changes: 19 additions & 0 deletions scripts/runtime-hotkey-import-boundary.d.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export interface RuntimeHotkeyBoundarySource {
path: string
source: string
}

export interface RuntimeHotkeyImportViolation {
path: string
line: number
column: number
allowedPath: string
message: string
}

export declare const RUNTIME_HOTKEY_ADAPTER_PATH: 'src/hooks/use-hotkey-registration.ts'

export declare function findReactHotkeysHookImportViolations(
sources: RuntimeHotkeyBoundarySource[],
allowedPath?: string,
): RuntimeHotkeyImportViolation[]
Loading
Loading