solid-pianoroll 0.0.2
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.md +36 -0
- package/dist/cjs/index.js +403 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/esm/index.js +401 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/source/PianoRoll.jsx +282 -0
- package/dist/source/helpers.js +3 -0
- package/dist/source/index.jsx +2 -0
- package/dist/source/useViewPortScaler.js +31 -0
- package/dist/types/PianoRoll.d.ts +19 -0
- package/dist/types/helpers.d.ts +1 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/useViewPortScaler.d.ts +15 -0
- package/package.json +65 -0
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
import { createMemo, splitProps, createSignal, onMount, onCleanup, Index, createEffect, Show, } from "solid-js";
|
|
2
|
+
import { clamp } from "./helpers";
|
|
3
|
+
import useViewPortScaler from "./useViewPortScaler";
|
|
4
|
+
const PianoRoll = (allProps) => {
|
|
5
|
+
let scrollContainerRef;
|
|
6
|
+
let scrollContentRef;
|
|
7
|
+
let notesContainerRef;
|
|
8
|
+
const [props, divProps] = splitProps(allProps, [
|
|
9
|
+
"ppq",
|
|
10
|
+
"notes",
|
|
11
|
+
"position",
|
|
12
|
+
"duration",
|
|
13
|
+
"zoom",
|
|
14
|
+
"verticalPosition",
|
|
15
|
+
"verticalZoom",
|
|
16
|
+
"onVerticalZoomChange",
|
|
17
|
+
"onVerticalPositionChange",
|
|
18
|
+
"onZoomChange",
|
|
19
|
+
"onPositionChange",
|
|
20
|
+
"onNoteChange",
|
|
21
|
+
]);
|
|
22
|
+
const [dimensions, setDimensions] = createSignal({
|
|
23
|
+
left: 0,
|
|
24
|
+
width: 0,
|
|
25
|
+
top: 0,
|
|
26
|
+
height: 0,
|
|
27
|
+
});
|
|
28
|
+
const resizeObserver = new ResizeObserver((event) => {
|
|
29
|
+
const { left, top, width, height } = scrollContainerRef?.getBoundingClientRect() ?? {
|
|
30
|
+
left: 0,
|
|
31
|
+
width: 0,
|
|
32
|
+
top: 0,
|
|
33
|
+
height: 0,
|
|
34
|
+
};
|
|
35
|
+
setDimensions({ left, top, width, height });
|
|
36
|
+
});
|
|
37
|
+
createEffect(() => {
|
|
38
|
+
console.log(props.notes);
|
|
39
|
+
});
|
|
40
|
+
onMount(() => {
|
|
41
|
+
if (!scrollContainerRef)
|
|
42
|
+
return;
|
|
43
|
+
resizeObserver.observe(scrollContainerRef);
|
|
44
|
+
});
|
|
45
|
+
onCleanup(() => {
|
|
46
|
+
resizeObserver.disconnect();
|
|
47
|
+
});
|
|
48
|
+
const horizontalViewPort = useViewPortScaler(() => ({
|
|
49
|
+
viewPortOffset: dimensions().left,
|
|
50
|
+
viewPortSize: dimensions().width,
|
|
51
|
+
virtualPosition: props.position,
|
|
52
|
+
virtualRange: props.duration,
|
|
53
|
+
zoom: props.zoom,
|
|
54
|
+
}));
|
|
55
|
+
const verticalViewPort = useViewPortScaler(() => ({
|
|
56
|
+
viewPortOffset: dimensions().top,
|
|
57
|
+
viewPortSize: dimensions().height,
|
|
58
|
+
virtualPosition: props.verticalPosition,
|
|
59
|
+
virtualRange: 128,
|
|
60
|
+
zoom: props.verticalZoom,
|
|
61
|
+
}));
|
|
62
|
+
const handleScroll = (event) => {
|
|
63
|
+
event.preventDefault();
|
|
64
|
+
if (didUpdateScroll) {
|
|
65
|
+
didUpdateScroll = false;
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const maxVerticalPosition = 128 - 128 / props.verticalZoom;
|
|
69
|
+
const maxPosition = props.duration - props.duration / props.zoom;
|
|
70
|
+
const { width, height } = dimensions();
|
|
71
|
+
const { scrollTop, scrollLeft, scrollWidth, scrollHeight } = event.currentTarget;
|
|
72
|
+
const scrollTopAmount = scrollTop / (scrollHeight - height);
|
|
73
|
+
const scrollLeftAmount = scrollLeft / (scrollWidth - width);
|
|
74
|
+
props.onVerticalPositionChange?.(maxVerticalPosition * scrollTopAmount);
|
|
75
|
+
props.onPositionChange?.(maxPosition * scrollLeftAmount);
|
|
76
|
+
};
|
|
77
|
+
let didUpdateScroll = false;
|
|
78
|
+
createEffect(() => {
|
|
79
|
+
const maxVerticalPosition = 128 - 128 / props.verticalZoom;
|
|
80
|
+
const maxPosition = props.duration - props.duration / props.zoom;
|
|
81
|
+
const scrollTopAmount = maxVerticalPosition > 0 ? props.verticalPosition / maxVerticalPosition : 0;
|
|
82
|
+
const scrollLeftAmount = maxPosition > 0 ? props.position / maxPosition : 0;
|
|
83
|
+
const { height, width } = dimensions();
|
|
84
|
+
if (!scrollContentRef?.parentElement)
|
|
85
|
+
return;
|
|
86
|
+
const scrollDivHeight = clamp(props.verticalZoom * height, height, 10000);
|
|
87
|
+
const scrollTop = scrollTopAmount * (scrollDivHeight - height);
|
|
88
|
+
const scrollDivWidth = clamp(props.zoom * width, width, 10000);
|
|
89
|
+
const scrollLeft = scrollLeftAmount * (scrollDivWidth - width);
|
|
90
|
+
didUpdateScroll = true;
|
|
91
|
+
scrollContentRef.style.height = `${scrollDivHeight}px`;
|
|
92
|
+
scrollContentRef.style.width = `${scrollDivWidth}px`;
|
|
93
|
+
scrollContentRef.parentElement.scrollTo({
|
|
94
|
+
left: scrollLeft,
|
|
95
|
+
top: scrollTop,
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
const forwardEventToNote = (event) => {
|
|
99
|
+
const x = "clientX" in event ? event.clientX : event.touches[0]?.clientX;
|
|
100
|
+
const y = "clientY" in event ? event.clientY : event.touches[0]?.clientY;
|
|
101
|
+
const elementUnderMouse = [...(notesContainerRef?.querySelectorAll?.("div") ?? [])].find((element) => {
|
|
102
|
+
const rect = element.getBoundingClientRect();
|
|
103
|
+
return (x >= rect.left &&
|
|
104
|
+
rect.left + rect.width > x &&
|
|
105
|
+
y >= rect.top &&
|
|
106
|
+
rect.top + rect.height > y);
|
|
107
|
+
});
|
|
108
|
+
if (elementUnderMouse) {
|
|
109
|
+
return elementUnderMouse.dispatchEvent(new MouseEvent(event.type, event));
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
const [noteDragMode, setNoteDragMode] = createSignal();
|
|
113
|
+
const [isDragging, setIsDragging] = createSignal(false);
|
|
114
|
+
return (<div {...divProps} class="PianoRoll" style={{
|
|
115
|
+
"box-sizing": "border-box",
|
|
116
|
+
display: "flex",
|
|
117
|
+
overflow: "hidden",
|
|
118
|
+
"flex-direction": "column",
|
|
119
|
+
padding: "16px",
|
|
120
|
+
...(typeof divProps.style === "object" && divProps.style),
|
|
121
|
+
}}>
|
|
122
|
+
<div style={{
|
|
123
|
+
overflow: "hidden",
|
|
124
|
+
height: "100%",
|
|
125
|
+
display: "flex",
|
|
126
|
+
"flex-direction": "row",
|
|
127
|
+
}}>
|
|
128
|
+
<div class="PianoRoll-Keys" style={{
|
|
129
|
+
position: "relative",
|
|
130
|
+
height: "100%",
|
|
131
|
+
width: `${50}px`,
|
|
132
|
+
}}>
|
|
133
|
+
<Index each={keys}>
|
|
134
|
+
{(key) => {
|
|
135
|
+
const virtualDimensions = createMemo(() => verticalViewPort.getVirtualDimensions(127 - key().number, 1));
|
|
136
|
+
return (<Show when={virtualDimensions().size > 0}>
|
|
137
|
+
<div class="PianoRoll-Key" title={key().name} data-index={key().number % 12} style={{
|
|
138
|
+
position: "absolute",
|
|
139
|
+
"box-sizing": "border-box",
|
|
140
|
+
left: 0,
|
|
141
|
+
top: `${virtualDimensions().offset}px`,
|
|
142
|
+
height: `${virtualDimensions().size}px`,
|
|
143
|
+
width: "100%",
|
|
144
|
+
"background-color": key().isBlack ? "#000" : "#fff",
|
|
145
|
+
"border-width": `${!key().isBlack && !blackKeys.includes((key().number + 1) % 12) ? "0.1px" : 0} 1px ${!key().isBlack && !blackKeys.includes((key().number - 1) % 12) ? "0.1px" : 0} 0`,
|
|
146
|
+
"border-color": "#000",
|
|
147
|
+
"border-style": "solid",
|
|
148
|
+
}}></div>
|
|
149
|
+
</Show>);
|
|
150
|
+
}}
|
|
151
|
+
</Index>
|
|
152
|
+
</div>
|
|
153
|
+
|
|
154
|
+
<div style={{
|
|
155
|
+
position: "relative",
|
|
156
|
+
display: "flex",
|
|
157
|
+
overflow: "hidden",
|
|
158
|
+
flex: 1,
|
|
159
|
+
}}>
|
|
160
|
+
<div class="PianoRoll-Notes-Container" style={{
|
|
161
|
+
position: "absolute",
|
|
162
|
+
width: `${dimensions().width}px`,
|
|
163
|
+
height: `${dimensions().height}px`,
|
|
164
|
+
}}>
|
|
165
|
+
<div class="PianoRoll-Notes" ref={notesContainerRef} style={{
|
|
166
|
+
position: "relative",
|
|
167
|
+
width: `${dimensions().width}px`,
|
|
168
|
+
height: "100%",
|
|
169
|
+
}}>
|
|
170
|
+
<Index each={props.notes}>
|
|
171
|
+
{(note, index) => {
|
|
172
|
+
const horizontalVirtualDimensions = createMemo(() => verticalViewPort.getVirtualDimensions(127 - note().midi, 1));
|
|
173
|
+
const verticalVirtualDimensions = createMemo(() => horizontalViewPort.getVirtualDimensions(note().ticks, note().durationTicks));
|
|
174
|
+
return (<Show when={!!horizontalVirtualDimensions().size && !!verticalVirtualDimensions().size}>
|
|
175
|
+
<div class="PianoRoll-Note" onMouseMove={(event) => {
|
|
176
|
+
if (isDragging())
|
|
177
|
+
return;
|
|
178
|
+
const relativeX = horizontalViewPort.getScaledValue(horizontalViewPort.getPosition(event.clientX));
|
|
179
|
+
const noteStartX = horizontalViewPort.getScaledValue(note().ticks);
|
|
180
|
+
const noteEndX = horizontalViewPort.getScaledValue(note().ticks + note().durationTicks);
|
|
181
|
+
setNoteDragMode(relativeX - noteStartX < 3
|
|
182
|
+
? "trimStart"
|
|
183
|
+
: noteEndX - relativeX < 3
|
|
184
|
+
? "trimEnd"
|
|
185
|
+
: "move");
|
|
186
|
+
}} onMouseDown={(event) => {
|
|
187
|
+
setIsDragging(true);
|
|
188
|
+
const initialPosition = horizontalViewPort.getPosition(event.clientX);
|
|
189
|
+
const diffPosition = initialPosition - note().ticks;
|
|
190
|
+
const handleMouseMove = (mouseMoveEvent) => {
|
|
191
|
+
const ticks = Math.max(horizontalViewPort.getPosition(mouseMoveEvent.clientX) - diffPosition, 0);
|
|
192
|
+
props.onNoteChange?.(index, {
|
|
193
|
+
...note(),
|
|
194
|
+
midi: Math.round(127 - verticalViewPort.getPosition(mouseMoveEvent.clientY)),
|
|
195
|
+
...((noteDragMode() === "move" || noteDragMode() === "trimStart") && {
|
|
196
|
+
ticks,
|
|
197
|
+
}),
|
|
198
|
+
...(noteDragMode() === "trimStart" && {
|
|
199
|
+
durationTicks: note().durationTicks + note().ticks - ticks,
|
|
200
|
+
}),
|
|
201
|
+
...(noteDragMode() === "trimEnd" && {
|
|
202
|
+
durationTicks: horizontalViewPort.getPosition(mouseMoveEvent.clientX) -
|
|
203
|
+
note().ticks,
|
|
204
|
+
}),
|
|
205
|
+
});
|
|
206
|
+
};
|
|
207
|
+
const handleMouseUp = () => {
|
|
208
|
+
setIsDragging(false);
|
|
209
|
+
window.removeEventListener("mousemove", handleMouseMove);
|
|
210
|
+
window.removeEventListener("mouseup", handleMouseUp);
|
|
211
|
+
};
|
|
212
|
+
window.addEventListener("mousemove", handleMouseMove);
|
|
213
|
+
window.addEventListener("mouseup", handleMouseUp);
|
|
214
|
+
}} style={{
|
|
215
|
+
"z-index": 1,
|
|
216
|
+
position: "absolute",
|
|
217
|
+
"box-sizing": "border-box",
|
|
218
|
+
top: `${horizontalVirtualDimensions().offset}px`,
|
|
219
|
+
height: `${horizontalVirtualDimensions().size}px`,
|
|
220
|
+
left: `${verticalVirtualDimensions().offset}px`,
|
|
221
|
+
width: `${verticalVirtualDimensions().size}px`,
|
|
222
|
+
"border-width": "0.5px",
|
|
223
|
+
"border-style": "solid",
|
|
224
|
+
"background-color": "#f00",
|
|
225
|
+
"border-color": "#a00",
|
|
226
|
+
}}></div>
|
|
227
|
+
</Show>);
|
|
228
|
+
}}
|
|
229
|
+
</Index>
|
|
230
|
+
</div>
|
|
231
|
+
</div>
|
|
232
|
+
<div ref={scrollContainerRef} class="PianoRoll-Vertical-Scroller" style={{
|
|
233
|
+
flex: 1,
|
|
234
|
+
...(noteDragMode() && {
|
|
235
|
+
cursor: noteDragMode() === "trimStart"
|
|
236
|
+
? "w-resize"
|
|
237
|
+
: noteDragMode() === "trimEnd"
|
|
238
|
+
? "e-resize"
|
|
239
|
+
: "pointer",
|
|
240
|
+
}),
|
|
241
|
+
//"box-sizing": "border-box",
|
|
242
|
+
"z-index": 1,
|
|
243
|
+
overflow: "scroll",
|
|
244
|
+
"pointer-events": "auto",
|
|
245
|
+
}} onScroll={handleScroll} onMouseDown={forwardEventToNote} onMouseMove={(event) => {
|
|
246
|
+
if (forwardEventToNote(event))
|
|
247
|
+
return;
|
|
248
|
+
if (isDragging())
|
|
249
|
+
return;
|
|
250
|
+
setNoteDragMode(undefined);
|
|
251
|
+
}} onClick={forwardEventToNote} onDblClick={(event) => {
|
|
252
|
+
if (!forwardEventToNote(event)) {
|
|
253
|
+
//TODO create new note with current gutter length
|
|
254
|
+
}
|
|
255
|
+
}} onMouseUp={forwardEventToNote} onTouchStart={forwardEventToNote} onTouchMove={forwardEventToNote} onTouchEnd={forwardEventToNote} onTouchCancel={forwardEventToNote} onDragStart={forwardEventToNote} onDrag={forwardEventToNote} onDragEnd={forwardEventToNote}>
|
|
256
|
+
<div ref={scrollContentRef}></div>
|
|
257
|
+
</div>
|
|
258
|
+
</div>
|
|
259
|
+
|
|
260
|
+
<input value={props.verticalZoom} onInput={(event) => props.onVerticalZoomChange?.(event.currentTarget.valueAsNumber)} type="range" min="1" max="11" step={0.01} {...{ orient: "vertical" }} style={{
|
|
261
|
+
width: "16px",
|
|
262
|
+
...{
|
|
263
|
+
"writing-mode": "bt-lr" /* IE */,
|
|
264
|
+
"-webkit-appearance": "slider-vertical" /* WebKit */,
|
|
265
|
+
},
|
|
266
|
+
}}/>
|
|
267
|
+
</div>
|
|
268
|
+
|
|
269
|
+
<input value={props.zoom} onInput={(event) => props.onZoomChange?.(event.currentTarget.valueAsNumber)} type="range" max="500" min="1" step={0.01} style={{
|
|
270
|
+
"margin-left": "50px",
|
|
271
|
+
"margin-right": "16px",
|
|
272
|
+
}}/>
|
|
273
|
+
</div>);
|
|
274
|
+
};
|
|
275
|
+
const blackKeys = [1, 3, 6, 8, 10];
|
|
276
|
+
const keyNames = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];
|
|
277
|
+
const keys = Array.from({ length: 128 }).map((_, index) => ({
|
|
278
|
+
number: index,
|
|
279
|
+
name: `${keyNames[index % 12]} ${Math.floor(index / 12) - 2}`,
|
|
280
|
+
isBlack: blackKeys.includes(index % 12),
|
|
281
|
+
}));
|
|
282
|
+
export default PianoRoll;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { createMemo } from "solid-js";
|
|
2
|
+
import { clamp } from "./helpers";
|
|
3
|
+
export default function useViewPortScaler(getState) {
|
|
4
|
+
const state = createMemo(() => getState());
|
|
5
|
+
function getScaledValue(position) {
|
|
6
|
+
const virtualSize = state().viewPortSize * state().zoom;
|
|
7
|
+
return (position / state().virtualRange) * virtualSize;
|
|
8
|
+
}
|
|
9
|
+
function getCoordinates(position) {
|
|
10
|
+
return getScaledValue(position) - getScaledValue(state().virtualPosition);
|
|
11
|
+
}
|
|
12
|
+
function getPosition(offset) {
|
|
13
|
+
const visibleDuration = state().virtualRange / state().zoom;
|
|
14
|
+
const percentX = (offset - state().viewPortOffset) / state().viewPortSize;
|
|
15
|
+
const position = state().virtualPosition + percentX * visibleDuration;
|
|
16
|
+
return position;
|
|
17
|
+
}
|
|
18
|
+
function getVirtualDimensions(position, length) {
|
|
19
|
+
const virtualLeft = getCoordinates(position);
|
|
20
|
+
const virtualWidth = getScaledValue(length);
|
|
21
|
+
const offset = clamp(virtualLeft, 0, state().viewPortSize);
|
|
22
|
+
const size = clamp(virtualWidth - (offset - virtualLeft), 0, state().viewPortSize - offset);
|
|
23
|
+
return { offset, size };
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
getScaledValue,
|
|
27
|
+
getCoordinates,
|
|
28
|
+
getPosition,
|
|
29
|
+
getVirtualDimensions,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Midi } from "@tonejs/midi";
|
|
2
|
+
import { JSX } from "solid-js";
|
|
3
|
+
type Note = Pick<ReturnType<Midi["tracks"][number]["notes"][number]["toJSON"]>, "durationTicks" | "midi" | "ticks" | "velocity">;
|
|
4
|
+
type PianoRollProps = {
|
|
5
|
+
ppq: number;
|
|
6
|
+
notes: Note[];
|
|
7
|
+
verticalPosition: number;
|
|
8
|
+
verticalZoom: number;
|
|
9
|
+
position: number;
|
|
10
|
+
duration: number;
|
|
11
|
+
zoom: number;
|
|
12
|
+
onVerticalZoomChange?: (zoom: number) => void;
|
|
13
|
+
onVerticalPositionChange?: (zoom: number) => void;
|
|
14
|
+
onZoomChange?: (zoom: number) => void;
|
|
15
|
+
onPositionChange?: (zoom: number) => void;
|
|
16
|
+
onNoteChange?: (index: number, note: Note) => void;
|
|
17
|
+
} & JSX.IntrinsicElements["div"];
|
|
18
|
+
declare const PianoRoll: (allProps: PianoRollProps) => JSX.Element;
|
|
19
|
+
export default PianoRoll;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const clamp: (value: number, min: number, max: number) => number;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export default function useViewPortScaler(getState: () => {
|
|
2
|
+
virtualPosition: number;
|
|
3
|
+
virtualRange: number;
|
|
4
|
+
viewPortOffset: number;
|
|
5
|
+
viewPortSize: number;
|
|
6
|
+
zoom: number;
|
|
7
|
+
}): {
|
|
8
|
+
getScaledValue: (position: number) => number;
|
|
9
|
+
getCoordinates: (position: number) => number;
|
|
10
|
+
getPosition: (offset: number) => number;
|
|
11
|
+
getVirtualDimensions: (position: number, length: number) => {
|
|
12
|
+
offset: number;
|
|
13
|
+
size: number;
|
|
14
|
+
};
|
|
15
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "0.0.2",
|
|
3
|
+
"name": "solid-pianoroll",
|
|
4
|
+
"description": "Pianoroll UI Control for Solid JS apps",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "jdachtera",
|
|
7
|
+
"contributors": [],
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/jdachtera/solid-pianoroll.git"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://github.com/jdachtera/solid-pianoroll#readme",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/jdachtera/solid-pianoroll/issues"
|
|
15
|
+
},
|
|
16
|
+
"private": false,
|
|
17
|
+
"type": "module",
|
|
18
|
+
"files": [
|
|
19
|
+
"dist"
|
|
20
|
+
],
|
|
21
|
+
"main": "dist/cjs/index.js",
|
|
22
|
+
"module": "dist/esm/index.js",
|
|
23
|
+
"types": "dist/types/index.d.ts",
|
|
24
|
+
"exports": {
|
|
25
|
+
".": {
|
|
26
|
+
"solid": "./dist/source/index.jsx",
|
|
27
|
+
"import": "./dist/esm/index.js",
|
|
28
|
+
"browser": {
|
|
29
|
+
"import": "./dist/esm/index.js",
|
|
30
|
+
"require": "./dist/cjs/index.js"
|
|
31
|
+
},
|
|
32
|
+
"require": "./dist/cjs/index.js",
|
|
33
|
+
"node": "./dist/cjs/index.js"
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"scripts": {
|
|
37
|
+
"dev": "vite serve dev",
|
|
38
|
+
"build-dev": "vite build dev",
|
|
39
|
+
"test": "echo \"test not setup\"",
|
|
40
|
+
"build": "rollup -c && yarn build-dev",
|
|
41
|
+
"prepublishOnly": "yarn build",
|
|
42
|
+
"format": "prettier -w \"src/**/*.{js,ts,json,css,tsx,jsx}\" \"dev/**/*.{js,ts,json,css,tsx,jsx}\"",
|
|
43
|
+
"update-deps": "taze -w && yarn",
|
|
44
|
+
"typecheck": "tsc --noEmit"
|
|
45
|
+
},
|
|
46
|
+
"optionalDependencies": {
|
|
47
|
+
"@tonejs/midi": "^2.0.28"
|
|
48
|
+
},
|
|
49
|
+
"peerDependencies": {
|
|
50
|
+
"solid-js": ">=1.0.0"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"prettier": "2.8.1",
|
|
54
|
+
"rollup": "^3.7.2",
|
|
55
|
+
"rollup-preset-solid": "^2.0.1",
|
|
56
|
+
"solid-js": "^1.6.4",
|
|
57
|
+
"taze": "^0.8.4",
|
|
58
|
+
"typescript": "^4.9.4",
|
|
59
|
+
"vite": "^4.0.0",
|
|
60
|
+
"vite-plugin-solid": "^2.5.0"
|
|
61
|
+
},
|
|
62
|
+
"keywords": [
|
|
63
|
+
"solid"
|
|
64
|
+
]
|
|
65
|
+
}
|