Show length on a staff under each fingering chart.
AppImage release / appimage (push) Successful in 50s

Dragging no longer selects text. The line box resizes, rests and spaces can be added, and Save PNG writes the row. Each chart keeps the foot at the top, and the treble staff shows the pitch with a notehead, stem, flags, and dots. Version 0.2.1.

Co-authored-by: catpersonisme <catpersonisme@gmail.com>
This commit is contained in:
Cursor Agentandchen committed 2026-10-11 09:49:17 +00:00
1 parent b354ea9f88
commit dcca7f5e83
11 files changed
+997 -177

No files matched your search

+55
View File
@@ -0,0 +1,55 @@
import type { LineNote } from "@/lib/line";
import { chartPixelHeight } from "@/lib/notation";
export const PNG_CHART_WIDTH = 88;
const GAP = 18;
const PAD = 28;
export { chartPixelHeight };
/** A quarter space is about one chart wide. Longer spaces take more room. */
export function spacePixelWidth(quarters: number, chartWidth: number): number {
return Math.max(Math.round(chartWidth * 0.45), Math.round(chartWidth * Math.max(quarters, 0)));
}
export type PngItem = {
kind: LineNote["kind"];
caption: string;
glyphWidth: number;
glyphHeight: number;
};
export function pngItemFromNote(note: Pick<LineNote, "kind" | "pitch" | "quarters" | "label">, chartWidth = PNG_CHART_WIDTH): PngItem {
const glyphHeight = chartPixelHeight(chartWidth);
const caption = note.kind === "note" ? note.pitch : note.kind === "rest" ? "Rest" : "Space";
return {
kind: note.kind,
caption,
glyphWidth: note.kind === "space" ? spacePixelWidth(note.quarters, chartWidth) : chartWidth,
glyphHeight,
};
}
export function pngSlotWidth(item: PngItem): number {
const text = item.caption.length * 8;
return Math.max(item.glyphWidth, text, 48);
}
export function pngLayout(items: PngItem[], title: string) {
const slots = items.map((item) => ({ item, width: pngSlotWidth(item) }));
const content =
slots.reduce((sum, slot) => sum + slot.width, 0) + GAP * Math.max(0, slots.length - 1);
const titleWidth = Math.max(220, title.length * 13 + 8);
const width = Math.max(titleWidth, content, 1) + PAD * 2;
const glyphHeight = chartPixelHeight(PNG_CHART_WIDTH);
const height = PAD + 44 + glyphHeight + 28 + PAD;
return { width, height, slots, glyphHeight, pad: PAD, gap: GAP };
}
export function pngFilename(title: string): string {
const slug = title
.toLowerCase()
.replace(/[^a-z0-9]+/g, "-")
.replace(/^-|-$/g, "");
return `${slug || "line"}.png`;
}