implemented the lower priority features; haven't tested them

This commit is contained in:
2026-05-05 20:46:55 -06:00
parent cde635a660
commit 4d4dfa7f7c
12 changed files with 957 additions and 60 deletions

View File

@ -14,6 +14,9 @@ import type {
SilenceTrimGroup,
TimelineMarker,
Chapter,
ZoomConfig,
ClipInfo,
BackgroundMusicConfig,
} from '../types/project';
interface EditorState {
@ -50,6 +53,10 @@ interface EditorState {
backendUrl: string;
zonePreviewPaddingSeconds: number;
zoomConfig: ZoomConfig;
additionalClips: ClipInfo[];
backgroundMusic: BackgroundMusicConfig | null;
}
interface EditorActions {
@ -104,6 +111,12 @@ interface EditorActions {
getWordAtTime: (time: number) => number;
loadProject: (projectData: any) => void;
reset: () => void;
setZoomConfig: (config: Partial<ZoomConfig>) => void;
addAdditionalClip: (path: string, label?: string) => void;
removeAdditionalClip: (id: string) => void;
reorderAdditionalClip: (id: string, direction: -1 | 1) => void;
setBackgroundMusic: (config: BackgroundMusicConfig | null) => void;
updateBackgroundMusic: (updates: Partial<BackgroundMusicConfig>) => void;
}
const ZONE_PREVIEW_PADDING_KEY = 'talkedit-zone-preview-padding-seconds';
@ -146,6 +159,9 @@ const initialState: EditorState = {
exportProgress: 0,
backendUrl: 'http://127.0.0.1:8000',
zonePreviewPaddingSeconds: getStoredZonePreviewPaddingSeconds(),
zoomConfig: { enabled: false, zoomFactor: 1, panX: 0, panY: 0 },
additionalClips: [],
backgroundMusic: null,
};
let nextRangeId = 1;
@ -190,7 +206,7 @@ export const useEditorStore = create<EditorState & EditorActions>()(
setTranscriptionModel: (model) => set({ transcriptionModel: model }),
saveProject: (): ProjectFile => {
const { videoPath, words, segments, cutRanges, muteRanges, gainRanges, speedRanges, globalGainDb, silenceTrimGroups, timelineMarkers, transcriptionModel, language, exportedAudioPath } = get();
const { videoPath, words, segments, cutRanges, muteRanges, gainRanges, speedRanges, globalGainDb, silenceTrimGroups, timelineMarkers, transcriptionModel, language, exportedAudioPath, zoomConfig, additionalClips, backgroundMusic } = get();
if (!videoPath) throw new Error('No video loaded');
const now = new Date().toISOString();
// Strip globalStartIndex (runtime-only field) before persisting.
@ -214,8 +230,11 @@ export const useEditorStore = create<EditorState & EditorActions>()(
silenceTrimGroups,
timelineMarkers,
language,
createdAt: now, // will be overwritten if we track original creation time later
createdAt: now,
modifiedAt: now,
zoomConfig,
additionalClips,
backgroundMusic: backgroundMusic ?? undefined,
};
},
@ -600,6 +619,43 @@ export const useEditorStore = create<EditorState & EditorActions>()(
return lo < words.length ? lo : words.length - 1;
},
setZoomConfig: (config) => {
const { zoomConfig } = get();
set({ zoomConfig: { ...zoomConfig, ...config } });
},
addAdditionalClip: (path, label) => {
const { additionalClips } = get();
const id = `clip_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`;
set({ additionalClips: [...additionalClips, { id, path, label: label || path.split(/[/\\]/).pop() || 'Clip' }] });
},
removeAdditionalClip: (id) => {
const { additionalClips } = get();
set({ additionalClips: additionalClips.filter((c) => c.id !== id) });
},
reorderAdditionalClip: (id, direction) => {
const { additionalClips } = get();
const idx = additionalClips.findIndex((c) => c.id === id);
if (idx === -1) return;
const target = idx + direction;
if (target < 0 || target >= additionalClips.length) return;
const reordered = [...additionalClips];
[reordered[idx], reordered[target]] = [reordered[target], reordered[idx]];
set({ additionalClips: reordered });
},
setBackgroundMusic: (config) => {
set({ backgroundMusic: config });
},
updateBackgroundMusic: (updates) => {
const { backgroundMusic } = get();
if (!backgroundMusic) return;
set({ backgroundMusic: { ...backgroundMusic, ...updates } });
},
loadProject: (data) => {
const { backendUrl, zonePreviewPaddingSeconds, projectFilePath } = get();
const url = `${backendUrl}/file?path=${encodeURIComponent(data.videoPath)}`;
@ -634,6 +690,9 @@ export const useEditorStore = create<EditorState & EditorActions>()(
transcriptionModel: data.transcriptionModel ?? null,
language: data.language || '',
exportedAudioPath: data.exportedAudioPath ?? null,
zoomConfig: data.zoomConfig || { enabled: false, zoomFactor: 1, panX: 0, panY: 0 },
additionalClips: data.additionalClips || [],
backgroundMusic: data.backgroundMusic || null,
});
},