js • Lines: 182export const DARK_THEME = {
bgPage: "bg-slate-950",
bgScore: "bg-slate-900",
borderScore: "border-transparent",
lineStaff: "#6a80b8",
lineTab: "#7a90b0",
lineBar: "#3060a8",
lineTie: "#8aa8d1",
textClef: "#64748b",
textTimeSig: "#fbbf24",
textTabLabel: "#7a60b6",
textTabString: "#911c6e",
bgTabRect: "#0f172a",
textTabNumber: "#e2e8f0",
lineStem: "#38bdf8",
fillNote: "#38bdf8",
fillRest: "#f43f5e",
textRhythm: "#94a3b8",
inactiveVoiceColor: "#8395af",
voice1Color: "#21cea3",
voice1Rhythm: "#818cf8",
voice1RhytmBg: "rgba(149, 165, 128, 0.08)",
voice2Color: "#fb923c",
voice2Rhythm: "#f472b6",
voice2RhythmBg: "rgba(244, 114, 182, 0.08)",
fillHoverHighlight: "rgba(56, 189, 248, 0.08)",
fillNoteHover: "#7dd3fc",
strokeNoteHover: "#ffffff",
textTabNumberHover: "#ffffff",
textRhythmHover: "#f8fafc",
sustainedNoteHighlight: "#6e899933",
bgInvalidMeasure: "rgba(225, 29, 72, 0.15)",
metronomeControlMedium: "#a78bfa", // Medium purple text and track slider control
metronomeDownBeat: "#ff2a5f", // Deep/Dark Intense ruby pink-red for Downbeats (Beat 1)
metronomeUpBeat: "#2ae8b2",
gridFaint: "rgba(255, 255, 255, 0.025)",
gridStroke: "rgba(255, 255, 255, 0.05)",
gridStrokeStrong: "rgba(255, 255, 255, 0.08)",
rippleColor: "rgba(56, 189, 248, 0.35)",
rippleStroke: "#7dd3fc",
progressTrack: "rgba(255, 255, 255, 0.08)",
progressFill: "#38bdf8",
progressThumb: "#7dd3fc",
};
export const CHROMATIC_MAP = {
0: [0, false],
1: [0, true],
2: [1, false],
3: [1, true],
4: [2, false],
5: [3, false],
6: [3, true],
7: [4, false],
8: [4, true],
9: [5, false],
10: [5, true],
11: [6, false]
};
export const DURATION_OPTIONS = [
{ value: 0.25, label: "Sixteenth note" },
{ value: 0.5, label: "Eighth note" },
{ value: 0.75, label: "Dotted eighth note" },
{ value: 1.0, label: "Quarter note" },
{ value: 1.5, label: "Dotted quarter note" },
{ value: 2.0, label: "Half note" },
{ value: 3.0, label: "Dotted half note" },
{ value: 4.0, label: "Whole note" },
{ value: 6.0, label: "Dotted whole note" }
];
export const getDurationLabel = beatValue => {
const option = DURATION_OPTIONS.find(
duration => duration.value === beatValue
);
return option ? option.label : `${beatValue} beats`;
};
export const parsePitchProperties = (midiNumber, clef, clefTopY, lineSpacing) => {
const visualAnchorMidi = clef === "treble" ? 64 : 43;
const visualAnchorY = clefTopY + 4 * lineSpacing;
const getDiatonicAbsoluteStep = midi => {
const pitchClass = midi % 12;
const octave = Math.floor(midi / 12);
const [step] = CHROMATIC_MAP[pitchClass];
return octave * 7 + step;
};
const currentStep = getDiatonicAbsoluteStep(midiNumber);
const anchorStep = getDiatonicAbsoluteStep(visualAnchorMidi);
const stepDiff = currentStep - anchorStep;
return {
y: visualAnchorY - stepDiff * (lineSpacing / 2),
isSharp: CHROMATIC_MAP[midiNumber % 12][1]
};
};
export const getFlagPath = (sx, sy, isDown = false, flags = 1) => {
const drawSlashSegment = yOffset => {
const sY = sy + yOffset;
if (isDown) {
return `M ${sx} ${sY} L ${sx + 7} ${sY - 8} L ${sx + 7} ${sY - 5} L ${sx} ${sY + 3} Z`;
}
return `M ${sx} ${sY} L ${sx + 7} ${sY + 8} L ${sx + 7} ${sY + 5} L ${sx} ${sY - 3} Z`;
};
let path = "";
for (let i = 0; i < flags; i++) {
path += drawSlashSegment(isDown ? -(i * 9) : i * 9) + " ";
}
return path.trim();
};
export const calculateSchedulerBoundaries = bpm => {
const beatDurationMs = (60 / bpm) * 1000;
const sixteenthNoteMs = beatDurationMs / 4;
// Lookahead Interval: roughly 40% of a sixteenth note, bounded between 15ms and 45ms.
const lookaheadInterval = Math.max(
15,
Math.min(45, Math.round(sixteenthNoteMs * 0.4))
);
// Schedule Ahead Time: The safety padding window (in seconds), minimum 1.0s to handle high BPM rendering load.
const scheduleAheadTime = Math.max(1.0, (lookaheadInterval * 10) / 1000);
return { lookaheadInterval, scheduleAheadTime };
};
export const outerContainerStyle = {
position: "relative", // Establishes boundary for absolute positioning
width: "100%", // Adapts to wherever you drop it in the page
overflow: "hidden", // Prevents content from breaking outside the box
border: "none",
boxSizing: "border-box",
padding: "0", // Reset any default padding
margin: "0" // Reset any default margin
};
export const fixedTopRightStyle = {
position: "absolute",
top: "10px",
right: "10px",
zIndex: 100,
// The changes:
width: "20%" /* Set your exact desired width here */,
boxSizing: "border-box" /* Keeps padding from breaking the width */,
padding: "8px 12px",
borderRadius: "4px",
pointerEvents: "auto"
};
export const scrollableContentStyle = {
width: "70%",
// The changes:
height: "60vh" /* Forces the container to exactly 55% of the viewport height */,
overflowY: "auto" /* Enables scrolling when content exceeds 55vh */,
paddingTop: "60px",
paddingLeft: "20px",
paddingRight: "20px",
paddingBottom: "20px",
boxSizing: "border-box"
};