@real-music-packages/web-core 0.34.1 → 0.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +25 -1
- package/dist/index.js +81 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -160,4 +160,28 @@ interface ChordVoicing {
|
|
|
160
160
|
*/
|
|
161
161
|
declare function distributeChord(midiNotes: number[]): ChordVoicing[];
|
|
162
162
|
|
|
163
|
-
|
|
163
|
+
interface ShareCardOptions {
|
|
164
|
+
appName: string;
|
|
165
|
+
accentColor: string;
|
|
166
|
+
drillTitle: string;
|
|
167
|
+
score: number;
|
|
168
|
+
total: number;
|
|
169
|
+
streakBest: number;
|
|
170
|
+
url: string;
|
|
171
|
+
logoText?: string;
|
|
172
|
+
}
|
|
173
|
+
declare function shareFilename(appName: string, drillTitle: string): string;
|
|
174
|
+
declare function shareCardLines(r: {
|
|
175
|
+
score: number;
|
|
176
|
+
total: number;
|
|
177
|
+
streakBest: number;
|
|
178
|
+
}): {
|
|
179
|
+
scoreLine: string;
|
|
180
|
+
percentLine: string;
|
|
181
|
+
streakLine: string;
|
|
182
|
+
};
|
|
183
|
+
declare function renderShareCard(opts: ShareCardOptions): Promise<Blob>;
|
|
184
|
+
/** Share via navigator.share(files) → clipboard image → download. Never throws. */
|
|
185
|
+
declare function shareOrDownload(blob: Blob, filename: string, shareText: string): Promise<'shared' | 'copied' | 'downloaded'>;
|
|
186
|
+
|
|
187
|
+
export { ALL_KEYS, type BestPositionOpts, CHORD_TEMPLATES, type ChordTemplate, type ChordVoicing, type FretPosition, INTERVALS, type IntervalDef, KEYS_PREFER_FLATS, MAJOR_SCALE_INTERVALS, MAX_FRET, NOTE_NAMES, NOTE_NAMES_FLAT, STRINGS, STRING_COUNT, type ShareCardOptions, bestPosition, buildPlacementMap, chosenPlacement, distributeChord, enumerateBoardCells, getMidiNote, getScaleDegree, getStability, intervalBySemitones, isInScale, midiToFrequency, midiToNoteName, midiToPositions, noteNameToIndex, noteToFret, pitchClass, renderShareCard, shareCardLines, shareFilename, shareOrDownload, useFlatsForKeyFifths, useFlatsForKeyName };
|
package/dist/index.js
CHANGED
|
@@ -50,6 +50,83 @@ var CHORD_TEMPLATES = [
|
|
|
50
50
|
{ name: "sus4", intervals: [0, 5, 7] },
|
|
51
51
|
{ name: "sus2", intervals: [0, 2, 7] }
|
|
52
52
|
];
|
|
53
|
+
|
|
54
|
+
// src/shareCard.ts
|
|
55
|
+
function shareFilename(appName, drillTitle) {
|
|
56
|
+
const slug = (s) => s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
57
|
+
return `${slug(appName)}-${slug(drillTitle)}-score.png`;
|
|
58
|
+
}
|
|
59
|
+
function shareCardLines(r) {
|
|
60
|
+
return {
|
|
61
|
+
scoreLine: `${r.score} / ${r.total}`,
|
|
62
|
+
percentLine: `${Math.round(r.score / r.total * 100)}%`,
|
|
63
|
+
streakLine: `Best streak: ${r.streakBest}`
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
var W = 1080;
|
|
67
|
+
var H = 1350;
|
|
68
|
+
var FONT = "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif";
|
|
69
|
+
async function renderShareCard(opts) {
|
|
70
|
+
const canvas = document.createElement("canvas");
|
|
71
|
+
canvas.width = W;
|
|
72
|
+
canvas.height = H;
|
|
73
|
+
const ctx = canvas.getContext("2d");
|
|
74
|
+
if (!ctx) throw new Error("canvas 2d unavailable");
|
|
75
|
+
const { scoreLine, percentLine, streakLine } = shareCardLines(opts);
|
|
76
|
+
ctx.fillStyle = "#0b0e14";
|
|
77
|
+
ctx.fillRect(0, 0, W, H);
|
|
78
|
+
const glow = ctx.createRadialGradient(W * 0.8, H * 0.15, 0, W * 0.8, H * 0.15, W * 0.9);
|
|
79
|
+
glow.addColorStop(0, opts.accentColor + "55");
|
|
80
|
+
glow.addColorStop(1, "transparent");
|
|
81
|
+
ctx.fillStyle = glow;
|
|
82
|
+
ctx.fillRect(0, 0, W, H);
|
|
83
|
+
ctx.textAlign = "center";
|
|
84
|
+
ctx.fillStyle = "#9aa3b2";
|
|
85
|
+
ctx.font = `600 44px ${FONT}`;
|
|
86
|
+
ctx.fillText((opts.logoText ?? opts.appName).toUpperCase(), W / 2, 140);
|
|
87
|
+
ctx.fillStyle = "#e8ecf3";
|
|
88
|
+
ctx.font = `800 72px ${FONT}`;
|
|
89
|
+
ctx.fillText(opts.drillTitle, W / 2, 320);
|
|
90
|
+
ctx.fillStyle = opts.accentColor;
|
|
91
|
+
ctx.font = `800 260px ${FONT}`;
|
|
92
|
+
ctx.fillText(percentLine, W / 2, 680);
|
|
93
|
+
ctx.fillStyle = "#e8ecf3";
|
|
94
|
+
ctx.font = `600 56px ${FONT}`;
|
|
95
|
+
ctx.fillText(`${scoreLine} correct`, W / 2, 790);
|
|
96
|
+
ctx.fillText(streakLine, W / 2, 880);
|
|
97
|
+
ctx.fillStyle = "#9aa3b2";
|
|
98
|
+
ctx.font = `600 44px ${FONT}`;
|
|
99
|
+
ctx.fillText("Can you beat it?", W / 2, 1120);
|
|
100
|
+
ctx.fillStyle = opts.accentColor;
|
|
101
|
+
ctx.font = `700 48px ${FONT}`;
|
|
102
|
+
ctx.fillText(opts.url.replace(/^https?:\/\//, ""), W / 2, 1200);
|
|
103
|
+
return await new Promise(
|
|
104
|
+
(resolve, reject) => canvas.toBlob((b) => b ? resolve(b) : reject(new Error("toBlob failed")), "image/png")
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
async function shareOrDownload(blob, filename, shareText) {
|
|
108
|
+
const file = new File([blob], filename, { type: "image/png" });
|
|
109
|
+
try {
|
|
110
|
+
if (typeof navigator !== "undefined" && navigator.canShare?.({ files: [file] })) {
|
|
111
|
+
await navigator.share({ files: [file], text: shareText });
|
|
112
|
+
return "shared";
|
|
113
|
+
}
|
|
114
|
+
} catch {
|
|
115
|
+
}
|
|
116
|
+
try {
|
|
117
|
+
if (typeof ClipboardItem !== "undefined" && navigator.clipboard?.write) {
|
|
118
|
+
await navigator.clipboard.write([new ClipboardItem({ "image/png": blob })]);
|
|
119
|
+
return "copied";
|
|
120
|
+
}
|
|
121
|
+
} catch {
|
|
122
|
+
}
|
|
123
|
+
const a = document.createElement("a");
|
|
124
|
+
a.href = URL.createObjectURL(blob);
|
|
125
|
+
a.download = filename;
|
|
126
|
+
a.click();
|
|
127
|
+
URL.revokeObjectURL(a.href);
|
|
128
|
+
return "downloaded";
|
|
129
|
+
}
|
|
53
130
|
export {
|
|
54
131
|
ALL_KEYS,
|
|
55
132
|
CHORD_TEMPLATES,
|
|
@@ -77,6 +154,10 @@ export {
|
|
|
77
154
|
noteNameToIndex,
|
|
78
155
|
noteToFret,
|
|
79
156
|
pitchClass,
|
|
157
|
+
renderShareCard,
|
|
158
|
+
shareCardLines,
|
|
159
|
+
shareFilename,
|
|
160
|
+
shareOrDownload,
|
|
80
161
|
useFlatsForKeyFifths,
|
|
81
162
|
useFlatsForKeyName
|
|
82
163
|
};
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/frequency.ts","../src/chords.ts"],"sourcesContent":["/** Equal-temperament MIDI → frequency (A4 = MIDI 69 = 440 Hz). */\nexport function midiToFrequency(midi: number): number {\n return 440 * Math.pow(2, (midi - 69) / 12);\n}\n","export interface ChordTemplate { name: string; intervals: number[] }\n\n/** Chord-quality templates, richer qualities first so naming matches the\n * most specific quality (e.g. maj7 before plain major). */\nexport const CHORD_TEMPLATES: ChordTemplate[] = [\n { name: 'maj7', intervals: [0, 4, 7, 11] },\n { name: '7', intervals: [0, 4, 7, 10] },\n { name: 'm7', intervals: [0, 3, 7, 10] },\n { name: 'dim7', intervals: [0, 3, 6, 9] },\n { name: 'm7b5', intervals: [0, 3, 6, 10] },\n { name: '', intervals: [0, 4, 7] },\n { name: 'm', intervals: [0, 3, 7] },\n { name: 'dim', intervals: [0, 3, 6] },\n { name: 'aug', intervals: [0, 4, 8] },\n { name: 'sus4', intervals: [0, 5, 7] },\n { name: 'sus2', intervals: [0, 2, 7] },\n];\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACO,SAAS,gBAAgB,MAAsB;AACpD,SAAO,MAAM,KAAK,IAAI,IAAI,OAAO,MAAM,EAAE;AAC3C;;;ACCO,IAAM,kBAAmC;AAAA,EAC9C,EAAE,MAAM,QAAQ,WAAW,CAAC,GAAG,GAAG,GAAG,EAAE,EAAE;AAAA,EACzC,EAAE,MAAM,KAAQ,WAAW,CAAC,GAAG,GAAG,GAAG,EAAE,EAAE;AAAA,EACzC,EAAE,MAAM,MAAQ,WAAW,CAAC,GAAG,GAAG,GAAG,EAAE,EAAE;AAAA,EACzC,EAAE,MAAM,QAAQ,WAAW,CAAC,GAAG,GAAG,GAAG,CAAC,EAAE;AAAA,EACxC,EAAE,MAAM,QAAQ,WAAW,CAAC,GAAG,GAAG,GAAG,EAAE,EAAE;AAAA,EACzC,EAAE,MAAM,IAAQ,WAAW,CAAC,GAAG,GAAG,CAAC,EAAE;AAAA,EACrC,EAAE,MAAM,KAAQ,WAAW,CAAC,GAAG,GAAG,CAAC,EAAE;AAAA,EACrC,EAAE,MAAM,OAAQ,WAAW,CAAC,GAAG,GAAG,CAAC,EAAE;AAAA,EACrC,EAAE,MAAM,OAAQ,WAAW,CAAC,GAAG,GAAG,CAAC,EAAE;AAAA,EACrC,EAAE,MAAM,QAAQ,WAAW,CAAC,GAAG,GAAG,CAAC,EAAE;AAAA,EACrC,EAAE,MAAM,QAAQ,WAAW,CAAC,GAAG,GAAG,CAAC,EAAE;AACvC;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/frequency.ts","../src/chords.ts","../src/shareCard.ts"],"sourcesContent":["/** Equal-temperament MIDI → frequency (A4 = MIDI 69 = 440 Hz). */\nexport function midiToFrequency(midi: number): number {\n return 440 * Math.pow(2, (midi - 69) / 12);\n}\n","export interface ChordTemplate { name: string; intervals: number[] }\n\n/** Chord-quality templates, richer qualities first so naming matches the\n * most specific quality (e.g. maj7 before plain major). */\nexport const CHORD_TEMPLATES: ChordTemplate[] = [\n { name: 'maj7', intervals: [0, 4, 7, 11] },\n { name: '7', intervals: [0, 4, 7, 10] },\n { name: 'm7', intervals: [0, 3, 7, 10] },\n { name: 'dim7', intervals: [0, 3, 6, 9] },\n { name: 'm7b5', intervals: [0, 3, 6, 10] },\n { name: '', intervals: [0, 4, 7] },\n { name: 'm', intervals: [0, 3, 7] },\n { name: 'dim', intervals: [0, 3, 6] },\n { name: 'aug', intervals: [0, 4, 8] },\n { name: 'sus4', intervals: [0, 5, 7] },\n { name: 'sus2', intervals: [0, 2, 7] },\n];\n","// Post-round score-share card: branded 1080×1350 PNG rendered client-side.\n// Pure-canvas, no font/image fetches — safe on any origin.\n\nexport interface ShareCardOptions {\n appName: string; // \"Real Ear Trainer\"\n accentColor: string; // app theme accent, e.g. \"#5492D4\"\n drillTitle: string; // \"Name the Interval\"\n score: number;\n total: number;\n streakBest: number;\n url: string; // canonical drill URL printed on the card\n logoText?: string; // defaults to appName\n}\n\nexport function shareFilename(appName: string, drillTitle: string): string {\n const slug = (s: string) =>\n s.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');\n return `${slug(appName)}-${slug(drillTitle)}-score.png`;\n}\n\nexport function shareCardLines(r: { score: number; total: number; streakBest: number }) {\n return {\n scoreLine: `${r.score} / ${r.total}`,\n percentLine: `${Math.round((r.score / r.total) * 100)}%`,\n streakLine: `Best streak: ${r.streakBest}`,\n };\n}\n\nconst W = 1080;\nconst H = 1350;\nconst FONT = \"-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif\";\n\nexport async function renderShareCard(opts: ShareCardOptions): Promise<Blob> {\n const canvas = document.createElement('canvas');\n canvas.width = W;\n canvas.height = H;\n const ctx = canvas.getContext('2d');\n if (!ctx) throw new Error('canvas 2d unavailable');\n const { scoreLine, percentLine, streakLine } = shareCardLines(opts);\n\n // Background: near-black with a soft accent glow top-right.\n ctx.fillStyle = '#0b0e14';\n ctx.fillRect(0, 0, W, H);\n const glow = ctx.createRadialGradient(W * 0.8, H * 0.15, 0, W * 0.8, H * 0.15, W * 0.9);\n glow.addColorStop(0, opts.accentColor + '55');\n glow.addColorStop(1, 'transparent');\n ctx.fillStyle = glow;\n ctx.fillRect(0, 0, W, H);\n\n ctx.textAlign = 'center';\n ctx.fillStyle = '#9aa3b2';\n ctx.font = `600 44px ${FONT}`;\n ctx.fillText((opts.logoText ?? opts.appName).toUpperCase(), W / 2, 140);\n\n ctx.fillStyle = '#e8ecf3';\n ctx.font = `800 72px ${FONT}`;\n ctx.fillText(opts.drillTitle, W / 2, 320);\n\n ctx.fillStyle = opts.accentColor;\n ctx.font = `800 260px ${FONT}`;\n ctx.fillText(percentLine, W / 2, 680);\n\n ctx.fillStyle = '#e8ecf3';\n ctx.font = `600 56px ${FONT}`;\n ctx.fillText(`${scoreLine} correct`, W / 2, 790);\n ctx.fillText(streakLine, W / 2, 880);\n\n ctx.fillStyle = '#9aa3b2';\n ctx.font = `600 44px ${FONT}`;\n ctx.fillText('Can you beat it?', W / 2, 1120);\n ctx.fillStyle = opts.accentColor;\n ctx.font = `700 48px ${FONT}`;\n ctx.fillText(opts.url.replace(/^https?:\\/\\//, ''), W / 2, 1200);\n\n return await new Promise<Blob>((resolve, reject) =>\n canvas.toBlob((b) => (b ? resolve(b) : reject(new Error('toBlob failed'))), 'image/png')\n );\n}\n\n/** Share via navigator.share(files) → clipboard image → download. Never throws. */\nexport async function shareOrDownload(\n blob: Blob,\n filename: string,\n shareText: string\n): Promise<'shared' | 'copied' | 'downloaded'> {\n const file = new File([blob], filename, { type: 'image/png' });\n try {\n if (typeof navigator !== 'undefined' && navigator.canShare?.({ files: [file] })) {\n await navigator.share({ files: [file], text: shareText });\n return 'shared';\n }\n } catch {\n /* user cancelled or share failed — fall through */\n }\n try {\n if (typeof ClipboardItem !== 'undefined' && navigator.clipboard?.write) {\n await navigator.clipboard.write([new ClipboardItem({ 'image/png': blob })]);\n return 'copied';\n }\n } catch {\n /* clipboard blocked — fall through */\n }\n const a = document.createElement('a');\n a.href = URL.createObjectURL(blob);\n a.download = filename;\n a.click();\n URL.revokeObjectURL(a.href);\n return 'downloaded';\n}\n"],"mappings":"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","names":[]}
|