Edit: IN/OUT marker triangles + Postavi IN/OUT buttons

User feedback: Workflow is - click + Enter sets a marker triangle, then
button moves the red handle to that triangle. Triangle near LEFT handle
= IN candidate (green), near RIGHT = OUT candidate (red).

Visual:
- Green triangle (▼) above trim bar = IN candidate position
- Red triangle (▼) above trim bar = OUT candidate position
- White line (playhead) = current video position (moves during playback)
- Red handles (existing) = actual clip start/end

Workflow:
1. Click on waveform → white playhead jumps there
2. Press Enter → playhead starts moving (plays)
   ALSO: triangle gets placed at current position
   - If position closer to LEFT handle → green IN triangle
   - If position closer to RIGHT handle → red OUT triangle
3. Listen, decide 'this is the right spot'
4. Click ▼ Postavi IN button → red LEFT handle jumps to green triangle
   (or ▼ Postavi OUT for right handle)
5. Now red handle and triangle are aligned = clip boundary committed

Triangles persist until next play press (= next candidate).
Buttons styled with matching color (green for IN, red for OUT).
This commit is contained in:
Sebastjan Artič 2026-04-30 13:42:30 +00:00
parent 4376f7529f
commit 5d817c586b

View File

@ -1085,6 +1085,16 @@
<div style="width:4px; height:32px; background:#fff; border-radius:2px;"></div> <div style="width:4px; height:32px; background:#fff; border-radius:2px;"></div>
</div> </div>
<!-- IN marker (zelen trikotnik — kjer si pritisnil play blizu levega handle) -->
<div id="marker-in" style="position:absolute; top:-14px; left:calc(${pctOfStr(startInit, videoDuration)}% - 7px); width:14px; height:14px; z-index:5; pointer-events:none;">
<div style="width:0; height:0; border-left:7px solid transparent; border-right:7px solid transparent; border-top:14px solid #4ade80; filter:drop-shadow(0 0 4px #4ade80);"></div>
</div>
<!-- OUT marker (rdeč trikotnik — kjer si pritisnil play blizu desnega handle) -->
<div id="marker-out" style="position:absolute; top:-14px; left:calc(${pctOfStr(endInit, videoDuration)}% - 7px); width:14px; height:14px; z-index:5; pointer-events:none;">
<div style="width:0; height:0; border-left:7px solid transparent; border-right:7px solid transparent; border-top:14px solid #ff6b6b; filter:drop-shadow(0 0 4px #ff6b6b);"></div>
</div>
<!-- Playhead (bel črtnik — "tracker") --> <!-- Playhead (bel črtnik — "tracker") -->
<div id="trim-playhead" style="position:absolute; top:-6px; bottom:-6px; left:0%; width:2px; background:#fff; z-index:2; pointer-events:none; opacity:1; box-shadow:0 0 8px rgba(255,255,255,0.8);"> <div id="trim-playhead" style="position:absolute; top:-6px; bottom:-6px; left:0%; width:2px; background:#fff; z-index:2; pointer-events:none; opacity:1; box-shadow:0 0 8px rgba(255,255,255,0.8);">
<!-- Trikotnik na vrhu --> <!-- Trikotnik na vrhu -->
@ -1099,7 +1109,7 @@
<!-- Hint --> <!-- Hint -->
<div style="font-size:11px; color:var(--muted); margin-top:6px; text-align:center;"> <div style="font-size:11px; color:var(--muted); margin-top:6px; text-align:center;">
Povleci rdeče ročaje · Klik na valove = skoči (bel črtnik) · Enter = play/pause od pozicije Klik = bel črtnik · Enter = play (postavi ▼ trikotnik kjer si bil) · "Postavi IN/OUT" = handle skoči na trikotnik
</div> </div>
</div> </div>
@ -1112,8 +1122,8 @@
</div> </div>
<div style="display:flex; gap:8px;"> <div style="display:flex; gap:8px;">
<button class="primary" id="preview-btn" onclick="previewSelection()" title="Predvajaj točno označen del" style="background:var(--accent); padding:8px 16px;">▶ Predvajaj odsek</button> <button class="primary" id="preview-btn" onclick="previewSelection()" title="Predvajaj točno označen del" style="background:var(--accent); padding:8px 16px;">▶ Predvajaj odsek</button>
<button class="small ghost" onclick="seekEditVideo('start')" title="Skoči na začetek">⤴ Začetek</button> <button class="small ghost" onclick="seekEditVideo('start')" title="Premakni levi handle na zelen trikotnik" style="border-color:#4ade80; color:#4ade80;">▼ Postavi IN</button>
<button class="small ghost" onclick="seekEditVideo('end')" title="Skoči na konec">↪ Konec</button> <button class="small ghost" onclick="seekEditVideo('end')" title="Premakni desni handle na rdeč trikotnik" style="border-color:#ff6b6b; color:#ff6b6b;">▼ Postavi OUT</button>
</div> </div>
</div> </div>
<div id="preview-status" style="margin-top:6px; font-size:12px; color:var(--muted); text-align:right;"></div> <div id="preview-status" style="margin-top:6px; font-size:12px; color:var(--muted); text-align:right;"></div>
@ -1154,6 +1164,17 @@
let trimEnd = endInit; let trimEnd = endInit;
let dragging = null; // 'left' / 'right' / null let dragging = null; // 'left' / 'right' / null
// Marker state — kje je bil zadnji play (loči levi/desni)
let markerInTime = startInit; // pozicija zelenega trikotnika
let markerOutTime = endInit; // pozicija rdečega trikotnika
const markerInEl = document.getElementById("marker-in");
const markerOutEl = document.getElementById("marker-out");
function renderMarkers() {
if (markerInEl) markerInEl.style.left = `calc(${pctOfStr(markerInTime, videoDuration)}% - 7px)`;
if (markerOutEl) markerOutEl.style.left = `calc(${pctOfStr(markerOutTime, videoDuration)}% - 7px)`;
}
function pctOf(t) { function pctOf(t) {
return (t / videoDuration) * 100; return (t / videoDuration) * 100;
} }
@ -1269,6 +1290,7 @@
trimScroll.scrollLeft = Math.max(0, scrollTarget); trimScroll.scrollLeft = Math.max(0, scrollTarget);
} }
renderTrim(); renderTrim();
renderMarkers();
}, 50); }, 50);
} }
@ -1288,6 +1310,17 @@
e.preventDefault(); e.preventDefault();
if (video) { if (video) {
if (video.paused) { if (video.paused) {
// Pred play-em: postavi trikotnik na trenutno pozicijo
// Loči levi/desni glede na bližino handle-jev
const t = video.currentTime;
const distToLeft = Math.abs(t - trimStart);
const distToRight = Math.abs(t - trimEnd);
if (distToLeft <= distToRight) {
markerInTime = t;
} else {
markerOutTime = t;
}
renderMarkers();
video.play().catch(() => {}); video.play().catch(() => {});
} else { } else {
video.pause(); video.pause();
@ -1317,12 +1350,25 @@
}); });
} }
// "⤴ Začetek" gumb = premakni LEVI handle na zelen trikotnik (commit IN)
// "↪ Konec" gumb = premakni DESNI handle na rdeč trikotnik (commit OUT)
window.seekEditVideo = function(which) { window.seekEditVideo = function(which) {
if (!video) return; if (!video) return;
const t = which === "start" ? trimStart : trimEnd;
video.currentTime = t;
if (which === "start") { if (which === "start") {
video.play(); // Commit IN: levi handle skoči na zelen trikotnik
if (markerInTime < trimEnd - 1) {
trimStart = markerInTime;
renderTrim();
// Skoči na to pozicijo
video.currentTime = trimStart;
}
} else {
// Commit OUT: desni handle skoči na rdeč trikotnik
if (markerOutTime > trimStart + 1) {
trimEnd = markerOutTime;
renderTrim();
video.currentTime = trimEnd;
}
} }
}; };
@ -1400,6 +1446,7 @@
const ro = new ResizeObserver(() => { const ro = new ResizeObserver(() => {
renderTrim(); renderTrim();
renderPlayhead(); renderPlayhead();
renderMarkers();
}); });
ro.observe(trimBar); ro.observe(trimBar);
@ -1408,6 +1455,7 @@
requestAnimationFrame(() => { requestAnimationFrame(() => {
renderTrim(); renderTrim();
renderPlayhead(); renderPlayhead();
renderMarkers();
// Nastavi 1x kot aktiven gumb // Nastavi 1x kot aktiven gumb
applyZoom(1); applyZoom(1);
console.log("[EditModal] after renderTrim", { console.log("[EditModal] after renderTrim", {