verified hotkeys

This commit is contained in:
2026-05-05 10:22:35 -06:00
parent 1678d28db7
commit 21e4255325
9 changed files with 341 additions and 132 deletions

View File

@ -12,6 +12,8 @@ import type {
SilenceDetectionRange,
SilenceTrimSettings,
SilenceTrimGroup,
TimelineMarker,
Chapter,
} from '../types/project';
interface EditorState {
@ -27,6 +29,7 @@ interface EditorState {
speedRanges: SpeedRange[];
globalGainDb: number;
silenceTrimGroups: SilenceTrimGroup[];
timelineMarkers: TimelineMarker[];
transcriptionModel: string | null;
language: string;
@ -89,6 +92,10 @@ interface EditorActions {
settings: SilenceTrimSettings;
}) => { groupId: string; appliedCount: number };
removeSilenceTrimGroup: (groupId: string) => void;
addTimelineMarker: (time: number, label?: string, color?: string) => void;
updateTimelineMarker: (id: string, updates: Partial<TimelineMarker>) => void;
removeTimelineMarker: (id: string) => void;
getChapters: () => Chapter[];
setTranscribing: (active: boolean, progress?: number, status?: string) => void;
setExporting: (active: boolean, progress?: number) => void;
setZonePreviewPaddingSeconds: (seconds: number) => void;
@ -122,6 +129,7 @@ const initialState: EditorState = {
speedRanges: [],
globalGainDb: 0,
silenceTrimGroups: [],
timelineMarkers: [],
transcriptionModel: null,
language: '',
currentTime: 0,
@ -182,7 +190,7 @@ export const useEditorStore = create<EditorState & EditorActions>()(
setTranscriptionModel: (model) => set({ transcriptionModel: model }),
saveProject: (): ProjectFile => {
const { videoPath, words, segments, cutRanges, muteRanges, gainRanges, speedRanges, globalGainDb, silenceTrimGroups, transcriptionModel, language, exportedAudioPath } = get();
const { videoPath, words, segments, cutRanges, muteRanges, gainRanges, speedRanges, globalGainDb, silenceTrimGroups, timelineMarkers, transcriptionModel, language, exportedAudioPath } = get();
if (!videoPath) throw new Error('No video loaded');
const now = new Date().toISOString();
// Strip globalStartIndex (runtime-only field) before persisting.
@ -204,6 +212,7 @@ export const useEditorStore = create<EditorState & EditorActions>()(
speedRanges,
globalGainDb,
silenceTrimGroups,
timelineMarkers,
language,
createdAt: now, // will be overwritten if we track original creation time later
modifiedAt: now,
@ -453,6 +462,40 @@ export const useEditorStore = create<EditorState & EditorActions>()(
});
},
addTimelineMarker: (time, label, color) => {
const { timelineMarkers } = get();
const newMarker: TimelineMarker = {
id: `marker_${nextRangeId++}`,
time,
label: label || `Marker ${timelineMarkers.length + 1}`,
color: color || '#6366f1',
};
set({ timelineMarkers: [...timelineMarkers, newMarker].sort((a, b) => a.time - b.time) });
},
updateTimelineMarker: (id, updates) => {
const { timelineMarkers } = get();
set({
timelineMarkers: timelineMarkers
.map((m) => (m.id === id ? { ...m, ...updates } : m))
.sort((a, b) => a.time - b.time),
});
},
removeTimelineMarker: (id) => {
const { timelineMarkers } = get();
set({ timelineMarkers: timelineMarkers.filter((m) => m.id !== id) });
},
getChapters: () => {
const { timelineMarkers } = get();
return timelineMarkers.map((m) => ({
markerId: m.id,
label: m.label,
startTime: m.time,
}));
},
setTranscribing: (active, progress, status) =>
set({
isTranscribing: active,
@ -587,6 +630,7 @@ export const useEditorStore = create<EditorState & EditorActions>()(
speedRanges: data.speedRanges || [],
globalGainDb: typeof data.globalGainDb === 'number' ? data.globalGainDb : 0,
silenceTrimGroups: data.silenceTrimGroups || [],
timelineMarkers: data.timelineMarkers || [],
transcriptionModel: data.transcriptionModel ?? null,
language: data.language || '',
exportedAudioPath: data.exportedAudioPath ?? null,