minikeys 0.1.2 → 0.2.1

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/README.md CHANGED
@@ -1,99 +1,88 @@
1
1
  # MiniKeys
2
2
 
3
- [![npm version](https://badge.fury.io/js/minikeys.svg)](https://badge.fury.io/js/minikeys) ![release](https://github.com/liampuk/minikeys/workflows/release/badge.svg)
3
+ <!-- TODO add badges from CD -->
4
4
 
5
- A tiny javascript library that plays the piano.
5
+ A tiny javascript library that plays the piano. [Try it out here!](http://liamp.uk/minikeys)
6
6
 
7
- ## Get Started
7
+ To use MiniKeys in a React app, you can use components from [@minikeys/react](https://npmjs.com/)
8
8
 
9
- MiniKeys is hosted via CDN and can be linked with the following:
10
- - Latest verison:
11
- ```html
12
- <script src="https://cdn.jsdelivr.net/npm/minikeys/dist/minikeys.min.js"></script>
13
- ```
14
- - Specific release (v0.1.1):
15
- ```html
16
- <script src="https://cdn.jsdelivr.net/npm/minikeys@0.1.1/dist/minikeys.min.js"></script>
17
- ```
9
+ <img width="1269" height="486" alt="Screenshot 2025-07-31 at 21 23 08" src="https://github.com/user-attachments/assets/1ff24de7-9222-42f0-ba74-9a3618ef9cd6" />
18
10
 
19
- ## Reference
11
+ ## Install
20
12
 
21
- First, instantiate a new instance of MiniKeys:
22
-
23
- ```js
24
- let miniKeys = new MiniKeys();
13
+ ```
14
+ $ yarn add minikeys
15
+ # OR
16
+ $ npm install minikeys
25
17
  ```
26
18
 
27
- Then call `init()` to initialise the Web Audio Api:
19
+ ## Using MiniKeys
28
20
 
29
- ```js
30
- miniKeys.init();
31
- ```
21
+ ### Basic Setup
32
22
 
33
- Load all samples with `loadSamples(samples)`. Pass in an array of urls to audio files with the naming convention `[p/f][midi-note]`, where `[p/f]` represents the notes dynamic (*p* for piano, *f* for forte) and `[midi-note]` is the corresponding midi value of the note (*60* for C4):
23
+ First, instantiate a new instance of MiniKeys, then call `loadNotes` with a list of sample audio files. You can then call `playNote` with either the midi note value or the note name (_A3_, _C#4_ etc.)
34
24
 
35
- ```js
36
- miniKeys.loadSamples([
37
- '/samples/p45.ogg',
38
- '/samples/f45.ogg'
39
- ]);
25
+ ```
26
+ const miniKeys = new MiniKeys();
27
+
28
+ miniKeys.loadNotes([
29
+ {
30
+ note: 'A#2',
31
+ url: '/samples/a2.ogg
32
+ velocity: 'piano'
33
+ },
34
+ {
35
+ note: 'A#6',
36
+ url: '/samples/a6.ogg
37
+ velocity: 'piano'
38
+ },
39
+ ])
40
+
41
+ miniKeys.playNoteFromName('C#4')
40
42
  ```
41
43
 
42
- MiniKeys works with any number of samples. When a note is played, MiniKeys finds the closest loaded sample and tunes it to the correct frequency. In the above example, only one sample has been loaded (with 2 dynamics), and so it will be used for all notes.
44
+ MiniKeys works with any number of samples. When a note is played, MiniKeys finds the closest loaded sample and tunes it to the correct frequency.
43
45
 
44
- To play a note use `playNote(note, velocity)` where note is a midi note (0 to 127), and velocity is a midi velocity value (between 0 and 127):
46
+ ### Main Functions
45
47
 
46
- ```js
47
- miniKeys.playNote(60, 100);
48
- ```
48
+ #### loadNotes
49
49
 
50
- To play a note via the keyboard, `getKeyMap()` returns a map of keyboard keys to midi notes in the format `"key": midi-note`. This is initialised as the following, where the highlighted section of the piano shows the notes playable on the keyboard:
50
+ `loadNotes` takes an array of Samples objects, where _note_ is the name of the note and _velocity_ is if the sample is a soft (piano) or hard (forte) sample.
51
51
 
52
- ![keys](https://i.imgur.com/RY63ar8.png)
52
+ ```
53
+ {
54
+ note: NoteName; // 'A4', 'C#3' etc.
55
+ url: string;
56
+ velocity: Velocity; // 'piano' or 'forte'
57
+ }[]
58
+ ```
53
59
 
54
- These mappings can be shifted up and down the keyboard with `shift(direction, shiftMode)`, where `direction` is either 1 (up the piano) or -1 (down the piano) and `shiftMode` is either `OCTAVE` or `TONE`. `OCTAVE` shifts the keyboard by an octave (8 notes) `TONE` shifts the keyboard by one white key:
60
+ `loadNotes` returns a Promise that resolves when all notes are loaded or if there is an error. You can provide `loadNotes` with a callback function to handle progress updates.
55
61
 
56
- ```js
57
- miniKeys.shift(-1, miniKeys.OCTAVE);
58
- ```
62
+ #### playNoteFromMidi
59
63
 
60
- Several methods are provided to make rendering a representation of the piano and keyboard easy. `getActiveKeys()` returns an array of which keyboard keys currently map to midi notes in the format `"key": midi-note` (for example, in the above image, *q* currently is not an active key). `getKeyIndex()` returns the current position on the piano (in terms of white keys) of the furthest left note. This can be used with `NUMKEYS` to highlight a section of the piano corresponding to the playable keys. `getWhiteMidiNotes()` and `getBlackMidiNotes()` return an array of length 52 containing the midi notes along the piano from left to right for white keys and black keys respectively. This is useful for rendering black keys as gaps between keys are represented with a `-1` in that location in the array (an example of this is included with the project).
64
+ `playNoteFromMidi` takes a midi note (0-127), and a velocity value (0-127). The sample closest to the midi value is chosen, and is pitch shifted to play the correct note. The volume and correct dynamic (_piano_ or _forte_) is chosen based on the velocity.
61
65
 
62
- Volume can be changed with `volume(value)`, where value is between 0 and 1:
66
+ #### playNoteFromName
63
67
 
64
- ```js
65
- miniKeys.volume(0.5);
66
- ```
68
+ This does the same as `playNoteFromMidi`, but with note names (_A3_, _C#4_ etc.)
67
69
 
68
- ## Build process
70
+ #### setSustain
69
71
 
70
- First, clone the project, then run the following commands:
72
+ This function takes a boolean value and behaves the same as a sustain pedal on a piano. If it sustain is set to true notes are held (including already played notes), if sustain is set to false notes are faded out (including already played notes)
71
73
 
72
- ```
73
- npm install
74
- npm run build
75
- ```
74
+ ### Keyboard Functions
76
75
 
77
- This will generate a `/dist` folder with the minimised version of the library `minikeys.min.js`.
76
+ Various functions are provided for interacting with MiniKeys through the keyboard. Full documentation is coming soon!
78
77
 
79
- To run tests, use the command:
78
+ ## Finding Samples
80
79
 
81
- ```
82
- npm test
83
- ```
80
+ [Pianobook](www.pianobook.co.uk) is a fantastic community project to provide free piano (and other instruments) samples. The samples used in the example are from a [Steinway Concert Grand in Kristiansand, Norway](https://www.pianobook.co.uk/packs/kristiansand-concert-steinway/). Thank you to [Pete Malkin](https://www.petemalkin.co.uk/) for sampling this lovely instrument!
84
81
 
85
82
  ## TODO
86
83
 
87
- - [x] clean up interface with keyboard (move validation of key into library)
88
- - [x] compress samples (.ogg)
89
- - [x] trim start of samples
90
- - [ ] tidy/comment code
91
- - [x] add tests
92
- - [ ] cover all functions with tests
93
- - [x] write build guide for readme
94
- - [x] add method for adding loading bar
95
- - [x] add animations to page
96
- - [ ] fix on iOS
97
- - [ ] add midi parser and player
98
- - [ ] add tuning mode (perfect atm, out of tune with actual piano)
99
- - [ ] move keyboard with mouse in example
84
+ - Function to play multiple notes
85
+ - Add more tests
86
+ - Look into replacing compressor
87
+ - New package for chords? @minikeys/chords?
88
+ - Custom labels for piano (for showing keyboard keys)
package/dist/index.cjs ADDED
@@ -0,0 +1 @@
1
+ "use strict";var f=Object.defineProperty;var g=Object.getOwnPropertyDescriptor;var D=Object.getOwnPropertyNames;var b=Object.prototype.hasOwnProperty;var F=(s,e)=>{for(var o in e)f(s,o,{get:e[o],enumerable:!0})},w=(s,e,o,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let t of D(e))!b.call(s,t)&&t!==o&&f(s,t,{get:()=>e[t],enumerable:!(i=g(e,t))||i.enumerable});return s};var G=s=>w(f({},"__esModule",{value:!0}),s);var K={};F(K,{MiniKeys:()=>y,MiniKeysKeyboard:()=>A,keyboardBlackNotes:()=>h,keyboardWhiteNotes:()=>p,midiToNote:()=>u,noteToMidi:()=>n});module.exports=G(K);var n={"C-1":0,"C#-1":1,"D-1":2,"D#-1":3,"E-1":4,"F-1":5,"F#-1":6,"G-1":7,"G#-1":8,"A-1":9,"A#-1":10,"B-1":11,C0:12,"C#0":13,D0:14,"D#0":15,E0:16,F0:17,"F#0":18,G0:19,"G#0":20,A0:21,"A#0":22,B0:23,C1:24,"C#1":25,D1:26,"D#1":27,E1:28,F1:29,"F#1":30,G1:31,"G#1":32,A1:33,"A#1":34,B1:35,C2:36,"C#2":37,D2:38,"D#2":39,E2:40,F2:41,"F#2":42,G2:43,"G#2":44,A2:45,"A#2":46,B2:47,C3:48,"C#3":49,D3:50,"D#3":51,E3:52,F3:53,"F#3":54,G3:55,"G#3":56,A3:57,"A#3":58,B3:59,C4:60,"C#4":61,D4:62,"D#4":63,E4:64,F4:65,"F#4":66,G4:67,"G#4":68,A4:69,"A#4":70,B4:71,C5:72,"C#5":73,D5:74,"D#5":75,E5:76,F5:77,"F#5":78,G5:79,"G#5":80,A5:81,"A#5":82,B5:83,C6:84,"C#6":85,D6:86,"D#6":87,E6:88,F6:89,"F#6":90,G6:91,"G#6":92,A6:93,"A#6":94,B6:95,C7:96,"C#7":97,D7:98,"D#7":99,E7:100,F7:101,"F#7":102,G7:103,"G#7":104,A7:105,"A#7":106,B7:107,C8:108,"C#8":109,D8:110,"D#8":111,E8:112,F8:113,"F#8":114,G8:115,"G#8":116,A8:117,"A#8":118,B8:119,C9:120,"C#9":121,D9:122,"D#9":123,E9:124,F9:125,"F#9":126,G9:127},u=Object.fromEntries(Object.entries(n).map(([s,e])=>[e,s])),h=[null,"A#0",null,"C#1","D#1",null,"F#1","G#1","A#1",null,"C#2","D#2",null,"F#2","G#2","A#2",null,"C#3","D#3",null,"F#3","G#3","A#3",null,"C#4","D#4",null,"F#4","G#4","A#4",null,"C#5","D#5",null,"F#5","G#5","A#5",null,"C#6","D#6",null,"F#6","G#6","A#6",null,"C#7","D#7",null,"F#7","G#7","A#7",null,null,null],p=["A0","B0","C1","D1","E1","F1","G1","A1","B1","C2","D2","E2","F2","G2","A2","B2","C3","D3","E3","F3","G3","A3","B3","C4","D4","E4","F4","G4","A4","B4","C5","D5","E5","F5","G5","A5","B5","C6","D6","E6","F6","G6","A6","B6","C7","D7","E7","F7","G7","A7","B7","C8"],m=[["Digit1","Digit2","Digit3","Digit4","Digit5","Digit6","Digit7","Digit8","Digit9","Digit0","Minus","Equal"],["KeyQ","KeyW","KeyE","KeyR","KeyT","KeyY","KeyU","KeyI","KeyO","KeyP","BracketLeft","BracketRight"],["KeyA","KeyS","KeyD","KeyF","KeyG","KeyH","KeyJ","KeyK","KeyL","Semicolon","Quote"],["KeyZ","KeyX","KeyC","KeyV","KeyB","KeyN","KeyM","Comma","Period","Slash"]],C=(s,e,o,i)=>{let t;if(i&&i<64&&e.size>0)t=e;else if(s.size>0)t=s;else throw new Error("No notes loaded");let r=Array.from(t.keys()).reduce((l,a)=>Math.abs(a-o)<Math.abs(l-o)?a:l);return{closestNoteMidi:r,closestNote:t.get(r)}};var y=class{audioContext;compressorNode;forteNotes=new Map;pianoNotes=new Map;sustain=!1;playingNotes=new Map;progress=0;constructor(){this.audioContext=new AudioContext,this.compressorNode=this.audioContext.createDynamicsCompressor(),this.compressorNode.threshold.value=-24,this.compressorNode.knee.value=0,this.compressorNode.ratio.value=2,this.compressorNode.attack.value=.001,this.compressorNode.release.value=.5,this.compressorNode.connect(this.audioContext.destination)}setSustain=e=>{this.sustain=e,e?this.playingNotes.forEach(o=>{let i=this.audioContext.currentTime;o.gain.cancelScheduledValues(i),o.gain.setValueAtTime(o.gain.value,i)}):this.playingNotes.forEach(o=>{let i=this.audioContext.currentTime;o.gain.cancelScheduledValues(i),o.gain.setValueAtTime(o.gain.value,i),o.gain.linearRampToValueAtTime(.001,i+.5)})};loadNotes=async(e,o)=>{this.forteNotes.clear(),this.pianoNotes.clear();let i=e.map(async t=>{let l=await(await fetch(t.url)).arrayBuffer(),a=await this.audioContext.decodeAudioData(l);if(t.velocity==="piano"?this.pianoNotes.set(n[t.note],a):this.forteNotes.set(n[t.note],a),o){let d=(this.pianoNotes.size+this.forteNotes.size)/e.length;d>this.progress&&(this.progress=d,o(this.progress))}});return await Promise.all(i)};playNoteFromMidi=(e,o)=>{if(e<0||e>127)throw new Error("Invalid midi note");if(o&&(o<0||o>127))throw new Error("Invalid velocity value");if(!this.audioContext)throw new Error("Audio context not initialized");let t=this.audioContext.createBufferSource(),{closestNoteMidi:r,closestNote:l}=C(this.forteNotes,this.pianoNotes,e,o);if(l){t.buffer=l;let a=this.audioContext.createGain(),d=Math.min(127,o??127);if(a.gain.value=(d/127*.9+.1)*.5,t.connect(a),a.connect(this.compressorNode),t.playbackRate.value=2**((e-r)/12),t.start(),this.playingNotes.set(t,a),!this.sustain){let c=this.audioContext.currentTime;a.gain.cancelScheduledValues(c),a.gain.setValueAtTime(a.gain.value,c),a.gain.exponentialRampToValueAtTime(Number.EPSILON,c+8)}t.onended=()=>{t.disconnect(),this.playingNotes.delete(t)}}else throw new Error("Note not found")};playNoteFromName=(e,o)=>{this.playNoteFromMidi(n[e],o)}};var A=class{mode;keyMap=new Map;offset;constructor(e){this.mode=e,e==="single"?this.offset=23:this.offset=16,this.buildNoteMap()}getNoteMap=()=>this.keyMap;getMidiRange=()=>{let e=Array.from(this.keyMap.values()).reduce((i,t)=>t.midiNote&&t.midiNote<i?t.midiNote:i,127),o=Array.from(this.keyMap.values()).reduce((i,t)=>t.midiNote&&t.midiNote>i?t.midiNote:i,0);return{low:e,high:o}};getNoteRange=()=>{let e=this.getMidiRange();return{low:u[e.low],high:u[e.high]}};shiftLeft=()=>{this.offset=Math.max(this.offset-1,0),this.keyMap=new Map,this.buildNoteMap()};shiftLeftOctave=()=>{this.offset=Math.max(this.offset-7,0),this.keyMap=new Map,this.buildNoteMap()};shiftRight=()=>{this.offset=Math.min(this.offset+1,this.mode==="dual"?30:41),this.keyMap=new Map,this.buildNoteMap()};shiftRightOctave=()=>{this.offset=Math.min(this.offset+7,this.mode==="dual"?30:41),this.keyMap=new Map,this.buildNoteMap()};buildNoteMap=()=>{this.mode==="single"?(N(this.keyMap,1,this.offset),M(this.keyMap,2,this.offset)):(N(this.keyMap,0,this.offset),M(this.keyMap,1,this.offset),N(this.keyMap,2,this.offset+12),M(this.keyMap,3,this.offset+12))}},N=(s,e,o)=>{m[e].forEach((i,t)=>{let r=h[o+t];if(r===null)s.set(i,{midiNote:null,type:"disabled"});else if(r!==void 0)s.set(i,{midiNote:n[r],type:"black"});else throw new Error("Invalid note")})},M=(s,e,o)=>{m[e].forEach((i,t)=>{let r=p[o+t];if(r)s.set(i,{midiNote:n[r],type:"white"});else throw new Error("Invalid note")})};0&&(module.exports={MiniKeys,MiniKeysKeyboard,keyboardBlackNotes,keyboardWhiteNotes,midiToNote,noteToMidi});
@@ -0,0 +1,186 @@
1
+ declare const noteToMidi: {
2
+ readonly "C-1": 0;
3
+ readonly "C#-1": 1;
4
+ readonly "D-1": 2;
5
+ readonly "D#-1": 3;
6
+ readonly "E-1": 4;
7
+ readonly "F-1": 5;
8
+ readonly "F#-1": 6;
9
+ readonly "G-1": 7;
10
+ readonly "G#-1": 8;
11
+ readonly "A-1": 9;
12
+ readonly "A#-1": 10;
13
+ readonly "B-1": 11;
14
+ readonly C0: 12;
15
+ readonly "C#0": 13;
16
+ readonly D0: 14;
17
+ readonly "D#0": 15;
18
+ readonly E0: 16;
19
+ readonly F0: 17;
20
+ readonly "F#0": 18;
21
+ readonly G0: 19;
22
+ readonly "G#0": 20;
23
+ readonly A0: 21;
24
+ readonly "A#0": 22;
25
+ readonly B0: 23;
26
+ readonly C1: 24;
27
+ readonly "C#1": 25;
28
+ readonly D1: 26;
29
+ readonly "D#1": 27;
30
+ readonly E1: 28;
31
+ readonly F1: 29;
32
+ readonly "F#1": 30;
33
+ readonly G1: 31;
34
+ readonly "G#1": 32;
35
+ readonly A1: 33;
36
+ readonly "A#1": 34;
37
+ readonly B1: 35;
38
+ readonly C2: 36;
39
+ readonly "C#2": 37;
40
+ readonly D2: 38;
41
+ readonly "D#2": 39;
42
+ readonly E2: 40;
43
+ readonly F2: 41;
44
+ readonly "F#2": 42;
45
+ readonly G2: 43;
46
+ readonly "G#2": 44;
47
+ readonly A2: 45;
48
+ readonly "A#2": 46;
49
+ readonly B2: 47;
50
+ readonly C3: 48;
51
+ readonly "C#3": 49;
52
+ readonly D3: 50;
53
+ readonly "D#3": 51;
54
+ readonly E3: 52;
55
+ readonly F3: 53;
56
+ readonly "F#3": 54;
57
+ readonly G3: 55;
58
+ readonly "G#3": 56;
59
+ readonly A3: 57;
60
+ readonly "A#3": 58;
61
+ readonly B3: 59;
62
+ readonly C4: 60;
63
+ readonly "C#4": 61;
64
+ readonly D4: 62;
65
+ readonly "D#4": 63;
66
+ readonly E4: 64;
67
+ readonly F4: 65;
68
+ readonly "F#4": 66;
69
+ readonly G4: 67;
70
+ readonly "G#4": 68;
71
+ readonly A4: 69;
72
+ readonly "A#4": 70;
73
+ readonly B4: 71;
74
+ readonly C5: 72;
75
+ readonly "C#5": 73;
76
+ readonly D5: 74;
77
+ readonly "D#5": 75;
78
+ readonly E5: 76;
79
+ readonly F5: 77;
80
+ readonly "F#5": 78;
81
+ readonly G5: 79;
82
+ readonly "G#5": 80;
83
+ readonly A5: 81;
84
+ readonly "A#5": 82;
85
+ readonly B5: 83;
86
+ readonly C6: 84;
87
+ readonly "C#6": 85;
88
+ readonly D6: 86;
89
+ readonly "D#6": 87;
90
+ readonly E6: 88;
91
+ readonly F6: 89;
92
+ readonly "F#6": 90;
93
+ readonly G6: 91;
94
+ readonly "G#6": 92;
95
+ readonly A6: 93;
96
+ readonly "A#6": 94;
97
+ readonly B6: 95;
98
+ readonly C7: 96;
99
+ readonly "C#7": 97;
100
+ readonly D7: 98;
101
+ readonly "D#7": 99;
102
+ readonly E7: 100;
103
+ readonly F7: 101;
104
+ readonly "F#7": 102;
105
+ readonly G7: 103;
106
+ readonly "G#7": 104;
107
+ readonly A7: 105;
108
+ readonly "A#7": 106;
109
+ readonly B7: 107;
110
+ readonly C8: 108;
111
+ readonly "C#8": 109;
112
+ readonly D8: 110;
113
+ readonly "D#8": 111;
114
+ readonly E8: 112;
115
+ readonly F8: 113;
116
+ readonly "F#8": 114;
117
+ readonly G8: 115;
118
+ readonly "G#8": 116;
119
+ readonly A8: 117;
120
+ readonly "A#8": 118;
121
+ readonly B8: 119;
122
+ readonly C9: 120;
123
+ readonly "C#9": 121;
124
+ readonly D9: 122;
125
+ readonly "D#9": 123;
126
+ readonly E9: 124;
127
+ readonly F9: 125;
128
+ readonly "F#9": 126;
129
+ readonly G9: 127;
130
+ };
131
+ declare const midiToNote: { [K in keyof typeof noteToMidi as (typeof noteToMidi)[K]]: K; };
132
+ declare const keyboardBlackNotes: readonly [null, "A#0", null, "C#1", "D#1", null, "F#1", "G#1", "A#1", null, "C#2", "D#2", null, "F#2", "G#2", "A#2", null, "C#3", "D#3", null, "F#3", "G#3", "A#3", null, "C#4", "D#4", null, "F#4", "G#4", "A#4", null, "C#5", "D#5", null, "F#5", "G#5", "A#5", null, "C#6", "D#6", null, "F#6", "G#6", "A#6", null, "C#7", "D#7", null, "F#7", "G#7", "A#7", null, null, null];
133
+ declare const keyboardWhiteNotes: readonly ["A0", "B0", "C1", "D1", "E1", "F1", "G1", "A1", "B1", "C2", "D2", "E2", "F2", "G2", "A2", "B2", "C3", "D3", "E3", "F3", "G3", "A3", "B3", "C4", "D4", "E4", "F4", "G4", "A4", "B4", "C5", "D5", "E5", "F5", "G5", "A5", "B5", "C6", "D6", "E6", "F6", "G6", "A6", "B6", "C7", "D7", "E7", "F7", "G7", "A7", "B7", "C8"];
134
+
135
+ type NoteName = keyof typeof noteToMidi;
136
+ type MidiRange = (typeof noteToMidi)[NoteName];
137
+ type Velocity = 'piano' | 'forte';
138
+ type Sample = {
139
+ note: NoteName;
140
+ url: string;
141
+ velocity: Velocity;
142
+ };
143
+ type KeyboardMode = 'dual' | 'single';
144
+ type KeyType = 'black' | 'white' | 'disabled';
145
+ type KeyMap = Map<string, {
146
+ midiNote: MidiRange | null;
147
+ type: KeyType;
148
+ }>;
149
+
150
+ declare class MiniKeys {
151
+ private audioContext;
152
+ private compressorNode;
153
+ private forteNotes;
154
+ private pianoNotes;
155
+ private sustain;
156
+ private playingNotes;
157
+ private progress;
158
+ constructor();
159
+ setSustain: (sustain: boolean) => void;
160
+ loadNotes: (samples: Sample[], handleSampleLoaded?: (progress: number) => void) => Promise<void[]>;
161
+ playNoteFromMidi: (midiNote: number, velocity?: number) => void;
162
+ playNoteFromName: (noteName: NoteName, velocity?: number) => void;
163
+ }
164
+
165
+ declare class MiniKeysKeyboard {
166
+ private mode;
167
+ private keyMap;
168
+ private offset;
169
+ constructor(mode: KeyboardMode);
170
+ getNoteMap: () => KeyMap;
171
+ getMidiRange: () => {
172
+ low: MidiRange;
173
+ high: MidiRange;
174
+ };
175
+ getNoteRange: () => {
176
+ low: "C-1" | "C#-1" | "D-1" | "D#-1" | "E-1" | "F-1" | "F#-1" | "G-1" | "G#-1" | "A-1" | "A#-1" | "B-1" | "C0" | "C#0" | "D0" | "D#0" | "E0" | "F0" | "F#0" | "G0" | "G#0" | "A0" | "A#0" | "B0" | "C1" | "C#1" | "D1" | "D#1" | "E1" | "F1" | "F#1" | "G1" | "G#1" | "A1" | "A#1" | "B1" | "C2" | "C#2" | "D2" | "D#2" | "E2" | "F2" | "F#2" | "G2" | "G#2" | "A2" | "A#2" | "B2" | "C3" | "C#3" | "D3" | "D#3" | "E3" | "F3" | "F#3" | "G3" | "G#3" | "A3" | "A#3" | "B3" | "C4" | "C#4" | "D4" | "D#4" | "E4" | "F4" | "F#4" | "G4" | "G#4" | "A4" | "A#4" | "B4" | "C5" | "C#5" | "D5" | "D#5" | "E5" | "F5" | "F#5" | "G5" | "G#5" | "A5" | "A#5" | "B5" | "C6" | "C#6" | "D6" | "D#6" | "E6" | "F6" | "F#6" | "G6" | "G#6" | "A6" | "A#6" | "B6" | "C7" | "C#7" | "D7" | "D#7" | "E7" | "F7" | "F#7" | "G7" | "G#7" | "A7" | "A#7" | "B7" | "C8" | "C#8" | "D8" | "D#8" | "E8" | "F8" | "F#8" | "G8" | "G#8" | "A8" | "A#8" | "B8" | "C9" | "C#9" | "D9" | "D#9" | "E9" | "F9" | "F#9" | "G9";
177
+ high: "C-1" | "C#-1" | "D-1" | "D#-1" | "E-1" | "F-1" | "F#-1" | "G-1" | "G#-1" | "A-1" | "A#-1" | "B-1" | "C0" | "C#0" | "D0" | "D#0" | "E0" | "F0" | "F#0" | "G0" | "G#0" | "A0" | "A#0" | "B0" | "C1" | "C#1" | "D1" | "D#1" | "E1" | "F1" | "F#1" | "G1" | "G#1" | "A1" | "A#1" | "B1" | "C2" | "C#2" | "D2" | "D#2" | "E2" | "F2" | "F#2" | "G2" | "G#2" | "A2" | "A#2" | "B2" | "C3" | "C#3" | "D3" | "D#3" | "E3" | "F3" | "F#3" | "G3" | "G#3" | "A3" | "A#3" | "B3" | "C4" | "C#4" | "D4" | "D#4" | "E4" | "F4" | "F#4" | "G4" | "G#4" | "A4" | "A#4" | "B4" | "C5" | "C#5" | "D5" | "D#5" | "E5" | "F5" | "F#5" | "G5" | "G#5" | "A5" | "A#5" | "B5" | "C6" | "C#6" | "D6" | "D#6" | "E6" | "F6" | "F#6" | "G6" | "G#6" | "A6" | "A#6" | "B6" | "C7" | "C#7" | "D7" | "D#7" | "E7" | "F7" | "F#7" | "G7" | "G#7" | "A7" | "A#7" | "B7" | "C8" | "C#8" | "D8" | "D#8" | "E8" | "F8" | "F#8" | "G8" | "G#8" | "A8" | "A#8" | "B8" | "C9" | "C#9" | "D9" | "D#9" | "E9" | "F9" | "F#9" | "G9";
178
+ };
179
+ shiftLeft: () => void;
180
+ shiftLeftOctave: () => void;
181
+ shiftRight: () => void;
182
+ shiftRightOctave: () => void;
183
+ private buildNoteMap;
184
+ }
185
+
186
+ export { type KeyMap, type KeyboardMode, MiniKeys, MiniKeysKeyboard, type NoteName, type Sample, type Velocity, keyboardBlackNotes, keyboardWhiteNotes, midiToNote, noteToMidi };
@@ -0,0 +1,186 @@
1
+ declare const noteToMidi: {
2
+ readonly "C-1": 0;
3
+ readonly "C#-1": 1;
4
+ readonly "D-1": 2;
5
+ readonly "D#-1": 3;
6
+ readonly "E-1": 4;
7
+ readonly "F-1": 5;
8
+ readonly "F#-1": 6;
9
+ readonly "G-1": 7;
10
+ readonly "G#-1": 8;
11
+ readonly "A-1": 9;
12
+ readonly "A#-1": 10;
13
+ readonly "B-1": 11;
14
+ readonly C0: 12;
15
+ readonly "C#0": 13;
16
+ readonly D0: 14;
17
+ readonly "D#0": 15;
18
+ readonly E0: 16;
19
+ readonly F0: 17;
20
+ readonly "F#0": 18;
21
+ readonly G0: 19;
22
+ readonly "G#0": 20;
23
+ readonly A0: 21;
24
+ readonly "A#0": 22;
25
+ readonly B0: 23;
26
+ readonly C1: 24;
27
+ readonly "C#1": 25;
28
+ readonly D1: 26;
29
+ readonly "D#1": 27;
30
+ readonly E1: 28;
31
+ readonly F1: 29;
32
+ readonly "F#1": 30;
33
+ readonly G1: 31;
34
+ readonly "G#1": 32;
35
+ readonly A1: 33;
36
+ readonly "A#1": 34;
37
+ readonly B1: 35;
38
+ readonly C2: 36;
39
+ readonly "C#2": 37;
40
+ readonly D2: 38;
41
+ readonly "D#2": 39;
42
+ readonly E2: 40;
43
+ readonly F2: 41;
44
+ readonly "F#2": 42;
45
+ readonly G2: 43;
46
+ readonly "G#2": 44;
47
+ readonly A2: 45;
48
+ readonly "A#2": 46;
49
+ readonly B2: 47;
50
+ readonly C3: 48;
51
+ readonly "C#3": 49;
52
+ readonly D3: 50;
53
+ readonly "D#3": 51;
54
+ readonly E3: 52;
55
+ readonly F3: 53;
56
+ readonly "F#3": 54;
57
+ readonly G3: 55;
58
+ readonly "G#3": 56;
59
+ readonly A3: 57;
60
+ readonly "A#3": 58;
61
+ readonly B3: 59;
62
+ readonly C4: 60;
63
+ readonly "C#4": 61;
64
+ readonly D4: 62;
65
+ readonly "D#4": 63;
66
+ readonly E4: 64;
67
+ readonly F4: 65;
68
+ readonly "F#4": 66;
69
+ readonly G4: 67;
70
+ readonly "G#4": 68;
71
+ readonly A4: 69;
72
+ readonly "A#4": 70;
73
+ readonly B4: 71;
74
+ readonly C5: 72;
75
+ readonly "C#5": 73;
76
+ readonly D5: 74;
77
+ readonly "D#5": 75;
78
+ readonly E5: 76;
79
+ readonly F5: 77;
80
+ readonly "F#5": 78;
81
+ readonly G5: 79;
82
+ readonly "G#5": 80;
83
+ readonly A5: 81;
84
+ readonly "A#5": 82;
85
+ readonly B5: 83;
86
+ readonly C6: 84;
87
+ readonly "C#6": 85;
88
+ readonly D6: 86;
89
+ readonly "D#6": 87;
90
+ readonly E6: 88;
91
+ readonly F6: 89;
92
+ readonly "F#6": 90;
93
+ readonly G6: 91;
94
+ readonly "G#6": 92;
95
+ readonly A6: 93;
96
+ readonly "A#6": 94;
97
+ readonly B6: 95;
98
+ readonly C7: 96;
99
+ readonly "C#7": 97;
100
+ readonly D7: 98;
101
+ readonly "D#7": 99;
102
+ readonly E7: 100;
103
+ readonly F7: 101;
104
+ readonly "F#7": 102;
105
+ readonly G7: 103;
106
+ readonly "G#7": 104;
107
+ readonly A7: 105;
108
+ readonly "A#7": 106;
109
+ readonly B7: 107;
110
+ readonly C8: 108;
111
+ readonly "C#8": 109;
112
+ readonly D8: 110;
113
+ readonly "D#8": 111;
114
+ readonly E8: 112;
115
+ readonly F8: 113;
116
+ readonly "F#8": 114;
117
+ readonly G8: 115;
118
+ readonly "G#8": 116;
119
+ readonly A8: 117;
120
+ readonly "A#8": 118;
121
+ readonly B8: 119;
122
+ readonly C9: 120;
123
+ readonly "C#9": 121;
124
+ readonly D9: 122;
125
+ readonly "D#9": 123;
126
+ readonly E9: 124;
127
+ readonly F9: 125;
128
+ readonly "F#9": 126;
129
+ readonly G9: 127;
130
+ };
131
+ declare const midiToNote: { [K in keyof typeof noteToMidi as (typeof noteToMidi)[K]]: K; };
132
+ declare const keyboardBlackNotes: readonly [null, "A#0", null, "C#1", "D#1", null, "F#1", "G#1", "A#1", null, "C#2", "D#2", null, "F#2", "G#2", "A#2", null, "C#3", "D#3", null, "F#3", "G#3", "A#3", null, "C#4", "D#4", null, "F#4", "G#4", "A#4", null, "C#5", "D#5", null, "F#5", "G#5", "A#5", null, "C#6", "D#6", null, "F#6", "G#6", "A#6", null, "C#7", "D#7", null, "F#7", "G#7", "A#7", null, null, null];
133
+ declare const keyboardWhiteNotes: readonly ["A0", "B0", "C1", "D1", "E1", "F1", "G1", "A1", "B1", "C2", "D2", "E2", "F2", "G2", "A2", "B2", "C3", "D3", "E3", "F3", "G3", "A3", "B3", "C4", "D4", "E4", "F4", "G4", "A4", "B4", "C5", "D5", "E5", "F5", "G5", "A5", "B5", "C6", "D6", "E6", "F6", "G6", "A6", "B6", "C7", "D7", "E7", "F7", "G7", "A7", "B7", "C8"];
134
+
135
+ type NoteName = keyof typeof noteToMidi;
136
+ type MidiRange = (typeof noteToMidi)[NoteName];
137
+ type Velocity = 'piano' | 'forte';
138
+ type Sample = {
139
+ note: NoteName;
140
+ url: string;
141
+ velocity: Velocity;
142
+ };
143
+ type KeyboardMode = 'dual' | 'single';
144
+ type KeyType = 'black' | 'white' | 'disabled';
145
+ type KeyMap = Map<string, {
146
+ midiNote: MidiRange | null;
147
+ type: KeyType;
148
+ }>;
149
+
150
+ declare class MiniKeys {
151
+ private audioContext;
152
+ private compressorNode;
153
+ private forteNotes;
154
+ private pianoNotes;
155
+ private sustain;
156
+ private playingNotes;
157
+ private progress;
158
+ constructor();
159
+ setSustain: (sustain: boolean) => void;
160
+ loadNotes: (samples: Sample[], handleSampleLoaded?: (progress: number) => void) => Promise<void[]>;
161
+ playNoteFromMidi: (midiNote: number, velocity?: number) => void;
162
+ playNoteFromName: (noteName: NoteName, velocity?: number) => void;
163
+ }
164
+
165
+ declare class MiniKeysKeyboard {
166
+ private mode;
167
+ private keyMap;
168
+ private offset;
169
+ constructor(mode: KeyboardMode);
170
+ getNoteMap: () => KeyMap;
171
+ getMidiRange: () => {
172
+ low: MidiRange;
173
+ high: MidiRange;
174
+ };
175
+ getNoteRange: () => {
176
+ low: "C-1" | "C#-1" | "D-1" | "D#-1" | "E-1" | "F-1" | "F#-1" | "G-1" | "G#-1" | "A-1" | "A#-1" | "B-1" | "C0" | "C#0" | "D0" | "D#0" | "E0" | "F0" | "F#0" | "G0" | "G#0" | "A0" | "A#0" | "B0" | "C1" | "C#1" | "D1" | "D#1" | "E1" | "F1" | "F#1" | "G1" | "G#1" | "A1" | "A#1" | "B1" | "C2" | "C#2" | "D2" | "D#2" | "E2" | "F2" | "F#2" | "G2" | "G#2" | "A2" | "A#2" | "B2" | "C3" | "C#3" | "D3" | "D#3" | "E3" | "F3" | "F#3" | "G3" | "G#3" | "A3" | "A#3" | "B3" | "C4" | "C#4" | "D4" | "D#4" | "E4" | "F4" | "F#4" | "G4" | "G#4" | "A4" | "A#4" | "B4" | "C5" | "C#5" | "D5" | "D#5" | "E5" | "F5" | "F#5" | "G5" | "G#5" | "A5" | "A#5" | "B5" | "C6" | "C#6" | "D6" | "D#6" | "E6" | "F6" | "F#6" | "G6" | "G#6" | "A6" | "A#6" | "B6" | "C7" | "C#7" | "D7" | "D#7" | "E7" | "F7" | "F#7" | "G7" | "G#7" | "A7" | "A#7" | "B7" | "C8" | "C#8" | "D8" | "D#8" | "E8" | "F8" | "F#8" | "G8" | "G#8" | "A8" | "A#8" | "B8" | "C9" | "C#9" | "D9" | "D#9" | "E9" | "F9" | "F#9" | "G9";
177
+ high: "C-1" | "C#-1" | "D-1" | "D#-1" | "E-1" | "F-1" | "F#-1" | "G-1" | "G#-1" | "A-1" | "A#-1" | "B-1" | "C0" | "C#0" | "D0" | "D#0" | "E0" | "F0" | "F#0" | "G0" | "G#0" | "A0" | "A#0" | "B0" | "C1" | "C#1" | "D1" | "D#1" | "E1" | "F1" | "F#1" | "G1" | "G#1" | "A1" | "A#1" | "B1" | "C2" | "C#2" | "D2" | "D#2" | "E2" | "F2" | "F#2" | "G2" | "G#2" | "A2" | "A#2" | "B2" | "C3" | "C#3" | "D3" | "D#3" | "E3" | "F3" | "F#3" | "G3" | "G#3" | "A3" | "A#3" | "B3" | "C4" | "C#4" | "D4" | "D#4" | "E4" | "F4" | "F#4" | "G4" | "G#4" | "A4" | "A#4" | "B4" | "C5" | "C#5" | "D5" | "D#5" | "E5" | "F5" | "F#5" | "G5" | "G#5" | "A5" | "A#5" | "B5" | "C6" | "C#6" | "D6" | "D#6" | "E6" | "F6" | "F#6" | "G6" | "G#6" | "A6" | "A#6" | "B6" | "C7" | "C#7" | "D7" | "D#7" | "E7" | "F7" | "F#7" | "G7" | "G#7" | "A7" | "A#7" | "B7" | "C8" | "C#8" | "D8" | "D#8" | "E8" | "F8" | "F#8" | "G8" | "G#8" | "A8" | "A#8" | "B8" | "C9" | "C#9" | "D9" | "D#9" | "E9" | "F9" | "F#9" | "G9";
178
+ };
179
+ shiftLeft: () => void;
180
+ shiftLeftOctave: () => void;
181
+ shiftRight: () => void;
182
+ shiftRightOctave: () => void;
183
+ private buildNoteMap;
184
+ }
185
+
186
+ export { type KeyMap, type KeyboardMode, MiniKeys, MiniKeysKeyboard, type NoteName, type Sample, type Velocity, keyboardBlackNotes, keyboardWhiteNotes, midiToNote, noteToMidi };
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ var n={"C-1":0,"C#-1":1,"D-1":2,"D#-1":3,"E-1":4,"F-1":5,"F#-1":6,"G-1":7,"G#-1":8,"A-1":9,"A#-1":10,"B-1":11,C0:12,"C#0":13,D0:14,"D#0":15,E0:16,F0:17,"F#0":18,G0:19,"G#0":20,A0:21,"A#0":22,B0:23,C1:24,"C#1":25,D1:26,"D#1":27,E1:28,F1:29,"F#1":30,G1:31,"G#1":32,A1:33,"A#1":34,B1:35,C2:36,"C#2":37,D2:38,"D#2":39,E2:40,F2:41,"F#2":42,G2:43,"G#2":44,A2:45,"A#2":46,B2:47,C3:48,"C#3":49,D3:50,"D#3":51,E3:52,F3:53,"F#3":54,G3:55,"G#3":56,A3:57,"A#3":58,B3:59,C4:60,"C#4":61,D4:62,"D#4":63,E4:64,F4:65,"F#4":66,G4:67,"G#4":68,A4:69,"A#4":70,B4:71,C5:72,"C#5":73,D5:74,"D#5":75,E5:76,F5:77,"F#5":78,G5:79,"G#5":80,A5:81,"A#5":82,B5:83,C6:84,"C#6":85,D6:86,"D#6":87,E6:88,F6:89,"F#6":90,G6:91,"G#6":92,A6:93,"A#6":94,B6:95,C7:96,"C#7":97,D7:98,"D#7":99,E7:100,F7:101,"F#7":102,G7:103,"G#7":104,A7:105,"A#7":106,B7:107,C8:108,"C#8":109,D8:110,"D#8":111,E8:112,F8:113,"F#8":114,G8:115,"G#8":116,A8:117,"A#8":118,B8:119,C9:120,"C#9":121,D9:122,"D#9":123,E9:124,F9:125,"F#9":126,G9:127},d=Object.fromEntries(Object.entries(n).map(([r,t])=>[t,r])),p=[null,"A#0",null,"C#1","D#1",null,"F#1","G#1","A#1",null,"C#2","D#2",null,"F#2","G#2","A#2",null,"C#3","D#3",null,"F#3","G#3","A#3",null,"C#4","D#4",null,"F#4","G#4","A#4",null,"C#5","D#5",null,"F#5","G#5","A#5",null,"C#6","D#6",null,"F#6","G#6","A#6",null,"C#7","D#7",null,"F#7","G#7","A#7",null,null,null],c=["A0","B0","C1","D1","E1","F1","G1","A1","B1","C2","D2","E2","F2","G2","A2","B2","C3","D3","E3","F3","G3","A3","B3","C4","D4","E4","F4","G4","A4","B4","C5","D5","E5","F5","G5","A5","B5","C6","D6","E6","F6","G6","A6","B6","C7","D7","E7","F7","G7","A7","B7","C8"],f=[["Digit1","Digit2","Digit3","Digit4","Digit5","Digit6","Digit7","Digit8","Digit9","Digit0","Minus","Equal"],["KeyQ","KeyW","KeyE","KeyR","KeyT","KeyY","KeyU","KeyI","KeyO","KeyP","BracketLeft","BracketRight"],["KeyA","KeyS","KeyD","KeyF","KeyG","KeyH","KeyJ","KeyK","KeyL","Semicolon","Quote"],["KeyZ","KeyX","KeyC","KeyV","KeyB","KeyN","KeyM","Comma","Period","Slash"]],N=(r,t,o,i)=>{let e;if(i&&i<64&&t.size>0)e=t;else if(r.size>0)e=r;else throw new Error("No notes loaded");let a=Array.from(e.keys()).reduce((l,s)=>Math.abs(s-o)<Math.abs(l-o)?s:l);return{closestNoteMidi:a,closestNote:e.get(a)}};var M=class{audioContext;compressorNode;forteNotes=new Map;pianoNotes=new Map;sustain=!1;playingNotes=new Map;progress=0;constructor(){this.audioContext=new AudioContext,this.compressorNode=this.audioContext.createDynamicsCompressor(),this.compressorNode.threshold.value=-24,this.compressorNode.knee.value=0,this.compressorNode.ratio.value=2,this.compressorNode.attack.value=.001,this.compressorNode.release.value=.5,this.compressorNode.connect(this.audioContext.destination)}setSustain=t=>{this.sustain=t,t?this.playingNotes.forEach(o=>{let i=this.audioContext.currentTime;o.gain.cancelScheduledValues(i),o.gain.setValueAtTime(o.gain.value,i)}):this.playingNotes.forEach(o=>{let i=this.audioContext.currentTime;o.gain.cancelScheduledValues(i),o.gain.setValueAtTime(o.gain.value,i),o.gain.linearRampToValueAtTime(.001,i+.5)})};loadNotes=async(t,o)=>{this.forteNotes.clear(),this.pianoNotes.clear();let i=t.map(async e=>{let l=await(await fetch(e.url)).arrayBuffer(),s=await this.audioContext.decodeAudioData(l);if(e.velocity==="piano"?this.pianoNotes.set(n[e.note],s):this.forteNotes.set(n[e.note],s),o){let u=(this.pianoNotes.size+this.forteNotes.size)/t.length;u>this.progress&&(this.progress=u,o(this.progress))}});return await Promise.all(i)};playNoteFromMidi=(t,o)=>{if(t<0||t>127)throw new Error("Invalid midi note");if(o&&(o<0||o>127))throw new Error("Invalid velocity value");if(!this.audioContext)throw new Error("Audio context not initialized");let e=this.audioContext.createBufferSource(),{closestNoteMidi:a,closestNote:l}=N(this.forteNotes,this.pianoNotes,t,o);if(l){e.buffer=l;let s=this.audioContext.createGain(),u=Math.min(127,o??127);if(s.gain.value=(u/127*.9+.1)*.5,e.connect(s),s.connect(this.compressorNode),e.playbackRate.value=2**((t-a)/12),e.start(),this.playingNotes.set(e,s),!this.sustain){let h=this.audioContext.currentTime;s.gain.cancelScheduledValues(h),s.gain.setValueAtTime(s.gain.value,h),s.gain.exponentialRampToValueAtTime(Number.EPSILON,h+8)}e.onended=()=>{e.disconnect(),this.playingNotes.delete(e)}}else throw new Error("Note not found")};playNoteFromName=(t,o)=>{this.playNoteFromMidi(n[t],o)}};var A=class{mode;keyMap=new Map;offset;constructor(t){this.mode=t,t==="single"?this.offset=23:this.offset=16,this.buildNoteMap()}getNoteMap=()=>this.keyMap;getMidiRange=()=>{let t=Array.from(this.keyMap.values()).reduce((i,e)=>e.midiNote&&e.midiNote<i?e.midiNote:i,127),o=Array.from(this.keyMap.values()).reduce((i,e)=>e.midiNote&&e.midiNote>i?e.midiNote:i,0);return{low:t,high:o}};getNoteRange=()=>{let t=this.getMidiRange();return{low:d[t.low],high:d[t.high]}};shiftLeft=()=>{this.offset=Math.max(this.offset-1,0),this.keyMap=new Map,this.buildNoteMap()};shiftLeftOctave=()=>{this.offset=Math.max(this.offset-7,0),this.keyMap=new Map,this.buildNoteMap()};shiftRight=()=>{this.offset=Math.min(this.offset+1,this.mode==="dual"?30:41),this.keyMap=new Map,this.buildNoteMap()};shiftRightOctave=()=>{this.offset=Math.min(this.offset+7,this.mode==="dual"?30:41),this.keyMap=new Map,this.buildNoteMap()};buildNoteMap=()=>{this.mode==="single"?(m(this.keyMap,1,this.offset),y(this.keyMap,2,this.offset)):(m(this.keyMap,0,this.offset),y(this.keyMap,1,this.offset),m(this.keyMap,2,this.offset+12),y(this.keyMap,3,this.offset+12))}},m=(r,t,o)=>{f[t].forEach((i,e)=>{let a=p[o+e];if(a===null)r.set(i,{midiNote:null,type:"disabled"});else if(a!==void 0)r.set(i,{midiNote:n[a],type:"black"});else throw new Error("Invalid note")})},y=(r,t,o)=>{f[t].forEach((i,e)=>{let a=c[o+e];if(a)r.set(i,{midiNote:n[a],type:"white"});else throw new Error("Invalid note")})};export{M as MiniKeys,A as MiniKeysKeyboard,p as keyboardBlackNotes,c as keyboardWhiteNotes,d as midiToNote,n as noteToMidi};
package/package.json CHANGED
@@ -1,26 +1,47 @@
1
1
  {
2
2
  "name": "minikeys",
3
- "version": "0.1.2",
3
+ "version": "0.2.1",
4
4
  "description": "A javascript library that plays the piano",
5
- "main": "dist/minikeys.min.js",
6
- "scripts": {
7
- "test": "mocha -u tdd --require @babel/register",
8
- "build": "webpack --mode production"
5
+ "keywords": [
6
+ "minikeys",
7
+ "piano"
8
+ ],
9
+ "main": "dist/index.js",
10
+ "types": "dist/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "import": "./dist/index.js",
15
+ "require": "./dist/index.cjs"
16
+ }
17
+ },
18
+ "homepage": "https://liamp.uk/minikeys",
19
+ "bugs": {
20
+ "url": "https://github.com/liampuk/minikeys/issues"
9
21
  },
10
- "keywords": [],
11
- "author": "liampuk",
12
- "license": "ISC",
22
+ "author": "Liam Piesley <liampuk@gmail.com> (https://liamp.uk)",
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "git+https://github.com/liampuk/minikeys.git"
26
+ },
27
+ "files": [
28
+ "dist",
29
+ "README.md"
30
+ ],
31
+ "type": "module",
32
+ "license": "MIT",
13
33
  "devDependencies": {
14
- "@babel/core": "^7.10.2",
15
- "@babel/preset-env": "^7.10.2",
16
- "@babel/register": "^7.10.1",
17
- "babel-loader": "^8.1.0",
18
- "chai": "^4.2.0",
19
- "mocha": "^8.0.1",
20
- "webpack": "^4.43.0",
21
- "webpack-cli": "^3.3.11"
34
+ "prettier": "^3.5.1",
35
+ "tsup": "^8.3.6",
36
+ "typescript": "^5.7.2",
37
+ "vitest": "^3.0.5"
22
38
  },
23
- "files": [
24
- "minikeys.min.js"
25
- ]
39
+ "scripts": {
40
+ "build": "tsup",
41
+ "ci": "npm run build && npm run check-format && npm run lint && npm run test",
42
+ "lint": "tsc",
43
+ "test": "vitest run",
44
+ "format": "prettier --write .",
45
+ "check-format": "prettier --check ."
46
+ }
26
47
  }