From 88e213d9aab38eeddb3d84d39e0b1d607eeeb3c2 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 18 Sep 2026 10:10:53 +0000 Subject: [PATCH 1/2] Release v0.3.0 The first release with the rebuilt control panel (#326): five hairline-separated columns in place of five bordered boxes of equal weight, a mode rail with the view controls in its foot, a permanent collapsible guidance column, instrument-styled readouts with frequency as the one lit reading, a twin-tab style panel that states its target, reversed-out selected rows, the selected feature haloed on the gram, and a transport bar with time bookmarks. What an analyst sees and reaches for changes more than in any release since the player, so a minor bump, as v0.2.0 was for region zoom and the player refinements. v0.2.1 is the last release with the legacy panel and stays published. Config tables and annotation storage are unchanged between the two. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01CrHtVussSR8ZxsxisaepJM --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 65a4405..56aed59 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "spectro-component", - "version": "0.2.1", + "version": "0.3.0", "type": "module", "scripts": { "dev": "vite", From 9b067ce9a5b308f2d550ad49afba10862041e3fb Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 18 Sep 2026 10:25:12 +0000 Subject: [PATCH 2/2] Poll the marker's position after a seek instead of reading it once The v0.2.1 release run failed on AS-4.3 in tests/player-annotations.spec.js with the marker measured hundreds of pixels from where the seek should have put it, while the same commit passed the Tests workflow on main and passes the spec repeatedly here. The seek redraws synchronously, but the media element's own `seeked` event redraws again a moment later, and a single bounding-box read landing between the state settling and that redraw sees a stale position. Poll it, as the suite does elsewhere, so the assertion waits for the drawing rather than racing it. The release gate is the same suite, so this rides the release PR. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01CrHtVussSR8ZxsxisaepJM --- tests/player-annotations.spec.js | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/tests/player-annotations.spec.js b/tests/player-annotations.spec.js index 97e882b..795fa72 100644 --- a/tests/player-annotations.spec.js +++ b/tests/player-annotations.spec.js @@ -84,13 +84,18 @@ test.describe('Story 4 — pause, annotate, resume', () => { await page.evaluate(() => window.GramFrame.getPlayer(0).seek(10)) await gfp.waitForState(s => s.player.viewTop === 10, { message: 'seek to 10 s' }) - const pos = await page.locator('.gram-frame-analysis-marker circle, .gram-frame-analysis-marker line').first().evaluate(el => { + // 4 s below the top edge → 320 px (the bounding-box read is border-box + // relative, as SVG units are). Polled rather than read once: the seek + // redraws synchronously, but the media element's own `seeked` event + // redraws again a moment later, and a single read landing between the + // state settling and that redraw failed the v0.2.1 release run while + // passing the same commit everywhere else. + const markerY = () => page.locator('.gram-frame-analysis-marker circle, .gram-frame-analysis-marker line').first().evaluate(el => { const bb = el.getBoundingClientRect(); const svg = document.querySelector('.gram-frame-svg').getBoundingClientRect() - return { y: bb.top + bb.height / 2 - svg.top } + return bb.top + bb.height / 2 - svg.top }) - // 4 s below the top edge → 320 px (the bounding-box read is border-box - // relative, as SVG units are) - expect(Math.abs(pos.y - expectedSVG(marker.time, marker.freq, 10).y)).toBeLessThan(6) + await expect.poll(async () => Math.abs(await markerY() - expectedSVG(marker.time, marker.freq, 10).y), + { message: 'the marker to be drawn 4 s below the top edge' }).toBeLessThan(6) await page.evaluate(() => window.GramFrame.getPlayer(0).seek(14)) await gfp.waitForState(s => s.player.viewTop === 14, { message: 'seek to 14 s' })