@elah/editor 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +127 -169
  2. package/package.json +31 -3
package/README.md CHANGED
@@ -1,169 +1,127 @@
1
- # @elah/editor
2
-
3
- Engine-first video timeline SDK for React. Internally layered as `core/` `timeline/` `editor/`.
4
-
5
- See the repo root [README](../../README.md) for project overview and the [core architecture reference](src/core/Architecture.md) for a cold-start guide to the engine.
6
-
7
- ## Install
8
-
9
- This package is part of the monorepo workspace:
10
-
11
- ```bash
12
- npm install
13
- ```
14
-
15
- Peer dependencies: `react`, `react-dom` (>= 18).
16
-
17
- ## Quick start
18
-
19
- ```tsx
20
- import { EditorProvider, Timeline } from '@elah/editor'
21
-
22
- function App() {
23
- return (
24
- <EditorProvider fps={30}>
25
- <Timeline style={{ height: 300 }} />
26
- </EditorProvider>
27
- )
28
- }
29
- ```
30
-
31
- ## Import media files
32
-
33
- Register local files into the media library from a file input or drop handler:
34
-
35
- ```ts
36
- import { importFiles, useMediaLibraryStore } from '@elah/editor'
37
-
38
- async function onFilesSelected(files: FileList | File[]) {
39
- const list = Array.from(files)
40
- const assets = await importFiles(list)
41
-
42
- // Assets are in the store immediately; thumbnails arrive shortly after.
43
- console.log(useMediaLibraryStore.getState().assets)
44
-
45
- // Subscribe in React via useMediaLibrary() for UI updates.
46
- return assets
47
- }
48
- ```
49
-
50
- `importFiles`:
51
-
52
- - Creates object URLs and probes duration/dimensions via DOM media elements
53
- - Skips unsupported MIME types with a console warning
54
- - Registers assets in `useMediaLibraryStore` synchronously
55
- - Generates JPEG thumbnails on the main thread and patches `thumbnailUrl` asynchronously
56
-
57
- ## AssetPanel
58
-
59
- Browse, drop, and drag media assets from a sidebar panel. Render as a sibling of `<Timeline>` inside `<EditorProvider>`:
60
-
61
- ```tsx
62
- import { EditorProvider, Timeline, AssetPanel } from '@elah/editor'
63
-
64
- function App() {
65
- return (
66
- <EditorProvider fps={30}>
67
- <div style={{ display: 'flex', height: '100vh' }}>
68
- <AssetPanel style={{ width: 220 }} />
69
- <Timeline style={{ flex: 1 }} />
70
- </div>
71
- </EditorProvider>
72
- )
73
- }
74
- ```
75
-
76
- - **Add** opens a file picker; **drop** onto the panel imports via `importFiles`
77
- - Thumbnails appear asynchronously after import
78
- - Drag a thumbnail onto a timeline track lane to create a clip (see Timeline drop below)
79
-
80
- ## Timeline drop
81
-
82
- With `<AssetPanel>` and `<Timeline>` as siblings inside `<EditorProvider>`, drag a thumbnail onto any track lane:
83
-
84
- - Drop position becomes the clip `startFrame` (respects timeline zoom)
85
- - Clip duration comes from the asset (`durationSec × project.fps`; images default to 5 seconds)
86
- - Video/image assets go on video tracks; audio on audio tracks
87
- - When snap is enabled (`usePlaybackStore.snapEnabled`), the drop snaps to the playhead and nearby clip edges
88
-
89
- No extra wiring beyond `TrackRow` — `useTimelineDrop` is attached automatically per lane.
90
-
91
- ## Render pixels with `<Preview>`
92
-
93
- `<Preview>` mounts the WebGL2 `GpuRenderer`, drives the RAF loop, and paints
94
- interactive transform overlays — drag / uniform-scale for video & image clips
95
- (`MediaTransformOverlay`), and drag / resize / inline-edit for text clips
96
- (`TextOverlay`) plus the project's audio.
97
- Inject a **demuxer factory** so the SDK never hard-depends on a decode backend:
98
-
99
- ```tsx
100
- import { EditorProvider, Preview, createMediabunnyBackend } from '@elah/editor'
101
- import * as mediabunny from 'mediabunny'
102
-
103
- const demuxerFactory = () =>
104
- createMediabunnyBackend(mediabunny, { blobResolver: (src) => fetch(src).then((r) => r.blob()) })
105
-
106
- function App() {
107
- return (
108
- <EditorProvider fps={30}>
109
- <Preview demuxerFactory={demuxerFactory} style={{ height: 480 }} />
110
- </EditorProvider>
111
- )
112
- }
113
- ```
114
-
115
- Omit `demuxerFactory` for a synthetic dev preview (no media files, no mediabunny).
116
- For a lower-level renderer handle, `GpuRenderer` is exported directly. See
117
- [`src/core/renderer/README.md`](src/core/renderer/README.md).
118
-
119
- ## Export to MP4
120
-
121
- ```ts
122
- import { exportVideo } from '@elah/editor'
123
-
124
- const blob = await exportVideo(engine.getProject(), {
125
- videoBitrate: 8_000_000,
126
- onProgress: ({ frame, totalFrames }) => setPct(Math.round((frame / totalFrames) * 100)),
127
- })
128
- ```
129
-
130
- Runs in a worker; reuses `resolveTimeline` + the renderer's placement math. See
131
- [`src/core/export/README.md`](src/core/export/README.md).
132
-
133
- ## Package layout
134
-
135
- ```
136
- src/
137
- core/ types, engine, playback, resolver, stores, assets, media, export, debug, actions
138
- timeline/ Timeline UI + hooks
139
- editor/ EditorProvider, AssetPanel, Preview, useResolvedScene
140
- ```
141
-
142
- ## Keyboard shortcuts
143
-
144
- The `<Timeline>` component registers these global keyboard shortcuts when focused:
145
-
146
- | Key | Action |
147
- |---|---|
148
- | **Space** | Play / pause |
149
- | **S** | Split selected clip at playhead |
150
- | **Delete** / **Backspace** | Delete selected clip(s) |
151
- | **Ctrl/Cmd + C** | Copy selected clip(s) to clipboard |
152
- | **Ctrl/Cmd + V** | Paste copied clip(s) — placed at current playhead position, same track |
153
- | **Ctrl/Cmd + Z** | Undo |
154
- | **Ctrl/Cmd + Shift + Z** / **Ctrl/Cmd + Y** | Redo |
155
- | **Ctrl/Cmd + scroll** | Zoom in / out |
156
- | **← / →** | Step one frame back / forward |
157
-
158
- **Right-click** any clip to open a context menu with a **Delete** option.
159
-
160
- ## Scripts
161
-
162
- ```bash
163
- npm run typecheck # from packages/editor
164
- npm run test
165
- ```
166
-
167
- ## License
168
-
169
- To be decided — see root README.
1
+ # @elah/editor
2
+
3
+ The full Elah video editor SDK for React. Combines the core engine, timeline UI, WebGL2 renderer, media library, and export pipeline into a single package.
4
+
5
+ [![npm](https://img.shields.io/npm/v/@elah/editor)](https://www.npmjs.com/package/@elah/editor)
6
+ [![license](https://img.shields.io/badge/license-ECL--1.0-blue)](https://github.com/elahlabs/elah/blob/main/LICENSE)
7
+
8
+ ---
9
+
10
+ ## Install
11
+
12
+ ```bash
13
+ npm install @elah/editor
14
+ ```
15
+
16
+ Peer dependencies: `react`, `react-dom` >= 18.
17
+
18
+ ---
19
+
20
+ ## Quick start
21
+
22
+ ```tsx
23
+ import { EditorProvider, Timeline } from '@elah/editor'
24
+
25
+ function App() {
26
+ return (
27
+ <EditorProvider fps={30}>
28
+ <Timeline style={{ height: 300 }} />
29
+ </EditorProvider>
30
+ )
31
+ }
32
+ ```
33
+
34
+ ---
35
+
36
+ ## With preview and asset panel
37
+
38
+ ```tsx
39
+ import { EditorProvider, Timeline, Preview, AssetPanel, createMediabunnyBackend } from '@elah/editor'
40
+ import * as mediabunny from 'mediabunny'
41
+
42
+ const demuxerFactory = () =>
43
+ createMediabunnyBackend(mediabunny, {
44
+ blobResolver: (src) => fetch(src).then((r) => r.blob()),
45
+ })
46
+
47
+ function App() {
48
+ return (
49
+ <EditorProvider fps={30}>
50
+ <div style={{ display: 'flex', height: '100vh' }}>
51
+ <AssetPanel style={{ width: 220 }} />
52
+ <div style={{ flex: 1, display: 'flex', flexDirection: 'column' }}>
53
+ <Preview demuxerFactory={demuxerFactory} style={{ flex: 1 }} />
54
+ <Timeline style={{ height: 300 }} />
55
+ </div>
56
+ </div>
57
+ </EditorProvider>
58
+ )
59
+ }
60
+ ```
61
+
62
+ ---
63
+
64
+ ## Import media
65
+
66
+ ```ts
67
+ import { importFiles, useMediaLibrary } from '@elah/editor'
68
+
69
+ await importFiles(Array.from(fileList))
70
+
71
+ // Subscribe in React
72
+ const assets = useMediaLibrary(s => s.assets)
73
+ ```
74
+
75
+ ---
76
+
77
+ ## Export to MP4
78
+
79
+ ```ts
80
+ import { exportVideo } from '@elah/editor'
81
+
82
+ const blob = await exportVideo(engine.getProject(), {
83
+ videoBitrate: 8_000_000,
84
+ onProgress: ({ frame, totalFrames }) => {
85
+ console.log(`${Math.round((frame / totalFrames) * 100)}%`)
86
+ },
87
+ })
88
+ ```
89
+
90
+ Runs in a web worker. Reuses `resolveTimeline` + the GPU renderer's placement math.
91
+
92
+ ---
93
+
94
+ ## Keyboard shortcuts
95
+
96
+ | Key | Action |
97
+ |---|---|
98
+ | `Space` | Play / pause |
99
+ | `S` | Split clip at playhead |
100
+ | `Delete` / `Backspace` | Delete selected clip(s) |
101
+ | `Ctrl/Cmd + C` | Copy |
102
+ | `Ctrl/Cmd + V` | Paste at playhead |
103
+ | `Ctrl/Cmd + Z` | Undo |
104
+ | `Ctrl/Cmd + Shift + Z` | Redo |
105
+ | `Ctrl/Cmd + scroll` | Zoom |
106
+ | `← / →` | Step one frame |
107
+
108
+ ---
109
+
110
+ ## Package layers
111
+
112
+ ```
113
+ @elah/core — engine, playback, resolver, stores, media, export (framework-agnostic)
114
+ @elah/timeline — React timeline UI components and hooks
115
+ @elah/editor — EditorProvider, Preview, AssetPanel + re-exports everything above
116
+ ```
117
+
118
+ Use `@elah/editor` for the full experience. Use `@elah/core` directly for headless or custom rendering pipelines.
119
+
120
+ ---
121
+
122
+ ## Links
123
+
124
+ - [Website](https://www.elah.dev)
125
+ - [GitHub](https://github.com/elahlabs/elah)
126
+ - [License](https://github.com/elahlabs/elah/blob/main/LICENSE)
127
+ - [Commercial licensing](mailto:contact@elah.dev)
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@elah/editor",
3
- "version": "0.1.0",
4
- "description": "Engine-first video timeline SDK",
3
+ "version": "0.1.1",
4
+ "description": "Full React video editor SDK — timeline UI, WebGL2 preview, media library, drag and drop, and MP4 export",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
@@ -12,7 +12,9 @@
12
12
  }
13
13
  },
14
14
  "files": [
15
- "dist"
15
+ "dist",
16
+ "README.md",
17
+ "LICENSE"
16
18
  ],
17
19
  "scripts": {
18
20
  "typecheck": "tsc --noEmit",
@@ -20,6 +22,32 @@
20
22
  "test:watch": "vitest",
21
23
  "build": "tsc --noEmit && tsc -p tsconfig.build.json"
22
24
  },
25
+ "keywords": [
26
+ "video-editor",
27
+ "react",
28
+ "sdk",
29
+ "timeline",
30
+ "webgl",
31
+ "webgl2",
32
+ "gpu",
33
+ "mp4",
34
+ "export",
35
+ "media-editor",
36
+ "browser-video-editor",
37
+ "video-sdk",
38
+ "asset-panel",
39
+ "preview",
40
+ "typescript"
41
+ ],
42
+ "homepage": "https://www.elah.dev",
43
+ "repository": {
44
+ "type": "git",
45
+ "url": "https://github.com/elahlabs/elah.git"
46
+ },
47
+ "bugs": {
48
+ "url": "https://github.com/elahlabs/elah/issues"
49
+ },
50
+ "license": "SEE LICENSE IN LICENSE",
23
51
  "peerDependencies": {
24
52
  "react": ">=18.0.0",
25
53
  "react-dom": ">=18.0.0"