141 lines
3.6 KiB
TypeScript
141 lines
3.6 KiB
TypeScript
import type { Marker, SwitchType } from './types';
|
|
import { ZOOM_DEFAULT } from './types';
|
|
|
|
export const app = $state({
|
|
markers: [] as Marker[],
|
|
selectedIds: new Set<string>(),
|
|
isPlaying: false,
|
|
currentTime: 0,
|
|
duration: 0,
|
|
fps: 30,
|
|
pixelsPerSecond: ZOOM_DEFAULT,
|
|
scrollLeft: 0,
|
|
activeSwitch: 'mxbrown' as SwitchType,
|
|
videoUrl: null as string | null,
|
|
videoName: null as string | null,
|
|
timelineWidth: 800,
|
|
previewSpeed: 1,
|
|
});
|
|
|
|
let markerCounter = 0;
|
|
|
|
export function createMarkerId(): string {
|
|
markerCounter += 1;
|
|
return `m${markerCounter}`;
|
|
}
|
|
|
|
export function setMarkers(value: Marker[]) {
|
|
app.markers = value;
|
|
const maxId = value.reduce((max, m) => {
|
|
const num = parseInt(m.id.replace(/\D/g, ''), 10);
|
|
return Number.isFinite(num) ? Math.max(max, num) : max;
|
|
}, 0);
|
|
markerCounter = maxId;
|
|
}
|
|
|
|
export function setSelectedIds(ids: Set<string>) {
|
|
app.selectedIds = ids;
|
|
}
|
|
|
|
export function setCurrentTime(value: number) {
|
|
app.currentTime = value;
|
|
}
|
|
|
|
export function setDuration(value: number) {
|
|
app.duration = value;
|
|
}
|
|
|
|
export function setIsPlaying(value: boolean) {
|
|
app.isPlaying = value;
|
|
}
|
|
|
|
export function setFps(value: number) {
|
|
app.fps = value;
|
|
}
|
|
|
|
export function setPreviewSpeed(value: number) {
|
|
app.previewSpeed = Math.max(0.25, Math.min(2, value));
|
|
}
|
|
|
|
export function setPixelsPerSecond(value: number) {
|
|
app.pixelsPerSecond = value;
|
|
}
|
|
|
|
export function setScrollLeft(value: number) {
|
|
app.scrollLeft = value;
|
|
}
|
|
|
|
export function setActiveSwitch(value: SwitchType) {
|
|
app.activeSwitch = value;
|
|
}
|
|
|
|
export function setTimelineWidth(value: number) {
|
|
app.timelineWidth = value;
|
|
}
|
|
|
|
export function addMarker(time: number, key: string, code?: string): Marker {
|
|
const marker: Marker = { id: createMarkerId(), time, key, code };
|
|
app.markers = [...app.markers, marker].sort((a, b) => a.time - b.time);
|
|
return marker;
|
|
}
|
|
|
|
export function updateMarker(
|
|
id: string,
|
|
patch: Partial<Pick<Marker, 'time' | 'key' | 'code' | 'releaseTime'>>,
|
|
) {
|
|
app.markers = app.markers
|
|
.map((m) => (m.id === id ? { ...m, ...patch } : m))
|
|
.sort((a, b) => a.time - b.time);
|
|
}
|
|
|
|
export function setMarkerTimes(updates: Map<string, number>) {
|
|
app.markers = app.markers
|
|
.map((m) => {
|
|
const time = updates.get(m.id);
|
|
return time === undefined ? m : { ...m, time };
|
|
})
|
|
.sort((a, b) => a.time - b.time);
|
|
}
|
|
|
|
export function deleteMarkers(ids: Set<string>) {
|
|
app.markers = app.markers.filter((m) => !ids.has(m.id));
|
|
const next = new Set(app.selectedIds);
|
|
for (const id of ids) next.delete(id);
|
|
app.selectedIds = next;
|
|
}
|
|
|
|
export function selectAll() {
|
|
app.selectedIds = new Set(app.markers.map((m) => m.id));
|
|
}
|
|
|
|
export function toggleSelection(id: string, additive: boolean) {
|
|
const next = additive ? new Set(app.selectedIds) : new Set<string>();
|
|
if (next.has(id)) next.delete(id);
|
|
else next.add(id);
|
|
app.selectedIds = next;
|
|
}
|
|
|
|
export function setVideoUrl(url: string | null, name: string | null = null) {
|
|
if (app.videoUrl) URL.revokeObjectURL(app.videoUrl);
|
|
app.videoUrl = url;
|
|
app.videoName = name;
|
|
}
|
|
|
|
export function snapToFrame(time: number): number {
|
|
const frameDuration = 1 / app.fps;
|
|
return Math.round(time / frameDuration) * frameDuration;
|
|
}
|
|
|
|
export function nudgeSelected(deltaFrames: number) {
|
|
if (app.selectedIds.size === 0) return;
|
|
const frameDuration = 1 / app.fps;
|
|
const delta = deltaFrames * frameDuration;
|
|
app.markers = app.markers
|
|
.map((m) => {
|
|
if (!app.selectedIds.has(m.id)) return m;
|
|
const next = Math.max(0, Math.min(app.duration || Infinity, m.time + delta));
|
|
return { ...m, time: snapToFrame(next) };
|
|
})
|
|
.sort((a, b) => a.time - b.time);
|
|
}
|