Add timestamp window

This commit is contained in:
Skyler Lehmkuhl 2025-10-23 06:21:02 -04:00
parent 9649fe173b
commit c46c28c9bb
5 changed files with 206 additions and 37 deletions

File diff suppressed because one or more lines are too long

View File

@ -5,7 +5,6 @@
<link rel="stylesheet" href="styles.css" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title>Lightningbeam</title>
<script src="Tone.js"></script>
<!-- Polyfill for web mode -->
<link type='text/css' rel='stylesheet' href='/jsMenus.css'>

View File

@ -1032,9 +1032,18 @@ async function handleAudioEvent(event) {
case 'PlaybackPosition':
// Sync frontend time with DAW time
if (playing) {
context.activeObject.currentTime = event.time;
// Quantize time to framerate for animation playback
const framerate = context.activeObject.frameRate;
const frameDuration = 1 / framerate;
const quantizedTime = Math.floor(event.time / frameDuration) * frameDuration;
context.activeObject.currentTime = quantizedTime;
if (context.timelineWidget?.timelineState) {
context.timelineWidget.timelineState.currentTime = event.time;
context.timelineWidget.timelineState.currentTime = quantizedTime;
}
// Update time display
if (context.updateTimeDisplay) {
context.updateTimeDisplay();
}
}
break;
@ -2569,7 +2578,7 @@ function stage() {
"image/jpeg",
"image/webp", //'image/svg+xml' // Disabling SVG until we can export them nicely
];
const audioTypes = ["audio/mpeg"]; // TODO: figure out what other audio formats Tone.js accepts
const audioTypes = ["audio/mpeg"];
if (e.dataTransfer.items) {
let i = 0;
for (let item of e.dataTransfer.items) {
@ -3826,16 +3835,111 @@ function timelineV2() {
controls.push(recordGroup);
// Time format toggle button
const toggleButton = document.createElement("button");
toggleButton.textContent = timelineWidget.timelineState.timeFormat === 'frames' ? 'Frames' : 'Seconds';
toggleButton.style.marginLeft = '10px';
toggleButton.addEventListener("click", () => {
timelineWidget.toggleTimeFormat();
toggleButton.textContent = timelineWidget.timelineState.timeFormat === 'frames' ? 'Frames' : 'Seconds';
updateCanvasSize(); // Redraw after format change
// Time display
const timeDisplay = document.createElement("div");
timeDisplay.className = "time-display";
timeDisplay.style.cursor = "pointer";
timeDisplay.title = "Click to change time format";
// Function to update time display
const updateTimeDisplay = () => {
const currentTime = context.activeObject?.currentTime || 0;
const timeFormat = timelineWidget.timelineState.timeFormat;
const framerate = timelineWidget.timelineState.framerate;
if (timeFormat === 'frames') {
// Frames mode: show frame number and framerate
const frameNumber = Math.floor(currentTime * framerate);
timeDisplay.innerHTML = `
<div class="time-value time-frame-clickable" data-action="toggle-format">${frameNumber}</div>
<div class="time-label">FRAME</div>
<div class="time-fps-group time-fps-clickable" data-action="edit-fps">
<div class="time-value">${framerate}</div>
<div class="time-label">FPS</div>
</div>
`;
} else {
// Seconds mode: show MM:SS.mmm or HH:MM:SS.mmm
const totalSeconds = Math.floor(currentTime);
const milliseconds = Math.floor((currentTime - totalSeconds) * 1000);
const seconds = totalSeconds % 60;
const minutes = Math.floor(totalSeconds / 60) % 60;
const hours = Math.floor(totalSeconds / 3600);
if (hours > 0) {
timeDisplay.innerHTML = `
<div class="time-value">${hours}:${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}.${String(milliseconds).padStart(3, '0')}</div>
<div class="time-label">SEC</div>
`;
} else {
timeDisplay.innerHTML = `
<div class="time-value">${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}.${String(milliseconds).padStart(3, '0')}</div>
<div class="time-label">SEC</div>
`;
}
}
};
// Click handler for time display
timeDisplay.addEventListener("click", (e) => {
const target = e.target.closest('[data-action]');
if (!target) {
// Clicked outside specific elements in frames mode or anywhere in seconds mode
// Toggle format
timelineWidget.toggleTimeFormat();
updateTimeDisplay();
updateCanvasSize();
return;
}
const action = target.getAttribute('data-action');
if (action === 'toggle-format') {
// Clicked on frame number - toggle format
timelineWidget.toggleTimeFormat();
updateTimeDisplay();
updateCanvasSize();
} else if (action === 'edit-fps') {
// Clicked on FPS - show input to edit framerate
console.log('[FPS Edit] Starting FPS edit');
const currentFps = timelineWidget.timelineState.framerate;
console.log('[FPS Edit] Current FPS:', currentFps);
const newFps = prompt('Enter framerate (FPS):', currentFps);
console.log('[FPS Edit] Prompt returned:', newFps);
if (newFps !== null && !isNaN(newFps) && newFps > 0) {
const fps = parseFloat(newFps);
console.log('[FPS Edit] Parsed FPS:', fps);
console.log('[FPS Edit] Setting framerate on timeline state');
timelineWidget.timelineState.framerate = fps;
console.log('[FPS Edit] Setting frameRate on activeObject');
context.activeObject.frameRate = fps;
console.log('[FPS Edit] Updating time display');
updateTimeDisplay();
console.log('[FPS Edit] Requesting redraw');
if (timelineWidget.requestRedraw) {
timelineWidget.requestRedraw();
}
console.log('[FPS Edit] Done');
}
}
});
controls.push(toggleButton);
// Initial update
updateTimeDisplay();
// Store reference for updates
context.timeDisplay = timeDisplay;
context.updateTimeDisplay = updateTimeDisplay;
controls.push(timeDisplay);
return controls;
};
@ -5689,27 +5793,6 @@ function getMimeType(filePath) {
}
}
function startToneOnUserInteraction() {
// Function to handle the first interaction (click or key press)
const startTone = () => {
Tone.start()
.then(() => {
console.log("Tone.js started!");
})
.catch((err) => {
console.error("Error starting Tone.js:", err);
});
// Remove the event listeners to prevent them from firing again
document.removeEventListener("click", startTone);
document.removeEventListener("keydown", startTone);
};
// Add event listeners for mouse click and key press
document.addEventListener("click", startTone);
document.addEventListener("keydown", startTone);
}
startToneOnUserInteraction();
function renderAll() {
try {

View File

@ -947,3 +947,88 @@ button {
background-color: #444;
}
}
/* Time Display */
.time-display {
display: inline-flex;
align-items: center;
gap: 8px;
background: linear-gradient(to bottom, #3a3a3a, #2a2a2a);
border: 1px solid #555;
border-radius: 6px;
padding: 6px 12px;
margin-left: 10px;
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.3), 0 1px 0 rgba(255, 255, 255, 0.1);
transition: all 0.15s ease;
}
.time-display:hover {
background: linear-gradient(to bottom, #4a4a4a, #3a3a3a);
border-color: #666;
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.3), 0 1px 0 rgba(255, 255, 255, 0.15);
}
.time-display:active {
background: linear-gradient(to bottom, #2a2a2a, #1a1a1a);
box-shadow: inset 0 2px 3px rgba(0, 0, 0, 0.5);
}
.time-value {
font-family: 'Courier New', monospace;
font-size: 18px;
font-weight: bold;
color: #f0f0f0;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
min-width: 24px;
text-align: center;
}
.time-fps-group {
display: inline-flex;
align-items: center;
gap: 8px;
}
.time-fps-clickable {
padding: 2px 6px;
border-radius: 3px;
transition: background-color 0.15s ease;
cursor: pointer;
}
.time-fps-clickable:hover {
background-color: rgba(100, 150, 255, 0.3);
}
.time-fps-clickable:hover .time-value {
text-decoration: underline;
}
.time-label {
font-size: 9px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
color: #999;
margin-right: 4px;
}
.time-label:last-child {
margin-right: 0;
}
/* Dark mode time display adjustments */
@media (prefers-color-scheme: dark) {
.time-display {
background: linear-gradient(to bottom, #3a3a3a, #2a2a2a);
border-color: #555;
}
.time-value {
color: #f0f0f0;
}
.time-label {
color: #999;
}
}

View File

@ -578,6 +578,11 @@ class TimelineWindowV2 extends Widget {
draw(ctx) {
ctx.save()
// Update time display if it exists
if (this.context.updateTimeDisplay) {
this.context.updateTimeDisplay();
}
// Draw background
ctx.fillStyle = backgroundColor
ctx.fillRect(0, 0, this.width, this.height)