完善韩泰 EOT 标注编辑器并更新标注规范与项目资料
This commit is contained in:
@@ -1,13 +0,0 @@
|
||||
/** Read-only source-table evidence, separate from timed transcripts and EOT labels. */
|
||||
export interface CallReference {
|
||||
call_id: string;
|
||||
source: {
|
||||
file: string;
|
||||
sheet: string;
|
||||
row: number;
|
||||
};
|
||||
fields: {
|
||||
label: string;
|
||||
value: string;
|
||||
}[];
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
export interface EditHistory<T> { past: T[]; present: T; future: T[] }
|
||||
const LIMIT = 40;
|
||||
|
||||
export function createEditHistory<T>(present: T): EditHistory<T> {
|
||||
return { past: [], present, future: [] };
|
||||
}
|
||||
|
||||
export function commitEdit<T>(history: EditHistory<T>, present: T): EditHistory<T> {
|
||||
if (present === history.present) return history;
|
||||
return { past: [...history.past, history.present].slice(-LIMIT), present, future: [] };
|
||||
}
|
||||
|
||||
export function undoEdit<T>(history: EditHistory<T>): EditHistory<T> {
|
||||
if (!history.past.length) return history;
|
||||
return { past: history.past.slice(0, -1), present: history.past.at(-1)!, future: [history.present, ...history.future] };
|
||||
}
|
||||
|
||||
export function redoEdit<T>(history: EditHistory<T>): EditHistory<T> {
|
||||
if (!history.future.length) return history;
|
||||
return { past: [...history.past, history.present].slice(-LIMIT), present: history.future[0], future: history.future.slice(1) };
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
export interface SnapPoint { ms: number; label: string }
|
||||
|
||||
/** Keep magnetic selection consistent with zoom without attracting distant audio. */
|
||||
export function snapTimelineTime(ms: number, points: SnapPoint[], duration: number, width: number, bypass = false) {
|
||||
const bounded = Math.round(Math.max(0, Math.min(duration, ms)));
|
||||
const radius = Math.min(500, duration / Math.max(1, width) * 8);
|
||||
let point: SnapPoint | null = null;
|
||||
let distance = radius;
|
||||
if (!bypass) {
|
||||
for (const candidate of points) {
|
||||
if (candidate.ms < 0 || candidate.ms > duration) continue;
|
||||
const delta = Math.abs(candidate.ms - bounded);
|
||||
if (delta <= distance && (!point || delta < distance)) { point = candidate; distance = delta; }
|
||||
}
|
||||
}
|
||||
return { ms: point?.ms ?? bounded, point };
|
||||
}
|
||||
|
||||
/** A resized segment must stay in the recording and retain a positive duration. */
|
||||
export function resizeTranscriptRange(start: number, end: number, edge: "start" | "end", ms: number, duration: number) {
|
||||
const value = Math.round(ms);
|
||||
return edge === "start"
|
||||
? { start_ms: Math.max(0, Math.min(end - 1, value)), end_ms: end }
|
||||
: { start_ms: start, end_ms: Math.max(start + 1, Math.min(duration, value)) };
|
||||
}
|
||||
Reference in New Issue
Block a user