solid-pianoroll 0.0.24 → 0.0.25

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.
Files changed (53) hide show
  1. package/dist/{cjs/index.js → dev.cjs} +408 -430
  2. package/dist/dev.css +212 -0
  3. package/dist/dev.js +1330 -0
  4. package/dist/dev.jsx +1130 -0
  5. package/dist/index.cjs +1330 -0
  6. package/dist/index.css +212 -0
  7. package/dist/{types/usePianoRollState.d.ts → index.d.ts} +38 -6
  8. package/dist/{esm/index.js → index.js} +412 -429
  9. package/dist/index.jsx +1130 -0
  10. package/package.json +43 -34
  11. package/dist/cjs/index.js.map +0 -1
  12. package/dist/esm/index.js.map +0 -1
  13. package/dist/source/PianoRoll.jsx +0 -82
  14. package/dist/source/PianoRollContext.jsx +0 -11
  15. package/dist/source/PianoRollGrid.jsx +0 -64
  16. package/dist/source/PianoRollKeys.jsx +0 -72
  17. package/dist/source/PianoRollNotes.jsx +0 -160
  18. package/dist/source/PianoRollNotesScroller.jsx +0 -16
  19. package/dist/source/PianoRollScale.jsx +0 -43
  20. package/dist/source/PianoRollScrollZoomViewPort.jsx +0 -49
  21. package/dist/source/PianoRollTrackList.jsx +0 -27
  22. package/dist/source/PianoRollTrackListScroller.jsx +0 -16
  23. package/dist/source/index.jsx +0 -5
  24. package/dist/source/types.js +0 -1
  25. package/dist/source/useBoundingClientRect.js +0 -29
  26. package/dist/source/useNotes.js +0 -23
  27. package/dist/source/usePianoRollGrid.js +0 -45
  28. package/dist/source/usePianoRollState.js +0 -126
  29. package/dist/source/viewport/PlayHead.jsx +0 -43
  30. package/dist/source/viewport/ScrollZoomContainer.jsx +0 -104
  31. package/dist/source/viewport/ScrollZoomViewPort.jsx +0 -31
  32. package/dist/source/viewport/ZoomSliderControl.jsx +0 -29
  33. package/dist/source/viewport/createViewPortDimension.js +0 -51
  34. package/dist/types/PianoRoll.d.ts +0 -8
  35. package/dist/types/PianoRollContext.d.ts +0 -45
  36. package/dist/types/PianoRollGrid.d.ts +0 -2
  37. package/dist/types/PianoRollKeys.d.ts +0 -9
  38. package/dist/types/PianoRollNotes.d.ts +0 -5
  39. package/dist/types/PianoRollNotesScroller.d.ts +0 -3
  40. package/dist/types/PianoRollScale.d.ts +0 -2
  41. package/dist/types/PianoRollScrollZoomViewPort.d.ts +0 -3
  42. package/dist/types/PianoRollTrackList.d.ts +0 -2
  43. package/dist/types/PianoRollTrackListScroller.d.ts +0 -3
  44. package/dist/types/index.d.ts +0 -6
  45. package/dist/types/types.d.ts +0 -8
  46. package/dist/types/useBoundingClientRect.d.ts +0 -3
  47. package/dist/types/useNotes.d.ts +0 -9
  48. package/dist/types/usePianoRollGrid.d.ts +0 -13
  49. package/dist/types/viewport/PlayHead.d.ts +0 -8
  50. package/dist/types/viewport/ScrollZoomContainer.d.ts +0 -8
  51. package/dist/types/viewport/ScrollZoomViewPort.d.ts +0 -39
  52. package/dist/types/viewport/ZoomSliderControl.d.ts +0 -6
  53. package/dist/types/viewport/createViewPortDimension.d.ts +0 -40
package/dist/index.cjs ADDED
@@ -0,0 +1,1330 @@
1
+ 'use strict';
2
+
3
+ var web = require('solid-js/web');
4
+ var solidJs = require('solid-js');
5
+ var store = require('solid-js/store');
6
+
7
+ var __defProp = Object.defineProperty;
8
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
9
+
10
+ // src/PianoRoll.module.css
11
+ var _default = {};
12
+ var defaultState = {
13
+ ppq: 0,
14
+ mode: "keys",
15
+ position: 0,
16
+ zoom: 10,
17
+ verticalZoom: 5,
18
+ verticalPosition: 44,
19
+ verticalTrackZoom: 0.5,
20
+ verticalTrackPosition: 0,
21
+ playHeadPosition: 0,
22
+ gridDivision: 4,
23
+ snapToGrid: true,
24
+ duration: 0,
25
+ tracks: [],
26
+ selectedTrackIndex: 0,
27
+ pressedKeys: {},
28
+ notesScrollerClientRect: { left: 0, width: 0, top: 0, height: 0 },
29
+ tracksScrollerClientRect: { left: 0, width: 0, top: 0, height: 0 }
30
+ };
31
+ var propNameToHandlerName = /* @__PURE__ */ __name((name) => `on${name[0]?.toUpperCase()}${name.slice(1)}Change`, "propNameToHandlerName");
32
+ var pianoRollStatePropNames = [
33
+ ...Object.keys(defaultState),
34
+ ...Object.keys(defaultState).map(propNameToHandlerName),
35
+ "onNoteChange",
36
+ "onInsertNote",
37
+ "onRemoveNote",
38
+ "onNoteDown",
39
+ "onNoteUp",
40
+ "isKeyDown",
41
+ "snapValueToGridIfEnabled"
42
+ ];
43
+ var createPianoRollstate = /* @__PURE__ */ __name((initialState) => {
44
+ const [state, setState] = store.createStore({
45
+ ...defaultState,
46
+ ...initialState
47
+ });
48
+ const handlers = Object.fromEntries(
49
+ Object.entries(state).map((entry) => {
50
+ const handlerName = propNameToHandlerName(entry[0]);
51
+ return [
52
+ handlerName,
53
+ (value, originalEvent) => {
54
+ const handler = initialState?.[handlerName];
55
+ setState(entry[0], value);
56
+ if (handler) {
57
+ handler(value, originalEvent);
58
+ }
59
+ }
60
+ ];
61
+ })
62
+ );
63
+ const onPlayheadPositionChange = /* @__PURE__ */ __name((playheadPosition, originalEvent) => {
64
+ handlers.onPlayHeadPositionChange(
65
+ snapValueToGridIfEnabled(playheadPosition, !!originalEvent?.altKey)
66
+ );
67
+ }, "onPlayheadPositionChange");
68
+ const updateNotes = /* @__PURE__ */ __name(async (trackIndex, getNotes) => {
69
+ const track = state.tracks[trackIndex];
70
+ if (!track)
71
+ return;
72
+ const notes = track.notes;
73
+ handlers.onTracksChange([
74
+ ...state.tracks.slice(0, trackIndex),
75
+ { ...track, notes: getNotes(notes) },
76
+ ...state.tracks.slice(trackIndex + 1)
77
+ ]);
78
+ }, "updateNotes");
79
+ const onNoteChange = /* @__PURE__ */ __name((trackIndex, noteIndex, note) => {
80
+ updateNotes(trackIndex, (notes) => [
81
+ ...notes.slice(0, noteIndex),
82
+ note,
83
+ ...notes.slice(noteIndex + 1)
84
+ ]);
85
+ }, "onNoteChange");
86
+ const onInsertNote = /* @__PURE__ */ __name((trackIndex, note) => {
87
+ const track = state.tracks[trackIndex];
88
+ if (!track)
89
+ return -1;
90
+ const notes = track.notes;
91
+ const newNoteIndex = Math.max(
92
+ notes.findIndex(({ ticks }) => ticks > note.ticks),
93
+ 0
94
+ );
95
+ updateNotes(trackIndex, (notes2) => [
96
+ ...notes2.slice(0, newNoteIndex),
97
+ note,
98
+ ...notes2.slice(newNoteIndex)
99
+ ]);
100
+ return newNoteIndex;
101
+ }, "onInsertNote");
102
+ const onRemoveNote = /* @__PURE__ */ __name((trackIndex, noteIndex) => {
103
+ updateNotes(trackIndex, (notes) => [
104
+ ...notes.slice(0, noteIndex),
105
+ ...notes.slice(noteIndex + 1)
106
+ ]);
107
+ }, "onRemoveNote");
108
+ const updateKeyPressedState = /* @__PURE__ */ __name((trackIndex, keyNumber, value) => {
109
+ handlers.onPressedKeysChange?.({
110
+ ...state.pressedKeys,
111
+ [trackIndex]: {
112
+ ...state.pressedKeys[trackIndex],
113
+ [keyNumber]: value
114
+ }
115
+ });
116
+ }, "updateKeyPressedState");
117
+ const onNoteDown = /* @__PURE__ */ __name((trackIndex, keyNumber) => {
118
+ updateKeyPressedState(trackIndex, keyNumber, true);
119
+ }, "onNoteDown");
120
+ const onNoteUp = /* @__PURE__ */ __name((trackIndex, keyNumber) => {
121
+ updateKeyPressedState(trackIndex, keyNumber, false);
122
+ }, "onNoteUp");
123
+ const isKeyDown = /* @__PURE__ */ __name((trackIndex, keyNumber) => !!state.pressedKeys[trackIndex]?.[keyNumber], "isKeyDown");
124
+ const snapValueToGridIfEnabled = /* @__PURE__ */ __name((value, altKey) => {
125
+ const gridDivisionTicks = state.ppq * 4 / state.gridDivision;
126
+ return state.snapToGrid && !altKey ? Math.round(value / gridDivisionTicks) * gridDivisionTicks : value;
127
+ }, "snapValueToGridIfEnabled");
128
+ return solidJs.mergeProps(state, {
129
+ ...handlers,
130
+ onNoteChange,
131
+ onInsertNote,
132
+ onRemoveNote,
133
+ onNoteDown,
134
+ onNoteUp,
135
+ isKeyDown,
136
+ snapValueToGridIfEnabled,
137
+ onPlayheadPositionChange
138
+ });
139
+ }, "createPianoRollstate");
140
+ exports.createPianoRollstate = createPianoRollstate;
141
+
142
+ // src/PianoRollContext.tsx
143
+ var PianoRollContext = solidJs.createContext();
144
+ var PianoRollContextProvider = PianoRollContext.Provider;
145
+ var usePianoRollContext = /* @__PURE__ */ __name(() => {
146
+ const context = solidJs.useContext(PianoRollContext);
147
+ if (!context)
148
+ throw new Error("No PianoRollContext found");
149
+ return context;
150
+ }, "usePianoRollContext");
151
+ var splitContextProps = /* @__PURE__ */ __name((allProps) => solidJs.splitProps(allProps, [...pianoRollStatePropNames, "showAllTracks", "showTrackList"]), "splitContextProps");
152
+
153
+ // src/PianoRollKeys.module.css
154
+ var _default2 = {};
155
+ function createViewPortDimension(getState) {
156
+ const getStateWithFunctions = /* @__PURE__ */ __name(() => {
157
+ const state2 = getState();
158
+ const onZoomChange = /* @__PURE__ */ __name((zoom) => state2.onZoomChange?.(clamp(zoom, state2.minZoom, state2.maxZoom)), "onZoomChange");
159
+ const onPositionChange = /* @__PURE__ */ __name((position) => state2.onPositionChange?.(position), "onPositionChange");
160
+ return {
161
+ ...state2,
162
+ onZoomChange,
163
+ onPositionChange,
164
+ calculatePixelOffset,
165
+ calculatePixelValue,
166
+ calculatePosition,
167
+ calculatePixelDimensions,
168
+ calculateVisibleRange,
169
+ calculateMaxPosition,
170
+ isVisible
171
+ };
172
+ }, "getStateWithFunctions");
173
+ const [state, setState] = store.createStore(getStateWithFunctions());
174
+ solidJs.createEffect(() => setState(getStateWithFunctions()));
175
+ function calculatePixelValue(position = state.position) {
176
+ const virtualSize = state.pixelSize * state.zoom;
177
+ return position / state.range * virtualSize;
178
+ }
179
+ __name(calculatePixelValue, "calculatePixelValue");
180
+ function calculatePixelOffset(position) {
181
+ return calculatePixelValue(position) - calculatePixelValue(state.position);
182
+ }
183
+ __name(calculatePixelOffset, "calculatePixelOffset");
184
+ function calculatePosition(offset) {
185
+ const percentX = (offset - state.pixelOffset) / state.pixelSize;
186
+ const position = state.position + percentX * calculateVisibleRange();
187
+ return position;
188
+ }
189
+ __name(calculatePosition, "calculatePosition");
190
+ function calculateVisibleRange() {
191
+ return state.range / state.zoom;
192
+ }
193
+ __name(calculateVisibleRange, "calculateVisibleRange");
194
+ function calculateMaxPosition() {
195
+ return state.range - state.range / state.zoom;
196
+ }
197
+ __name(calculateMaxPosition, "calculateMaxPosition");
198
+ function calculatePixelDimensions(position, length) {
199
+ const offset = calculatePixelOffset(position);
200
+ const size = calculatePixelValue(length);
201
+ return { offset, size };
202
+ }
203
+ __name(calculatePixelDimensions, "calculatePixelDimensions");
204
+ function isVisible({ offset, size }) {
205
+ return offset + size > 0 && offset < state.pixelSize;
206
+ }
207
+ __name(isVisible, "isVisible");
208
+ return state;
209
+ }
210
+ __name(createViewPortDimension, "createViewPortDimension");
211
+ var clamp = /* @__PURE__ */ __name((value, min, max) => Math.max(min, Math.min(max, value)), "clamp");
212
+
213
+ // src/viewport/ScrollZoomViewPort.tsx
214
+ var defaultViewPortProps = {
215
+ pixelOffset: 0,
216
+ position: 0,
217
+ range: 1,
218
+ zoom: 1,
219
+ minZoom: 1,
220
+ maxZoom: Infinity
221
+ };
222
+ var ScrollZoomViewPort = /* @__PURE__ */ __name((props) => {
223
+ const viewPorts = Object.entries(props.dimensions).map(([name, viewPortProps]) => createViewPortDimension(() => ({
224
+ name,
225
+ ...defaultViewPortProps,
226
+ ...viewPortProps()
227
+ })));
228
+ const parentViewPorts = solidJs.useContext(ScrollZoomViewPortContext);
229
+ return web.createComponent(ScrollZoomViewPortContext.Provider, {
230
+ get value() {
231
+ return [...parentViewPorts, ...viewPorts];
232
+ },
233
+ get children() {
234
+ return props.children;
235
+ }
236
+ });
237
+ }, "ScrollZoomViewPort");
238
+ var ScrollZoomViewPortContext = solidJs.createContext([]);
239
+ var useViewPortDimension = /* @__PURE__ */ __name((name) => {
240
+ const viewPorts = solidJs.useContext(ScrollZoomViewPortContext);
241
+ const viewPort = name ? viewPorts.find((viewPort2) => viewPort2.name === name) : viewPorts[viewPorts.length - 1];
242
+ if (!viewPort)
243
+ throw new Error(`ViewPort dimension "${name}" not found.`);
244
+ return viewPort;
245
+ }, "useViewPortDimension");
246
+
247
+ // src/viewport/ScrollZoomContainer.module.css
248
+ var _default3 = {};
249
+
250
+ // src/viewport/ScrollZoomContainer.tsx
251
+ var _tmpl$ = /* @__PURE__ */ web.template(`<div><div><div><div></div></div></div></div>`, 8);
252
+ var ScrollZoomContainer = /* @__PURE__ */ __name((props) => {
253
+ let scrollContentRef;
254
+ const [ownProps, divProps] = solidJs.splitProps(props, ["ref", "verticalDimensionName", "horizontalDimensionName", "showScrollbar"]);
255
+ const propsWithDefaults = solidJs.mergeProps({
256
+ verticalDimensionName: "vertical",
257
+ horizontalDimensionName: "horizontal",
258
+ showScrollbar: true
259
+ }, ownProps);
260
+ const verticalViewPort = solidJs.createMemo(() => useViewPortDimension(propsWithDefaults.verticalDimensionName));
261
+ const horizontalViewPort = solidJs.createMemo(() => useViewPortDimension(propsWithDefaults.horizontalDimensionName));
262
+ const handleScroll = /* @__PURE__ */ __name((event) => {
263
+ event.preventDefault();
264
+ if (didUpdateScroll) {
265
+ didUpdateScroll = false;
266
+ return;
267
+ }
268
+ const maxVerticalPosition = verticalViewPort().calculateMaxPosition();
269
+ const maxPosition = horizontalViewPort().calculateMaxPosition();
270
+ const height = verticalViewPort().pixelSize;
271
+ const width = horizontalViewPort().pixelSize;
272
+ const {
273
+ scrollTop,
274
+ scrollLeft,
275
+ scrollWidth,
276
+ scrollHeight
277
+ } = event.currentTarget;
278
+ const scrollTopAmount = scrollTop / (scrollHeight - height);
279
+ const scrollLeftAmount = scrollLeft / (scrollWidth - width);
280
+ verticalViewPort().onPositionChange?.(maxVerticalPosition * scrollTopAmount);
281
+ horizontalViewPort().onPositionChange?.(maxPosition * scrollLeftAmount);
282
+ }, "handleScroll");
283
+ const handleWheel = /* @__PURE__ */ __name((event) => {
284
+ if (event.altKey) {
285
+ event.preventDefault();
286
+ if (Math.abs(event.deltaX) > Math.abs(event.deltaY)) {
287
+ horizontalViewPort()?.onZoomChange?.(horizontalViewPort().zoom / (1 + event.deltaX / horizontalViewPort().pixelSize));
288
+ const maxPosition = horizontalViewPort().calculateMaxPosition();
289
+ horizontalViewPort()?.onPositionChange?.(Math.min(maxPosition, horizontalViewPort()?.position));
290
+ } else {
291
+ verticalViewPort()?.onZoomChange?.(verticalViewPort().zoom / (1 + event.deltaY / verticalViewPort().pixelSize));
292
+ const maxVerticalPosition = verticalViewPort().calculateMaxPosition();
293
+ verticalViewPort()?.onPositionChange?.(Math.min(maxVerticalPosition, verticalViewPort()?.position));
294
+ }
295
+ } else if (!props.showScrollbar) {
296
+ event.preventDefault();
297
+ event.currentTarget.scrollLeft += event.deltaX;
298
+ event.currentTarget.scrollTop += event.deltaY;
299
+ }
300
+ }, "handleWheel");
301
+ let didUpdateScroll = false;
302
+ solidJs.createEffect(() => {
303
+ const maxVerticalPosition = verticalViewPort().calculateMaxPosition();
304
+ const maxPosition = horizontalViewPort().calculateMaxPosition();
305
+ const scrollTopAmount = maxVerticalPosition > 0 ? verticalViewPort().position / maxVerticalPosition : 0;
306
+ const scrollLeftAmount = maxPosition > 0 ? horizontalViewPort().position / maxPosition : 0;
307
+ const height = verticalViewPort().pixelSize;
308
+ const width = horizontalViewPort().pixelSize;
309
+ if (!scrollContentRef?.parentElement)
310
+ return;
311
+ const scrollDivHeight = verticalViewPort().zoom * verticalViewPort().pixelSize;
312
+ const scrollTop = scrollTopAmount * (scrollDivHeight - height);
313
+ const scrollDivWidth = horizontalViewPort().zoom * horizontalViewPort().pixelSize;
314
+ const scrollLeft = scrollLeftAmount * (scrollDivWidth - width);
315
+ didUpdateScroll = true;
316
+ scrollContentRef.style.height = `${scrollDivHeight}px`;
317
+ scrollContentRef.style.width = `${scrollDivWidth}px`;
318
+ scrollContentRef.parentElement.scrollTo({
319
+ left: scrollLeft,
320
+ top: scrollTop
321
+ });
322
+ });
323
+ return (() => {
324
+ const _el$ = _tmpl$.cloneNode(true), _el$2 = _el$.firstChild, _el$3 = _el$2.firstChild, _el$4 = _el$3.firstChild;
325
+ _el$.addEventListener("wheel", handleWheel);
326
+ _el$.addEventListener("scroll", handleScroll);
327
+ const _ref$ = props.ref;
328
+ typeof _ref$ === "function" ? web.use(_ref$, _el$) : props.ref = _el$;
329
+ web.spread(_el$, web.mergeProps(divProps, {
330
+ get style() {
331
+ return {
332
+ overflow: propsWithDefaults.showScrollbar ? "scroll" : "hidden",
333
+ ...typeof divProps.style === "object" && divProps.style
334
+ };
335
+ },
336
+ get classList() {
337
+ return {
338
+ [_default3.ScrollZoomContainer]: true,
339
+ ...divProps.classList
340
+ };
341
+ }
342
+ }), false, true);
343
+ const _ref$2 = scrollContentRef;
344
+ typeof _ref$2 === "function" ? web.use(_ref$2, _el$2) : scrollContentRef = _el$2;
345
+ _el$3.style.setProperty("top", "0");
346
+ _el$3.style.setProperty("left", "0");
347
+ _el$3.style.setProperty("position", "sticky");
348
+ _el$3.style.setProperty("display", "flex");
349
+ _el$4.style.setProperty("position", "relative");
350
+ web.insert(_el$4, () => props.children);
351
+ web.effect((_p$) => {
352
+ const _v$ = `${verticalViewPort().pixelSize}px`, _v$2 = `${horizontalViewPort().pixelSize}px`, _v$3 = `${verticalViewPort().pixelSize}px`, _v$4 = `${horizontalViewPort().pixelSize}px`;
353
+ _v$ !== _p$._v$ && _el$3.style.setProperty("height", _p$._v$ = _v$);
354
+ _v$2 !== _p$._v$2 && _el$3.style.setProperty("width", _p$._v$2 = _v$2);
355
+ _v$3 !== _p$._v$3 && _el$4.style.setProperty("height", _p$._v$3 = _v$3);
356
+ _v$4 !== _p$._v$4 && _el$4.style.setProperty("width", _p$._v$4 = _v$4);
357
+ return _p$;
358
+ }, {
359
+ _v$: void 0,
360
+ _v$2: void 0,
361
+ _v$3: void 0,
362
+ _v$4: void 0
363
+ });
364
+ return _el$;
365
+ })();
366
+ }, "ScrollZoomContainer");
367
+ var ScrollZoomContainer_default = ScrollZoomContainer;
368
+
369
+ // src/PianoRollKeys.tsx
370
+ var _tmpl$2 = /* @__PURE__ */ web.template(`<div></div>`, 2);
371
+ var PianoRollKeys = /* @__PURE__ */ __name(() => {
372
+ const verticalViewPort = solidJs.createMemo(() => useViewPortDimension("vertical"));
373
+ const horizontalViewPort = solidJs.createMemo(() => useViewPortDimension("horizontalKeys"));
374
+ const [isMouseDown, setIsMouseDown] = solidJs.createSignal(false);
375
+ const context = usePianoRollContext();
376
+ const handleMouseUp = /* @__PURE__ */ __name(() => {
377
+ setIsMouseDown(false);
378
+ }, "handleMouseUp");
379
+ solidJs.createEffect(() => {
380
+ if (isMouseDown()) {
381
+ window.addEventListener("mouseup", handleMouseUp);
382
+ } else {
383
+ window.removeEventListener("mouseup", handleMouseUp);
384
+ }
385
+ });
386
+ return (() => {
387
+ const _el$ = _tmpl$2.cloneNode(true);
388
+ web.insert(_el$, web.createComponent(ScrollZoomContainer_default, {
389
+ horizontalDimensionName: "horizontalKeys",
390
+ showScrollbar: false,
391
+ get children() {
392
+ return web.createComponent(solidJs.Index, {
393
+ each: keys,
394
+ children: (key) => {
395
+ const isDown = solidJs.createMemo(() => context.isKeyDown(context.selectedTrackIndex, key().number));
396
+ const virtualDimensions = solidJs.createMemo(() => verticalViewPort().calculatePixelDimensions(127 - key().number, 1));
397
+ const previousIsBlack = blackKeys.includes((key().number - 1) % 12);
398
+ const nextIsBlack = blackKeys.includes((key().number + 1) % 12);
399
+ return web.createComponent(solidJs.Show, {
400
+ get when() {
401
+ return verticalViewPort().isVisible(virtualDimensions());
402
+ },
403
+ get children() {
404
+ const _el$2 = _tmpl$2.cloneNode(true);
405
+ _el$2.addEventListener("mouseleave", () => {
406
+ if (isMouseDown()) {
407
+ context.onNoteUp(context.selectedTrackIndex, key().number);
408
+ }
409
+ });
410
+ _el$2.addEventListener("mouseenter", () => {
411
+ if (isMouseDown()) {
412
+ context.onNoteDown(context.selectedTrackIndex, key().number);
413
+ }
414
+ });
415
+ _el$2.$$mouseup = () => {
416
+ context.onNoteUp(context.selectedTrackIndex, key().number);
417
+ };
418
+ _el$2.$$mousedown = () => {
419
+ setIsMouseDown(true);
420
+ context.onNoteDown(context.selectedTrackIndex, key().number);
421
+ };
422
+ web.effect((_p$) => {
423
+ const _v$3 = {
424
+ [_default2["Key"]]: true,
425
+ [_default2["black"]]: key().isBlack,
426
+ [_default2["white"]]: !key().isBlack,
427
+ [_default2["down"]]: isDown()
428
+ }, _v$4 = key().name, _v$5 = key().number % 12, _v$6 = `${virtualDimensions().offset - virtualDimensions().size * (!key().isBlack ? nextIsBlack ? 1 / 2 : 0 : 0)}px`, _v$7 = `${virtualDimensions().size + (!key().isBlack && nextIsBlack ? virtualDimensions().size / 2 : 0) + (!key().isBlack && previousIsBlack ? virtualDimensions().size / 2 : 0)}px`, _v$8 = `0px 0px ${Math.min(virtualDimensions().size / 20, 1)}px ${Math.min(virtualDimensions().size / 50, 1)}px rgba(0, 0, 0, 0.5) ${key().isBlack ? "" : "inset"}` + isDown() ? `0px 0px ${Math.min(virtualDimensions().size / 8, 2)}px ${Math.min(virtualDimensions().size / 20, 2)}px rgba(0, 0, 0, 0.5) inset` : "";
429
+ _p$._v$3 = web.classList(_el$2, _v$3, _p$._v$3);
430
+ _v$4 !== _p$._v$4 && web.setAttribute(_el$2, "title", _p$._v$4 = _v$4);
431
+ _v$5 !== _p$._v$5 && web.setAttribute(_el$2, "data-index", _p$._v$5 = _v$5);
432
+ _v$6 !== _p$._v$6 && _el$2.style.setProperty("top", _p$._v$6 = _v$6);
433
+ _v$7 !== _p$._v$7 && _el$2.style.setProperty("height", _p$._v$7 = _v$7);
434
+ _v$8 !== _p$._v$8 && _el$2.style.setProperty("box-shadow", _p$._v$8 = _v$8);
435
+ return _p$;
436
+ }, {
437
+ _v$3: void 0,
438
+ _v$4: void 0,
439
+ _v$5: void 0,
440
+ _v$6: void 0,
441
+ _v$7: void 0,
442
+ _v$8: void 0
443
+ });
444
+ return _el$2;
445
+ }
446
+ });
447
+ }
448
+ });
449
+ }
450
+ }));
451
+ web.effect((_p$) => {
452
+ const _v$ = _default2.PianoRollKeys, _v$2 = `${horizontalViewPort().pixelSize}px`;
453
+ _v$ !== _p$._v$ && web.className(_el$, _p$._v$ = _v$);
454
+ _v$2 !== _p$._v$2 && _el$.style.setProperty("width", _p$._v$2 = _v$2);
455
+ return _p$;
456
+ }, {
457
+ _v$: void 0,
458
+ _v$2: void 0
459
+ });
460
+ return _el$;
461
+ })();
462
+ }, "PianoRollKeys");
463
+ var blackKeys = [1, 3, 6, 8, 10];
464
+ var keyNames = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];
465
+ var keys = Array.from({
466
+ length: 128
467
+ }).map((_, index) => ({
468
+ number: index,
469
+ name: `${keyNames[index % 12]} ${Math.floor(index / 12) - 2}`,
470
+ isBlack: blackKeys.includes(index % 12)
471
+ }));
472
+ var PianoRollKeys_default = PianoRollKeys;
473
+ web.delegateEvents(["mousedown", "mouseup"]);
474
+
475
+ // src/PianoRollNotes.module.css
476
+ var _default4 = {};
477
+
478
+ // src/PianoRollNotes.tsx
479
+ var _tmpl$3 = /* @__PURE__ */ web.template(`<div></div>`, 2);
480
+ var PianoRollNotes = /* @__PURE__ */ __name((props) => {
481
+ const context = usePianoRollContext();
482
+ const verticalViewPort = solidJs.createMemo(() => useViewPortDimension(context.mode === "keys" ? "vertical" : "verticalTracks"));
483
+ const horizontalViewPort = solidJs.createMemo(() => useViewPortDimension("horizontal"));
484
+ const gridDivisionTicks = solidJs.createMemo(() => context.ppq * 4 / context.gridDivision);
485
+ const [isDragging, setIsDragging] = solidJs.createSignal(false);
486
+ const [noteDragMode, setNoteDragMode] = solidJs.createSignal();
487
+ const [currentNoteIndex, setCurrentNoteIndex] = solidJs.createSignal(-1);
488
+ const [currentNoteTrackIndex, setCurrentNoteTrackIndex] = solidJs.createSignal(-1);
489
+ const [diffPosition, setDiffPosition] = solidJs.createSignal(0);
490
+ const [getInitialNote, setInitialNote] = solidJs.createSignal();
491
+ const calculateNoteDragValues = /* @__PURE__ */ __name((event) => {
492
+ const targetTrackIndex = context.mode === "tracks" ? clamp(Math.floor(verticalViewPort().calculatePosition(event.clientY)), 0, context.tracks.length - 1) : currentNoteTrackIndex() > -1 ? currentNoteTrackIndex() : context.selectedTrackIndex;
493
+ const midi = context.mode === "keys" ? Math.floor(128 - verticalViewPort().calculatePosition(event.clientY)) : getInitialNote()?.midi ?? 60;
494
+ const horiontalPosition = horizontalViewPort().calculatePosition(event.clientX);
495
+ return {
496
+ targetTrackIndex,
497
+ midi,
498
+ horiontalPosition
499
+ };
500
+ }, "calculateNoteDragValues");
501
+ const handleMouseMove = /* @__PURE__ */ __name((mouseMoveEvent) => {
502
+ mouseMoveEvent.preventDefault();
503
+ mouseMoveEvent.stopPropagation();
504
+ const note = getInitialNote();
505
+ if (!note)
506
+ return;
507
+ const {
508
+ targetTrackIndex,
509
+ midi,
510
+ horiontalPosition
511
+ } = calculateNoteDragValues(mouseMoveEvent);
512
+ const ticks = context.snapValueToGridIfEnabled(horiontalPosition - diffPosition(), mouseMoveEvent.altKey);
513
+ const updatedNote = {
514
+ ...note,
515
+ midi,
516
+ ...noteDragMode() === "move" && {
517
+ ticks
518
+ },
519
+ ...noteDragMode() === "trimStart" && {
520
+ ticks: ticks < note.ticks + note.durationTicks ? ticks : note.ticks + note.durationTicks,
521
+ durationTicks: ticks < note.ticks + note.durationTicks ? note.ticks + note.durationTicks - ticks : context.snapValueToGridIfEnabled(horiontalPosition - note.ticks, mouseMoveEvent.altKey)
522
+ },
523
+ ...noteDragMode() === "trimEnd" && {
524
+ ticks: ticks < note.ticks ? ticks : note.ticks,
525
+ durationTicks: ticks < note.ticks ? note.ticks - ticks : context.snapValueToGridIfEnabled(horiontalPosition - note.ticks, mouseMoveEvent.altKey)
526
+ }
527
+ };
528
+ const previousTrackIndex = currentNoteTrackIndex();
529
+ const previousNoteIndex = currentNoteIndex();
530
+ if (targetTrackIndex === previousTrackIndex) {
531
+ context.onNoteChange?.(previousTrackIndex, previousNoteIndex, updatedNote);
532
+ } else {
533
+ if (context.onInsertNote) {
534
+ context.onRemoveNote?.(previousTrackIndex, previousNoteIndex);
535
+ const newNoteIndex = context.onInsertNote(targetTrackIndex, updatedNote);
536
+ setCurrentNoteTrackIndex(targetTrackIndex);
537
+ setCurrentNoteIndex(newNoteIndex);
538
+ }
539
+ }
540
+ }, "handleMouseMove");
541
+ const stopDragging = /* @__PURE__ */ __name(() => {
542
+ window.removeEventListener("mousemove", handleMouseMove);
543
+ window.removeEventListener("mouseup", stopDragging);
544
+ setIsDragging(false);
545
+ setCurrentNoteIndex(-1);
546
+ setCurrentNoteTrackIndex(-1);
547
+ }, "stopDragging");
548
+ const startDragging = /* @__PURE__ */ __name(() => {
549
+ window.addEventListener("mousemove", handleMouseMove);
550
+ window.addEventListener("mouseup", stopDragging);
551
+ }, "startDragging");
552
+ const getClasses = /* @__PURE__ */ __name((noteDragMode2) => {
553
+ return noteDragMode2 ? [_default4.Note, _default4[noteDragMode2]] : [_default4.Note];
554
+ }, "getClasses");
555
+ return (() => {
556
+ const _el$ = _tmpl$3.cloneNode(true);
557
+ _el$.$$mousedown = (mouseDownEvent) => {
558
+ mouseDownEvent.preventDefault();
559
+ mouseDownEvent.stopPropagation();
560
+ const {
561
+ targetTrackIndex,
562
+ midi,
563
+ horiontalPosition
564
+ } = calculateNoteDragValues(mouseDownEvent);
565
+ const ticks = context.snapValueToGridIfEnabled(horiontalPosition, mouseDownEvent.altKey, context);
566
+ const durationTicks = gridDivisionTicks();
567
+ const newNote = {
568
+ midi,
569
+ ticks,
570
+ durationTicks,
571
+ velocity: 100
572
+ };
573
+ const newNoteIndex = context.onInsertNote(targetTrackIndex, newNote);
574
+ setIsDragging(true);
575
+ setCurrentNoteTrackIndex(targetTrackIndex);
576
+ setCurrentNoteIndex(newNoteIndex);
577
+ setInitialNote(newNote);
578
+ setDiffPosition(0);
579
+ setNoteDragMode("trimEnd");
580
+ startDragging();
581
+ };
582
+ const _ref$ = props.ref;
583
+ typeof _ref$ === "function" ? web.use(_ref$, _el$) : props.ref = _el$;
584
+ web.insert(_el$, web.createComponent(solidJs.For, {
585
+ get each() {
586
+ return context.tracks;
587
+ },
588
+ children: (track, trackIndex) => {
589
+ return web.createComponent(solidJs.Show, {
590
+ get when() {
591
+ return trackIndex() === context.selectedTrackIndex || context.showAllTracks || context.mode === "tracks";
592
+ },
593
+ get children() {
594
+ return web.createComponent(solidJs.For, {
595
+ get each() {
596
+ return track.notes;
597
+ },
598
+ children: (note, noteIndex) => {
599
+ const verticalVirtualDimensions = solidJs.createMemo(() => verticalViewPort().calculatePixelDimensions(context.mode === "keys" ? 127 - note.midi : trackIndex(), 1));
600
+ const horizontalDimensions = solidJs.createMemo(() => horizontalViewPort().calculatePixelDimensions(note.ticks, note.durationTicks));
601
+ return web.createComponent(solidJs.Show, {
602
+ get when() {
603
+ return web.memo(() => !!verticalViewPort().isVisible(verticalVirtualDimensions()))() && horizontalViewPort().isVisible(horizontalDimensions());
604
+ },
605
+ get children() {
606
+ const _el$2 = _tmpl$3.cloneNode(true);
607
+ _el$2.$$mousedown = (event) => {
608
+ event.stopPropagation();
609
+ const initialPosition = horizontalViewPort().calculatePosition(event.clientX);
610
+ setDiffPosition(noteDragMode() === "trimEnd" ? -(note.ticks + note.durationTicks - initialPosition) : initialPosition - note.ticks);
611
+ setIsDragging(true);
612
+ setCurrentNoteIndex(noteIndex());
613
+ setCurrentNoteTrackIndex(trackIndex());
614
+ setInitialNote(note);
615
+ startDragging();
616
+ };
617
+ _el$2.$$dblclick = (event) => {
618
+ event.stopPropagation();
619
+ context.onRemoveNote?.(trackIndex(), noteIndex());
620
+ };
621
+ _el$2.$$mousemove = (event) => {
622
+ if (isDragging())
623
+ return;
624
+ event.stopPropagation();
625
+ const relativeX = horizontalViewPort().calculatePixelValue(horizontalViewPort().calculatePosition(event.clientX));
626
+ const noteStartX = horizontalViewPort().calculatePixelValue(note.ticks);
627
+ const noteEndX = horizontalViewPort().calculatePixelValue(note.ticks + note.durationTicks);
628
+ setNoteDragMode(relativeX - noteStartX < 3 ? "trimStart" : noteEndX - relativeX < 3 ? "trimEnd" : "move");
629
+ };
630
+ web.setAttribute(_el$2, "draggable", false);
631
+ web.effect((_p$) => {
632
+ const _v$ = getClasses(noteDragMode()).join(" "), _v$2 = `${track.color}`, _v$3 = (note.velocity / 128 + 2) / 3, _v$4 = `${verticalVirtualDimensions().offset}px`, _v$5 = `${verticalVirtualDimensions().size}px`, _v$6 = `${horizontalDimensions().offset}px`, _v$7 = `${horizontalDimensions().size}px`;
633
+ _v$ !== _p$._v$ && web.className(_el$2, _p$._v$ = _v$);
634
+ _v$2 !== _p$._v$2 && _el$2.style.setProperty("background-color", _p$._v$2 = _v$2);
635
+ _v$3 !== _p$._v$3 && _el$2.style.setProperty("opacity", _p$._v$3 = _v$3);
636
+ _v$4 !== _p$._v$4 && _el$2.style.setProperty("top", _p$._v$4 = _v$4);
637
+ _v$5 !== _p$._v$5 && _el$2.style.setProperty("height", _p$._v$5 = _v$5);
638
+ _v$6 !== _p$._v$6 && _el$2.style.setProperty("left", _p$._v$6 = _v$6);
639
+ _v$7 !== _p$._v$7 && _el$2.style.setProperty("width", _p$._v$7 = _v$7);
640
+ return _p$;
641
+ }, {
642
+ _v$: void 0,
643
+ _v$2: void 0,
644
+ _v$3: void 0,
645
+ _v$4: void 0,
646
+ _v$5: void 0,
647
+ _v$6: void 0,
648
+ _v$7: void 0
649
+ });
650
+ return _el$2;
651
+ }
652
+ });
653
+ }
654
+ });
655
+ }
656
+ });
657
+ }
658
+ }));
659
+ web.effect((_$p) => web.classList(_el$, {
660
+ [_default4.PianoRollNotes]: true
661
+ }, _$p));
662
+ return _el$;
663
+ })();
664
+ }, "PianoRollNotes");
665
+ var PianoRollNotes_default = PianoRollNotes;
666
+ web.delegateEvents(["mousedown", "mousemove", "dblclick"]);
667
+
668
+ // src/PianoRollGrid.module.css
669
+ var _default5 = {};
670
+ var usePianoRollGrid = /* @__PURE__ */ __name(() => {
671
+ const context = usePianoRollContext();
672
+ const horizontalViewPort = solidJs.createMemo(() => useViewPortDimension("horizontal"));
673
+ const measureTicks = solidJs.createMemo(() => context.ppq * 4);
674
+ const selectedGridDivisorTicks = solidJs.createMemo(() => measureTicks() / context.gridDivision);
675
+ function calculateVisibleGridDivisorTicks(value) {
676
+ const visibleRange = horizontalViewPort().calculateVisibleRange();
677
+ if (visibleRange <= 0)
678
+ return 0;
679
+ if (visibleRange / value > 100) {
680
+ return calculateVisibleGridDivisorTicks(value * 2);
681
+ }
682
+ if (visibleRange / value < 30) {
683
+ return calculateVisibleGridDivisorTicks(value / 2);
684
+ }
685
+ return value;
686
+ }
687
+ __name(calculateVisibleGridDivisorTicks, "calculateVisibleGridDivisorTicks");
688
+ const gridDivisorTicks = solidJs.createMemo(
689
+ () => calculateVisibleGridDivisorTicks(selectedGridDivisorTicks())
690
+ );
691
+ const gridArray = solidJs.createMemo(() => {
692
+ const numberOfLines = Math.ceil(
693
+ horizontalViewPort().calculateVisibleRange() / gridDivisorTicks() + 1
694
+ );
695
+ const startIndex = Math.floor(context.position / gridDivisorTicks());
696
+ return Array.from({ length: numberOfLines }).map((_, i) => {
697
+ const index = i + startIndex;
698
+ const ticks = index * gridDivisorTicks();
699
+ const measurePosition = ticks / context.ppq / 4 + 1;
700
+ const bars = Math.floor(measurePosition);
701
+ const beats = Math.floor((measurePosition - bars) * 4);
702
+ const label = `${bars}${beats ? `.${beats}` : ""}`;
703
+ return {
704
+ index,
705
+ ticks,
706
+ isHighlighted: Math.ceil((index + 1) * selectedGridDivisorTicks() / measureTicks()) % 2 === 0,
707
+ hasHighlightedBorder: index * gridDivisorTicks() % selectedGridDivisorTicks() === 0,
708
+ showLabel: index * selectedGridDivisorTicks() / measureTicks() % 2 === 0,
709
+ virtualDimensions: horizontalViewPort().calculatePixelDimensions(ticks, gridDivisorTicks()),
710
+ label
711
+ };
712
+ });
713
+ });
714
+ return gridArray;
715
+ }, "usePianoRollGrid");
716
+ var usePianoRollGrid_default = usePianoRollGrid;
717
+
718
+ // src/PianoRollGrid.tsx
719
+ var _tmpl$4 = /* @__PURE__ */ web.template(`<div></div>`, 2);
720
+ var PianoRollGrid = /* @__PURE__ */ __name(() => {
721
+ const context = usePianoRollContext();
722
+ const verticalViewPort = solidJs.createMemo(() => useViewPortDimension(context.mode === "keys" ? "vertical" : "verticalTracks"));
723
+ const horizontalViewPort = solidJs.createMemo(() => useViewPortDimension("horizontal"));
724
+ const gridArray = usePianoRollGrid_default();
725
+ return (() => {
726
+ const _el$ = _tmpl$4.cloneNode(true);
727
+ web.insert(_el$, web.createComponent(solidJs.Index, {
728
+ get each() {
729
+ return gridArray();
730
+ },
731
+ children: (entry) => {
732
+ return web.createComponent(solidJs.Show, {
733
+ get when() {
734
+ return horizontalViewPort().isVisible(entry().virtualDimensions);
735
+ },
736
+ get children() {
737
+ const _el$2 = _tmpl$4.cloneNode(true);
738
+ web.effect((_p$) => {
739
+ const _v$ = {
740
+ [_default5["PianoRollGrid-Time"]]: true,
741
+ [_default5["Highlighted"]]: entry().isHighlighted,
742
+ [_default5["HighlightedBorder"]]: entry().hasHighlightedBorder
743
+ }, _v$2 = `${entry().virtualDimensions.offset}px`, _v$3 = `${entry().virtualDimensions.size}px`;
744
+ _p$._v$ = web.classList(_el$2, _v$, _p$._v$);
745
+ _v$2 !== _p$._v$2 && _el$2.style.setProperty("left", _p$._v$2 = _v$2);
746
+ _v$3 !== _p$._v$3 && _el$2.style.setProperty("width", _p$._v$3 = _v$3);
747
+ return _p$;
748
+ }, {
749
+ _v$: void 0,
750
+ _v$2: void 0,
751
+ _v$3: void 0
752
+ });
753
+ return _el$2;
754
+ }
755
+ });
756
+ }
757
+ }), null);
758
+ web.insert(_el$, web.createComponent(solidJs.Show, {
759
+ get when() {
760
+ return context.mode === "keys";
761
+ },
762
+ get children() {
763
+ return web.createComponent(solidJs.Index, {
764
+ each: keys,
765
+ children: (key) => {
766
+ const virtualDimensions = solidJs.createMemo(() => verticalViewPort().calculatePixelDimensions(127 - key().number, 1));
767
+ return web.createComponent(solidJs.Show, {
768
+ get when() {
769
+ return verticalViewPort().isVisible(virtualDimensions());
770
+ },
771
+ get children() {
772
+ const _el$3 = _tmpl$4.cloneNode(true);
773
+ web.effect((_p$) => {
774
+ const _v$4 = {
775
+ [_default5["PianoRollGrid-Row"]]: true,
776
+ [_default5["Key"]]: true,
777
+ [_default5["IsDark"]]: key().isBlack,
778
+ [_default5["IsBlackAndNextIsWhite"]]: !key().isBlack && !blackKeys.includes((key().number + 1) % 12)
779
+ }, _v$5 = `${virtualDimensions().offset}px`, _v$6 = `${virtualDimensions().size}px`;
780
+ _p$._v$4 = web.classList(_el$3, _v$4, _p$._v$4);
781
+ _v$5 !== _p$._v$5 && _el$3.style.setProperty("top", _p$._v$5 = _v$5);
782
+ _v$6 !== _p$._v$6 && _el$3.style.setProperty("height", _p$._v$6 = _v$6);
783
+ return _p$;
784
+ }, {
785
+ _v$4: void 0,
786
+ _v$5: void 0,
787
+ _v$6: void 0
788
+ });
789
+ return _el$3;
790
+ }
791
+ });
792
+ }
793
+ });
794
+ }
795
+ }), null);
796
+ web.insert(_el$, web.createComponent(solidJs.Show, {
797
+ get when() {
798
+ return context.mode === "tracks";
799
+ },
800
+ get children() {
801
+ return web.createComponent(solidJs.For, {
802
+ get each() {
803
+ return context.tracks;
804
+ },
805
+ children: (track, index) => {
806
+ const virtualDimensions = solidJs.createMemo(() => verticalViewPort().calculatePixelDimensions(index(), 1));
807
+ return web.createComponent(solidJs.Show, {
808
+ get when() {
809
+ return verticalViewPort().isVisible(virtualDimensions());
810
+ },
811
+ get children() {
812
+ const _el$4 = _tmpl$4.cloneNode(true);
813
+ web.effect((_p$) => {
814
+ const _v$7 = {
815
+ [_default5["PianoRollGrid-Row"]]: true,
816
+ [_default5["IsDark"]]: index() % 2 === 0
817
+ }, _v$8 = `${virtualDimensions().offset}px`, _v$9 = `${virtualDimensions().size}px`;
818
+ _p$._v$7 = web.classList(_el$4, _v$7, _p$._v$7);
819
+ _v$8 !== _p$._v$8 && _el$4.style.setProperty("top", _p$._v$8 = _v$8);
820
+ _v$9 !== _p$._v$9 && _el$4.style.setProperty("height", _p$._v$9 = _v$9);
821
+ return _p$;
822
+ }, {
823
+ _v$7: void 0,
824
+ _v$8: void 0,
825
+ _v$9: void 0
826
+ });
827
+ return _el$4;
828
+ }
829
+ });
830
+ }
831
+ });
832
+ }
833
+ }), null);
834
+ web.effect(() => web.className(_el$, _default5.PianoRollGrid));
835
+ return _el$;
836
+ })();
837
+ }, "PianoRollGrid");
838
+ var PianoRollGrid_default = PianoRollGrid;
839
+ var _tmpl$5 = /* @__PURE__ */ web.template(`<input step="0.01">`, 1);
840
+ var ZoomSliderControl = /* @__PURE__ */ __name((props) => {
841
+ const propsWithDefaults = solidJs.mergeProps({
842
+ orientation: "horizontal"
843
+ }, props);
844
+ const dimension = solidJs.createMemo(() => useViewPortDimension(props.dimensionName ?? propsWithDefaults.orientation));
845
+ return (() => {
846
+ const _el$ = _tmpl$5.cloneNode(true);
847
+ web.spread(_el$, web.mergeProps({
848
+ get min() {
849
+ return dimension().minZoom;
850
+ },
851
+ get max() {
852
+ return dimension().maxZoom;
853
+ }
854
+ }, props, {
855
+ get value() {
856
+ return dimension().zoom;
857
+ },
858
+ "onInput": (event) => {
859
+ const value = event.currentTarget.valueAsNumber;
860
+ dimension().onZoomChange?.(value);
861
+ },
862
+ "type": "range"
863
+ }, () => ({
864
+ orient: propsWithDefaults.orientation
865
+ }), {
866
+ get style() {
867
+ return propsWithDefaults.orientation === "vertical" ? {
868
+ margin: 0,
869
+ "margin-left": "4px",
870
+ "margin-right": "4px",
871
+ width: "16px",
872
+ transform: "rotate(180deg)",
873
+ ...{
874
+ "writing-mode": "bt-lr",
875
+ "-webkit-appearance": "slider-vertical"
876
+ },
877
+ ...typeof props.style === "object" && props.style
878
+ } : {
879
+ margin: 0,
880
+ "margin-top": "4px",
881
+ "margin-bottom": "4px",
882
+ ...typeof props.style === "object" && props.style
883
+ };
884
+ }
885
+ }), false, false);
886
+ return _el$;
887
+ })();
888
+ }, "ZoomSliderControl");
889
+ var ZoomSliderControl_default = ZoomSliderControl;
890
+
891
+ // src/PianoRollTrackList.module.css
892
+ var _default6 = {};
893
+ var _tmpl$6 = /* @__PURE__ */ web.template(`<div></div>`, 2);
894
+ var _tmpl$22 = /* @__PURE__ */ web.template(`<div> </div>`, 2);
895
+ var PianoRollTrackList = /* @__PURE__ */ __name(() => {
896
+ const viewPort = solidJs.createMemo(() => useViewPortDimension("verticalTracks"));
897
+ const context = usePianoRollContext();
898
+ return (() => {
899
+ const _el$ = _tmpl$6.cloneNode(true);
900
+ web.insert(_el$, web.createComponent(solidJs.Index, {
901
+ get each() {
902
+ return context.tracks;
903
+ },
904
+ children: (track, index) => {
905
+ const virtualDimensions = solidJs.createMemo(() => viewPort().calculatePixelDimensions(index, 1));
906
+ return web.createComponent(solidJs.Show, {
907
+ get when() {
908
+ return virtualDimensions().size > 0;
909
+ },
910
+ get children() {
911
+ const _el$2 = _tmpl$22.cloneNode(true), _el$3 = _el$2.firstChild;
912
+ _el$2.$$click = () => context.onSelectedTrackIndexChange?.(index);
913
+ web.insert(_el$2, index, _el$3);
914
+ web.insert(_el$2, () => track().name || "[unnamed track]", null);
915
+ web.effect((_p$) => {
916
+ const _v$ = {
917
+ [_default6["Track"]]: true,
918
+ [_default6["selected"]]: index === context.selectedTrackIndex
919
+ }, _v$2 = track().name, _v$3 = `${virtualDimensions().offset}px`, _v$4 = `${virtualDimensions().size}px`;
920
+ _p$._v$ = web.classList(_el$2, _v$, _p$._v$);
921
+ _v$2 !== _p$._v$2 && web.setAttribute(_el$2, "title", _p$._v$2 = _v$2);
922
+ _v$3 !== _p$._v$3 && _el$2.style.setProperty("top", _p$._v$3 = _v$3);
923
+ _v$4 !== _p$._v$4 && _el$2.style.setProperty("height", _p$._v$4 = _v$4);
924
+ return _p$;
925
+ }, {
926
+ _v$: void 0,
927
+ _v$2: void 0,
928
+ _v$3: void 0,
929
+ _v$4: void 0
930
+ });
931
+ return _el$2;
932
+ }
933
+ });
934
+ }
935
+ }));
936
+ web.effect(() => web.className(_el$, _default6.PianoRollTrackList));
937
+ return _el$;
938
+ })();
939
+ }, "PianoRollTrackList");
940
+ var PianoRollTrackList_default = PianoRollTrackList;
941
+ web.delegateEvents(["click"]);
942
+ var PianoRollScrollZoomViewPort = /* @__PURE__ */ __name((props) => {
943
+ const zoomFactor = 500;
944
+ const context = usePianoRollContext();
945
+ return web.createComponent(ScrollZoomViewPort, {
946
+ dimensions: {
947
+ horizontal: () => ({
948
+ pixelOffset: context.notesScrollerClientRect.left,
949
+ pixelSize: context.notesScrollerClientRect.width,
950
+ position: context.position,
951
+ range: context.duration,
952
+ zoom: context.zoom * (zoomFactor / context.notesScrollerClientRect.width),
953
+ onPositionChange: context.onPositionChange,
954
+ onZoomChange: (zoom) => context.onZoomChange?.(zoom / (zoomFactor / context.notesScrollerClientRect.width)),
955
+ minZoom: 1,
956
+ maxZoom: 500
957
+ }),
958
+ vertical: () => ({
959
+ pixelOffset: context.notesScrollerClientRect.top,
960
+ pixelSize: context.notesScrollerClientRect.height,
961
+ position: context.verticalPosition,
962
+ range: 128,
963
+ zoom: context.verticalZoom * (zoomFactor / context.notesScrollerClientRect.height),
964
+ onPositionChange: context.onVerticalPositionChange,
965
+ onZoomChange: (verticalZoom) => context.onVerticalZoomChange?.(verticalZoom / (zoomFactor / context.notesScrollerClientRect.height)),
966
+ minZoom: 1,
967
+ maxZoom: 10
968
+ }),
969
+ horizontalTracks: () => ({
970
+ pixelOffset: 0,
971
+ pixelSize: context.tracksScrollerClientRect.width
972
+ }),
973
+ verticalTracks: () => ({
974
+ pixelOffset: context.tracksScrollerClientRect.top,
975
+ pixelSize: context.tracksScrollerClientRect.height,
976
+ position: context.verticalTrackPosition,
977
+ range: context.tracks.length,
978
+ zoom: context.verticalTrackZoom * (zoomFactor / context.tracksScrollerClientRect.height),
979
+ onPositionChange: context.onVerticalTrackPositionChange,
980
+ onZoomChange: (verticalTrackZoom) => context.onVerticalTrackZoomChange?.(verticalTrackZoom / (zoomFactor / context.tracksScrollerClientRect.height)),
981
+ minZoom: 0.8,
982
+ maxZoom: 3
983
+ }),
984
+ horizontalKeys: () => ({
985
+ pixelSize: 60
986
+ })
987
+ },
988
+ get children() {
989
+ return props.children;
990
+ }
991
+ });
992
+ }, "PianoRollScrollZoomViewPort");
993
+ var PianoRollScrollZoomViewPort_default = PianoRollScrollZoomViewPort;
994
+ var defaultRect = {
995
+ left: 0,
996
+ width: 0,
997
+ top: 0,
998
+ height: 0,
999
+ bottom: 0,
1000
+ right: 0,
1001
+ x: 0,
1002
+ y: 0
1003
+ };
1004
+ function useBoundingClientRect(containerRef) {
1005
+ const [boundingClientRect, setBoundingClientRect] = solidJs.createSignal(defaultRect);
1006
+ solidJs.createEffect(() => {
1007
+ const container = containerRef();
1008
+ if (!container)
1009
+ return;
1010
+ const updateBoundingClientRect = /* @__PURE__ */ __name(() => {
1011
+ setBoundingClientRect(container.getBoundingClientRect() ?? defaultRect);
1012
+ }, "updateBoundingClientRect");
1013
+ const resizeObserver = new ResizeObserver(updateBoundingClientRect);
1014
+ resizeObserver.observe(container);
1015
+ updateBoundingClientRect();
1016
+ return () => {
1017
+ resizeObserver.disconnect();
1018
+ };
1019
+ });
1020
+ return boundingClientRect;
1021
+ }
1022
+ __name(useBoundingClientRect, "useBoundingClientRect");
1023
+
1024
+ // src/PianoRollNotesScroller.tsx
1025
+ var PianoRollNotesScroller = /* @__PURE__ */ __name((props) => {
1026
+ const context = usePianoRollContext();
1027
+ const [ref, setRef] = solidJs.createSignal();
1028
+ const clientRect = useBoundingClientRect(ref);
1029
+ solidJs.createEffect(() => {
1030
+ context.onNotesScrollerClientRectChange(clientRect());
1031
+ });
1032
+ return web.createComponent(ScrollZoomContainer_default, {
1033
+ ref: setRef,
1034
+ get verticalDimensionName() {
1035
+ return context.mode === "keys" ? "vertical" : "verticalTracks";
1036
+ },
1037
+ get children() {
1038
+ return props.children;
1039
+ }
1040
+ });
1041
+ }, "PianoRollNotesScroller");
1042
+ var PianoRollNotesScroller_default = PianoRollNotesScroller;
1043
+ var PianoRollTrackListScroller = /* @__PURE__ */ __name((props) => {
1044
+ const context = usePianoRollContext();
1045
+ const [ref, setRef] = solidJs.createSignal();
1046
+ const clientRect = useBoundingClientRect(ref);
1047
+ solidJs.createEffect(() => {
1048
+ context.onTracksScrollerClientRectChange(clientRect());
1049
+ });
1050
+ return web.createComponent(ScrollZoomContainer_default, {
1051
+ ref: setRef,
1052
+ horizontalDimensionName: "horizontalTracks",
1053
+ verticalDimensionName: "verticalTracks",
1054
+ get showScrollbar() {
1055
+ return context.mode === "keys";
1056
+ },
1057
+ get children() {
1058
+ return props.children;
1059
+ }
1060
+ });
1061
+ }, "PianoRollTrackListScroller");
1062
+ var PianoRollTrackListScroller_default = PianoRollTrackListScroller;
1063
+
1064
+ // src/PianoRollScale.module.css
1065
+ var _default7 = {};
1066
+
1067
+ // src/PianoRollScale.tsx
1068
+ var _tmpl$7 = /* @__PURE__ */ web.template(`<div></div>`, 2);
1069
+ var PianoRollScale = /* @__PURE__ */ __name(() => {
1070
+ const horizontalViewPort = solidJs.createMemo(() => useViewPortDimension("horizontal"));
1071
+ const grid = usePianoRollGrid_default();
1072
+ const context = usePianoRollContext();
1073
+ const updatePlayheadPosition = /* @__PURE__ */ __name((event) => {
1074
+ const position = horizontalViewPort().calculatePosition(event.clientX);
1075
+ context.onPlayHeadPositionChange(position, event);
1076
+ }, "updatePlayheadPosition");
1077
+ return (() => {
1078
+ const _el$ = _tmpl$7.cloneNode(true);
1079
+ _el$.$$mousedown = (event) => {
1080
+ updatePlayheadPosition(event);
1081
+ const handleMouseUp = /* @__PURE__ */ __name(() => {
1082
+ window.removeEventListener("mousemove", updatePlayheadPosition);
1083
+ window.removeEventListener("mouseup", handleMouseUp);
1084
+ }, "handleMouseUp");
1085
+ window.addEventListener("mousemove", updatePlayheadPosition);
1086
+ window.addEventListener("mouseup", handleMouseUp);
1087
+ };
1088
+ web.insert(_el$, web.createComponent(solidJs.Index, {
1089
+ get each() {
1090
+ return grid();
1091
+ },
1092
+ children: (entry) => {
1093
+ return web.createComponent(solidJs.Show, {
1094
+ get when() {
1095
+ return horizontalViewPort().isVisible(entry().virtualDimensions);
1096
+ },
1097
+ get children() {
1098
+ const _el$2 = _tmpl$7.cloneNode(true);
1099
+ web.insert(_el$2, web.createComponent(solidJs.Show, {
1100
+ get when() {
1101
+ return entry().showLabel;
1102
+ },
1103
+ get children() {
1104
+ const _el$3 = _tmpl$7.cloneNode(true);
1105
+ web.insert(_el$3, () => entry().label);
1106
+ web.effect(() => web.className(_el$3, _default7["PianoRollScale-Label"]));
1107
+ return _el$3;
1108
+ }
1109
+ }));
1110
+ web.effect((_p$) => {
1111
+ const _v$ = {
1112
+ [_default7["PianoRollScale-Time"]]: true,
1113
+ [_default7["Highlighted"]]: entry().isHighlighted,
1114
+ [_default7["HighlightedBorder"]]: entry().hasHighlightedBorder
1115
+ }, _v$2 = `${entry().virtualDimensions.offset}px`, _v$3 = `${entry().virtualDimensions.size}px`;
1116
+ _p$._v$ = web.classList(_el$2, _v$, _p$._v$);
1117
+ _v$2 !== _p$._v$2 && _el$2.style.setProperty("left", _p$._v$2 = _v$2);
1118
+ _v$3 !== _p$._v$3 && _el$2.style.setProperty("width", _p$._v$3 = _v$3);
1119
+ return _p$;
1120
+ }, {
1121
+ _v$: void 0,
1122
+ _v$2: void 0,
1123
+ _v$3: void 0
1124
+ });
1125
+ return _el$2;
1126
+ }
1127
+ });
1128
+ }
1129
+ }));
1130
+ web.effect(() => web.className(_el$, _default7.PianoRollScale));
1131
+ return _el$;
1132
+ })();
1133
+ }, "PianoRollScale");
1134
+ var PianoRollScale_default = PianoRollScale;
1135
+ web.delegateEvents(["mousedown"]);
1136
+
1137
+ // src/PianoRoll.tsx
1138
+ var _tmpl$8 = /* @__PURE__ */ web.template(`<div><button><div></div></button></div>`, 6);
1139
+ var _tmpl$23 = /* @__PURE__ */ web.template(`<div><div></div><div></div></div>`, 6);
1140
+ var _tmpl$32 = /* @__PURE__ */ web.template(`<div><div><div></div></div></div>`, 6);
1141
+ var _tmpl$42 = /* @__PURE__ */ web.template(`<div></div>`, 2);
1142
+ var PianoRoll = /* @__PURE__ */ __name((allProps) => {
1143
+ const propsWithDefaults = solidJs.mergeProps({
1144
+ showAllTracks: false
1145
+ }, allProps);
1146
+ const [context, divProps] = splitContextProps(propsWithDefaults);
1147
+ return web.createComponent(PianoRollContextProvider, {
1148
+ value: context,
1149
+ get children() {
1150
+ const _el$ = _tmpl$42.cloneNode(true);
1151
+ web.spread(_el$, web.mergeProps(divProps, {
1152
+ get ["class"]() {
1153
+ return _default.PianoRoll;
1154
+ }
1155
+ }), false, true);
1156
+ web.insert(_el$, web.createComponent(PianoRollScrollZoomViewPort_default, {
1157
+ get children() {
1158
+ return [(() => {
1159
+ const _el$2 = _tmpl$32.cloneNode(true), _el$3 = _el$2.firstChild, _el$7 = _el$3.firstChild;
1160
+ web.insert(_el$2, web.createComponent(ZoomSliderControl_default, {
1161
+ orientation: "vertical",
1162
+ dimensionName: "verticalTracks"
1163
+ }), _el$3);
1164
+ web.insert(_el$3, web.createComponent(solidJs.Show, {
1165
+ get when() {
1166
+ return context.showTrackList;
1167
+ },
1168
+ get children() {
1169
+ const _el$4 = _tmpl$8.cloneNode(true), _el$5 = _el$4.firstChild, _el$6 = _el$5.firstChild;
1170
+ _el$4.style.setProperty("height", "30px");
1171
+ _el$4.style.setProperty("border-right", "1px black solid");
1172
+ _el$5.$$click = () => context.onModeChange(context.mode === "keys" ? "tracks" : "keys");
1173
+ _el$5.style.setProperty("font-size", "16px");
1174
+ _el$5.style.setProperty("line-height", "16px");
1175
+ _el$5.style.setProperty("cursor", "pointer");
1176
+ _el$5.style.setProperty("overflow", "hidden");
1177
+ web.insert(_el$6, () => context.mode === "keys" ? "\u2261" : "\u{1F3B9}");
1178
+ web.effect(() => web.setAttribute(_el$5, "title", context.mode === "keys" ? "Tracks Mode" : "Keys Mode"));
1179
+ return _el$4;
1180
+ }
1181
+ }), _el$7);
1182
+ _el$7.style.setProperty("flex", "1");
1183
+ _el$7.style.setProperty("display", "flex");
1184
+ _el$7.style.setProperty("overflow", "hidden");
1185
+ web.insert(_el$7, web.createComponent(solidJs.Show, {
1186
+ get when() {
1187
+ return context.showTrackList;
1188
+ },
1189
+ get children() {
1190
+ return web.createComponent(PianoRollTrackListScroller_default, {
1191
+ get children() {
1192
+ return web.createComponent(PianoRollTrackList_default, {});
1193
+ }
1194
+ });
1195
+ }
1196
+ }), null);
1197
+ web.insert(_el$7, web.createComponent(solidJs.Show, {
1198
+ get when() {
1199
+ return context.mode === "keys";
1200
+ },
1201
+ get children() {
1202
+ return web.createComponent(PianoRollKeys_default, {});
1203
+ }
1204
+ }), null);
1205
+ web.insert(_el$2, web.createComponent(PianoRollNotesScroller_default, {
1206
+ get children() {
1207
+ return [web.memo(() => allProps.children), (() => {
1208
+ const _el$8 = _tmpl$23.cloneNode(true), _el$9 = _el$8.firstChild, _el$10 = _el$9.nextSibling;
1209
+ _el$8.style.setProperty("width", "100%");
1210
+ _el$8.style.setProperty("display", "flex");
1211
+ _el$8.style.setProperty("height", "100%");
1212
+ _el$8.style.setProperty("flex-direction", "column");
1213
+ _el$8.style.setProperty("overflow", "hidden");
1214
+ _el$9.style.setProperty("height", "30px");
1215
+ web.insert(_el$9, web.createComponent(PianoRollScale_default, {}));
1216
+ _el$10.style.setProperty("flex", "1");
1217
+ _el$10.style.setProperty("position", "relative");
1218
+ _el$10.style.setProperty("overflow", "hidden");
1219
+ web.insert(_el$10, web.createComponent(PianoRollGrid_default, {}), null);
1220
+ web.insert(_el$10, web.createComponent(PianoRollNotes_default, {}), null);
1221
+ return _el$8;
1222
+ })()];
1223
+ }
1224
+ }), null);
1225
+ web.insert(_el$2, web.createComponent(ZoomSliderControl_default, {
1226
+ orientation: "vertical",
1227
+ get disabled() {
1228
+ return context.mode !== "keys";
1229
+ }
1230
+ }), null);
1231
+ web.effect((_p$) => {
1232
+ const _v$ = _default.PianoRollContainer, _v$2 = {
1233
+ [_default.PianoRollLeftColumn]: true,
1234
+ [_default.showTrackList]: context.showTrackList
1235
+ };
1236
+ _v$ !== _p$._v$ && web.className(_el$2, _p$._v$ = _v$);
1237
+ _p$._v$2 = web.classList(_el$3, _v$2, _p$._v$2);
1238
+ return _p$;
1239
+ }, {
1240
+ _v$: void 0,
1241
+ _v$2: void 0
1242
+ });
1243
+ return _el$2;
1244
+ })(), web.createComponent(ZoomSliderControl_default, {
1245
+ orientation: "horizontal",
1246
+ get style() {
1247
+ return {
1248
+ "margin-left": `${context.tracksScrollerClientRect.width + 24}px`,
1249
+ "margin-right": "24px"
1250
+ };
1251
+ }
1252
+ })];
1253
+ }
1254
+ }));
1255
+ return _el$;
1256
+ }
1257
+ });
1258
+ }, "PianoRoll");
1259
+ exports.PianoRoll = PianoRoll;
1260
+ web.delegateEvents(["click"]);
1261
+ var _tmpl$9 = /* @__PURE__ */ web.template(`<div class="PlayHead"></div>`, 2);
1262
+ var PlayHead = /* @__PURE__ */ __name((allProps) => {
1263
+ const [props, divProps] = solidJs.splitProps(allProps, ["position", "sync", "onPositionChange", "dimensionName"]);
1264
+ const viewPort = useViewPortDimension(props.dimensionName ?? "horizontal");
1265
+ solidJs.createEffect(() => {
1266
+ if (!props.sync)
1267
+ return;
1268
+ const maxPosition = viewPort.range;
1269
+ const newPosition = clamp(props.position - viewPort.range / viewPort.zoom / 2, 0, maxPosition);
1270
+ viewPort.onPositionChange?.(newPosition);
1271
+ });
1272
+ const leftPosition = solidJs.createMemo(() => viewPort.calculatePixelOffset(props.position));
1273
+ return (() => {
1274
+ const _el$ = _tmpl$9.cloneNode(true);
1275
+ web.spread(_el$, web.mergeProps(divProps, {
1276
+ get style() {
1277
+ return {
1278
+ position: "absolute",
1279
+ height: "100%",
1280
+ width: "2px",
1281
+ left: `${leftPosition()}px`,
1282
+ "background-color": "green",
1283
+ cursor: "pointer",
1284
+ ...typeof divProps.style === "object" && divProps.style
1285
+ };
1286
+ },
1287
+ "onMouseDown": (event) => {
1288
+ event.preventDefault();
1289
+ event.stopPropagation();
1290
+ const handleMouseMove = /* @__PURE__ */ __name((event2) => {
1291
+ const newPosition = viewPort.calculatePosition(event2.clientX);
1292
+ props.onPositionChange?.(newPosition, event2);
1293
+ }, "handleMouseMove");
1294
+ const handleMouseUp = /* @__PURE__ */ __name(() => {
1295
+ window.removeEventListener("mousemove", handleMouseMove);
1296
+ window.removeEventListener("mouseup", handleMouseUp);
1297
+ }, "handleMouseUp");
1298
+ window.addEventListener("mousemove", handleMouseMove);
1299
+ window.addEventListener("mouseup", handleMouseUp);
1300
+ }
1301
+ }), false, false);
1302
+ return _el$;
1303
+ })();
1304
+ }, "PlayHead");
1305
+ exports.PlayHead = PlayHead;
1306
+ var useNotes = /* @__PURE__ */ __name(() => {
1307
+ const [notes, onNotesChange] = solidJs.createSignal([]);
1308
+ const onNoteChange = /* @__PURE__ */ __name((index, note) => {
1309
+ onNotesChange([...notes().slice(0, index), note, ...notes().splice(index + 1)]);
1310
+ }, "onNoteChange");
1311
+ const onInsertNote = /* @__PURE__ */ __name((note) => {
1312
+ const index = Math.max(
1313
+ notes().findIndex(({ ticks }) => ticks > note.ticks),
1314
+ 0
1315
+ );
1316
+ onNotesChange([...notes().slice(0, index), note, ...notes().splice(index)]);
1317
+ return index;
1318
+ }, "onInsertNote");
1319
+ const onRemoveNote = /* @__PURE__ */ __name((index) => {
1320
+ onNotesChange([...notes().slice(0, index), ...notes().splice(index + 1)]);
1321
+ }, "onRemoveNote");
1322
+ return {
1323
+ notes,
1324
+ onNotesChange,
1325
+ onNoteChange,
1326
+ onInsertNote,
1327
+ onRemoveNote
1328
+ };
1329
+ }, "useNotes");
1330
+ exports.useNotes = useNotes;