Skip to content

Commit 1e16f2e

Browse files
committed
Version 1.2.5
- Small visual fixes - Improved SequenceHeader/Playhead interaction
1 parent 5371986 commit 1e16f2e

File tree

8 files changed

+81
-101
lines changed

8 files changed

+81
-101
lines changed

.github/dependabot.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@ updates:
77
- package-ecosystem: "npm"
88
directory: "/"
99
schedule:
10-
interval: "weekly"
10+
interval: "daily"
1111
open-pull-requests-limit: 10
1212
labels:
1313
- "dependencies"
1414

1515
- package-ecosystem: "github-actions"
1616
directory: "/"
1717
schedule:
18-
interval: "weekly"
18+
interval: "daily"
1919
open-pull-requests-limit: 10
2020
labels:
2121
- "dependencies"

package-lock.json

Lines changed: 30 additions & 94 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "quick-beats",
33
"private": true,
4-
"version": "1.2.4",
4+
"version": "1.2.5",
55
"type": "module",
66
"scripts": {
77
"dev": "vite",

src/App.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -300,8 +300,8 @@ function App() {
300300
<div className="flex-1 flex flex-col overflow-hidden">
301301
<header className="flex-none flex items-center justify-between px-2 py-1 md:px-6 md:py-3">
302302
<div className="flex items-center gap-4">
303-
<h1 className="text-md md:text-2xl font-black tracking-tighter text-white uppercase flex items-center gap-2 select-none">
304-
<Icon id="logo" className="w-6 h-6 md:w-8 md:h-8 text-[#3b82f6] md:mt-0.5" />
303+
<h1 className="text-md md:text-xl font-black tracking-tighter text-white uppercase flex items-center gap-2 select-none">
304+
<Icon id="logo" className="w-6 h-6 md:w-7 md:h-7 text-[#3b82f6] md:mt-0.5" />
305305
Quick Beats
306306
</h1>
307307
</div>

src/components/Setup.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export default function Setup({ onSelect, onConfirm, selectedSig, onShowHelp })
1313
onClick={onShowHelp}
1414
className="absolute top-4 right-6 text-slate-700 hover:text-white transition-colors font-mono text-[10px] uppercase tracking-tighter"
1515
>
16-
v1.2.4
16+
v1.2.5
1717
</button>
1818
<div className="max-w-2xl w-full">
1919
<div className="text-center mb-8 md:mb-16">

src/components/Setup.test.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ describe('Setup', () => {
9090
const onShowHelp = vi.fn();
9191
renderWithSprite(<Setup {...defaultProps} onShowHelp={onShowHelp} />);
9292

93-
const versionButton = screen.getByText('v1.2.4');
93+
const versionButton = screen.getByText('v1.2.5');
9494
fireEvent.click(versionButton);
9595

9696
expect(onShowHelp).toHaveBeenCalledTimes(1);

src/hooks/useAudio.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export function useAudio() {
1818
const isLoadedRef = useRef(false);
1919
const perfTickRef = useRef(0);
2020
const wakeLockRef = useRef(null);
21+
const playheadVersionRef = useRef(0);
2122

2223
const loadKit = useCallback(async (kitId) => {
2324
const kit = KITS[kitId];
@@ -67,6 +68,9 @@ export function useAudio() {
6768
}
6869

6970
if (Tone.getTransport().state === "started") {
71+
// Invalidate any already queued UI updates from the previous
72+
// transport timeline before stopping.
73+
playheadVersionRef.current += 1;
7074
// Pause playback but preserve the current playhead position so resume
7175
// continues from the same step. `handleReset` is responsible for
7276
// explicitly resetting to step 0.
@@ -164,7 +168,11 @@ export function useAudio() {
164168
});
165169

166170
// Schedule UI update
171+
const scheduledPlayheadVersion = playheadVersionRef.current;
167172
Tone.getDraw().schedule(() => {
173+
// Ignore stale scheduled updates after a manual seek/stop.
174+
if (scheduledPlayheadVersion !== playheadVersionRef.current) return;
175+
168176
if (perfEnabled) {
169177
performance.mark(uiMarkName);
170178
performance.measure(measureName, audioMarkName, uiMarkName);
@@ -192,6 +200,8 @@ export function useAudio() {
192200
}, []);
193201

194202
const setStep = useCallback((step) => {
203+
// Invalidate queued draw updates from the previous timeline position.
204+
playheadVersionRef.current += 1;
195205
stepRef.current = step;
196206
setCurrentStep(step);
197207
}, []);

src/hooks/useAudio.test.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,40 @@ describe('useAudio', () => {
259259
expect(mockPlayersInstance.player).not.toHaveBeenCalled();
260260
});
261261

262+
it('ignores stale scheduled UI updates after manual seek', async () => {
263+
const scheduledCallbacks = [];
264+
Tone.getDraw.mockReturnValue({
265+
schedule: vi.fn((cb) => {
266+
scheduledCallbacks.push(cb);
267+
})
268+
});
269+
270+
const { result } = renderHook(() => useAudio());
271+
await act(async () => { await result.current.loadKit('black-pearl'); });
272+
273+
act(() => {
274+
result.current.updateGrid([[true, false]]);
275+
});
276+
277+
const loopCallback = Tone.Loop.mock.calls[0][0];
278+
279+
act(() => {
280+
loopCallback(10);
281+
});
282+
283+
act(() => {
284+
result.current.setStep(7);
285+
});
286+
287+
expect(result.current.currentStep).toBe(7);
288+
289+
act(() => {
290+
scheduledCallbacks[0]();
291+
});
292+
293+
expect(result.current.currentStep).toBe(7);
294+
});
295+
262296
it('cleans up on unmount', async () => {
263297
const { result, unmount } = renderHook(() => useAudio());
264298
await act(async () => { await result.current.loadKit('black-pearl'); });

0 commit comments

Comments
 (0)