@pixerate/editor-svelte 0.61.0 → 0.62.0

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.
@@ -2,6 +2,8 @@
2
2
  import {
3
3
  findVideoClip,
4
4
  formatVideoTimecode,
5
+ VIDEO_FADE_CURVE_LABELS,
6
+ VIDEO_FADE_CURVES,
5
7
  VIDEO_TRANSITION_TYPES,
6
8
  videoKeyframeRows,
7
9
  videoKeyframeToggleEdit,
@@ -149,6 +151,16 @@
149
151
  <input class={input} type="number" min="0.05" step="0.05" placeholder="1" aria-label="Speed" {disabled} value={clip.speed ?? ''} onchange={(e) => number('speed', e.currentTarget.value, 'Change speed', { above: 0, what: 'Speed must be a number above 0 (1 = normal speed).' })} data-field="speed" />
150
152
  <span class="text-muted-foreground">Muted</span>
151
153
  <input type="checkbox" class="justify-self-start" aria-label="Muted" {disabled} checked={!!clip.muted} onchange={(e) => set('muted', e.currentTarget.checked || undefined, e.currentTarget.checked ? 'Mute clip' : 'Unmute clip')} data-field="muted" />
154
+ {#each [{ key: 'fadeInCurve', on: clip.fadeIn, label: 'Fade in sound', aria: 'Fade in curve', change: 'Change fade in curve' }, { key: 'fadeOutCurve', on: clip.fadeOut, label: 'Fade out sound', aria: 'Fade out curve', change: 'Change fade out curve' }] as const as row (row.key)}
155
+ {#if row.on}
156
+ <span class="text-muted-foreground">{row.label}</span>
157
+ <select class={input} aria-label={row.aria} {disabled} value={clip[row.key] ?? 'linear'} onchange={(e) => set(row.key, e.currentTarget.value === 'linear' ? undefined : e.currentTarget.value, row.change)} data-field={row.key}>
158
+ {#each VIDEO_FADE_CURVES as c (c)}
159
+ <option value={c}>{VIDEO_FADE_CURVE_LABELS[c]}</option>
160
+ {/each}
161
+ </select>
162
+ {/if}
163
+ {/each}
152
164
  {:else if clip.type === 'text'}
153
165
  <span class="text-muted-foreground">Text</span>
154
166
  <textarea class={input} rows="2" aria-label="Text" {disabled} value={clip.text} onchange={(e) => text('text', e.currentTarget.value, 'Change text', true)} data-field="text"></textarea>
@@ -1 +1 @@
1
- {"version":3,"file":"ClipInspector.svelte.d.ts","sourceRoot":"","sources":["../../src/video-editor/ClipInspector.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,EAQH,KAAK,qBAAqB,EAG3B,MAAM,+BAA+B,CAAC;AAIvC,UAAU,KAAK;IACb,UAAU,EAAE,qBAAqB,CAAC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA6JH,QAAA,MAAM,aAAa,2CAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"ClipInspector.svelte.d.ts","sourceRoot":"","sources":["../../src/video-editor/ClipInspector.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,EAUH,KAAK,qBAAqB,EAG3B,MAAM,+BAA+B,CAAC;AAIvC,UAAU,KAAK;IACb,UAAU,EAAE,qBAAqB,CAAC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAuKH,QAAA,MAAM,aAAa,2CAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
@@ -6,14 +6,19 @@
6
6
  videoDragLabel,
7
7
  videoAssetMap,
8
8
  videoClipLabel,
9
+ videoClipShowsWaveform,
10
+ videoClipWaveformPath,
9
11
  videoClipTint,
10
12
  videoDropTracks,
11
13
  videoRulerTicks,
14
+ type VideoAsset,
12
15
  type VideoClip,
13
16
  type VideoClipDragMode,
14
17
  type VideoClipDragPlan,
15
18
  type VideoEditorController,
19
+ type VideoMediaClip,
16
20
  type VideoTrack,
21
+ type VideoWaveformMode,
17
22
  } from '@pixerate/editor/video-editor';
18
23
  import { onDestroy } from 'svelte';
19
24
  import { videoEditorStateOf } from './createVideoEditor.svelte.js';
@@ -26,10 +31,22 @@
26
31
  onpxpersecondchange?: (pxPerSecond: number) => void;
27
32
  /** Allow moving and trimming clips, the track toggles and the edit keys (default true). False still scrubs, selects and plays. */
28
33
  editable?: boolean;
34
+ /** Draw waveforms in audio clips (default), in every clip with sound (`all`), or not at all. Sound is decoded once per asset through `controller.audioPeaks`. */
35
+ waveforms?: VideoWaveformMode;
29
36
  class?: string;
30
37
  }
31
38
 
32
- let { controller, pxPerSecond = $bindable(100), onpxpersecondchange, editable = true, class: className = '' }: Props = $props();
39
+ let { controller, pxPerSecond = $bindable(100), onpxpersecondchange, editable = true, waveforms = 'audio', class: className = '' }: Props = $props();
40
+ /** Bumped when more sound peaks have loaded, so waveforms redraw. */
41
+ let peaksVersion = $state(0);
42
+ $effect(() => controller.audioPeaks.subscribe(() => peaksVersion++));
43
+ const CLIP_HEIGHT = 40;
44
+ /** The waveform path for a clip, starting to load its asset's sound the first time. */
45
+ function waveformPath(clip: VideoMediaClip, asset: VideoAsset, w: number): string {
46
+ void peaksVersion;
47
+ const peaks = controller.audioPeaks.get(asset);
48
+ return peaks ? videoClipWaveformPath(clip, peaks, w, CLIP_HEIGHT) : '';
49
+ }
33
50
  const view = videoEditorStateOf(() => controller);
34
51
  const editor = $derived(view.current);
35
52
  const project = $derived(editor.project);
@@ -320,10 +337,17 @@
320
337
  data-dragging={item.dragging ? '' : undefined}
321
338
  onpointerdown={(e) => onclipdown(e, clip, track)}
322
339
  >
340
+ {#if clip.type === 'media' && videoClipShowsWaveform(clip, asset, waveforms)}
341
+ {@const w = Math.max(2, x(clip.duration))}
342
+ {@const d = waveformPath(clip, asset, w)}
343
+ {#if d}
344
+ <svg class="pointer-events-none absolute inset-0" width={w} height={CLIP_HEIGHT} aria-hidden="true" data-waveform={clip.id}><path {d} fill="currentColor" fill-opacity="0.45" /></svg>
345
+ {/if}
346
+ {/if}
323
347
  {#if clip.transition}
324
- <span class="shrink-0 rounded bg-black/40 px-1 text-[9px]" title="{clip.transition.type} {clip.transition.duration} ms" data-transition={clip.transition.type}>⋈</span>
348
+ <span class="relative shrink-0 rounded bg-black/40 px-1 text-[9px]" title="{clip.transition.type} {clip.transition.duration} ms" data-transition={clip.transition.type}>⋈</span>
325
349
  {/if}
326
- <span class="truncate drop-shadow">{videoClipLabel(clip, asset)}</span>
350
+ <span class="relative truncate drop-shadow">{videoClipLabel(clip, asset)}</span>
327
351
  {#if canEdit && !track.locked}
328
352
  <div class="absolute inset-y-0 left-0 w-1.5 cursor-ew-resize bg-white/0 hover:bg-white/40" data-edge="start" aria-hidden="true"></div>
329
353
  <div class="absolute inset-y-0 right-0 w-1.5 cursor-ew-resize bg-white/0 hover:bg-white/40" data-edge="end" aria-hidden="true"></div>
@@ -1,4 +1,4 @@
1
- import { type VideoEditorController } from '@pixerate/editor/video-editor';
1
+ import { type VideoEditorController, type VideoWaveformMode } from '@pixerate/editor/video-editor';
2
2
  interface Props {
3
3
  controller: VideoEditorController;
4
4
  /** Zoom: pixels per second of timeline (default 100). Bindable. */
@@ -7,6 +7,8 @@ interface Props {
7
7
  onpxpersecondchange?: (pxPerSecond: number) => void;
8
8
  /** Allow moving and trimming clips, the track toggles and the edit keys (default true). False still scrubs, selects and plays. */
9
9
  editable?: boolean;
10
+ /** Draw waveforms in audio clips (default), in every clip with sound (`all`), or not at all. Sound is decoded once per asset through `controller.audioPeaks`. */
11
+ waveforms?: VideoWaveformMode;
10
12
  class?: string;
11
13
  }
12
14
  declare const VideoTimeline: import("svelte").Component<Props, {}, "pxPerSecond">;
@@ -1 +1 @@
1
- {"version":3,"file":"VideoTimeline.svelte.d.ts","sourceRoot":"","sources":["../../src/video-editor/VideoTimeline.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,EAaH,KAAK,qBAAqB,EAE3B,MAAM,+BAA+B,CAAC;AAKvC,UAAU,KAAK;IACb,UAAU,EAAE,qBAAqB,CAAC;IAClC,mEAAmE;IACnE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0DAA0D;IAC1D,mBAAmB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IACpD,kIAAkI;IAClI,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA0QH,QAAA,MAAM,aAAa,sDAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"VideoTimeline.svelte.d.ts","sourceRoot":"","sources":["../../src/video-editor/VideoTimeline.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,EAgBH,KAAK,qBAAqB,EAG1B,KAAK,iBAAiB,EACvB,MAAM,+BAA+B,CAAC;AAKvC,UAAU,KAAK;IACb,UAAU,EAAE,qBAAqB,CAAC;IAClC,mEAAmE;IACnE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0DAA0D;IAC1D,mBAAmB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IACpD,kIAAkI;IAClI,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,iKAAiK;IACjK,SAAS,CAAC,EAAE,iBAAiB,CAAC;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA2RH,QAAA,MAAM,aAAa,sDAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pixerate/editor-svelte",
3
- "version": "0.61.0",
3
+ "version": "0.62.0",
4
4
  "description": "Svelte bindings and prompt/rich-text components for @pixerate/editor",
5
5
  "author": "Pixerate",
6
6
  "license": "MIT",
@@ -131,7 +131,7 @@
131
131
  "@tiptap/extension-bubble-menu": "^2.11.5",
132
132
  "@tiptap/extension-placeholder": "^2.11.5",
133
133
  "@tiptap/starter-kit": "^2.11.5",
134
- "@pixerate/editor": "0.82.0"
134
+ "@pixerate/editor": "0.83.0"
135
135
  },
136
136
  "peerDependencies": {
137
137
  "@dagrejs/dagre": "^1.1.4",