char-slop/chordpad.vibed.software

git clone https://git.t4t.associates/char-slop/chordpad.vibed.software

Charlotte Somallow midi import as chords040fec5

main
2.2 KiB70 linesraw
1import { readSection } from "../import/hookpad.ts";
2import type { TheoryTab } from "../import/theorytab.ts";
3
4const SCALE_NAMES: Record<string, string> = { harmonicMinor: "harmonic minor" };
5const pretty = (symbol: string) =>
6  symbol
7    .replace(/([A-G])b/g, "$1♭")
8    .replace(/b(?=\d)/g, "♭")
9    .replaceAll("#", "♯");
10
11/** ask which sections of a TheoryTab to import: resolves to them, or null if cancelled */
12export function chooseSections(tab: TheoryTab): Promise<TheoryTab["sections"] | null> {
13  return new Promise(resolve => {
14    const done = (choice: TheoryTab["sections"] | null) => {
15      dialog.close();
16      dialog.remove();
17      resolve(choice);
18    };
19
20    const option = (
21      label: string,
22      detail: string,
23      preview: string,
24      sections: TheoryTab["sections"],
25    ) => (
26      <button type="button" class="tt-option" _onclick={() => done(sections)}>
27        <b>{label}</b>
28        <span class="tt-detail">{detail}</span>
29        <span class="tt-preview">{preview}</span>
30      </button>
31    );
32
33    const read = tab.sections.map(s => ({ ...s, read: readSection(s.song) }));
34    const bars = (r: (typeof read)[number]["read"]) => Math.ceil(r.length / r.beatsPerBar);
35    const filled = read.filter(s => s.read.chords.length || s.read.notes.length);
36
37    const dialog = (
38      <dialog class="dialog tt-dialog" _oncancel={() => done(null)}>
39        <h2>{tab.title}</h2>
40        <div class="tt-options">
41          {option(
42            "all sections",
43            `${filled.reduce((n, s) => n + bars(s.read), 0)} bars`,
44            "",
45            tab.sections,
46          )}
47          {filled.map(s =>
48            option(
49              s.name,
50              `${s.read.key.tonic} ${SCALE_NAMES[s.read.key.scale] ?? s.read.key.scale} · ${bars(s.read)} bars`,
51              s.read.chords
52                .slice(0, 8)
53                .map(c => (c.symbol ? pretty(c.symbol) : "…"))
54                .join("  "),
55              [s],
56            ),
57          )}
58        </div>
59        <div class="dialog-actions">
60          <button type="button" _onclick={() => done(null)}>
61            cancel
62          </button>
63        </div>
64      </dialog>
65    ) as HTMLDialogElement;
66
67    document.body.append(dialog);
68    dialog.showModal();
69  });
70}