@aicut/core 0.4.3 → 0.5.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.
- package/README.md +112 -1
- package/dist/i18n-B-DFWgKe.d.cts +51 -0
- package/dist/i18n-B-DFWgKe.d.ts +51 -0
- package/dist/index.cjs +429 -72
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +200 -6
- package/dist/index.d.ts +200 -6
- package/dist/index.js +371 -17
- package/dist/index.js.map +1 -1
- package/dist/lighting/index.d.cts +2 -1
- package/dist/lighting/index.d.ts +2 -1
- package/dist/playback/webcodecs/index.cjs +10572 -0
- package/dist/playback/webcodecs/index.cjs.map +1 -0
- package/dist/playback/webcodecs/index.d.cts +109 -0
- package/dist/playback/webcodecs/index.d.ts +109 -0
- package/dist/playback/webcodecs/index.js +10568 -0
- package/dist/playback/webcodecs/index.js.map +1 -0
- package/dist/types-CHplD9V5.d.cts +70 -0
- package/dist/types-CHplD9V5.d.ts +70 -0
- package/dist/types-DvKlxylu.d.cts +75 -0
- package/dist/types-rwZx6FxE.d.ts +75 -0
- package/package.json +15 -4
- package/styles/theme.css +7 -2
- package/dist/types-C95koNwJ.d.cts +0 -120
- package/dist/types-C95koNwJ.d.ts +0 -120
package/dist/index.cjs
CHANGED
|
@@ -144,18 +144,26 @@ function projectDuration(project) {
|
|
|
144
144
|
}
|
|
145
145
|
|
|
146
146
|
// src/timeline/layout.ts
|
|
147
|
-
|
|
148
|
-
|
|
147
|
+
exports.TRACK_HEIGHT = 56;
|
|
148
|
+
exports.RULER_HEIGHT = 24;
|
|
149
149
|
var HEADER_WIDTH = 96;
|
|
150
150
|
var HANDLE_PX = 8;
|
|
151
151
|
var CLIP_INSET = 6;
|
|
152
152
|
var SCALE_MIN = 10;
|
|
153
153
|
var SCALE_MAX = 400;
|
|
154
|
+
function setTimelineMetrics(opts) {
|
|
155
|
+
if (opts.trackHeight != null && opts.trackHeight > 0) {
|
|
156
|
+
exports.TRACK_HEIGHT = Math.round(opts.trackHeight);
|
|
157
|
+
}
|
|
158
|
+
if (opts.rulerHeight != null && opts.rulerHeight > 0) {
|
|
159
|
+
exports.RULER_HEIGHT = Math.round(opts.rulerHeight);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
154
162
|
var SCROLLBAR_THICKNESS = 10;
|
|
155
163
|
var SCROLLBAR_MIN_THUMB = 24;
|
|
156
164
|
var SCROLLBAR_INSET = 2;
|
|
157
165
|
function contentHeight(tracks, isDragging) {
|
|
158
|
-
return tracks.length * TRACK_HEIGHT + (isDragging ? TRACK_HEIGHT : 0);
|
|
166
|
+
return tracks.length * exports.TRACK_HEIGHT + (isDragging ? exports.TRACK_HEIGHT : 0);
|
|
159
167
|
}
|
|
160
168
|
function contentWidth(project, pxPerSec) {
|
|
161
169
|
let max = 0;
|
|
@@ -168,12 +176,12 @@ function contentWidth(project, pxPerSec) {
|
|
|
168
176
|
return max / 1e3 * pxPerSec;
|
|
169
177
|
}
|
|
170
178
|
function trackY(index) {
|
|
171
|
-
return RULER_HEIGHT + index * TRACK_HEIGHT;
|
|
179
|
+
return exports.RULER_HEIGHT + index * exports.TRACK_HEIGHT;
|
|
172
180
|
}
|
|
173
181
|
function trackIndexAt(y, trackCount, scrollTop = 0) {
|
|
174
|
-
if (y < RULER_HEIGHT) return -1;
|
|
175
|
-
const contentY = y - RULER_HEIGHT + scrollTop;
|
|
176
|
-
const idx = Math.floor(contentY / TRACK_HEIGHT);
|
|
182
|
+
if (y < exports.RULER_HEIGHT) return -1;
|
|
183
|
+
const contentY = y - exports.RULER_HEIGHT + scrollTop;
|
|
184
|
+
const idx = Math.floor(contentY / exports.TRACK_HEIGHT);
|
|
177
185
|
if (idx < 0 || idx >= trackCount) return -1;
|
|
178
186
|
return idx;
|
|
179
187
|
}
|
|
@@ -253,8 +261,8 @@ function uncoveredIntervals(project) {
|
|
|
253
261
|
return gaps;
|
|
254
262
|
}
|
|
255
263
|
|
|
256
|
-
// src/playback.ts
|
|
257
|
-
var
|
|
264
|
+
// src/playback/html-video.ts
|
|
265
|
+
var HtmlVideoEngine = class {
|
|
258
266
|
host;
|
|
259
267
|
mount;
|
|
260
268
|
videos = /* @__PURE__ */ new Map();
|
|
@@ -270,9 +278,9 @@ var PlaybackEngine = class {
|
|
|
270
278
|
onError;
|
|
271
279
|
onReady;
|
|
272
280
|
onSourceMetadata;
|
|
273
|
-
constructor(
|
|
274
|
-
this.host = host;
|
|
275
|
-
this.project = project;
|
|
281
|
+
constructor(opts) {
|
|
282
|
+
this.host = opts.host;
|
|
283
|
+
this.project = opts.project;
|
|
276
284
|
this.mount = document.createElement("div");
|
|
277
285
|
this.mount.className = "aicut-preview";
|
|
278
286
|
this.host.appendChild(this.mount);
|
|
@@ -514,6 +522,328 @@ var PlaybackEngine = class {
|
|
|
514
522
|
this.onTimeUpdate?.(this.timeMs);
|
|
515
523
|
}
|
|
516
524
|
};
|
|
525
|
+
var htmlVideoEngineFactory = (opts) => new HtmlVideoEngine(opts);
|
|
526
|
+
|
|
527
|
+
// src/playback/canvas-compositor.ts
|
|
528
|
+
var CanvasCompositorEngine = class {
|
|
529
|
+
host;
|
|
530
|
+
mount;
|
|
531
|
+
canvas;
|
|
532
|
+
ctx;
|
|
533
|
+
/** Only created when constructed with `debug: true`. */
|
|
534
|
+
badge = null;
|
|
535
|
+
videos = /* @__PURE__ */ new Map();
|
|
536
|
+
project;
|
|
537
|
+
currentClipId = null;
|
|
538
|
+
playing = false;
|
|
539
|
+
timeMs = 0;
|
|
540
|
+
rafHandle = null;
|
|
541
|
+
lastFrameTs = 0;
|
|
542
|
+
paintedFrames = 0;
|
|
543
|
+
onTimeUpdate;
|
|
544
|
+
onEnded;
|
|
545
|
+
onError;
|
|
546
|
+
onReady;
|
|
547
|
+
onSourceMetadata;
|
|
548
|
+
constructor(opts) {
|
|
549
|
+
this.host = opts.host;
|
|
550
|
+
this.project = opts.project;
|
|
551
|
+
this.mount = document.createElement("div");
|
|
552
|
+
this.mount.className = "aicut-preview aicut-preview--canvas";
|
|
553
|
+
Object.assign(this.mount.style, {
|
|
554
|
+
position: "absolute",
|
|
555
|
+
inset: "0",
|
|
556
|
+
width: "100%",
|
|
557
|
+
height: "100%"
|
|
558
|
+
});
|
|
559
|
+
this.canvas = document.createElement("canvas");
|
|
560
|
+
Object.assign(this.canvas.style, {
|
|
561
|
+
position: "absolute",
|
|
562
|
+
inset: "0",
|
|
563
|
+
width: "100%",
|
|
564
|
+
height: "100%",
|
|
565
|
+
// Stretch with letterboxing handled by the draw loop.
|
|
566
|
+
objectFit: "contain",
|
|
567
|
+
// Black until the first frame is drawn so the swap from the
|
|
568
|
+
// previous engine doesn't flash the host background.
|
|
569
|
+
background: "#000"
|
|
570
|
+
});
|
|
571
|
+
this.mount.appendChild(this.canvas);
|
|
572
|
+
const ctx = this.canvas.getContext("2d");
|
|
573
|
+
if (!ctx) throw new Error("CanvasCompositorEngine: 2d context unavailable");
|
|
574
|
+
this.ctx = ctx;
|
|
575
|
+
if (opts.debug) {
|
|
576
|
+
const badge = document.createElement("div");
|
|
577
|
+
badge.className = "aicut-preview__badge";
|
|
578
|
+
Object.assign(badge.style, {
|
|
579
|
+
position: "absolute",
|
|
580
|
+
top: "8px",
|
|
581
|
+
left: "8px",
|
|
582
|
+
padding: "4px 8px",
|
|
583
|
+
borderRadius: "6px",
|
|
584
|
+
background: "rgba(0, 0, 0, 0.55)",
|
|
585
|
+
color: "rgba(255, 255, 255, 0.92)",
|
|
586
|
+
font: "11px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace",
|
|
587
|
+
pointerEvents: "none",
|
|
588
|
+
zIndex: "2",
|
|
589
|
+
letterSpacing: "0.02em"
|
|
590
|
+
});
|
|
591
|
+
badge.textContent = "engine: canvas compositor";
|
|
592
|
+
this.mount.appendChild(badge);
|
|
593
|
+
this.badge = badge;
|
|
594
|
+
}
|
|
595
|
+
this.host.appendChild(this.mount);
|
|
596
|
+
this.syncSources();
|
|
597
|
+
this.resizeCanvas();
|
|
598
|
+
this.startTickLoop();
|
|
599
|
+
}
|
|
600
|
+
setProject(next) {
|
|
601
|
+
this.project = next;
|
|
602
|
+
this.syncSources();
|
|
603
|
+
const clip = this.clipAtTime(this.timeMs);
|
|
604
|
+
if (!clip) {
|
|
605
|
+
this.timeMs = 0;
|
|
606
|
+
this.activate(null);
|
|
607
|
+
this.onTimeUpdate?.(0);
|
|
608
|
+
} else {
|
|
609
|
+
this.activate(clip);
|
|
610
|
+
this.seekVideoToClipOffset(clip, this.timeMs - clip.start);
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
play() {
|
|
614
|
+
if (this.playing) return;
|
|
615
|
+
if (this.totalDuration() <= 0) return;
|
|
616
|
+
const clip = this.clipAtTime(this.timeMs) ?? this.nextClipAfterTime(this.timeMs);
|
|
617
|
+
if (!clip) return;
|
|
618
|
+
if (this.timeMs < clip.start) this.timeMs = clip.start;
|
|
619
|
+
this.activate(clip);
|
|
620
|
+
this.seekVideoToClipOffset(clip, this.timeMs - clip.start);
|
|
621
|
+
const v = this.videos.get(clip.sourceId);
|
|
622
|
+
if (!v) return;
|
|
623
|
+
void v.play().catch((err) => this.onError?.(err));
|
|
624
|
+
this.playing = true;
|
|
625
|
+
this.lastFrameTs = performance.now();
|
|
626
|
+
}
|
|
627
|
+
pause() {
|
|
628
|
+
if (!this.playing) return;
|
|
629
|
+
this.playing = false;
|
|
630
|
+
if (this.currentClipId) {
|
|
631
|
+
const clip = this.clipById(this.currentClipId);
|
|
632
|
+
if (clip) this.videos.get(clip.sourceId)?.pause();
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
isPlaying() {
|
|
636
|
+
return this.playing;
|
|
637
|
+
}
|
|
638
|
+
getTime() {
|
|
639
|
+
return this.timeMs;
|
|
640
|
+
}
|
|
641
|
+
seek(timeMs) {
|
|
642
|
+
const total = this.totalDuration();
|
|
643
|
+
if (total <= 0) {
|
|
644
|
+
this.timeMs = 0;
|
|
645
|
+
return;
|
|
646
|
+
}
|
|
647
|
+
const clamped = Math.max(0, Math.min(timeMs, total));
|
|
648
|
+
this.timeMs = clamped;
|
|
649
|
+
const clip = this.clipAtTime(clamped);
|
|
650
|
+
if (clip) {
|
|
651
|
+
this.activate(clip);
|
|
652
|
+
this.seekVideoToClipOffset(clip, clamped - clip.start);
|
|
653
|
+
} else {
|
|
654
|
+
this.activate(null);
|
|
655
|
+
}
|
|
656
|
+
this.onTimeUpdate?.(clamped);
|
|
657
|
+
}
|
|
658
|
+
destroy() {
|
|
659
|
+
this.stopTickLoop();
|
|
660
|
+
for (const v of this.videos.values()) {
|
|
661
|
+
v.pause();
|
|
662
|
+
v.removeAttribute("src");
|
|
663
|
+
v.load();
|
|
664
|
+
}
|
|
665
|
+
this.videos.clear();
|
|
666
|
+
this.mount.remove();
|
|
667
|
+
}
|
|
668
|
+
// --- internals -------------------------------------------------------
|
|
669
|
+
syncSources() {
|
|
670
|
+
const wanted = new Set(this.project.sources.map((s) => s.id));
|
|
671
|
+
for (const [id, v] of this.videos) {
|
|
672
|
+
if (!wanted.has(id)) {
|
|
673
|
+
v.pause();
|
|
674
|
+
this.videos.delete(id);
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
for (const src of this.project.sources) {
|
|
678
|
+
if (src.kind !== "video") continue;
|
|
679
|
+
if (this.videos.has(src.id)) continue;
|
|
680
|
+
const v = document.createElement("video");
|
|
681
|
+
v.preload = "auto";
|
|
682
|
+
v.playsInline = true;
|
|
683
|
+
v.muted = false;
|
|
684
|
+
v.src = src.url;
|
|
685
|
+
const sourceId = src.id;
|
|
686
|
+
v.addEventListener(
|
|
687
|
+
"error",
|
|
688
|
+
() => this.onError?.(new Error(`Failed to load ${src.url}`))
|
|
689
|
+
);
|
|
690
|
+
v.addEventListener("loadedmetadata", () => {
|
|
691
|
+
this.onReady?.();
|
|
692
|
+
const durMs = Math.round(v.duration * 1e3);
|
|
693
|
+
if (Number.isFinite(durMs) && durMs > 0) {
|
|
694
|
+
this.onSourceMetadata?.(sourceId, durMs);
|
|
695
|
+
}
|
|
696
|
+
});
|
|
697
|
+
this.videos.set(src.id, v);
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
activate(clip) {
|
|
701
|
+
if (clip?.id === this.currentClipId) return;
|
|
702
|
+
if (this.currentClipId) {
|
|
703
|
+
const prev = this.clipById(this.currentClipId);
|
|
704
|
+
if (prev) this.videos.get(prev.sourceId)?.pause();
|
|
705
|
+
}
|
|
706
|
+
this.currentClipId = clip ? clip.id : null;
|
|
707
|
+
}
|
|
708
|
+
seekVideoToClipOffset(clip, offsetMs) {
|
|
709
|
+
const v = this.videos.get(clip.sourceId);
|
|
710
|
+
if (!v) return;
|
|
711
|
+
const target = (clip.in + Math.max(0, offsetMs)) / 1e3;
|
|
712
|
+
if (Math.abs(v.currentTime - target) > 0.05) {
|
|
713
|
+
v.currentTime = target;
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
clipById(id) {
|
|
717
|
+
for (const t of this.project.tracks) {
|
|
718
|
+
for (const c of t.clips) if (c.id === id) return c;
|
|
719
|
+
}
|
|
720
|
+
return null;
|
|
721
|
+
}
|
|
722
|
+
clipAtTime(timeMs) {
|
|
723
|
+
for (const t of this.project.tracks) {
|
|
724
|
+
if (t.kind !== "video") continue;
|
|
725
|
+
for (const c of t.clips) {
|
|
726
|
+
if (timeMs >= c.start && timeMs < c.start + (c.out - c.in)) return c;
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
return null;
|
|
730
|
+
}
|
|
731
|
+
nextClipAfterTime(timeMs) {
|
|
732
|
+
let best = null;
|
|
733
|
+
for (const t of this.project.tracks) {
|
|
734
|
+
if (t.kind !== "video") continue;
|
|
735
|
+
for (const c of t.clips) {
|
|
736
|
+
if (c.start >= timeMs && (!best || c.start < best.start)) best = c;
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
return best;
|
|
740
|
+
}
|
|
741
|
+
totalDuration() {
|
|
742
|
+
let max = 0;
|
|
743
|
+
for (const t of this.project.tracks) {
|
|
744
|
+
if (t.kind !== "video") continue;
|
|
745
|
+
for (const c of t.clips) {
|
|
746
|
+
const e = c.start + (c.out - c.in);
|
|
747
|
+
if (e > max) max = e;
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
return max;
|
|
751
|
+
}
|
|
752
|
+
resizeCanvas() {
|
|
753
|
+
const rect = this.mount.getBoundingClientRect();
|
|
754
|
+
const dpr = window.devicePixelRatio || 1;
|
|
755
|
+
const w = Math.max(1, Math.floor(rect.width * dpr));
|
|
756
|
+
const h = Math.max(1, Math.floor(rect.height * dpr));
|
|
757
|
+
if (this.canvas.width !== w || this.canvas.height !== h) {
|
|
758
|
+
this.canvas.width = w;
|
|
759
|
+
this.canvas.height = h;
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
startTickLoop() {
|
|
763
|
+
this.lastFrameTs = performance.now();
|
|
764
|
+
const tick = (now) => {
|
|
765
|
+
this.resizeCanvas();
|
|
766
|
+
if (this.playing) {
|
|
767
|
+
const dtMs = now - this.lastFrameTs;
|
|
768
|
+
this.lastFrameTs = now;
|
|
769
|
+
this.advance(dtMs);
|
|
770
|
+
}
|
|
771
|
+
this.paint();
|
|
772
|
+
this.rafHandle = requestAnimationFrame(tick);
|
|
773
|
+
};
|
|
774
|
+
this.rafHandle = requestAnimationFrame(tick);
|
|
775
|
+
}
|
|
776
|
+
stopTickLoop() {
|
|
777
|
+
if (this.rafHandle != null) {
|
|
778
|
+
cancelAnimationFrame(this.rafHandle);
|
|
779
|
+
this.rafHandle = null;
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
advance(dtMs) {
|
|
783
|
+
if (this.project.tracks.length === 0) return;
|
|
784
|
+
this.timeMs += dtMs;
|
|
785
|
+
const totalDur = this.totalDuration();
|
|
786
|
+
if (this.timeMs >= totalDur) {
|
|
787
|
+
this.timeMs = totalDur;
|
|
788
|
+
this.onTimeUpdate?.(this.timeMs);
|
|
789
|
+
this.pause();
|
|
790
|
+
this.onEnded?.();
|
|
791
|
+
return;
|
|
792
|
+
}
|
|
793
|
+
const clip = this.clipAtTime(this.timeMs);
|
|
794
|
+
if (!clip) {
|
|
795
|
+
const next = this.nextClipAfterTime(this.timeMs);
|
|
796
|
+
if (next) {
|
|
797
|
+
this.timeMs = next.start;
|
|
798
|
+
this.activate(next);
|
|
799
|
+
this.seekVideoToClipOffset(next, 0);
|
|
800
|
+
const v = this.videos.get(next.sourceId);
|
|
801
|
+
if (v) void v.play().catch((err) => this.onError?.(err));
|
|
802
|
+
} else {
|
|
803
|
+
this.pause();
|
|
804
|
+
this.onEnded?.();
|
|
805
|
+
return;
|
|
806
|
+
}
|
|
807
|
+
} else if (clip.id !== this.currentClipId) {
|
|
808
|
+
this.activate(clip);
|
|
809
|
+
this.seekVideoToClipOffset(clip, this.timeMs - clip.start);
|
|
810
|
+
const v = this.videos.get(clip.sourceId);
|
|
811
|
+
if (v) void v.play().catch((err) => this.onError?.(err));
|
|
812
|
+
}
|
|
813
|
+
this.onTimeUpdate?.(this.timeMs);
|
|
814
|
+
}
|
|
815
|
+
/**
|
|
816
|
+
* One paint per rAF — clears the canvas, draws the current active
|
|
817
|
+
* video frame letterboxed to fit, then refreshes the HUD. Done
|
|
818
|
+
* unconditionally (not just on `playing`) so the HUD frame counter
|
|
819
|
+
* and the seek preview both update when paused.
|
|
820
|
+
*/
|
|
821
|
+
paint() {
|
|
822
|
+
const cw = this.canvas.width;
|
|
823
|
+
const ch = this.canvas.height;
|
|
824
|
+
this.ctx.clearRect(0, 0, cw, ch);
|
|
825
|
+
const clip = this.currentClipId ? this.clipById(this.currentClipId) : null;
|
|
826
|
+
const v = clip ? this.videos.get(clip.sourceId) : null;
|
|
827
|
+
if (v && v.videoWidth > 0 && v.videoHeight > 0) {
|
|
828
|
+
const vw = v.videoWidth;
|
|
829
|
+
const vh = v.videoHeight;
|
|
830
|
+
const scale = Math.min(cw / vw, ch / vh);
|
|
831
|
+
const dw = vw * scale;
|
|
832
|
+
const dh = vh * scale;
|
|
833
|
+
const dx = (cw - dw) / 2;
|
|
834
|
+
const dy = (ch - dh) / 2;
|
|
835
|
+
this.ctx.drawImage(v, dx, dy, dw, dh);
|
|
836
|
+
this.paintedFrames += 1;
|
|
837
|
+
}
|
|
838
|
+
this.updateBadge();
|
|
839
|
+
}
|
|
840
|
+
updateBadge() {
|
|
841
|
+
if (!this.badge) return;
|
|
842
|
+
const sec = (this.timeMs / 1e3).toFixed(2);
|
|
843
|
+
this.badge.textContent = `engine: canvas compositor \u2022 t=${sec}s \u2022 frames painted: ${this.paintedFrames}`;
|
|
844
|
+
}
|
|
845
|
+
};
|
|
846
|
+
var canvasCompositorEngineFactory = (opts) => new CanvasCompositorEngine(opts);
|
|
517
847
|
|
|
518
848
|
// src/theme.ts
|
|
519
849
|
var THEME_VARS = {
|
|
@@ -657,11 +987,19 @@ var ThumbnailRibbon = class {
|
|
|
657
987
|
/**
|
|
658
988
|
* Paint thumbnails for the clip's visible window onto `ctx`. The
|
|
659
989
|
* canvas is the per-clip strip — width = clip's px width, height =
|
|
660
|
-
*
|
|
661
|
-
* and the px range we're
|
|
990
|
+
* `pxHeight` (defaults to the cached `THUMB_HEIGHT`). Source-time
|
|
991
|
+
* range derives from the clip's `in/out` and the px range we're
|
|
992
|
+
* drawing into.
|
|
993
|
+
*
|
|
994
|
+
* `pxHeight` lets the caller stretch thumbs to fill a taller clip
|
|
995
|
+
* body when `trackHeight` is configured above the default. Aspect
|
|
996
|
+
* ratio is already broken per-thumb (we slice variable widths from a
|
|
997
|
+
* fixed-aspect cached bitmap), so stretching height too is fine — it
|
|
998
|
+
* preserves the "filmstrip" look without leaving an empty bottom
|
|
999
|
+
* band of the brand gradient showing through.
|
|
662
1000
|
*/
|
|
663
|
-
paintStrip(ctx, sourceId, sourceInMs, sourceOutMs, pxWidth) {
|
|
664
|
-
ctx.clearRect(0, 0, pxWidth,
|
|
1001
|
+
paintStrip(ctx, sourceId, sourceInMs, sourceOutMs, pxWidth, pxHeight = THUMB_HEIGHT) {
|
|
1002
|
+
ctx.clearRect(0, 0, pxWidth, pxHeight);
|
|
665
1003
|
const st = this.sources.get(sourceId);
|
|
666
1004
|
if (!st) return;
|
|
667
1005
|
if (sourceOutMs <= sourceInMs || pxWidth <= 0) return;
|
|
@@ -674,10 +1012,10 @@ var ThumbnailRibbon = class {
|
|
|
674
1012
|
const x = Math.round(i * pxWidth / count);
|
|
675
1013
|
const w = Math.round((i + 1) * pxWidth / count) - x;
|
|
676
1014
|
if (bmp) {
|
|
677
|
-
ctx.drawImage(bmp, x, 0, w,
|
|
1015
|
+
ctx.drawImage(bmp, x, 0, w, pxHeight);
|
|
678
1016
|
} else {
|
|
679
1017
|
ctx.fillStyle = "rgba(255,255,255,0.04)";
|
|
680
|
-
ctx.fillRect(x, 0, w,
|
|
1018
|
+
ctx.fillRect(x, 0, w, pxHeight);
|
|
681
1019
|
this.enqueue(st, bucket);
|
|
682
1020
|
}
|
|
683
1021
|
}
|
|
@@ -770,10 +1108,10 @@ function drawAll(ctx, state, style, thumbs) {
|
|
|
770
1108
|
ctx.fillRect(0, 0, W, H);
|
|
771
1109
|
const baseX = state.showHeader ? HEADER_WIDTH : 0;
|
|
772
1110
|
const trackAreaW = W - baseX - SCROLLBAR_THICKNESS;
|
|
773
|
-
const trackAreaH = H - RULER_HEIGHT - SCROLLBAR_THICKNESS;
|
|
1111
|
+
const trackAreaH = H - exports.RULER_HEIGHT - SCROLLBAR_THICKNESS;
|
|
774
1112
|
ctx.save();
|
|
775
1113
|
ctx.beginPath();
|
|
776
|
-
ctx.rect(baseX, RULER_HEIGHT, trackAreaW, trackAreaH);
|
|
1114
|
+
ctx.rect(baseX, exports.RULER_HEIGHT, trackAreaW, trackAreaH);
|
|
777
1115
|
ctx.clip();
|
|
778
1116
|
ctx.translate(0, -state.scrollTop);
|
|
779
1117
|
drawTracks(ctx, state, style, thumbs);
|
|
@@ -785,7 +1123,7 @@ function drawAll(ctx, state, style, thumbs) {
|
|
|
785
1123
|
if (state.showHeader) {
|
|
786
1124
|
ctx.save();
|
|
787
1125
|
ctx.beginPath();
|
|
788
|
-
ctx.rect(0, RULER_HEIGHT, HEADER_WIDTH, trackAreaH);
|
|
1126
|
+
ctx.rect(0, exports.RULER_HEIGHT, HEADER_WIDTH, trackAreaH);
|
|
789
1127
|
ctx.clip();
|
|
790
1128
|
ctx.translate(0, -state.scrollTop);
|
|
791
1129
|
drawHeaders(ctx, state, style);
|
|
@@ -793,7 +1131,7 @@ function drawAll(ctx, state, style, thumbs) {
|
|
|
793
1131
|
}
|
|
794
1132
|
ctx.save();
|
|
795
1133
|
ctx.beginPath();
|
|
796
|
-
ctx.rect(baseX, 0, trackAreaW, RULER_HEIGHT);
|
|
1134
|
+
ctx.rect(baseX, 0, trackAreaW, exports.RULER_HEIGHT);
|
|
797
1135
|
ctx.clip();
|
|
798
1136
|
drawRuler(ctx, state, style);
|
|
799
1137
|
ctx.restore();
|
|
@@ -817,7 +1155,7 @@ function drawCoverageGaps(ctx, state, style) {
|
|
|
817
1155
|
const gaps = uncoveredIntervals(state.project);
|
|
818
1156
|
if (gaps.length === 0) return;
|
|
819
1157
|
const baseX = state.showHeader ? HEADER_WIDTH : 0;
|
|
820
|
-
const trackStackH = state.viewportHeight - RULER_HEIGHT - SCROLLBAR_THICKNESS;
|
|
1158
|
+
const trackStackH = state.viewportHeight - exports.RULER_HEIGHT - SCROLLBAR_THICKNESS;
|
|
821
1159
|
for (const [s, e] of gaps) {
|
|
822
1160
|
const x1 = Math.max(
|
|
823
1161
|
baseX,
|
|
@@ -829,16 +1167,16 @@ function drawCoverageGaps(ctx, state, style) {
|
|
|
829
1167
|
);
|
|
830
1168
|
if (x2 <= x1) continue;
|
|
831
1169
|
ctx.fillStyle = "rgba(250, 167, 0, 0.35)";
|
|
832
|
-
ctx.fillRect(x1, 0, x2 - x1, RULER_HEIGHT);
|
|
1170
|
+
ctx.fillRect(x1, 0, x2 - x1, exports.RULER_HEIGHT);
|
|
833
1171
|
ctx.fillStyle = "rgba(250, 167, 0, 0.12)";
|
|
834
|
-
ctx.fillRect(x1, RULER_HEIGHT, x2 - x1, trackStackH);
|
|
1172
|
+
ctx.fillRect(x1, exports.RULER_HEIGHT, x2 - x1, trackStackH);
|
|
835
1173
|
ctx.save();
|
|
836
1174
|
ctx.strokeStyle = "rgba(250, 167, 0, 0.6)";
|
|
837
1175
|
ctx.lineWidth = 1;
|
|
838
1176
|
ctx.beginPath();
|
|
839
1177
|
for (let hx = Math.floor(x1); hx < x2; hx += 6) {
|
|
840
|
-
ctx.moveTo(hx, RULER_HEIGHT - 1);
|
|
841
|
-
ctx.lineTo(hx + 6, RULER_HEIGHT - 7);
|
|
1178
|
+
ctx.moveTo(hx, exports.RULER_HEIGHT - 1);
|
|
1179
|
+
ctx.lineTo(hx + 6, exports.RULER_HEIGHT - 7);
|
|
842
1180
|
}
|
|
843
1181
|
ctx.stroke();
|
|
844
1182
|
ctx.restore();
|
|
@@ -887,7 +1225,7 @@ function drawDragGhost(ctx, state, style, thumbs) {
|
|
|
887
1225
|
}
|
|
888
1226
|
function drawDropOutline(ctx, startX, trackIndex, widthPx, color, emphasized) {
|
|
889
1227
|
const y = trackY(trackIndex) + CLIP_INSET - 1;
|
|
890
|
-
const h = TRACK_HEIGHT - CLIP_INSET * 2 + 2;
|
|
1228
|
+
const h = exports.TRACK_HEIGHT - CLIP_INSET * 2 + 2;
|
|
891
1229
|
ctx.save();
|
|
892
1230
|
if (emphasized) {
|
|
893
1231
|
ctx.shadowColor = withAlpha(color, 0.45);
|
|
@@ -904,22 +1242,22 @@ function drawPhantomRow(ctx, trackIndex, baseX, state, style) {
|
|
|
904
1242
|
const w = state.viewportWidth - baseX;
|
|
905
1243
|
ctx.save();
|
|
906
1244
|
ctx.fillStyle = withAlpha(style.info, 0.04);
|
|
907
|
-
ctx.fillRect(baseX, y, w, TRACK_HEIGHT);
|
|
1245
|
+
ctx.fillRect(baseX, y, w, exports.TRACK_HEIGHT);
|
|
908
1246
|
ctx.strokeStyle = withAlpha(style.info, 0.35);
|
|
909
1247
|
ctx.lineWidth = 1;
|
|
910
1248
|
ctx.setLineDash([3, 4]);
|
|
911
1249
|
ctx.beginPath();
|
|
912
1250
|
ctx.moveTo(baseX, y + 0.5);
|
|
913
1251
|
ctx.lineTo(baseX + w, y + 0.5);
|
|
914
|
-
ctx.moveTo(baseX, y + TRACK_HEIGHT - 0.5);
|
|
915
|
-
ctx.lineTo(baseX + w, y + TRACK_HEIGHT - 0.5);
|
|
1252
|
+
ctx.moveTo(baseX, y + exports.TRACK_HEIGHT - 0.5);
|
|
1253
|
+
ctx.lineTo(baseX + w, y + exports.TRACK_HEIGHT - 0.5);
|
|
916
1254
|
ctx.stroke();
|
|
917
1255
|
ctx.setLineDash([]);
|
|
918
1256
|
if (state.showHeader) {
|
|
919
1257
|
ctx.fillStyle = withAlpha(style.info, 0.7);
|
|
920
1258
|
ctx.font = "10px system-ui, -apple-system, sans-serif";
|
|
921
1259
|
ctx.textBaseline = "middle";
|
|
922
|
-
ctx.fillText(state.locale.newTrack, 12, y + TRACK_HEIGHT / 2);
|
|
1260
|
+
ctx.fillText(state.locale.newTrack, 12, y + exports.TRACK_HEIGHT / 2);
|
|
923
1261
|
}
|
|
924
1262
|
ctx.restore();
|
|
925
1263
|
}
|
|
@@ -928,12 +1266,12 @@ function drawRuler(ctx, state, style) {
|
|
|
928
1266
|
const baseX = state.showHeader ? HEADER_WIDTH : 0;
|
|
929
1267
|
const rulerW = W - baseX;
|
|
930
1268
|
ctx.fillStyle = style.bg;
|
|
931
|
-
ctx.fillRect(baseX, 0, rulerW, RULER_HEIGHT);
|
|
1269
|
+
ctx.fillRect(baseX, 0, rulerW, exports.RULER_HEIGHT);
|
|
932
1270
|
ctx.strokeStyle = style.border;
|
|
933
1271
|
ctx.lineWidth = 1;
|
|
934
1272
|
ctx.beginPath();
|
|
935
|
-
ctx.moveTo(baseX, RULER_HEIGHT - 0.5);
|
|
936
|
-
ctx.lineTo(W, RULER_HEIGHT - 0.5);
|
|
1273
|
+
ctx.moveTo(baseX, exports.RULER_HEIGHT - 0.5);
|
|
1274
|
+
ctx.lineTo(W, exports.RULER_HEIGHT - 0.5);
|
|
937
1275
|
ctx.stroke();
|
|
938
1276
|
const minPx = 80;
|
|
939
1277
|
const tickSec = niceTickSeconds(minPx / pxPerSec);
|
|
@@ -954,12 +1292,12 @@ function drawRuler(ctx, state, style) {
|
|
|
954
1292
|
ctx.lineWidth = 1;
|
|
955
1293
|
const h = isMajor ? 10 : 6;
|
|
956
1294
|
ctx.beginPath();
|
|
957
|
-
ctx.moveTo(x + 0.5, RULER_HEIGHT - h);
|
|
958
|
-
ctx.lineTo(x + 0.5, RULER_HEIGHT - 1);
|
|
1295
|
+
ctx.moveTo(x + 0.5, exports.RULER_HEIGHT - h);
|
|
1296
|
+
ctx.lineTo(x + 0.5, exports.RULER_HEIGHT - 1);
|
|
959
1297
|
ctx.stroke();
|
|
960
1298
|
if (isMajor) {
|
|
961
1299
|
ctx.fillStyle = withAlpha(style.textMuted, 0.85);
|
|
962
|
-
ctx.fillText(formatRulerLabel(s), x + 3, RULER_HEIGHT - 12);
|
|
1300
|
+
ctx.fillText(formatRulerLabel(s), x + 3, exports.RULER_HEIGHT - 12);
|
|
963
1301
|
}
|
|
964
1302
|
}
|
|
965
1303
|
}
|
|
@@ -974,12 +1312,12 @@ function drawTrackRow(ctx, trackIndex, track, sources, state, style, thumbs) {
|
|
|
974
1312
|
const baseX = state.showHeader ? HEADER_WIDTH : 0;
|
|
975
1313
|
const y = trackY(trackIndex);
|
|
976
1314
|
ctx.fillStyle = style.trackBg;
|
|
977
|
-
ctx.fillRect(baseX, y, W - baseX, TRACK_HEIGHT);
|
|
1315
|
+
ctx.fillRect(baseX, y, W - baseX, exports.TRACK_HEIGHT);
|
|
978
1316
|
ctx.strokeStyle = style.border;
|
|
979
1317
|
ctx.lineWidth = 1;
|
|
980
1318
|
ctx.beginPath();
|
|
981
|
-
ctx.moveTo(baseX, y + TRACK_HEIGHT - 0.5);
|
|
982
|
-
ctx.lineTo(W, y + TRACK_HEIGHT - 0.5);
|
|
1319
|
+
ctx.moveTo(baseX, y + exports.TRACK_HEIGHT - 0.5);
|
|
1320
|
+
ctx.lineTo(W, y + exports.TRACK_HEIGHT - 0.5);
|
|
983
1321
|
ctx.stroke();
|
|
984
1322
|
if (state.dropTargetTrackIndex === trackIndex) {
|
|
985
1323
|
ctx.strokeStyle = withAlpha(style.info, 0.45);
|
|
@@ -987,8 +1325,8 @@ function drawTrackRow(ctx, trackIndex, track, sources, state, style, thumbs) {
|
|
|
987
1325
|
ctx.beginPath();
|
|
988
1326
|
ctx.moveTo(baseX, y + 0.5);
|
|
989
1327
|
ctx.lineTo(W, y + 0.5);
|
|
990
|
-
ctx.moveTo(baseX, y + TRACK_HEIGHT - 0.5);
|
|
991
|
-
ctx.lineTo(W, y + TRACK_HEIGHT - 0.5);
|
|
1328
|
+
ctx.moveTo(baseX, y + exports.TRACK_HEIGHT - 0.5);
|
|
1329
|
+
ctx.lineTo(W, y + exports.TRACK_HEIGHT - 0.5);
|
|
992
1330
|
ctx.stroke();
|
|
993
1331
|
}
|
|
994
1332
|
for (const clip of track.clips) {
|
|
@@ -1013,7 +1351,7 @@ function drawClipAt(ctx, clip, trackIndex, startMs, sources, state, style, thumb
|
|
|
1013
1351
|
const startX = baseX + startMs / 1e3 * pxPerSec - scrollLeft;
|
|
1014
1352
|
const widthPx = Math.max(2, (clip.out - clip.in) / 1e3 * pxPerSec);
|
|
1015
1353
|
const y = trackY(trackIndex) + CLIP_INSET;
|
|
1016
|
-
const h = TRACK_HEIGHT - CLIP_INSET * 2;
|
|
1354
|
+
const h = exports.TRACK_HEIGHT - CLIP_INSET * 2;
|
|
1017
1355
|
if (startX + widthPx < baseX || startX > state.viewportWidth) return;
|
|
1018
1356
|
ctx.save();
|
|
1019
1357
|
if (dim) ctx.globalAlpha = 0.3;
|
|
@@ -1032,7 +1370,7 @@ function drawClipAt(ctx, clip, trackIndex, startMs, sources, state, style, thumb
|
|
|
1032
1370
|
roundRect(ctx, startX, y, widthPx, h, 6);
|
|
1033
1371
|
ctx.clip();
|
|
1034
1372
|
ctx.translate(startX, y);
|
|
1035
|
-
thumbs.paintStrip(ctx, clip.sourceId, clip.in, clip.out, widthPx);
|
|
1373
|
+
thumbs.paintStrip(ctx, clip.sourceId, clip.in, clip.out, widthPx, h);
|
|
1036
1374
|
ctx.restore();
|
|
1037
1375
|
ctx.strokeStyle = "rgba(255,255,255,0.2)";
|
|
1038
1376
|
ctx.lineWidth = 1;
|
|
@@ -1076,18 +1414,18 @@ function drawHeaders(ctx, state, style) {
|
|
|
1076
1414
|
const y = trackY(i);
|
|
1077
1415
|
ctx.strokeStyle = style.border;
|
|
1078
1416
|
ctx.beginPath();
|
|
1079
|
-
ctx.moveTo(0, y + TRACK_HEIGHT - 0.5);
|
|
1080
|
-
ctx.lineTo(HEADER_WIDTH, y + TRACK_HEIGHT - 0.5);
|
|
1417
|
+
ctx.moveTo(0, y + exports.TRACK_HEIGHT - 0.5);
|
|
1418
|
+
ctx.lineTo(HEADER_WIDTH, y + exports.TRACK_HEIGHT - 0.5);
|
|
1081
1419
|
ctx.stroke();
|
|
1082
1420
|
ctx.fillStyle = withAlpha(style.text, 0.7);
|
|
1083
1421
|
const template = t.kind === "video" ? state.locale.videoTrackLabel : state.locale.audioTrackLabel;
|
|
1084
1422
|
const label = formatLabel(template, { n: i + 1 });
|
|
1085
|
-
ctx.fillText(label, 12, y + TRACK_HEIGHT / 2);
|
|
1423
|
+
ctx.fillText(label, 12, y + exports.TRACK_HEIGHT / 2);
|
|
1086
1424
|
if (t.clips.length === 0) {
|
|
1087
1425
|
const hovered = state.hoveredTrackIndex === i;
|
|
1088
1426
|
const btnSize = 18;
|
|
1089
1427
|
const btnLeft = HEADER_WIDTH - btnSize - 6;
|
|
1090
|
-
const btnTop = y + (TRACK_HEIGHT - btnSize) / 2;
|
|
1428
|
+
const btnTop = y + (exports.TRACK_HEIGHT - btnSize) / 2;
|
|
1091
1429
|
ctx.save();
|
|
1092
1430
|
if (hovered) {
|
|
1093
1431
|
ctx.fillStyle = withAlpha(style.text, 0.1);
|
|
@@ -1150,11 +1488,11 @@ function drawSnapGuide(ctx, state, style) {
|
|
|
1150
1488
|
}
|
|
1151
1489
|
function drawScrollbarV(ctx, state, style) {
|
|
1152
1490
|
if (state.scrollbarOpacityY <= 0.01) return;
|
|
1153
|
-
const visibleH = state.viewportHeight - RULER_HEIGHT - SCROLLBAR_THICKNESS;
|
|
1491
|
+
const visibleH = state.viewportHeight - exports.RULER_HEIGHT - SCROLLBAR_THICKNESS;
|
|
1154
1492
|
const contentH = contentHeight(state.project.tracks, state.isDragging);
|
|
1155
1493
|
if (contentH <= visibleH) return;
|
|
1156
1494
|
const trackX = state.viewportWidth - SCROLLBAR_THICKNESS + SCROLLBAR_INSET;
|
|
1157
|
-
const trackY0 = RULER_HEIGHT + SCROLLBAR_INSET;
|
|
1495
|
+
const trackY0 = exports.RULER_HEIGHT + SCROLLBAR_INSET;
|
|
1158
1496
|
const trackLen = visibleH - SCROLLBAR_INSET * 2;
|
|
1159
1497
|
const thumbLen = Math.max(
|
|
1160
1498
|
SCROLLBAR_MIN_THUMB,
|
|
@@ -1266,16 +1604,16 @@ function parseColor(s) {
|
|
|
1266
1604
|
function hitTest(x, y, ctx) {
|
|
1267
1605
|
if (y < 0 || x < 0) return { kind: "outside" };
|
|
1268
1606
|
const baseX = ctx.showHeader ? HEADER_WIDTH : 0;
|
|
1269
|
-
const visibleH = ctx.viewportHeight - RULER_HEIGHT - SCROLLBAR_THICKNESS;
|
|
1607
|
+
const visibleH = ctx.viewportHeight - exports.RULER_HEIGHT - SCROLLBAR_THICKNESS;
|
|
1270
1608
|
const contentH = contentHeight(ctx.project.tracks, ctx.isDragging);
|
|
1271
|
-
if (contentH > visibleH && x >= ctx.viewportWidth - SCROLLBAR_THICKNESS && x < ctx.viewportWidth && y >= RULER_HEIGHT && y < ctx.viewportHeight - SCROLLBAR_THICKNESS) {
|
|
1609
|
+
if (contentH > visibleH && x >= ctx.viewportWidth - SCROLLBAR_THICKNESS && x < ctx.viewportWidth && y >= exports.RULER_HEIGHT && y < ctx.viewportHeight - SCROLLBAR_THICKNESS) {
|
|
1272
1610
|
const trackLen = visibleH - SCROLLBAR_INSET * 2;
|
|
1273
1611
|
const thumbLen = Math.max(
|
|
1274
1612
|
SCROLLBAR_MIN_THUMB,
|
|
1275
1613
|
trackLen * (visibleH / contentH)
|
|
1276
1614
|
);
|
|
1277
1615
|
const maxScroll = contentH - visibleH;
|
|
1278
|
-
const thumbY = RULER_HEIGHT + SCROLLBAR_INSET + (maxScroll > 0 ? ctx.scrollTop / maxScroll * (trackLen - thumbLen) : 0);
|
|
1616
|
+
const thumbY = exports.RULER_HEIGHT + SCROLLBAR_INSET + (maxScroll > 0 ? ctx.scrollTop / maxScroll * (trackLen - thumbLen) : 0);
|
|
1279
1617
|
if (y >= thumbY && y <= thumbY + thumbLen) {
|
|
1280
1618
|
return { kind: "scrollbar-thumb-v", thumbY, thumbLen };
|
|
1281
1619
|
}
|
|
@@ -1296,14 +1634,14 @@ function hitTest(x, y, ctx) {
|
|
|
1296
1634
|
}
|
|
1297
1635
|
return { kind: "scrollbar-track-h", before: x < thumbX };
|
|
1298
1636
|
}
|
|
1299
|
-
if (ctx.showHeader && x < HEADER_WIDTH && y >= RULER_HEIGHT) {
|
|
1637
|
+
if (ctx.showHeader && x < HEADER_WIDTH && y >= exports.RULER_HEIGHT) {
|
|
1300
1638
|
const ti2 = trackIndexAt(y, ctx.project.tracks.length, ctx.scrollTop);
|
|
1301
1639
|
if (ti2 >= 0) {
|
|
1302
1640
|
const track2 = ctx.project.tracks[ti2];
|
|
1303
1641
|
if (track2.clips.length === 0) {
|
|
1304
1642
|
const btnSize = 18;
|
|
1305
1643
|
const btnLeft = HEADER_WIDTH - btnSize - 6;
|
|
1306
|
-
const btnTop = RULER_HEIGHT + ti2 *
|
|
1644
|
+
const btnTop = exports.RULER_HEIGHT + ti2 * exports.TRACK_HEIGHT + (exports.TRACK_HEIGHT - btnSize) / 2 - ctx.scrollTop;
|
|
1307
1645
|
if (x >= btnLeft && x <= btnLeft + btnSize && y >= btnTop && y <= btnTop + btnSize) {
|
|
1308
1646
|
return { kind: "header-delete", trackIndex: ti2 };
|
|
1309
1647
|
}
|
|
@@ -1312,7 +1650,7 @@ function hitTest(x, y, ctx) {
|
|
|
1312
1650
|
}
|
|
1313
1651
|
return { kind: "outside" };
|
|
1314
1652
|
}
|
|
1315
|
-
if (y < RULER_HEIGHT) return { kind: "ruler" };
|
|
1653
|
+
if (y < exports.RULER_HEIGHT) return { kind: "ruler" };
|
|
1316
1654
|
const ti = trackIndexAt(y, ctx.project.tracks.length, ctx.scrollTop);
|
|
1317
1655
|
if (ti < 0) return { kind: "outside" };
|
|
1318
1656
|
const track = ctx.project.tracks[ti];
|
|
@@ -1560,14 +1898,14 @@ var Timeline = class _Timeline {
|
|
|
1560
1898
|
for (const c of t.clips) {
|
|
1561
1899
|
const x = baseX + c.start / 1e3 * this.pxPerSec - this.scrollLeft;
|
|
1562
1900
|
const width = (c.out - c.in) / 1e3 * this.pxPerSec;
|
|
1563
|
-
const y = RULER_HEIGHT + ti * TRACK_HEIGHT + 6;
|
|
1901
|
+
const y = exports.RULER_HEIGHT + ti * exports.TRACK_HEIGHT + 6;
|
|
1564
1902
|
clips.push({
|
|
1565
1903
|
id: c.id,
|
|
1566
1904
|
trackIndex: ti,
|
|
1567
1905
|
x,
|
|
1568
1906
|
width,
|
|
1569
1907
|
y,
|
|
1570
|
-
height: TRACK_HEIGHT - 12
|
|
1908
|
+
height: exports.TRACK_HEIGHT - 12
|
|
1571
1909
|
});
|
|
1572
1910
|
}
|
|
1573
1911
|
}
|
|
@@ -1597,13 +1935,12 @@ var Timeline = class _Timeline {
|
|
|
1597
1935
|
const rect = this.canvas.getBoundingClientRect();
|
|
1598
1936
|
this.viewportWidth = Math.max(1, Math.floor(rect.width));
|
|
1599
1937
|
this.viewportHeight = Math.max(
|
|
1600
|
-
Math.floor(rect.height) || RULER_HEIGHT + TRACK_HEIGHT + SCROLLBAR_THICKNESS,
|
|
1601
|
-
RULER_HEIGHT + TRACK_HEIGHT + SCROLLBAR_THICKNESS
|
|
1938
|
+
Math.floor(rect.height) || exports.RULER_HEIGHT + exports.TRACK_HEIGHT + SCROLLBAR_THICKNESS,
|
|
1939
|
+
exports.RULER_HEIGHT + exports.TRACK_HEIGHT + SCROLLBAR_THICKNESS
|
|
1602
1940
|
);
|
|
1603
1941
|
const dpr = window.devicePixelRatio || 1;
|
|
1604
1942
|
this.canvas.width = Math.floor(this.viewportWidth * dpr);
|
|
1605
1943
|
this.canvas.height = Math.floor(this.viewportHeight * dpr);
|
|
1606
|
-
this.canvas.style.height = `${this.viewportHeight}px`;
|
|
1607
1944
|
this.ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
1608
1945
|
}
|
|
1609
1946
|
computeFitScale() {
|
|
@@ -1620,7 +1957,7 @@ var Timeline = class _Timeline {
|
|
|
1620
1957
|
return Math.max(0, cw - visibleW + 24);
|
|
1621
1958
|
}
|
|
1622
1959
|
maxScrollTop() {
|
|
1623
|
-
const visibleH = this.viewportHeight - RULER_HEIGHT - SCROLLBAR_THICKNESS;
|
|
1960
|
+
const visibleH = this.viewportHeight - exports.RULER_HEIGHT - SCROLLBAR_THICKNESS;
|
|
1624
1961
|
const ch = contentHeight(this.project.tracks, this.drag?.kind === "move");
|
|
1625
1962
|
return Math.max(0, ch - visibleH);
|
|
1626
1963
|
}
|
|
@@ -1781,8 +2118,8 @@ var Timeline = class _Timeline {
|
|
|
1781
2118
|
}
|
|
1782
2119
|
if (target.kind === "scrollbar-track-v") {
|
|
1783
2120
|
const page = Math.max(
|
|
1784
|
-
TRACK_HEIGHT,
|
|
1785
|
-
this.viewportHeight - RULER_HEIGHT - SCROLLBAR_THICKNESS
|
|
2121
|
+
exports.TRACK_HEIGHT,
|
|
2122
|
+
this.viewportHeight - exports.RULER_HEIGHT - SCROLLBAR_THICKNESS
|
|
1786
2123
|
);
|
|
1787
2124
|
this.scrollTop += target.before ? -page : page;
|
|
1788
2125
|
this.clampScroll();
|
|
@@ -1881,7 +2218,7 @@ var Timeline = class _Timeline {
|
|
|
1881
2218
|
const { x, y } = this.localCoords(e);
|
|
1882
2219
|
if (this.scrollbarDrag) {
|
|
1883
2220
|
if (this.scrollbarDrag.axis === "v") {
|
|
1884
|
-
const visibleH = this.viewportHeight - RULER_HEIGHT - SCROLLBAR_THICKNESS;
|
|
2221
|
+
const visibleH = this.viewportHeight - exports.RULER_HEIGHT - SCROLLBAR_THICKNESS;
|
|
1885
2222
|
const contentH = contentHeight(
|
|
1886
2223
|
this.project.tracks,
|
|
1887
2224
|
this.drag?.kind === "move"
|
|
@@ -2019,8 +2356,9 @@ var Timeline = class _Timeline {
|
|
|
2019
2356
|
nextStart = this.applySnap(nextStart, drag.clipId);
|
|
2020
2357
|
const tiRaw = this.trackIndexAtY(y);
|
|
2021
2358
|
const phantomIdx = this.project.tracks.length;
|
|
2022
|
-
const phantomScreenY = RULER_HEIGHT + phantomIdx * TRACK_HEIGHT - this.scrollTop;
|
|
2023
|
-
const
|
|
2359
|
+
const phantomScreenY = exports.RULER_HEIGHT + phantomIdx * exports.TRACK_HEIGHT - this.scrollTop;
|
|
2360
|
+
const viewportBottom = this.viewportHeight - SCROLLBAR_THICKNESS;
|
|
2361
|
+
const onPhantom = y >= phantomScreenY && y < Math.max(phantomScreenY + exports.TRACK_HEIGHT, viewportBottom);
|
|
2024
2362
|
const intendedTrackIndex = onPhantom ? phantomIdx : tiRaw >= 0 ? tiRaw : drag.trackIndex;
|
|
2025
2363
|
let ghostTrackIndex = intendedTrackIndex;
|
|
2026
2364
|
let overlap = false;
|
|
@@ -2058,7 +2396,7 @@ var Timeline = class _Timeline {
|
|
|
2058
2396
|
dragScrollSpeedY() {
|
|
2059
2397
|
if (!this.drag || this.drag.kind !== "move") return 0;
|
|
2060
2398
|
const y = this.lastDragPointerY;
|
|
2061
|
-
const top = RULER_HEIGHT;
|
|
2399
|
+
const top = exports.RULER_HEIGHT;
|
|
2062
2400
|
const bottom = this.viewportHeight - SCROLLBAR_THICKNESS;
|
|
2063
2401
|
const zone = 36;
|
|
2064
2402
|
const maxSpeed = 16;
|
|
@@ -2785,7 +3123,19 @@ var Editor = class _Editor {
|
|
|
2785
3123
|
this.pxPerSec = clampScale2(opts.initialScale ?? DEFAULT_PX_PER_SEC);
|
|
2786
3124
|
this.snap = opts.initialSnap !== false;
|
|
2787
3125
|
this.locale = mergeLocale(opts.locale);
|
|
3126
|
+
if (opts.trackHeight != null || opts.rulerHeight != null) {
|
|
3127
|
+
setTimelineMetrics({
|
|
3128
|
+
...opts.trackHeight != null ? { trackHeight: opts.trackHeight } : {},
|
|
3129
|
+
...opts.rulerHeight != null ? { rulerHeight: opts.rulerHeight } : {}
|
|
3130
|
+
});
|
|
3131
|
+
}
|
|
2788
3132
|
applyTheme(this.container, opts.theme);
|
|
3133
|
+
if (opts.timelineHeight != null && opts.timelineHeight > 0) {
|
|
3134
|
+
this.container.style.setProperty(
|
|
3135
|
+
"--aicut-timeline-height",
|
|
3136
|
+
`${Math.round(opts.timelineHeight)}px`
|
|
3137
|
+
);
|
|
3138
|
+
}
|
|
2789
3139
|
this.ui = new EditorUI(this.container, this, {
|
|
2790
3140
|
onPlayToggle: () => this.togglePlay(),
|
|
2791
3141
|
onSplit: () => this.split(),
|
|
@@ -2803,7 +3153,11 @@ var Editor = class _Editor {
|
|
|
2803
3153
|
onMoveClip: (id, opts2) => this.moveClip(id, opts2),
|
|
2804
3154
|
onResizeClip: (id, edits) => this.resizeClip(id, edits)
|
|
2805
3155
|
});
|
|
2806
|
-
|
|
3156
|
+
const engineFactory = opts.playbackEngine ?? ((o) => new HtmlVideoEngine(o));
|
|
3157
|
+
this.engine = engineFactory({
|
|
3158
|
+
host: this.ui.previewHost,
|
|
3159
|
+
project: this.project
|
|
3160
|
+
});
|
|
2807
3161
|
this.engine.onTimeUpdate = (ms) => {
|
|
2808
3162
|
this.bus.emit("time", { timeMs: ms });
|
|
2809
3163
|
this.ui.onTimeTick(ms);
|
|
@@ -3348,17 +3702,20 @@ function clampScale2(s) {
|
|
|
3348
3702
|
return Math.max(MIN_PX_PER_SEC, Math.min(MAX_PX_PER_SEC, s));
|
|
3349
3703
|
}
|
|
3350
3704
|
|
|
3705
|
+
exports.CanvasCompositorEngine = CanvasCompositorEngine;
|
|
3351
3706
|
exports.Editor = Editor;
|
|
3352
3707
|
exports.HEADER_WIDTH = HEADER_WIDTH;
|
|
3353
|
-
exports.
|
|
3354
|
-
exports.TRACK_HEIGHT = TRACK_HEIGHT;
|
|
3708
|
+
exports.HtmlVideoEngine = HtmlVideoEngine;
|
|
3355
3709
|
exports.Timeline = Timeline;
|
|
3710
|
+
exports.canvasCompositorEngineFactory = canvasCompositorEngineFactory;
|
|
3356
3711
|
exports.createEmptyProject = createEmptyProject;
|
|
3357
3712
|
exports.createId = createId;
|
|
3358
3713
|
exports.formatLabel = formatLabel;
|
|
3714
|
+
exports.htmlVideoEngineFactory = htmlVideoEngineFactory;
|
|
3359
3715
|
exports.localeEn = localeEn;
|
|
3360
3716
|
exports.localeZh = localeZh;
|
|
3361
3717
|
exports.mergeLocale = mergeLocale;
|
|
3362
3718
|
exports.normalizeProject = normalizeProject;
|
|
3719
|
+
exports.setTimelineMetrics = setTimelineMetrics;
|
|
3363
3720
|
//# sourceMappingURL=index.cjs.map
|
|
3364
3721
|
//# sourceMappingURL=index.cjs.map
|