feat: 'add frames per second' <select /> per project - #50
Conversation
📝 WalkthroughWalkthroughThe Program Monitor now provides a project FPS dropdown with preset and Custom options. Changes update ChangesProject FPS selection
Estimated code review effort: 2 (Simple) | ~10 minutes Sequence Diagram(s)sequenceDiagram
participant fpsSel
participant syncFpsSel
participant project
participant MediaTimeline
fpsSel->>project: update project.fps
project->>syncFpsSel: synchronize preset or Custom option
syncFpsSel->>MediaTimeline: seek paused media
project->>project: mark timeline dirty and schedule saving
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@app.js`:
- Around line 5573-5578: Update syncAspectSel so option labels are assigned
through textContent rather than interpolating project.width and project.height
into innerHTML. Build the ASPECT_PRESETS options and the custom option as DOM
option elements, preserving selection behavior and the existing custom label.
In `@style.css`:
- Line 923: Update the `#fpsSel.head-select` styling so custom FPS values such as
“Custom · 29.97 fps” remain fully readable, either by increasing max-width to
accommodate common values or by using a shorter selected-value label while
preserving the existing selector behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 48504a05-62b9-4422-ab61-8506b1d5a28b
📒 Files selected for processing (6)
CHANGELOG.mdCLAUDE.mdREADME.mdapp.jsindex.htmlstyle.css
| function syncAspectSel() { | ||
| if (!els.aspectSel) return; | ||
| const i = ASPECT_PRESETS.findIndex((a) => a.w === project.width && a.h === project.height); | ||
| els.aspectSel.innerHTML = | ||
| ASPECT_PRESETS.map((a, j) => `<option value="${j}" ${j === i ? "selected" : ""}>${a.label}</option>`).join("") + | ||
| (i < 0 ? `<option value="custom" selected>Custom · ${project.width}×${project.height}</option>` : ""); |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Avoid interpolating project dimensions into innerHTML.
project.width and project.height come from loaded project data and are inserted directly into the custom option label. Build <option> elements and assign textContent instead; this avoids malformed or imported project data being interpreted as markup.
Proposed fix
function syncAspectSel() {
if (!els.aspectSel) return;
const i = ASPECT_PRESETS.findIndex((a) => a.w === project.width && a.h === project.height);
- els.aspectSel.innerHTML =
- ASPECT_PRESETS.map((a, j) => `<option value="${j}" ${j === i ? "selected" : ""}>${a.label}</option>`).join("") +
- (i < 0 ? `<option value="custom" selected>Custom · ${project.width}×${project.height}</option>` : "");
+ const options = ASPECT_PRESETS.map((a, j) => {
+ const option = document.createElement("option");
+ option.value = String(j);
+ option.textContent = a.label;
+ option.selected = j === i;
+ return option;
+ });
+ if (i < 0) {
+ const option = document.createElement("option");
+ option.value = "custom";
+ option.textContent = `Custom · ${project.width}×${project.height}`;
+ option.selected = true;
+ options.push(option);
+ }
+ els.aspectSel.replaceChildren(...options);
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| function syncAspectSel() { | |
| if (!els.aspectSel) return; | |
| const i = ASPECT_PRESETS.findIndex((a) => a.w === project.width && a.h === project.height); | |
| els.aspectSel.innerHTML = | |
| ASPECT_PRESETS.map((a, j) => `<option value="${j}" ${j === i ? "selected" : ""}>${a.label}</option>`).join("") + | |
| (i < 0 ? `<option value="custom" selected>Custom · ${project.width}×${project.height}</option>` : ""); | |
| function syncAspectSel() { | |
| if (!els.aspectSel) return; | |
| const i = ASPECT_PRESETS.findIndex((a) => a.w === project.width && a.h === project.height); | |
| const options = ASPECT_PRESETS.map((a, j) => { | |
| const option = document.createElement("option"); | |
| option.value = String(j); | |
| option.textContent = a.label; | |
| option.selected = j === i; | |
| return option; | |
| }); | |
| if (i < 0) { | |
| const option = document.createElement("option"); | |
| option.value = "custom"; | |
| option.textContent = `Custom · ${project.width}×${project.height}`; | |
| option.selected = true; | |
| options.push(option); | |
| } | |
| els.aspectSel.replaceChildren(...options); | |
| } |
🧰 Tools
🪛 ast-grep (0.45.0)
[warning] 5575-5577: Avoid assigning untrusted data to innerHTML/outerHTML or document.write
Context: els.aspectSel.innerHTML =
ASPECT_PRESETS.map((a, j) => <option value="${j}" ${j === i ? "selected" : ""}>${a.label}</option>).join("") +
(i < 0 ? <option value="custom" selected>Custom · ${project.width}×${project.height}</option> : "")
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting').
(inner-outer-html)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@app.js` around lines 5573 - 5578, Update syncAspectSel so option labels are
assigned through textContent rather than interpolating project.width and
project.height into innerHTML. Build the ASPECT_PRESETS options and the custom
option as DOM option elements, preserving selection behavior and the existing
custom label.
Source: Linters/SAST tools
| font: 12px "Segoe UI", sans-serif; | ||
| max-width: 170px; | ||
| } | ||
| #fpsSel.head-select { max-width: 88px; } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Keep custom FPS values readable.
Custom · 29.97 fps is wider than the 88px limit, so the selected value can be clipped and hide the actual project rate. Use a shorter display label or allow enough width for common custom values.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@style.css` at line 923, Update the `#fpsSel.head-select` styling so custom FPS
values such as “Custom · 29.97 fps” remain fully readable, either by increasing
max-width to accommodate common values or by using a shorter selected-value
label while preserving the existing selector behavior.
Allows to set a fps value per project
fps can be set per project; only the resolution was settable before
Type of change
How was it verified?
node --check server.js && node --check app.js && node --check mcp-server.jspassesCLAUDE.md/README.mdif the schema, props, or API changedChecklist
Summary by CodeRabbit