@mcptoolshop/ai_jam_session 0.1.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/LICENSE +21 -0
- package/README.es.md +212 -0
- package/README.fr.md +212 -0
- package/README.hi.md +212 -0
- package/README.it.md +212 -0
- package/README.ja.md +214 -0
- package/README.md +214 -0
- package/README.pt-BR.md +212 -0
- package/dist/audio-engine.d.ts +9 -0
- package/dist/audio-engine.d.ts.map +1 -0
- package/dist/audio-engine.js +422 -0
- package/dist/audio-engine.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +551 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +41 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp-server.d.ts +3 -0
- package/dist/mcp-server.d.ts.map +1 -0
- package/dist/mcp-server.js +903 -0
- package/dist/mcp-server.js.map +1 -0
- package/dist/midi/parser.d.ts +16 -0
- package/dist/midi/parser.d.ts.map +1 -0
- package/dist/midi/parser.js +192 -0
- package/dist/midi/parser.js.map +1 -0
- package/dist/midi/types.d.ts +44 -0
- package/dist/midi/types.d.ts.map +1 -0
- package/dist/midi/types.js +8 -0
- package/dist/midi/types.js.map +1 -0
- package/dist/note-parser.d.ts +105 -0
- package/dist/note-parser.d.ts.map +1 -0
- package/dist/note-parser.js +278 -0
- package/dist/note-parser.js.map +1 -0
- package/dist/playback/controls.d.ts +124 -0
- package/dist/playback/controls.d.ts.map +1 -0
- package/dist/playback/controls.js +252 -0
- package/dist/playback/controls.js.map +1 -0
- package/dist/playback/midi-engine.d.ts +68 -0
- package/dist/playback/midi-engine.d.ts.map +1 -0
- package/dist/playback/midi-engine.js +227 -0
- package/dist/playback/midi-engine.js.map +1 -0
- package/dist/playback/position.d.ts +95 -0
- package/dist/playback/position.d.ts.map +1 -0
- package/dist/playback/position.js +223 -0
- package/dist/playback/position.js.map +1 -0
- package/dist/playback/timing.d.ts +31 -0
- package/dist/playback/timing.d.ts.map +1 -0
- package/dist/playback/timing.js +57 -0
- package/dist/playback/timing.js.map +1 -0
- package/dist/sample-engine.d.ts +17 -0
- package/dist/sample-engine.d.ts.map +1 -0
- package/dist/sample-engine.js +428 -0
- package/dist/sample-engine.js.map +1 -0
- package/dist/schemas.d.ts +40 -0
- package/dist/schemas.d.ts.map +1 -0
- package/dist/schemas.js +42 -0
- package/dist/schemas.js.map +1 -0
- package/dist/session.d.ts +106 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +361 -0
- package/dist/session.js.map +1 -0
- package/dist/sfz-parser.d.ts +36 -0
- package/dist/sfz-parser.d.ts.map +1 -0
- package/dist/sfz-parser.js +95 -0
- package/dist/sfz-parser.js.map +1 -0
- package/dist/smoke.d.ts +2 -0
- package/dist/smoke.d.ts.map +1 -0
- package/dist/smoke.js +512 -0
- package/dist/smoke.js.map +1 -0
- package/dist/songs/config/loader.d.ts +14 -0
- package/dist/songs/config/loader.d.ts.map +1 -0
- package/dist/songs/config/loader.js +53 -0
- package/dist/songs/config/loader.js.map +1 -0
- package/dist/songs/config/schema.d.ts +70 -0
- package/dist/songs/config/schema.d.ts.map +1 -0
- package/dist/songs/config/schema.js +53 -0
- package/dist/songs/config/schema.js.map +1 -0
- package/dist/songs/index.d.ts +16 -0
- package/dist/songs/index.d.ts.map +1 -0
- package/dist/songs/index.js +20 -0
- package/dist/songs/index.js.map +1 -0
- package/dist/songs/jam.d.ts +48 -0
- package/dist/songs/jam.d.ts.map +1 -0
- package/dist/songs/jam.js +324 -0
- package/dist/songs/jam.js.map +1 -0
- package/dist/songs/loader.d.ts +27 -0
- package/dist/songs/loader.d.ts.map +1 -0
- package/dist/songs/loader.js +90 -0
- package/dist/songs/loader.js.map +1 -0
- package/dist/songs/midi/hands.d.ts +46 -0
- package/dist/songs/midi/hands.d.ts.map +1 -0
- package/dist/songs/midi/hands.js +134 -0
- package/dist/songs/midi/hands.js.map +1 -0
- package/dist/songs/midi/ingest.d.ts +8 -0
- package/dist/songs/midi/ingest.d.ts.map +1 -0
- package/dist/songs/midi/ingest.js +191 -0
- package/dist/songs/midi/ingest.js.map +1 -0
- package/dist/songs/midi/measures.d.ts +41 -0
- package/dist/songs/midi/measures.d.ts.map +1 -0
- package/dist/songs/midi/measures.js +64 -0
- package/dist/songs/midi/measures.js.map +1 -0
- package/dist/songs/midi/types.d.ts +25 -0
- package/dist/songs/midi/types.d.ts.map +1 -0
- package/dist/songs/midi/types.js +8 -0
- package/dist/songs/midi/types.js.map +1 -0
- package/dist/songs/registry.d.ts +37 -0
- package/dist/songs/registry.d.ts.map +1 -0
- package/dist/songs/registry.js +197 -0
- package/dist/songs/registry.js.map +1 -0
- package/dist/songs/types.d.ts +99 -0
- package/dist/songs/types.d.ts.map +1 -0
- package/dist/songs/types.js +27 -0
- package/dist/songs/types.js.map +1 -0
- package/dist/teaching/live-midi-feedback.d.ts +36 -0
- package/dist/teaching/live-midi-feedback.d.ts.map +1 -0
- package/dist/teaching/live-midi-feedback.js +259 -0
- package/dist/teaching/live-midi-feedback.js.map +1 -0
- package/dist/teaching/midi-feedback.d.ts +33 -0
- package/dist/teaching/midi-feedback.d.ts.map +1 -0
- package/dist/teaching/midi-feedback.js +208 -0
- package/dist/teaching/midi-feedback.js.map +1 -0
- package/dist/teaching/sing-on-midi.d.ts +77 -0
- package/dist/teaching/sing-on-midi.d.ts.map +1 -0
- package/dist/teaching/sing-on-midi.js +186 -0
- package/dist/teaching/sing-on-midi.js.map +1 -0
- package/dist/teaching.d.ts +148 -0
- package/dist/teaching.d.ts.map +1 -0
- package/dist/teaching.js +453 -0
- package/dist/teaching.js.map +1 -0
- package/dist/test-sound.d.ts +3 -0
- package/dist/test-sound.d.ts.map +1 -0
- package/dist/test-sound.js +41 -0
- package/dist/test-sound.js.map +1 -0
- package/dist/types.d.ts +229 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +22 -0
- package/dist/types.js.map +1 -0
- package/dist/vmpk.d.ts +23 -0
- package/dist/vmpk.d.ts.map +1 -0
- package/dist/vmpk.js +236 -0
- package/dist/vmpk.js.map +1 -0
- package/logo.png +0 -0
- package/package.json +70 -0
- package/songs/builtin/a-change-is-gonna-come.json +95 -0
- package/songs/builtin/a-thousand-years.json +93 -0
- package/songs/builtin/aint-no-sunshine.json +98 -0
- package/songs/builtin/all-blues.json +92 -0
- package/songs/builtin/autumn-leaves.json +100 -0
- package/songs/builtin/baba-oriley.json +91 -0
- package/songs/builtin/bach-invention-no1.json +70 -0
- package/songs/builtin/bach-prelude-c-major-bwv846.json +1282 -0
- package/songs/builtin/basic-12-bar-blues.json +119 -0
- package/songs/builtin/beethoven-waldstein-mvt1.json +7766 -0
- package/songs/builtin/bennie-and-the-jets.json +92 -0
- package/songs/builtin/besame-mucho.json +93 -0
- package/songs/builtin/black-orpheus.json +92 -0
- package/songs/builtin/blue-bossa.json +94 -0
- package/songs/builtin/blues-in-g.json +92 -0
- package/songs/builtin/bohemian-rhapsody-intro.json +94 -0
- package/songs/builtin/boogie-woogie-basics.json +93 -0
- package/songs/builtin/bossa-nova-basic.json +95 -0
- package/songs/builtin/chopin-nocturne-op9-no2.json +70 -0
- package/songs/builtin/cinema-paradiso.json +94 -0
- package/songs/builtin/clair-de-lune.json +11511 -0
- package/songs/builtin/clocks.json +91 -0
- package/songs/builtin/crystal-stream.json +70 -0
- package/songs/builtin/desafinado.json +93 -0
- package/songs/builtin/dont-stop-believin.json +91 -0
- package/songs/builtin/dream-on.json +100 -0
- package/songs/builtin/easy-winners.json +70 -0
- package/songs/builtin/el-condor-pasa.json +93 -0
- package/songs/builtin/elite-syncopations.json +70 -0
- package/songs/builtin/evening-calm.json +70 -0
- package/songs/builtin/everyday-blues.json +93 -0
- package/songs/builtin/fly-me-to-the-moon.json +91 -0
- package/songs/builtin/forrest-gump-suite.json +93 -0
- package/songs/builtin/fur-elise.json +20094 -0
- package/songs/builtin/georgia-on-my-mind.json +93 -0
- package/songs/builtin/girl-from-ipanema.json +92 -0
- package/songs/builtin/gladiolus-rag.json +70 -0
- package/songs/builtin/great-balls-of-fire.json +92 -0
- package/songs/builtin/guantanamera.json +92 -0
- package/songs/builtin/hallelujah.json +92 -0
- package/songs/builtin/hedwigs-theme.json +93 -0
- package/songs/builtin/hotel-california.json +92 -0
- package/songs/builtin/imagine.json +92 -0
- package/songs/builtin/just-the-two-of-us.json +92 -0
- package/songs/builtin/la-bamba.json +92 -0
- package/songs/builtin/layla-piano-coda.json +93 -0
- package/songs/builtin/lean-on-me.json +91 -0
- package/songs/builtin/let-it-be.json +101 -0
- package/songs/builtin/lets-stay-together.json +93 -0
- package/songs/builtin/magnetic-rag.json +70 -0
- package/songs/builtin/maple-leaf-rag.json +99 -0
- package/songs/builtin/mia-and-sebastians-theme.json +93 -0
- package/songs/builtin/minor-blues-in-a.json +94 -0
- package/songs/builtin/misty.json +94 -0
- package/songs/builtin/moon-river.json +93 -0
- package/songs/builtin/moonlight-sonata-mvt1.json +101 -0
- package/songs/builtin/morning-light.json +70 -0
- package/songs/builtin/mountain-dawn.json +70 -0
- package/songs/builtin/mozart-k545-mvt1.json +2956 -0
- package/songs/builtin/my-girl.json +92 -0
- package/songs/builtin/night-train.json +92 -0
- package/songs/builtin/november-rain.json +93 -0
- package/songs/builtin/ocean-waves.json +70 -0
- package/songs/builtin/over-the-rainbow.json +93 -0
- package/songs/builtin/oye-como-va.json +93 -0
- package/songs/builtin/peacherine-rag.json +70 -0
- package/songs/builtin/piano-man.json +92 -0
- package/songs/builtin/pineapple-rag.json +70 -0
- package/songs/builtin/pink-panther.json +94 -0
- package/songs/builtin/ragtime-dance.json +70 -0
- package/songs/builtin/river-flows-in-you.json +102 -0
- package/songs/builtin/rocket-man.json +92 -0
- package/songs/builtin/satie-gymnopedie-no1.json +70 -0
- package/songs/builtin/satin-doll.json +93 -0
- package/songs/builtin/schindlers-list.json +96 -0
- package/songs/builtin/schumann-traumerei.json +70 -0
- package/songs/builtin/sitting-on-the-dock.json +91 -0
- package/songs/builtin/slow-blues-in-bb.json +98 -0
- package/songs/builtin/snowfall.json +70 -0
- package/songs/builtin/so-what.json +92 -0
- package/songs/builtin/solace.json +70 -0
- package/songs/builtin/someone-like-you.json +92 -0
- package/songs/builtin/spirited-away.json +94 -0
- package/songs/builtin/st-louis-blues.json +93 -0
- package/songs/builtin/stairway-intro.json +93 -0
- package/songs/builtin/starlight-waltz.json +70 -0
- package/songs/builtin/stay-with-me.json +93 -0
- package/songs/builtin/stormy-monday.json +94 -0
- package/songs/builtin/superstition.json +93 -0
- package/songs/builtin/sweet-home-chicago.json +93 -0
- package/songs/builtin/take-five.json +92 -0
- package/songs/builtin/take-the-a-train.json +93 -0
- package/songs/builtin/the-entertainer.json +98 -0
- package/songs/builtin/the-godfather-waltz.json +93 -0
- package/songs/builtin/thrill-is-gone.json +94 -0
- package/songs/builtin/twilight-garden.json +70 -0
- package/songs/builtin/watermark.json +70 -0
- package/songs/builtin/wave.json +93 -0
- package/songs/builtin/whats-going-on.json +93 -0
- package/songs/builtin/yesterday.json +92 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/songs/midi/types.ts"],"names":[],"mappings":"AAOA,sDAAsD;AACtD,MAAM,WAAW,YAAY;IAC3B,8BAA8B;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,8CAA8C;IAC9C,SAAS,EAAE,MAAM,CAAC;IAClB,yBAAyB;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,sBAAsB;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,oBAAoB;IACpB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wDAAwD;AACxD,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AAED,0DAA0D;AAC1D,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// ─── MIDI Ingest Types ──────────────────────────────────────────────────────
|
|
2
|
+
//
|
|
3
|
+
// Tick-based types for the MIDI-to-SongEntry conversion pipeline.
|
|
4
|
+
// These are separate from src/midi/types.ts which uses seconds-based timing
|
|
5
|
+
// for real-time playback.
|
|
6
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/songs/midi/types.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,EAAE;AACF,kEAAkE;AAClE,4EAA4E;AAC5E,0BAA0B;AAC1B,gFAAgF"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { SongEntry, Genre, Difficulty, SearchOptions, RegistryStats } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Register a song in the global registry.
|
|
4
|
+
* Throws if the ID is already taken or validation fails.
|
|
5
|
+
*/
|
|
6
|
+
export declare function registerSong(song: SongEntry): void;
|
|
7
|
+
/**
|
|
8
|
+
* Register multiple songs at once.
|
|
9
|
+
*/
|
|
10
|
+
export declare function registerSongs(entries: SongEntry[]): void;
|
|
11
|
+
/**
|
|
12
|
+
* Validate a single song entry. Returns an array of error messages (empty = valid).
|
|
13
|
+
*/
|
|
14
|
+
export declare function validateSong(song: SongEntry): string[];
|
|
15
|
+
/**
|
|
16
|
+
* Validate the entire registry. Throws on first invalid song.
|
|
17
|
+
*/
|
|
18
|
+
export declare function validateRegistry(): void;
|
|
19
|
+
/** Get a song by its unique ID. */
|
|
20
|
+
export declare function getSong(id: string): SongEntry | undefined;
|
|
21
|
+
/** Get all registered songs. */
|
|
22
|
+
export declare function getAllSongs(): SongEntry[];
|
|
23
|
+
/** Get all songs in a specific genre. */
|
|
24
|
+
export declare function getSongsByGenre(genre: Genre): SongEntry[];
|
|
25
|
+
/** Get all songs at a specific difficulty level. */
|
|
26
|
+
export declare function getSongsByDifficulty(difficulty: Difficulty): SongEntry[];
|
|
27
|
+
/**
|
|
28
|
+
* Search songs with flexible filtering.
|
|
29
|
+
* The `query` field does a case-insensitive match against title, composer, tags,
|
|
30
|
+
* and musicalLanguage.description.
|
|
31
|
+
*/
|
|
32
|
+
export declare function searchSongs(options: SearchOptions): SongEntry[];
|
|
33
|
+
/** Get summary statistics about the registry. */
|
|
34
|
+
export declare function getStats(): RegistryStats;
|
|
35
|
+
/** Clear all registered songs. Used when reloading or in tests. */
|
|
36
|
+
export declare function clearRegistry(): void;
|
|
37
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/songs/registry.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACV,SAAS,EACT,KAAK,EACL,UAAU,EACV,aAAa,EACb,aAAa,EACd,MAAM,YAAY,CAAC;AASpB;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI,CAWlD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,IAAI,CAIxD;AAID;;GAEG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,EAAE,CAuFtD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAMvC;AAID,mCAAmC;AACnC,wBAAgB,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAEzD;AAED,gCAAgC;AAChC,wBAAgB,WAAW,IAAI,SAAS,EAAE,CAEzC;AAED,yCAAyC;AACzC,wBAAgB,eAAe,CAAC,KAAK,EAAE,KAAK,GAAG,SAAS,EAAE,CAEzD;AAED,oDAAoD;AACpD,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,UAAU,GAAG,SAAS,EAAE,CAExE;AAID;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,aAAa,GAAG,SAAS,EAAE,CAsC/D;AAID,iDAAiD;AACjD,wBAAgB,QAAQ,IAAI,aAAa,CAsBxC;AAID,mEAAmE;AACnE,wBAAgB,aAAa,IAAI,IAAI,CAEpC"}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
// ─── Song Registry ──────────────────────────────────────────────────────────
|
|
2
|
+
//
|
|
3
|
+
// Single source of truth for all songs loaded at runtime.
|
|
4
|
+
// Songs are loaded from JSON files (builtin + user directories).
|
|
5
|
+
// The registry provides validation, search, filtering, and stats.
|
|
6
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
7
|
+
import { GENRES, DIFFICULTIES } from "./types.js";
|
|
8
|
+
// ─── Internal state ─────────────────────────────────────────────────────────
|
|
9
|
+
const songs = new Map();
|
|
10
|
+
// ─── Registration ───────────────────────────────────────────────────────────
|
|
11
|
+
/**
|
|
12
|
+
* Register a song in the global registry.
|
|
13
|
+
* Throws if the ID is already taken or validation fails.
|
|
14
|
+
*/
|
|
15
|
+
export function registerSong(song) {
|
|
16
|
+
const errors = validateSong(song);
|
|
17
|
+
if (errors.length > 0) {
|
|
18
|
+
throw new Error(`Invalid song "${song.id}":\n - ${errors.join("\n - ")}`);
|
|
19
|
+
}
|
|
20
|
+
if (songs.has(song.id)) {
|
|
21
|
+
throw new Error(`Duplicate song ID: "${song.id}"`);
|
|
22
|
+
}
|
|
23
|
+
songs.set(song.id, song);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Register multiple songs at once.
|
|
27
|
+
*/
|
|
28
|
+
export function registerSongs(entries) {
|
|
29
|
+
for (const song of entries) {
|
|
30
|
+
registerSong(song);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
// ─── Validation ─────────────────────────────────────────────────────────────
|
|
34
|
+
/**
|
|
35
|
+
* Validate a single song entry. Returns an array of error messages (empty = valid).
|
|
36
|
+
*/
|
|
37
|
+
export function validateSong(song) {
|
|
38
|
+
const errors = [];
|
|
39
|
+
if (!song.id || typeof song.id !== "string") {
|
|
40
|
+
errors.push("id is required and must be a non-empty string");
|
|
41
|
+
}
|
|
42
|
+
else if (!/^[a-z0-9-]+$/.test(song.id)) {
|
|
43
|
+
errors.push("id must be kebab-case (lowercase alphanumeric + hyphens)");
|
|
44
|
+
}
|
|
45
|
+
if (!song.title || typeof song.title !== "string") {
|
|
46
|
+
errors.push("title is required");
|
|
47
|
+
}
|
|
48
|
+
if (!GENRES.includes(song.genre)) {
|
|
49
|
+
errors.push(`genre must be one of: ${GENRES.join(", ")}`);
|
|
50
|
+
}
|
|
51
|
+
if (!DIFFICULTIES.includes(song.difficulty)) {
|
|
52
|
+
errors.push(`difficulty must be one of: ${DIFFICULTIES.join(", ")}`);
|
|
53
|
+
}
|
|
54
|
+
if (!song.key || typeof song.key !== "string") {
|
|
55
|
+
errors.push("key signature is required");
|
|
56
|
+
}
|
|
57
|
+
if (typeof song.tempo !== "number" || song.tempo < 20 || song.tempo > 300) {
|
|
58
|
+
errors.push("tempo must be a number between 20 and 300 BPM");
|
|
59
|
+
}
|
|
60
|
+
if (!song.timeSignature || !/^\d+\/\d+$/.test(song.timeSignature)) {
|
|
61
|
+
errors.push('timeSignature must be in "N/N" format');
|
|
62
|
+
}
|
|
63
|
+
if (typeof song.durationSeconds !== "number" ||
|
|
64
|
+
song.durationSeconds <= 0) {
|
|
65
|
+
errors.push("durationSeconds must be a positive number");
|
|
66
|
+
}
|
|
67
|
+
// Musical language layer
|
|
68
|
+
if (!song.musicalLanguage) {
|
|
69
|
+
errors.push("musicalLanguage is required");
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
if (!song.musicalLanguage.description) {
|
|
73
|
+
errors.push("musicalLanguage.description is required");
|
|
74
|
+
}
|
|
75
|
+
if (!song.musicalLanguage.structure) {
|
|
76
|
+
errors.push("musicalLanguage.structure is required");
|
|
77
|
+
}
|
|
78
|
+
if (!Array.isArray(song.musicalLanguage.keyMoments) ||
|
|
79
|
+
song.musicalLanguage.keyMoments.length === 0) {
|
|
80
|
+
errors.push("musicalLanguage.keyMoments must have at least one entry");
|
|
81
|
+
}
|
|
82
|
+
if (!Array.isArray(song.musicalLanguage.teachingGoals) ||
|
|
83
|
+
song.musicalLanguage.teachingGoals.length === 0) {
|
|
84
|
+
errors.push("musicalLanguage.teachingGoals must have at least one entry");
|
|
85
|
+
}
|
|
86
|
+
if (!Array.isArray(song.musicalLanguage.styleTips)) {
|
|
87
|
+
errors.push("musicalLanguage.styleTips must be an array");
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// Measures
|
|
91
|
+
if (!Array.isArray(song.measures) || song.measures.length === 0) {
|
|
92
|
+
errors.push("measures must be a non-empty array");
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
for (let i = 0; i < song.measures.length; i++) {
|
|
96
|
+
const m = song.measures[i];
|
|
97
|
+
if (typeof m.number !== "number" || m.number !== i + 1) {
|
|
98
|
+
errors.push(`measure[${i}].number should be ${i + 1}, got ${m.number}`);
|
|
99
|
+
}
|
|
100
|
+
if (!m.rightHand && !m.leftHand) {
|
|
101
|
+
errors.push(`measure[${i}] must have at least rightHand or leftHand`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if (!Array.isArray(song.tags)) {
|
|
106
|
+
errors.push("tags must be an array");
|
|
107
|
+
}
|
|
108
|
+
return errors;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Validate the entire registry. Throws on first invalid song.
|
|
112
|
+
*/
|
|
113
|
+
export function validateRegistry() {
|
|
114
|
+
const allSongs = getAllSongs();
|
|
115
|
+
if (allSongs.length === 0) {
|
|
116
|
+
throw new Error("Registry is empty — no songs registered");
|
|
117
|
+
}
|
|
118
|
+
console.error(`Registry valid: ${allSongs.length} songs across ${new Set(allSongs.map((s) => s.genre)).size} genres.`);
|
|
119
|
+
}
|
|
120
|
+
// ─── Lookup ─────────────────────────────────────────────────────────────────
|
|
121
|
+
/** Get a song by its unique ID. */
|
|
122
|
+
export function getSong(id) {
|
|
123
|
+
return songs.get(id);
|
|
124
|
+
}
|
|
125
|
+
/** Get all registered songs. */
|
|
126
|
+
export function getAllSongs() {
|
|
127
|
+
return [...songs.values()];
|
|
128
|
+
}
|
|
129
|
+
/** Get all songs in a specific genre. */
|
|
130
|
+
export function getSongsByGenre(genre) {
|
|
131
|
+
return getAllSongs().filter((s) => s.genre === genre);
|
|
132
|
+
}
|
|
133
|
+
/** Get all songs at a specific difficulty level. */
|
|
134
|
+
export function getSongsByDifficulty(difficulty) {
|
|
135
|
+
return getAllSongs().filter((s) => s.difficulty === difficulty);
|
|
136
|
+
}
|
|
137
|
+
// ─── Search ─────────────────────────────────────────────────────────────────
|
|
138
|
+
/**
|
|
139
|
+
* Search songs with flexible filtering.
|
|
140
|
+
* The `query` field does a case-insensitive match against title, composer, tags,
|
|
141
|
+
* and musicalLanguage.description.
|
|
142
|
+
*/
|
|
143
|
+
export function searchSongs(options) {
|
|
144
|
+
let results = getAllSongs();
|
|
145
|
+
if (options.genre) {
|
|
146
|
+
results = results.filter((s) => s.genre === options.genre);
|
|
147
|
+
}
|
|
148
|
+
if (options.difficulty) {
|
|
149
|
+
results = results.filter((s) => s.difficulty === options.difficulty);
|
|
150
|
+
}
|
|
151
|
+
if (options.tags && options.tags.length > 0) {
|
|
152
|
+
const wanted = new Set(options.tags.map((t) => t.toLowerCase()));
|
|
153
|
+
results = results.filter((s) => s.tags.some((t) => wanted.has(t.toLowerCase())));
|
|
154
|
+
}
|
|
155
|
+
if (options.maxDuration !== undefined) {
|
|
156
|
+
results = results.filter((s) => s.durationSeconds <= options.maxDuration);
|
|
157
|
+
}
|
|
158
|
+
if (options.minDuration !== undefined) {
|
|
159
|
+
results = results.filter((s) => s.durationSeconds >= options.minDuration);
|
|
160
|
+
}
|
|
161
|
+
if (options.query) {
|
|
162
|
+
const q = options.query.toLowerCase();
|
|
163
|
+
results = results.filter((s) => s.title.toLowerCase().includes(q) ||
|
|
164
|
+
s.composer?.toLowerCase().includes(q) ||
|
|
165
|
+
s.tags.some((t) => t.toLowerCase().includes(q)) ||
|
|
166
|
+
s.musicalLanguage.description.toLowerCase().includes(q));
|
|
167
|
+
}
|
|
168
|
+
return results;
|
|
169
|
+
}
|
|
170
|
+
// ─── Stats ──────────────────────────────────────────────────────────────────
|
|
171
|
+
/** Get summary statistics about the registry. */
|
|
172
|
+
export function getStats() {
|
|
173
|
+
const allSongs = getAllSongs();
|
|
174
|
+
const byGenre = {};
|
|
175
|
+
for (const g of GENRES)
|
|
176
|
+
byGenre[g] = 0;
|
|
177
|
+
for (const s of allSongs)
|
|
178
|
+
byGenre[s.genre]++;
|
|
179
|
+
const byDifficulty = {};
|
|
180
|
+
for (const d of DIFFICULTIES)
|
|
181
|
+
byDifficulty[d] = 0;
|
|
182
|
+
for (const s of allSongs)
|
|
183
|
+
byDifficulty[s.difficulty]++;
|
|
184
|
+
const totalMeasures = allSongs.reduce((sum, s) => sum + s.measures.length, 0);
|
|
185
|
+
return {
|
|
186
|
+
totalSongs: allSongs.length,
|
|
187
|
+
byGenre,
|
|
188
|
+
byDifficulty,
|
|
189
|
+
totalMeasures,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
// ─── Reset ──────────────────────────────────────────────────────────────────
|
|
193
|
+
/** Clear all registered songs. Used when reloading or in tests. */
|
|
194
|
+
export function clearRegistry() {
|
|
195
|
+
songs.clear();
|
|
196
|
+
}
|
|
197
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/songs/registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/** Genres supported by the library. */
|
|
2
|
+
export declare const GENRES: readonly ["classical", "jazz", "pop", "blues", "rock", "rnb", "latin", "film", "ragtime", "new-age"];
|
|
3
|
+
export type Genre = (typeof GENRES)[number];
|
|
4
|
+
/** Difficulty levels for song entries. */
|
|
5
|
+
export declare const DIFFICULTIES: readonly ["beginner", "intermediate", "advanced"];
|
|
6
|
+
export type Difficulty = (typeof DIFFICULTIES)[number];
|
|
7
|
+
/** How notes are expressed in the measures array. */
|
|
8
|
+
export type NoteFormat = "scientific" | "midi-number" | "letter";
|
|
9
|
+
/**
|
|
10
|
+
* Human-readable musical context — the part an LLM reads to *understand*
|
|
11
|
+
* the song before deciding how to teach or perform it.
|
|
12
|
+
*/
|
|
13
|
+
export interface MusicalLanguage {
|
|
14
|
+
/** 1–3 sentence overview of the piece (mood, era, why it matters). */
|
|
15
|
+
description: string;
|
|
16
|
+
/** Musical structure: "ABA", "Verse-Chorus-Verse", "Rondo ABACA", etc. */
|
|
17
|
+
structure: string;
|
|
18
|
+
/** Notable moments an LLM can reference when teaching. */
|
|
19
|
+
keyMoments: string[];
|
|
20
|
+
/** Pedagogical notes: what the student will learn from this piece. */
|
|
21
|
+
teachingGoals: string[];
|
|
22
|
+
/** Style/feel hints for performance: "legato", "swing eighths", etc. */
|
|
23
|
+
styleTips: string[];
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* A single measure of piano music, code-ready for MIDI playback or analysis.
|
|
27
|
+
*
|
|
28
|
+
* Notes use scientific pitch notation by default: "C4", "F#5", "Bb3".
|
|
29
|
+
* Chords are space-separated: "C4 E4 G4".
|
|
30
|
+
* Rests are written as "R".
|
|
31
|
+
* Duration is encoded inline: "C4:q" (quarter), "E4:h" (half), "G4:w" (whole),
|
|
32
|
+
* "A4:e" (eighth), "B4:s" (sixteenth). Default is quarter if omitted.
|
|
33
|
+
*/
|
|
34
|
+
export interface Measure {
|
|
35
|
+
/** 1-based measure number. */
|
|
36
|
+
number: number;
|
|
37
|
+
/** Right-hand notes (treble clef). */
|
|
38
|
+
rightHand: string;
|
|
39
|
+
/** Left-hand notes (bass clef). */
|
|
40
|
+
leftHand: string;
|
|
41
|
+
/** Suggested fingering, e.g. "1-2-3-5" or "RH: 1-3-5, LH: 5-3-1". */
|
|
42
|
+
fingering?: string;
|
|
43
|
+
/** Per-measure teaching note the LLM can read aloud or display. */
|
|
44
|
+
teachingNote?: string;
|
|
45
|
+
/**
|
|
46
|
+
* Optional dynamics/expression mark for this measure.
|
|
47
|
+
* Standard markings: "pp", "p", "mp", "mf", "f", "ff", "crescendo", "decrescendo".
|
|
48
|
+
*/
|
|
49
|
+
dynamics?: string;
|
|
50
|
+
/** Tempo override for this measure (rubato, ritardando, etc.). */
|
|
51
|
+
tempoOverride?: number;
|
|
52
|
+
}
|
|
53
|
+
export interface SongEntry {
|
|
54
|
+
/** Unique slug: kebab-case, e.g. "moonlight-sonata-mvt1". */
|
|
55
|
+
id: string;
|
|
56
|
+
/** Human-readable title. */
|
|
57
|
+
title: string;
|
|
58
|
+
/** Genre — must be one of the 10 canonical genres. */
|
|
59
|
+
genre: Genre;
|
|
60
|
+
/** Composer or artist (undefined for traditional/anonymous). */
|
|
61
|
+
composer?: string;
|
|
62
|
+
/** Arranger (if this is a simplified or adapted version). */
|
|
63
|
+
arranger?: string;
|
|
64
|
+
/** Difficulty level. */
|
|
65
|
+
difficulty: Difficulty;
|
|
66
|
+
/** Key signature, e.g. "C major", "A minor", "Bb major". */
|
|
67
|
+
key: string;
|
|
68
|
+
/** Tempo in BPM (the default/starting tempo). */
|
|
69
|
+
tempo: number;
|
|
70
|
+
/** Time signature, e.g. "4/4", "3/4", "6/8". */
|
|
71
|
+
timeSignature: string;
|
|
72
|
+
/** Approximate duration in seconds at the given tempo. */
|
|
73
|
+
durationSeconds: number;
|
|
74
|
+
/** The LLM-readable musical language layer. */
|
|
75
|
+
musicalLanguage: MusicalLanguage;
|
|
76
|
+
/** Code-ready measure data for playback and analysis. */
|
|
77
|
+
measures: Measure[];
|
|
78
|
+
/** Freeform tags for search/filtering. */
|
|
79
|
+
tags: string[];
|
|
80
|
+
/** Source/attribution for the arrangement. */
|
|
81
|
+
source?: string;
|
|
82
|
+
}
|
|
83
|
+
/** Summary stats returned by the registry. */
|
|
84
|
+
export interface RegistryStats {
|
|
85
|
+
totalSongs: number;
|
|
86
|
+
byGenre: Record<Genre, number>;
|
|
87
|
+
byDifficulty: Record<Difficulty, number>;
|
|
88
|
+
totalMeasures: number;
|
|
89
|
+
}
|
|
90
|
+
/** Search options for filtering songs. */
|
|
91
|
+
export interface SearchOptions {
|
|
92
|
+
genre?: Genre;
|
|
93
|
+
difficulty?: Difficulty;
|
|
94
|
+
query?: string;
|
|
95
|
+
tags?: string[];
|
|
96
|
+
maxDuration?: number;
|
|
97
|
+
minDuration?: number;
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/songs/types.ts"],"names":[],"mappings":"AAaA,uCAAuC;AACvC,eAAO,MAAM,MAAM,sGAWT,CAAC;AAEX,MAAM,MAAM,KAAK,GAAG,CAAC,OAAO,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC;AAE5C,0CAA0C;AAC1C,eAAO,MAAM,YAAY,mDAAoD,CAAC;AAC9E,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC;AAEvD,qDAAqD;AACrD,MAAM,MAAM,UAAU,GAAG,YAAY,GAAG,aAAa,GAAG,QAAQ,CAAC;AAIjE;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,sEAAsE;IACtE,WAAW,EAAE,MAAM,CAAC;IAEpB,0EAA0E;IAC1E,SAAS,EAAE,MAAM,CAAC;IAElB,0DAA0D;IAC1D,UAAU,EAAE,MAAM,EAAE,CAAC;IAErB,sEAAsE;IACtE,aAAa,EAAE,MAAM,EAAE,CAAC;IAExB,wEAAwE;IACxE,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAID;;;;;;;;GAQG;AACH,MAAM,WAAW,OAAO;IACtB,8BAA8B;IAC9B,MAAM,EAAE,MAAM,CAAC;IAEf,sCAAsC;IACtC,SAAS,EAAE,MAAM,CAAC;IAElB,mCAAmC;IACnC,QAAQ,EAAE,MAAM,CAAC;IAEjB,qEAAqE;IACrE,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,mEAAmE;IACnE,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,kEAAkE;IAClE,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAID,MAAM,WAAW,SAAS;IACxB,6DAA6D;IAC7D,EAAE,EAAE,MAAM,CAAC;IAEX,4BAA4B;IAC5B,KAAK,EAAE,MAAM,CAAC;IAEd,sDAAsD;IACtD,KAAK,EAAE,KAAK,CAAC;IAEb,gEAAgE;IAChE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,wBAAwB;IACxB,UAAU,EAAE,UAAU,CAAC;IAEvB,4DAA4D;IAC5D,GAAG,EAAE,MAAM,CAAC;IAEZ,iDAAiD;IACjD,KAAK,EAAE,MAAM,CAAC;IAEd,gDAAgD;IAChD,aAAa,EAAE,MAAM,CAAC;IAEtB,0DAA0D;IAC1D,eAAe,EAAE,MAAM,CAAC;IAExB,+CAA+C;IAC/C,eAAe,EAAE,eAAe,CAAC;IAEjC,yDAAyD;IACzD,QAAQ,EAAE,OAAO,EAAE,CAAC;IAEpB,0CAA0C;IAC1C,IAAI,EAAE,MAAM,EAAE,CAAC;IAEf,8CAA8C;IAC9C,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAID,8CAA8C;AAC9C,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IAC/B,YAAY,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;IACzC,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,0CAA0C;AAC1C,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// ─── Song Types ─────────────────────────────────────────────────────────────
|
|
2
|
+
//
|
|
3
|
+
// The hybrid format has three layers:
|
|
4
|
+
// 1. Metadata — structured JSON (genre, key, tempo, difficulty, etc.)
|
|
5
|
+
// 2. Musical Language — human-readable descriptions for LLM reasoning
|
|
6
|
+
// 3. Code-ready — measure-by-measure note data the runtime can consume
|
|
7
|
+
//
|
|
8
|
+
// An LLM can read the "musicalLanguage" block to explain a song to a student,
|
|
9
|
+
// then use the "measures" array to drive MIDI playback or generate exercises.
|
|
10
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
11
|
+
// ─── Enums ──────────────────────────────────────────────────────────────────
|
|
12
|
+
/** Genres supported by the library. */
|
|
13
|
+
export const GENRES = [
|
|
14
|
+
"classical",
|
|
15
|
+
"jazz",
|
|
16
|
+
"pop",
|
|
17
|
+
"blues",
|
|
18
|
+
"rock",
|
|
19
|
+
"rnb",
|
|
20
|
+
"latin",
|
|
21
|
+
"film",
|
|
22
|
+
"ragtime",
|
|
23
|
+
"new-age",
|
|
24
|
+
];
|
|
25
|
+
/** Difficulty levels for song entries. */
|
|
26
|
+
export const DIFFICULTIES = ["beginner", "intermediate", "advanced"];
|
|
27
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/songs/types.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,EAAE;AACF,sCAAsC;AACtC,0EAA0E;AAC1E,wEAAwE;AACxE,yEAAyE;AACzE,EAAE;AACF,8EAA8E;AAC9E,8EAA8E;AAC9E,gFAAgF;AAEhF,+EAA+E;AAE/E,uCAAuC;AACvC,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,WAAW;IACX,MAAM;IACN,KAAK;IACL,OAAO;IACP,MAAM;IACN,KAAK;IACL,OAAO;IACP,MAAM;IACN,SAAS;IACT,SAAS;CACD,CAAC;AAIX,0CAA0C;AAC1C,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,UAAU,EAAE,cAAc,EAAE,UAAU,CAAU,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { ParsedMidi } from "../midi/types.js";
|
|
2
|
+
import type { TeachingHook, VoiceDirective, VoiceSink, AsideDirective, AsideSink } from "../types.js";
|
|
3
|
+
import { PositionTracker } from "../playback/position.js";
|
|
4
|
+
export interface LiveMidiFeedbackOptions {
|
|
5
|
+
/** Emit voice encouragement every N measures (default: 8). */
|
|
6
|
+
voiceInterval?: number;
|
|
7
|
+
/** Warn about wide range passages (default: true). */
|
|
8
|
+
warnOnWideRange?: boolean;
|
|
9
|
+
/** Warn about velocity shifts (default: true). */
|
|
10
|
+
warnOnVelocityShift?: boolean;
|
|
11
|
+
/** Warn about dense passages (default: true). */
|
|
12
|
+
warnOnDensity?: boolean;
|
|
13
|
+
/** Detect section boundaries (default: true). */
|
|
14
|
+
detectSections?: boolean;
|
|
15
|
+
/** Announce milestone percentages (default: true). */
|
|
16
|
+
announceMilestones?: boolean;
|
|
17
|
+
/** Voice preset. */
|
|
18
|
+
voice?: string;
|
|
19
|
+
/** Speech speed. Default: 1.0. */
|
|
20
|
+
speechSpeed?: number;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Create a position-aware live feedback hook for MIDI playback.
|
|
24
|
+
*
|
|
25
|
+
* Uses PositionTracker to analyse measure context before each measure plays.
|
|
26
|
+
* Provides: range warnings, velocity shift detection, density alerts,
|
|
27
|
+
* section boundary markers, milestone announcements, periodic encouragement.
|
|
28
|
+
*
|
|
29
|
+
* Designed to be composed with createSingOnMidiHook for the full experience.
|
|
30
|
+
*/
|
|
31
|
+
export declare function createLiveMidiFeedbackHook(voiceSink: VoiceSink, asideSink: AsideSink, midi: ParsedMidi, options?: LiveMidiFeedbackOptions): TeachingHook & {
|
|
32
|
+
voiceDirectives: VoiceDirective[];
|
|
33
|
+
asideDirectives: AsideDirective[];
|
|
34
|
+
tracker: PositionTracker;
|
|
35
|
+
};
|
|
36
|
+
//# sourceMappingURL=live-midi-feedback.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"live-midi-feedback.d.ts","sourceRoot":"","sources":["../../src/teaching/live-midi-feedback.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,KAAK,EACV,YAAY,EACZ,cAAc,EACd,SAAS,EACT,cAAc,EACd,SAAS,EACV,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AA0E1D,MAAM,WAAW,uBAAuB;IACtC,8DAA8D;IAC9D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,sDAAsD;IACtD,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,kDAAkD;IAClD,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,iDAAiD;IACjD,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,iDAAiD;IACjD,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,sDAAsD;IACtD,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,oBAAoB;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kCAAkC;IAClC,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAID;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CACxC,SAAS,EAAE,SAAS,EACpB,SAAS,EAAE,SAAS,EACpB,IAAI,EAAE,UAAU,EAChB,OAAO,GAAE,uBAA4B,GACpC,YAAY,GAAG;IAChB,eAAe,EAAE,cAAc,EAAE,CAAC;IAClC,eAAe,EAAE,cAAc,EAAE,CAAC;IAClC,OAAO,EAAE,eAAe,CAAC;CAC1B,CAoMA"}
|