@editframe/create 0.44.0 → 0.45.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.
- package/dist/index.js +16 -28
- package/dist/index.js.map +1 -1
- package/dist/skills/editframe-brand-video-generator/README.md +155 -0
- package/dist/skills/editframe-brand-video-generator/SKILL.md +207 -0
- package/dist/skills/editframe-brand-video-generator/references/brand-examples.md +178 -0
- package/dist/skills/editframe-brand-video-generator/references/color-psychology.md +227 -0
- package/dist/skills/editframe-brand-video-generator/references/composition-patterns.md +383 -0
- package/dist/skills/editframe-brand-video-generator/references/editing.md +66 -0
- package/dist/skills/editframe-brand-video-generator/references/emotional-arcs.md +496 -0
- package/dist/skills/editframe-brand-video-generator/references/genre-selection.md +135 -0
- package/dist/skills/editframe-brand-video-generator/references/transition-styles.md +611 -0
- package/dist/skills/editframe-brand-video-generator/references/typography-personalities.md +326 -0
- package/dist/skills/editframe-brand-video-generator/references/video-archetypes.md +86 -0
- package/dist/skills/editframe-brand-video-generator/references/video-fundamentals.md +169 -0
- package/dist/skills/editframe-brand-video-generator/references/visual-metaphors.md +50 -0
- package/dist/skills/editframe-composition/SKILL.md +169 -0
- package/dist/skills/editframe-composition/references/audio.md +483 -0
- package/dist/skills/editframe-composition/references/captions.md +844 -0
- package/dist/skills/editframe-composition/references/composition-model.md +73 -0
- package/dist/skills/editframe-composition/references/configuration.md +403 -0
- package/dist/skills/editframe-composition/references/css-parts.md +105 -0
- package/dist/skills/editframe-composition/references/css-variables.md +640 -0
- package/dist/skills/editframe-composition/references/entry-points.md +810 -0
- package/dist/skills/editframe-composition/references/events.md +499 -0
- package/dist/skills/editframe-composition/references/getting-started.md +259 -0
- package/dist/skills/editframe-composition/references/hooks.md +234 -0
- package/dist/skills/editframe-composition/references/image.md +241 -0
- package/dist/skills/editframe-composition/references/r3f.md +580 -0
- package/dist/skills/editframe-composition/references/render-api.md +484 -0
- package/dist/skills/editframe-composition/references/render-strategies.md +119 -0
- package/dist/skills/editframe-composition/references/render-to-video.md +1101 -0
- package/dist/skills/editframe-composition/references/scripting.md +606 -0
- package/dist/skills/editframe-composition/references/sequencing.md +116 -0
- package/dist/skills/editframe-composition/references/server-rendering.md +753 -0
- package/dist/skills/editframe-composition/references/surface.md +329 -0
- package/dist/skills/editframe-composition/references/text.md +627 -0
- package/dist/skills/editframe-composition/references/time-model.md +99 -0
- package/dist/skills/editframe-composition/references/timegroup-modes.md +102 -0
- package/dist/skills/editframe-composition/references/timegroup.md +457 -0
- package/dist/skills/editframe-composition/references/timeline-root.md +398 -0
- package/dist/skills/editframe-composition/references/transcription.md +47 -0
- package/dist/skills/editframe-composition/references/transitions.md +608 -0
- package/dist/skills/editframe-composition/references/use-media-info.md +357 -0
- package/dist/skills/editframe-composition/references/video.md +506 -0
- package/dist/skills/editframe-composition/references/waveform.md +327 -0
- package/dist/skills/editframe-editor-gui/SKILL.md +152 -0
- package/dist/skills/editframe-editor-gui/references/active-root-temporal.md +657 -0
- package/dist/skills/editframe-editor-gui/references/canvas.md +947 -0
- package/dist/skills/editframe-editor-gui/references/controls.md +366 -0
- package/dist/skills/editframe-editor-gui/references/dial.md +756 -0
- package/dist/skills/editframe-editor-gui/references/editor-toolkit.md +587 -0
- package/dist/skills/editframe-editor-gui/references/filmstrip.md +460 -0
- package/dist/skills/editframe-editor-gui/references/fit-scale.md +772 -0
- package/dist/skills/editframe-editor-gui/references/focus-overlay.md +561 -0
- package/dist/skills/editframe-editor-gui/references/hierarchy.md +544 -0
- package/dist/skills/editframe-editor-gui/references/overlay-item.md +634 -0
- package/dist/skills/editframe-editor-gui/references/overlay-layer.md +429 -0
- package/dist/skills/editframe-editor-gui/references/pan-zoom.md +568 -0
- package/dist/skills/editframe-editor-gui/references/pause.md +397 -0
- package/dist/skills/editframe-editor-gui/references/play.md +370 -0
- package/dist/skills/editframe-editor-gui/references/preview.md +391 -0
- package/dist/skills/editframe-editor-gui/references/resizable-box.md +749 -0
- package/dist/skills/editframe-editor-gui/references/scrubber.md +588 -0
- package/dist/skills/editframe-editor-gui/references/thumbnail-strip.md +566 -0
- package/dist/skills/editframe-editor-gui/references/time-display.md +492 -0
- package/dist/skills/editframe-editor-gui/references/timeline-ruler.md +489 -0
- package/dist/skills/editframe-editor-gui/references/timeline.md +604 -0
- package/dist/skills/editframe-editor-gui/references/toggle-loop.md +618 -0
- package/dist/skills/editframe-editor-gui/references/toggle-play.md +526 -0
- package/dist/skills/editframe-editor-gui/references/transform-handles.md +924 -0
- package/dist/skills/editframe-editor-gui/references/trim-handles.md +725 -0
- package/dist/skills/editframe-editor-gui/references/workbench.md +453 -0
- package/dist/skills/editframe-motion-design/SKILL.md +101 -0
- package/dist/skills/editframe-motion-design/references/0-editframe.md +299 -0
- package/dist/skills/editframe-motion-design/references/1-intent.md +201 -0
- package/dist/skills/editframe-motion-design/references/2-physics-model.md +405 -0
- package/dist/skills/editframe-motion-design/references/3-attention.md +350 -0
- package/dist/skills/editframe-motion-design/references/4-process.md +418 -0
- package/dist/skills/editframe-vite-plugin/SKILL.md +75 -0
- package/dist/skills/editframe-vite-plugin/references/file-api.md +111 -0
- package/dist/skills/editframe-vite-plugin/references/getting-started.md +96 -0
- package/dist/skills/editframe-vite-plugin/references/jit-transcoding.md +91 -0
- package/dist/skills/editframe-vite-plugin/references/local-assets.md +75 -0
- package/dist/skills/editframe-vite-plugin/references/visual-testing.md +136 -0
- package/dist/skills/editframe-webhooks/SKILL.md +126 -0
- package/dist/skills/editframe-webhooks/references/events.md +382 -0
- package/dist/skills/editframe-webhooks/references/getting-started.md +232 -0
- package/dist/skills/editframe-webhooks/references/security.md +418 -0
- package/dist/skills/editframe-webhooks/references/testing.md +409 -0
- package/dist/skills/editframe-webhooks/references/troubleshooting.md +457 -0
- package/dist/templates/html/AGENTS.md +13 -0
- package/dist/templates/react/AGENTS.md +13 -0
- package/dist/utils.js +15 -16
- package/dist/utils.js.map +1 -1
- package/package.json +1 -1
- package/tsdown.config.ts +4 -0
- package/dist/detectAgent.js +0 -89
- package/dist/detectAgent.js.map +0 -1
|
@@ -0,0 +1,753 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Server-Side Rendering
|
|
3
|
+
description: Use SSR-safe imports and integrate Editframe with Next.js, Remix, and other server-rendered frameworks without hydration errors.
|
|
4
|
+
type: reference
|
|
5
|
+
nav:
|
|
6
|
+
parent: "Advanced"
|
|
7
|
+
priority: 30
|
|
8
|
+
api:
|
|
9
|
+
entry_points:
|
|
10
|
+
- name: "@editframe/elements/server"
|
|
11
|
+
description: SSR-safe types and utilities (no DOM, no Web Components)
|
|
12
|
+
exports:
|
|
13
|
+
- getRenderInfo
|
|
14
|
+
- RenderInfo (type)
|
|
15
|
+
- EFTimegroup (type)
|
|
16
|
+
- EFMedia (type)
|
|
17
|
+
- RenderToVideoOptions (type)
|
|
18
|
+
- RenderProgress (type)
|
|
19
|
+
- ContentReadyMode (type)
|
|
20
|
+
- name: "@editframe/react/server"
|
|
21
|
+
description: SSR-safe React components and types
|
|
22
|
+
exports:
|
|
23
|
+
- Audio
|
|
24
|
+
- Captions
|
|
25
|
+
- CaptionsActiveWord
|
|
26
|
+
- CaptionsAfterActiveWord
|
|
27
|
+
- CaptionsBeforeActiveWord
|
|
28
|
+
- CaptionsSegment
|
|
29
|
+
- Text
|
|
30
|
+
- TextSegment
|
|
31
|
+
- Image
|
|
32
|
+
- Surface
|
|
33
|
+
- Timegroup
|
|
34
|
+
- Video
|
|
35
|
+
- Waveform
|
|
36
|
+
- PanZoom
|
|
37
|
+
- RenderToVideoOptions (type)
|
|
38
|
+
- RenderProgress (type)
|
|
39
|
+
functions:
|
|
40
|
+
- name: getRenderInfo
|
|
41
|
+
signature: "getRenderInfo(html: string): Promise<RenderInfo>"
|
|
42
|
+
description: Extract render metadata from HTML composition (Node.js safe)
|
|
43
|
+
returns: RenderInfo
|
|
44
|
+
react:
|
|
45
|
+
generate: true
|
|
46
|
+
componentName: "Server-Side Rendering"
|
|
47
|
+
importPath: "@editframe/react"
|
|
48
|
+
nav:
|
|
49
|
+
parent: "Advanced / Server Integration"
|
|
50
|
+
priority: 85
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
# Server-Side Rendering
|
|
54
|
+
|
|
55
|
+
<!-- html-only -->
|
|
56
|
+
Editframe packages provide SSR-safe entry points for server-side rendering with Next.js, Remix, and other React frameworks. Import from `@editframe/elements/server` or `@editframe/react/server` to access types and components without triggering browser-specific code.
|
|
57
|
+
<!-- /html-only -->
|
|
58
|
+
<!-- react-only -->
|
|
59
|
+
Editframe provides an SSR-safe entry point for server-side rendering with Next.js, Remix, and other React frameworks.
|
|
60
|
+
<!-- /react-only -->
|
|
61
|
+
|
|
62
|
+
## Problem: Browser APIs in SSR
|
|
63
|
+
|
|
64
|
+
<!-- html-only -->
|
|
65
|
+
The main package entries (`@editframe/elements` and `@editframe/react`) import browser-specific code at the module level:
|
|
66
|
+
- Web Components (`customElements.define()`)
|
|
67
|
+
- DOM APIs (`HTMLElement`, `document`, `window`)
|
|
68
|
+
- Canvas, WebGL, WebCodecs APIs
|
|
69
|
+
<!-- /html-only -->
|
|
70
|
+
<!-- react-only -->
|
|
71
|
+
The main entry point (`@editframe/react`) imports browser-specific code at the module level:
|
|
72
|
+
- Web Components (`customElements.define()`)
|
|
73
|
+
- DOM APIs (`document`, `window`)
|
|
74
|
+
- Browser-only hooks
|
|
75
|
+
- Canvas, WebGL, WebCodecs APIs
|
|
76
|
+
<!-- /react-only -->
|
|
77
|
+
|
|
78
|
+
This code **will crash** if imported during server-side rendering because Node.js doesn't provide these APIs.
|
|
79
|
+
|
|
80
|
+
## Solution: SSR-Safe Entry Points
|
|
81
|
+
|
|
82
|
+
<!-- html-only -->
|
|
83
|
+
Use the `/server` subpath exports that only include types and SSR-safe utilities.
|
|
84
|
+
|
|
85
|
+
## @editframe/elements/server
|
|
86
|
+
|
|
87
|
+
Import **types only** and the `getRenderInfo` utility:
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
import type {
|
|
91
|
+
EFTimegroup,
|
|
92
|
+
EFMedia,
|
|
93
|
+
RenderToVideoOptions,
|
|
94
|
+
RenderProgress,
|
|
95
|
+
} from "@editframe/elements/server";
|
|
96
|
+
|
|
97
|
+
import { getRenderInfo } from "@editframe/elements/server";
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### What's Included
|
|
101
|
+
|
|
102
|
+
- **Types:** TypeScript type definitions for all elements (zero runtime code)
|
|
103
|
+
- **getRenderInfo:** Function to extract render metadata from HTML (Node.js safe)
|
|
104
|
+
|
|
105
|
+
### What's NOT Included
|
|
106
|
+
|
|
107
|
+
- Web Component class definitions
|
|
108
|
+
- DOM manipulation utilities
|
|
109
|
+
- Browser-specific rendering code
|
|
110
|
+
- Canvas or WebGL APIs
|
|
111
|
+
|
|
112
|
+
### getRenderInfo Example
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
// server.ts (Node.js safe)
|
|
116
|
+
import { getRenderInfo } from "@editframe/elements/server";
|
|
117
|
+
|
|
118
|
+
const html = `
|
|
119
|
+
<ef-timegroup mode="sequence" duration="30s">
|
|
120
|
+
<ef-video src="intro.mp4"></ef-video>
|
|
121
|
+
<ef-video src="outro.mp4"></ef-video>
|
|
122
|
+
</ef-timegroup>
|
|
123
|
+
`;
|
|
124
|
+
|
|
125
|
+
const info = await getRenderInfo(html);
|
|
126
|
+
console.log(info.durationMs); // 30000
|
|
127
|
+
console.log(info.width); // 1920
|
|
128
|
+
console.log(info.height); // 1080
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## @editframe/react/server
|
|
132
|
+
|
|
133
|
+
Import **React components** that render to HTML without triggering browser APIs:
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
import {
|
|
137
|
+
Timegroup,
|
|
138
|
+
Video,
|
|
139
|
+
Audio,
|
|
140
|
+
Image,
|
|
141
|
+
Text,
|
|
142
|
+
Captions,
|
|
143
|
+
Surface,
|
|
144
|
+
Waveform,
|
|
145
|
+
PanZoom,
|
|
146
|
+
} from "@editframe/react/server";
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### What's Included
|
|
150
|
+
|
|
151
|
+
- All composition React components (render to `<ef-*>` tags)
|
|
152
|
+
- Caption styling sub-components
|
|
153
|
+
- All types from `@editframe/elements/server`
|
|
154
|
+
|
|
155
|
+
### What's NOT Included
|
|
156
|
+
|
|
157
|
+
- GUI components (Preview, Workbench, Controls, Timeline, etc.)
|
|
158
|
+
- Hooks that depend on browser APIs (`useTimingInfo`, `usePlayback`, etc.)
|
|
159
|
+
- Browser rendering utilities
|
|
160
|
+
<!-- /html-only -->
|
|
161
|
+
<!-- react-only -->
|
|
162
|
+
Import from `@editframe/react/server` to access composition components without triggering browser code:
|
|
163
|
+
|
|
164
|
+
```tsx
|
|
165
|
+
import {
|
|
166
|
+
Timegroup,
|
|
167
|
+
Video,
|
|
168
|
+
Audio,
|
|
169
|
+
Image,
|
|
170
|
+
Text,
|
|
171
|
+
Captions,
|
|
172
|
+
Surface,
|
|
173
|
+
Waveform,
|
|
174
|
+
PanZoom,
|
|
175
|
+
} from "@editframe/react/server";
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## What's Included
|
|
179
|
+
|
|
180
|
+
**Composition Components:**
|
|
181
|
+
- `Timegroup` - Timeline container
|
|
182
|
+
- `Video`, `Audio`, `Image` - Media elements
|
|
183
|
+
- `Text`, `TextSegment` - Text rendering
|
|
184
|
+
- `Captions` + sub-components - Caption styling
|
|
185
|
+
- `Surface` - Element mirroring
|
|
186
|
+
- `Waveform` - Audio visualization
|
|
187
|
+
- `PanZoom` - Pan/zoom container
|
|
188
|
+
|
|
189
|
+
**Types:**
|
|
190
|
+
- All render option types (`RenderToVideoOptions`, `RenderProgress`, etc.)
|
|
191
|
+
- All component prop types
|
|
192
|
+
|
|
193
|
+
## What's NOT Included
|
|
194
|
+
|
|
195
|
+
**GUI Components:**
|
|
196
|
+
- `Preview`, `Workbench`, `Timeline`, `Controls`
|
|
197
|
+
- `Scrubber`, `Filmstrip`, `TimelineRuler`
|
|
198
|
+
- `Canvas`, `TransformHandles`, `Hierarchy`
|
|
199
|
+
- Any editor/preview UI components
|
|
200
|
+
|
|
201
|
+
**Hooks:**
|
|
202
|
+
- `useTimingInfo`, `usePlayback`, `useCompositionTime`
|
|
203
|
+
- `usePanZoomTransform`, `useMediaInfo`
|
|
204
|
+
- All browser-dependent hooks
|
|
205
|
+
|
|
206
|
+
**Browser Utilities:**
|
|
207
|
+
- `renderToVideo`, `createRenderClone`
|
|
208
|
+
- Browser-specific helpers
|
|
209
|
+
<!-- /react-only -->
|
|
210
|
+
|
|
211
|
+
## Next.js Integration
|
|
212
|
+
|
|
213
|
+
### App Router (React Server Components)
|
|
214
|
+
|
|
215
|
+
<!-- html-only -->
|
|
216
|
+
```typescript
|
|
217
|
+
// app/video/[id]/page.tsx (Server Component)
|
|
218
|
+
import { Timegroup, Video, Audio } from "@editframe/react/server";
|
|
219
|
+
|
|
220
|
+
export default function VideoPage({ params }: { params: { id: string } }) {
|
|
221
|
+
return (
|
|
222
|
+
<Timegroup mode="sequence" className="w-[1920px] h-[1080px]">
|
|
223
|
+
<Video src={`/api/videos/${params.id}/intro.mp4`} className="size-full" />
|
|
224
|
+
<Audio src={`/api/videos/${params.id}/music.mp3`} volume={0.3} />
|
|
225
|
+
</Timegroup>
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
<!-- /html-only -->
|
|
230
|
+
<!-- react-only -->
|
|
231
|
+
Import from `/server` in Server Components:
|
|
232
|
+
|
|
233
|
+
```tsx
|
|
234
|
+
// app/video/[id]/page.tsx (Server Component)
|
|
235
|
+
import { Timegroup, Video, Audio } from "@editframe/react/server";
|
|
236
|
+
|
|
237
|
+
interface Props {
|
|
238
|
+
params: { id: string };
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export default function VideoPage({ params }: Props) {
|
|
242
|
+
return (
|
|
243
|
+
<Timegroup mode="sequence" className="w-[1920px] h-[1080px] bg-black">
|
|
244
|
+
<Video src={`/api/videos/${params.id}/intro.mp4`} className="size-full" />
|
|
245
|
+
<Audio src={`/api/videos/${params.id}/music.mp3`} volume={0.3} />
|
|
246
|
+
</Timegroup>
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
```
|
|
250
|
+
<!-- /react-only -->
|
|
251
|
+
|
|
252
|
+
### Pages Router (SSR)
|
|
253
|
+
|
|
254
|
+
Use dynamic imports with `ssr: false` for browser-only code:
|
|
255
|
+
|
|
256
|
+
<!-- html-only -->
|
|
257
|
+
```typescript
|
|
258
|
+
// pages/editor.tsx
|
|
259
|
+
import dynamic from "next/dynamic";
|
|
260
|
+
|
|
261
|
+
// Safe: imports from /server
|
|
262
|
+
import { Timegroup, Video } from "@editframe/react/server";
|
|
263
|
+
|
|
264
|
+
// Unsafe: imports from main entry, requires client-side only
|
|
265
|
+
const Preview = dynamic(
|
|
266
|
+
() => import("@editframe/react").then((mod) => mod.Preview),
|
|
267
|
+
{ ssr: false }
|
|
268
|
+
);
|
|
269
|
+
|
|
270
|
+
const Workbench = dynamic(
|
|
271
|
+
() => import("@editframe/react").then((mod) => mod.Workbench),
|
|
272
|
+
{ ssr: false }
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
export default function EditorPage() {
|
|
276
|
+
return (
|
|
277
|
+
<div>
|
|
278
|
+
{/* This renders on server */}
|
|
279
|
+
<Timegroup mode="sequence" className="w-[1920px] h-[1080px]">
|
|
280
|
+
<Video src="/videos/intro.mp4" className="size-full" />
|
|
281
|
+
</Timegroup>
|
|
282
|
+
|
|
283
|
+
{/* This only renders on client */}
|
|
284
|
+
<Workbench rendering={false} />
|
|
285
|
+
</div>
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
```
|
|
289
|
+
<!-- /html-only -->
|
|
290
|
+
<!-- react-only -->
|
|
291
|
+
```tsx
|
|
292
|
+
// pages/video/[id].tsx
|
|
293
|
+
import type { GetServerSideProps } from "next";
|
|
294
|
+
import { Timegroup, Video } from "@editframe/react/server";
|
|
295
|
+
|
|
296
|
+
interface Props {
|
|
297
|
+
videoUrl: string;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
export const getServerSideProps: GetServerSideProps<Props> = async ({ params }) => {
|
|
301
|
+
const videoUrl = await fetchVideoUrl(params?.id);
|
|
302
|
+
return { props: { videoUrl } };
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
export default function VideoPage({ videoUrl }: Props) {
|
|
306
|
+
return (
|
|
307
|
+
<Timegroup mode="fixed" duration="10s" className="w-[1920px] h-[1080px]">
|
|
308
|
+
<Video src={videoUrl} className="size-full" />
|
|
309
|
+
</Timegroup>
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### Client-Only Components
|
|
315
|
+
|
|
316
|
+
For editor UI components, use dynamic imports with `ssr: false`:
|
|
317
|
+
|
|
318
|
+
```tsx
|
|
319
|
+
// pages/editor.tsx
|
|
320
|
+
import dynamic from "next/dynamic";
|
|
321
|
+
import { Timegroup, Video } from "@editframe/react/server";
|
|
322
|
+
|
|
323
|
+
// Safe: SSR-safe components
|
|
324
|
+
const composition = (
|
|
325
|
+
<Timegroup mode="sequence" className="w-[1920px] h-[1080px]">
|
|
326
|
+
<Video src="/video.mp4" className="size-full" />
|
|
327
|
+
</Timegroup>
|
|
328
|
+
);
|
|
329
|
+
|
|
330
|
+
// Safe: Client-only dynamic import
|
|
331
|
+
const Workbench = dynamic(
|
|
332
|
+
() => import("@editframe/react").then((mod) => mod.Workbench),
|
|
333
|
+
{ ssr: false }
|
|
334
|
+
);
|
|
335
|
+
|
|
336
|
+
const Preview = dynamic(
|
|
337
|
+
() => import("@editframe/react").then((mod) => mod.Preview),
|
|
338
|
+
{ ssr: false }
|
|
339
|
+
);
|
|
340
|
+
|
|
341
|
+
export default function EditorPage() {
|
|
342
|
+
return (
|
|
343
|
+
<div>
|
|
344
|
+
{composition}
|
|
345
|
+
<Workbench rendering={false} />
|
|
346
|
+
</div>
|
|
347
|
+
);
|
|
348
|
+
}
|
|
349
|
+
```
|
|
350
|
+
<!-- /react-only -->
|
|
351
|
+
|
|
352
|
+
## Remix Integration
|
|
353
|
+
|
|
354
|
+
Use the `/server` entry for loaders and actions:
|
|
355
|
+
|
|
356
|
+
```typescript
|
|
357
|
+
// routes/video.$id.tsx
|
|
358
|
+
import type { LoaderFunctionArgs } from "@remix-run/node";
|
|
359
|
+
import { json } from "@remix-run/node";
|
|
360
|
+
import { useLoaderData } from "@remix-run/react";
|
|
361
|
+
import { Timegroup, Video, Audio } from "@editframe/react/server";
|
|
362
|
+
|
|
363
|
+
export async function loader({ params }: LoaderFunctionArgs) {
|
|
364
|
+
const videoData = await fetchVideoData(params.id);
|
|
365
|
+
return json({ videoData });
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
export default function VideoRoute() {
|
|
369
|
+
const { videoData } = useLoaderData<typeof loader>();
|
|
370
|
+
|
|
371
|
+
return (
|
|
372
|
+
<Timegroup mode="sequence" className="w-[1920px] h-[1080px]">
|
|
373
|
+
<Video src={videoData.introUrl} className="size-full" />
|
|
374
|
+
<Audio src={videoData.musicUrl} volume={0.3} />
|
|
375
|
+
</Timegroup>
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
For client-only components, use `ClientOnly`:
|
|
381
|
+
|
|
382
|
+
```typescript
|
|
383
|
+
import { ClientOnly } from "remix-utils/client-only";
|
|
384
|
+
<!-- html-only -->
|
|
385
|
+
|
|
386
|
+
export default function EditorRoute() {
|
|
387
|
+
return (
|
|
388
|
+
<ClientOnly>
|
|
389
|
+
{() => {
|
|
390
|
+
// Import happens only on client
|
|
391
|
+
const { Workbench } = require("@editframe/react");
|
|
392
|
+
return <Workbench rendering={false} />;
|
|
393
|
+
}}
|
|
394
|
+
</ClientOnly>
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
```
|
|
398
|
+
<!-- /html-only -->
|
|
399
|
+
<!-- react-only -->
|
|
400
|
+
import { Timegroup, Video } from "@editframe/react/server";
|
|
401
|
+
|
|
402
|
+
export default function EditorRoute() {
|
|
403
|
+
return (
|
|
404
|
+
<>
|
|
405
|
+
<Timegroup mode="sequence" className="w-[1920px] h-[1080px]">
|
|
406
|
+
<Video src="/video.mp4" className="size-full" />
|
|
407
|
+
</Timegroup>
|
|
408
|
+
|
|
409
|
+
<ClientOnly>
|
|
410
|
+
{() => {
|
|
411
|
+
const { Workbench } = require("@editframe/react");
|
|
412
|
+
return <Workbench rendering={false} />;
|
|
413
|
+
}}
|
|
414
|
+
</ClientOnly>
|
|
415
|
+
</>
|
|
416
|
+
);
|
|
417
|
+
}
|
|
418
|
+
```
|
|
419
|
+
<!-- /react-only -->
|
|
420
|
+
|
|
421
|
+
## Pre-rendering Static HTML
|
|
422
|
+
|
|
423
|
+
Generate static HTML for compositions:
|
|
424
|
+
|
|
425
|
+
```typescript
|
|
426
|
+
// build.ts (Node.js)
|
|
427
|
+
import { renderToString } from "react-dom/server";
|
|
428
|
+
import { Timegroup, Video, Text } from "@editframe/react/server";
|
|
429
|
+
import { writeFileSync } from "fs";
|
|
430
|
+
|
|
431
|
+
const composition = (
|
|
432
|
+
<Timegroup mode="fixed" duration="10s" className="w-[1920px] h-[1080px]">
|
|
433
|
+
<Video src="/assets/background.mp4" className="size-full" />
|
|
434
|
+
<Text className="absolute inset-0 flex items-center justify-center text-6xl">
|
|
435
|
+
Hello World
|
|
436
|
+
</Text>
|
|
437
|
+
</Timegroup>
|
|
438
|
+
);
|
|
439
|
+
|
|
440
|
+
const html = renderToString(composition);
|
|
441
|
+
writeFileSync("./dist/composition.html", html);
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
This HTML can then be hydrated on the client with the full browser-side code.
|
|
445
|
+
|
|
446
|
+
<!-- react-only -->
|
|
447
|
+
```tsx
|
|
448
|
+
// client.tsx
|
|
449
|
+
import { hydrateRoot } from "react-dom/client";
|
|
450
|
+
import { Timegroup, Video, Text } from "@editframe/react"; // Full browser version
|
|
451
|
+
|
|
452
|
+
hydrateRoot(
|
|
453
|
+
document.getElementById("root")!,
|
|
454
|
+
<Timegroup mode="fixed" duration="10s" className="w-[1920px] h-[1080px] bg-black">
|
|
455
|
+
<Video src="/assets/background.mp4" className="size-full object-cover" />
|
|
456
|
+
<Text className="absolute inset-0 flex items-center justify-center text-6xl text-white">
|
|
457
|
+
Hello World
|
|
458
|
+
</Text>
|
|
459
|
+
</Timegroup>
|
|
460
|
+
);
|
|
461
|
+
```
|
|
462
|
+
<!-- /react-only -->
|
|
463
|
+
|
|
464
|
+
## Type Imports
|
|
465
|
+
|
|
466
|
+
When you only need types (not runtime code), use type-only imports for maximum safety:
|
|
467
|
+
|
|
468
|
+
```typescript
|
|
469
|
+
// Safe in any environment (no runtime code)
|
|
470
|
+
import type { EFTimegroup, RenderToVideoOptions } from "@editframe/elements/server";
|
|
471
|
+
<!-- react-only -->
|
|
472
|
+
import type { TimegroupProps } from "@editframe/react/server";
|
|
473
|
+
<!-- /react-only -->
|
|
474
|
+
|
|
475
|
+
function createRenderConfig(): RenderToVideoOptions {
|
|
476
|
+
return {
|
|
477
|
+
fps: 30,
|
|
478
|
+
codec: "h264",
|
|
479
|
+
scale: 1,
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
<!-- react-only -->
|
|
485
|
+
## Type-Only Imports
|
|
486
|
+
|
|
487
|
+
```typescript
|
|
488
|
+
// Safe in any environment (no runtime code)
|
|
489
|
+
import type { TimegroupProps, VideoProps, RenderToVideoOptions } from "@editframe/react/server";
|
|
490
|
+
|
|
491
|
+
function createVideoConfig(): VideoProps {
|
|
492
|
+
return {
|
|
493
|
+
src: "/video.mp4",
|
|
494
|
+
className: "size-full",
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
function createRenderOptions(): RenderToVideoOptions {
|
|
499
|
+
return {
|
|
500
|
+
fps: 30,
|
|
501
|
+
codec: "h264",
|
|
502
|
+
scale: 1,
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
## Environment-Based Imports
|
|
508
|
+
|
|
509
|
+
Dynamically import based on environment:
|
|
510
|
+
|
|
511
|
+
```tsx
|
|
512
|
+
// VideoEditor.tsx
|
|
513
|
+
import type { FC } from "react";
|
|
514
|
+
|
|
515
|
+
let Timegroup: any;
|
|
516
|
+
let Video: any;
|
|
517
|
+
let Workbench: any;
|
|
518
|
+
|
|
519
|
+
if (typeof window === "undefined") {
|
|
520
|
+
// Server: import from /server
|
|
521
|
+
const server = await import("@editframe/react/server");
|
|
522
|
+
Timegroup = server.Timegroup;
|
|
523
|
+
Video = server.Video;
|
|
524
|
+
} else {
|
|
525
|
+
// Browser: import from main entry
|
|
526
|
+
const browser = await import("@editframe/react");
|
|
527
|
+
Timegroup = browser.Timegroup;
|
|
528
|
+
Video = browser.Video;
|
|
529
|
+
Workbench = browser.Workbench;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
export const VideoEditor: FC = () => {
|
|
533
|
+
return (
|
|
534
|
+
<>
|
|
535
|
+
<Timegroup mode="sequence" className="w-[1920px] h-[1080px]">
|
|
536
|
+
<Video src="/video.mp4" className="size-full" />
|
|
537
|
+
</Timegroup>
|
|
538
|
+
{typeof window !== "undefined" && <Workbench rendering={false} />}
|
|
539
|
+
</>
|
|
540
|
+
);
|
|
541
|
+
};
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
## Typical Setup
|
|
545
|
+
|
|
546
|
+
### Development (main.tsx)
|
|
547
|
+
|
|
548
|
+
```tsx
|
|
549
|
+
// src/main.tsx (browser only)
|
|
550
|
+
import ReactDOM from "react-dom/client";
|
|
551
|
+
import { Configuration, TimelineRoot } from "@editframe/react";
|
|
552
|
+
import { Video } from "./Video";
|
|
553
|
+
import "@editframe/elements/styles.css";
|
|
554
|
+
|
|
555
|
+
ReactDOM.createRoot(document.getElementById("root")!).render(
|
|
556
|
+
<Configuration mediaEngine="local">
|
|
557
|
+
<TimelineRoot id="root" component={Video} />
|
|
558
|
+
</Configuration>
|
|
559
|
+
);
|
|
560
|
+
```
|
|
561
|
+
|
|
562
|
+
### Video Composition (Video.tsx)
|
|
563
|
+
|
|
564
|
+
```tsx
|
|
565
|
+
// src/Video.tsx (SSR-safe)
|
|
566
|
+
import { Timegroup, Video, Audio, Text } from "@editframe/react/server";
|
|
567
|
+
|
|
568
|
+
export const Video = () => {
|
|
569
|
+
return (
|
|
570
|
+
<Timegroup mode="sequence" className="w-[1920px] h-[1080px] bg-black">
|
|
571
|
+
<Timegroup mode="fixed" duration="5s" className="absolute inset-0">
|
|
572
|
+
<Video src="/assets/intro.mp4" className="size-full object-cover" />
|
|
573
|
+
</Timegroup>
|
|
574
|
+
<Timegroup mode="fixed" duration="10s" className="absolute inset-0">
|
|
575
|
+
<Video src="/assets/main.mp4" className="size-full object-cover" />
|
|
576
|
+
<Audio src="/assets/music.mp3" volume={0.3} />
|
|
577
|
+
<Text className="absolute bottom-0 left-0 p-8 text-4xl text-white">
|
|
578
|
+
Hello World
|
|
579
|
+
</Text>
|
|
580
|
+
</Timegroup>
|
|
581
|
+
</Timegroup>
|
|
582
|
+
);
|
|
583
|
+
};
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
This pattern works in:
|
|
587
|
+
- Browser (with `@editframe/react`)
|
|
588
|
+
- SSR (with `@editframe/react/server`)
|
|
589
|
+
- Static generation (with `@editframe/react/server`)
|
|
590
|
+
<!-- /react-only -->
|
|
591
|
+
|
|
592
|
+
## Common Patterns
|
|
593
|
+
|
|
594
|
+
### Hydration After SSR
|
|
595
|
+
|
|
596
|
+
**Server:**
|
|
597
|
+
```typescript
|
|
598
|
+
// server.tsx
|
|
599
|
+
import { renderToString } from "react-dom/server";
|
|
600
|
+
import { Timegroup, Video } from "@editframe/react/server";
|
|
601
|
+
|
|
602
|
+
const html = renderToString(
|
|
603
|
+
<Timegroup mode="sequence" className="w-[1920px] h-[1080px]">
|
|
604
|
+
<Video src="/video.mp4" className="size-full" />
|
|
605
|
+
</Timegroup>
|
|
606
|
+
);
|
|
607
|
+
|
|
608
|
+
// Send html to client
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
**Client:**
|
|
612
|
+
```typescript
|
|
613
|
+
// client.tsx
|
|
614
|
+
import { hydrateRoot } from "react-dom/client";
|
|
615
|
+
import { Timegroup, Video } from "@editframe/react"; // Full browser version
|
|
616
|
+
|
|
617
|
+
hydrateRoot(
|
|
618
|
+
document.getElementById("root"),
|
|
619
|
+
<Timegroup mode="sequence" className="w-[1920px] h-[1080px]">
|
|
620
|
+
<Video src="/video.mp4" className="size-full" />
|
|
621
|
+
</Timegroup>
|
|
622
|
+
);
|
|
623
|
+
```
|
|
624
|
+
|
|
625
|
+
### Conditional Imports
|
|
626
|
+
|
|
627
|
+
```typescript
|
|
628
|
+
// utils.ts
|
|
629
|
+
export async function loadEditorComponents() {
|
|
630
|
+
if (typeof window === "undefined") {
|
|
631
|
+
throw new Error("Editor components require browser environment");
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
const { Workbench, Preview, Timeline } = await import("@editframe/react");
|
|
635
|
+
return { Workbench, Preview, Timeline };
|
|
636
|
+
}
|
|
637
|
+
```
|
|
638
|
+
|
|
639
|
+
## Troubleshooting
|
|
640
|
+
|
|
641
|
+
### Error: `customElements is not defined`
|
|
642
|
+
|
|
643
|
+
**Problem:** Importing from main entry point during SSR.
|
|
644
|
+
|
|
645
|
+
**Solution:** Use `/server` entry point:
|
|
646
|
+
```typescript
|
|
647
|
+
// ❌ Wrong
|
|
648
|
+
import { Timegroup } from "@editframe/react";
|
|
649
|
+
|
|
650
|
+
// ✅ Correct
|
|
651
|
+
import { Timegroup } from "@editframe/react/server";
|
|
652
|
+
```
|
|
653
|
+
|
|
654
|
+
### Error: `HTMLElement is not defined`
|
|
655
|
+
|
|
656
|
+
**Problem:** Web Component class definition loaded during SSR.
|
|
657
|
+
|
|
658
|
+
**Solution:** Use dynamic import with `ssr: false` or conditional import.
|
|
659
|
+
|
|
660
|
+
### Error: `document is not defined`
|
|
661
|
+
|
|
662
|
+
**Problem:** Browser-specific code running on server.
|
|
663
|
+
|
|
664
|
+
**Solution:** Check imports and ensure all browser code is client-only.
|
|
665
|
+
|
|
666
|
+
<!-- react-only -->
|
|
667
|
+
### Error: `Cannot find module '@editframe/react/server'`
|
|
668
|
+
|
|
669
|
+
**Problem:** Outdated package version.
|
|
670
|
+
|
|
671
|
+
**Solution:**
|
|
672
|
+
```bash
|
|
673
|
+
npm install @editframe/react@latest
|
|
674
|
+
```
|
|
675
|
+
|
|
676
|
+
Ensure version is 0.37.0 or higher.
|
|
677
|
+
|
|
678
|
+
### Error: `window is not defined`
|
|
679
|
+
|
|
680
|
+
**Problem:** Browser code running on server.
|
|
681
|
+
|
|
682
|
+
**Solution:** Use dynamic import with `ssr: false` or check environment:
|
|
683
|
+
```tsx
|
|
684
|
+
if (typeof window !== "undefined") {
|
|
685
|
+
// Browser-only code
|
|
686
|
+
}
|
|
687
|
+
```
|
|
688
|
+
|
|
689
|
+
### Component renders but doesn't work
|
|
690
|
+
|
|
691
|
+
**Problem:** Hydration mismatch between server and client.
|
|
692
|
+
|
|
693
|
+
**Solution:** Ensure identical imports on both sides or use client-only rendering:
|
|
694
|
+
```tsx
|
|
695
|
+
import dynamic from "next/dynamic";
|
|
696
|
+
|
|
697
|
+
const ClientComponent = dynamic(() => import("./Component"), {
|
|
698
|
+
ssr: false,
|
|
699
|
+
});
|
|
700
|
+
```
|
|
701
|
+
|
|
702
|
+
## What About GUI Components?
|
|
703
|
+
|
|
704
|
+
GUI components like `Workbench`, `Preview`, and `Timeline` are **never** SSR-safe because they:
|
|
705
|
+
- Depend on browser APIs (`ResizeObserver`, `IntersectionObserver`, etc.)
|
|
706
|
+
- Use Web Components internally
|
|
707
|
+
- Require canvas and WebGL contexts
|
|
708
|
+
- Need interactive event handling
|
|
709
|
+
|
|
710
|
+
Always use dynamic imports for GUI components:
|
|
711
|
+
|
|
712
|
+
```tsx
|
|
713
|
+
const Workbench = dynamic(
|
|
714
|
+
() => import("@editframe/react").then((m) => m.Workbench),
|
|
715
|
+
{ ssr: false }
|
|
716
|
+
);
|
|
717
|
+
```
|
|
718
|
+
<!-- /react-only -->
|
|
719
|
+
|
|
720
|
+
## Package Structure
|
|
721
|
+
|
|
722
|
+
Both packages expose <!-- html-only -->three<!-- /html-only --> entry points:
|
|
723
|
+
|
|
724
|
+
### @editframe/elements
|
|
725
|
+
```json
|
|
726
|
+
{
|
|
727
|
+
"exports": {
|
|
728
|
+
".": "./dist/index.js", // Browser: Full package (Web Components, DOM APIs)
|
|
729
|
+
"./server": "./dist/server.js", // SSR: Types + getRenderInfo only
|
|
730
|
+
"./node": "./dist/node.js" // Node: Same as /server (backward compat)
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
```
|
|
734
|
+
|
|
735
|
+
### @editframe/react
|
|
736
|
+
```json
|
|
737
|
+
{
|
|
738
|
+
"exports": {
|
|
739
|
+
".": "./dist/index.js", // Browser: Full package (all components + hooks)
|
|
740
|
+
"./server": "./dist/server.js", // SSR: Composition components only
|
|
741
|
+
"./r3f": "./dist/r3f/index.js" // Browser: React Three Fiber integration
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
```
|
|
745
|
+
|
|
746
|
+
## Related
|
|
747
|
+
|
|
748
|
+
- [entry-points.md](references/entry-points.md) - Complete guide to package entry points
|
|
749
|
+
- [r3f.md](references/r3f.md) - React Three Fiber integration
|
|
750
|
+
- [getting-started.md](references/getting-started.md) - Installation and setup
|
|
751
|
+
<!-- react-only -->
|
|
752
|
+
- [configuration.md](references/configuration.md) - Configuration component
|
|
753
|
+
<!-- /react-only -->
|