Commit Graph
4242 Commits
Author SHA1 Message Date
Jordan Eldredge 23b9c98bae Add changelog entry and cite version in docs 2026-10-15 10:20:45 -07:00
Jordan Eldredge d02b873460 Add updateTrack instance method
Allow embedders to rename a track, or replace its metadata, without
reloading it. defaultName is set with a new SET_TRACK_DEFAULT_NAME action;
metaData reuses SET_MEDIA_TAGS, which preserves the track's kbps/kHz/channels.
2026-10-15 10:20:44 -07:00
Jordan Eldredge c85e44c96b Add setTracks instance method (#1380)
* Add setTracks instance method

setTracksToPlay always starts playing the first track, so embedders who want
to swap the source without autoplaying had to dispose() and create a new
instance. setTracks replaces the playlist, stops any current playback, and
loads the first track (like initialTracks) without playing it.

* Add changelog entry and cite version in docs
2026-10-15 10:06:33 -07:00
Jordan EldredgeandClaude Opus 5.5 9999094dde Make the new UI's grid and feed two views of the same list
The feed was its own shuffled list (museum favorites plus random skins).
Now it continues through whatever the grid shows, the museum or a search:
tapping a skin opens the feed on it with the list before and after it,
and going back lands the grid on the skin you were on. Feed URLs are
plain permalinks; the list lives in the tab (listPosition.ts), and a
permalink opened fresh is followed by the museum. /feed redirects to the
museum's first skin.

- Event logging v3: one session per tab visit spanning both views, with
  view_switch events and each feed view's list, list index and depth.
  Older events are still accepted.
- The feed loads readmes and like counts on demand.
- Keyboard paging steps from the skin actually on screen, so it can't get
  stuck after a scroll is interrupted.
- Going back to the grid animates the skin into its tile. React's
  <ViewTransition> can't animate Back with Next's router, so it's done
  by hand.
- Grid tiles and view transition snapshots render pixelated.
- Download saves the skin under its file name, not its md5.
- A feed URL for a missing skin returns a 404 status.
- The list position is kept apart from the server actions, so pages with
  only the menu bar don't each compile another copy of the server code;
  this kept the build within the server's memory.
- Declare reselect (used by legacy-client) and give knexfile.ts named
  exports.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01QzNErwA6wVTLaUTNMARPQt
2026-10-15 10:05:25 -07:00
Jordan Eldredge 37523b3e2e Add onMediaError instance method (#1379)
* Add onMediaError instance method

When a track fails to load or decode, Webamp logs the error and continues as
if the track had ended, so embedders could not tell an unplayable file from
the end of a track.

ElementSource now emits an "error" event with the MediaError code, which
Media re-emits and the media middleware dispatches as MEDIA_ERROR. The new
webamp.onMediaError(cb) subscribes to it. The existing behavior of treating
the error as the end of the track is unchanged.

* Fix lint and type errors in onMediaError test

* Add changelog entry and cite version in docs
2026-10-15 09:51:40 -07:00
Jordan Eldredge 84f9156c22 Add changelog entries for merged accessibility and types fixes (#1382)
Un-comment the Unreleased section and note #1362, #1363, #1364, #1365 and
2026-10-15 09:37:16 -07:00
Jordan Eldredge 90877cf5ad Expose toggle button state with aria-pressed (#1383)
* Expose toggle state of toggle buttons with aria-pressed

Shuffle, Repeat, the EQ and PL window toggles, and the equalizer's On and Auto
buttons show their state visually via the skin's selected sprite. Mirror that
state with aria-pressed so screen readers announce it.

Originally #1367, which was merged into its stacked base branch instead of
master.

* Add changelog entry for aria-pressed
2026-10-15 09:34:49 -07:00
Jordan Eldredge 83177e4922 Pin npm to v11 in the release job (#1378)
npm install -g npm@latest now installs npm 12, which requires Node 22+, so the
release job fails on Node 20 before publishing anything. Every push to master
since npm 12's release has failed to publish webamp@next.

npm 11 supports Node 20 and includes trusted publishing (11.5.1+).
2026-10-15 09:24:38 -07:00
Jordan EldredgeandClaude Opus 5.5 4b59984006 Keep the feed's bottom fade fixed above the menu bar
Each page drew its own fade over the bottom of its readme, so it scrolled
along with the page. One fixed fade now sits above the menu bar; the expanded
readme keeps its own.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 22:47:38 -07:00
Jordan EldredgeandClaude Opus 5.5 4d225960a1 Blur NSFW skins in the new UI; return to your search from the feed
- NSFW skins are blurred in the grid and feed (and Webamp isn't loaded over
  them) until the visitor chooses to see them, for the rest of the visit.
- The feed's back arrow goes back to the grid you came from, with its search
  and scroll position. The grid's URL updates now reach Next's router, so the
  search survives in history.
- A page loaded with ?query= starts with that search's results, run on the
  server, instead of the museum's first page.
- SQLite search supports filter:nsfw / -filter:nsfw, as Algolia did.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 22:38:27 -07:00
Jordan EldredgeandClaude Opus 5.5 2cf5954b23 Tint the feed's backdrop with each skin's colors; name skins on grid hover
- The feed's spotlight takes on a faint tint of the visible skin's average
  color, morphs to the next skin's as you scroll, and fades in on arrival.
- Grid tiles show the skin's file name (always .wsz) on hover and focus.
- One-time hint pills for the feed and the grid.
- More room between the skin and its readme on desktop.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 21:00:01 -07:00
Jordan EldredgeandClaude Opus 5.5 37c883ed19 Stop a long press on Webamp opening its context menu; drop the grab outline
A long press fires contextmenu, which Webamp handles as a right-click. Block
it (from Webamp too, not just the browser) when it follows a touch on Webamp;
a mouse right-click still opens Webamp's menu. The outline shown while a
press-and-hold had grabbed Webamp read as a stray focus ring, so it's gone
(the vibration remains).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 18:46:44 -07:00
Jordan EldredgeandClaude Opus 5.5 b344d862f9 Outline the whole skin while a press-and-hold has grabbed Webamp
The outline was on Webamp's host, which stays Webamp's unscaled size while
Webamp is zoomed to fit the skin, so it showed up smaller than the player and
behind it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 18:44:39 -07:00
Jordan EldredgeandClaude Opus 5.5 ec2a088fa2 Let touches on Webamp in the feed mean what the visitor intends
webampTouch.ts holds back the start of each touch on Webamp until it can
tell what it is: a tap or horizontal drag goes to Webamp, a vertical swipe
scrolls the feed (so swiping over the equalizer no longer changes it), and a
press-and-hold grabs the control under the finger for dragging. Webamp gets
the press replayed only when it's meant for it. This replaces the earlier
special cases (the equalizer/scrollbar touch-action exceptions and the
playlist touch block).

Also: ?ui=new / ?ui=classic set a per-visitor override of MUSEUM_UI (and
?ui=default clears it), so the new UI can be tried in production before it's
the default.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 18:43:04 -07:00
Jordan EldredgeandClaude Opus 5.5 1b15f58271 Let deploy.sh's health check follow redirects
A deploy failed its health check because the running (pre-pull) script
checked a path the new build redirects. Following redirects means a page
moving can't fail the check again.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 18:34:17 -07:00
Jordan EldredgeandClaude Opus 5.5 dc65f2ca41 Serve the new museum UI at the museum's own URLs, with a switch back
middleware.ts maps the public URLs (/, /?query=, /feed, /skin/:md5[/:fileName],
/about, /feedback) onto the new UI's routes, unless MUSEUM_UI=classic. It's
read per request, so `scripts/deploy.sh --classic` / `--new` switch UIs with a
restart rather than a rebuild. Upload, review and a skin's debug/file views
stay on the classic UI. The /scroll experiment's URLs and /index.html redirect
to the public ones in either mode.

- The new UI links to and shares the public URLs, and the grid uses ?query=
  like the classic museum.
- Only the classic UI creates the legacy Redux store (it fetched museum data
  on every page), so new UI pages now render on the server. The grid no
  longer touches `window` while rendering.
- scripts/check-routes.sh checks every URL pattern against either UI.
- Enter dismisses the keyboard in the grid's search; touches on Webamp's
  playlist no longer drag tracks; swiping down closes the expanded readme.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 18:31:40 -07:00
Jordan EldredgeandClaude Opus 5.5 924c60bb99 Make /scroll event tracking ready for rollout, and feed polish
Event tracking (schema v2):
- Batch events client-side and send them to /scroll/events (sendBeacon when
  the page is hidden), each with a version, sequence number and client time.
- Only write sessions once a browser reports in, so crawlers and link
  previews no longer create sessions.
- Record dwell as total and active (visible) time, mark the view a session
  exits on, and log page hide/show and Webamp playback changes.
- Give each view its feed position, source (curated/random/link) and how it
  was reached; sessions record entry point, layout and pointer type.
- Log the first Webamp interaction per view, and grid opens, searches and
  clicks (joined to the feed session they lead to).

Feed:
- Let vertical swipes over Webamp scroll the feed on touch screens.
- Up/Down arrows move between skins instead of changing Webamp's volume.
- Back-to-grid arrow and a centered title; placeholder for skins with no
  readme.

Also point the legacy client's GraphQL requests at the same origin, so dev
uses the local server instead of production.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 18:24:26 -07:00
Jordan EldredgeandClaude Opus 5.5 14c180dbae Stop deploy builds from dirtying tsconfig.json
next build adds the dist directory's types to tsconfig.json's include when
they're missing, which left the server checkout dirty after a build into
.next-build and blocked the next deploy's pull. Include it up front, and
have deploy.sh restore the file after building in case it changes again.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 17:52:39 -07:00
Jordan EldredgeandClaude Opus 5.5 4d7cb2c2b2 Pick the feed's random skins without sorting every skin
The random half of getScrollPage sorted all ~100k classic skins with
ORDER BY RANDOM() and checked each against every session that had ever
seen it. In production that took ~1.5s, nearly all of the time to open a
skin from the grid. Sample random rowids and filter them instead (~5ms).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 17:52:10 -07:00
Jordan EldredgeandClaude Opus 5.5 54a70fc601 Replace the stale skin-database deploy script
deploy.ts expected pm2 and an Apache config the server no longer has, so it
failed immediately. deploy.sh does what the server actually needs: pull,
install, build into .next-build with a capped heap while the live sites keep
serving .next, swap it in, restart blue and green, and check both come up,
rolling back automatically if they don't. `--rollback` restores the previous
build.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 17:36:01 -07:00
Jordan EldredgeandClaude Opus 5.5 022ea2bc30 Let skin-database deploys build alongside the live build
NEXT_DIST_DIR lets a deploy build into .next-build while the running
servers keep serving .next, then swap. Also turn on Next's webpack memory
optimizations, since the production server has little memory to spare.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 17:00:13 -07:00
Jordan EldredgeandClaude Opus 5.5 3d48ac5a24 Clean up the /scroll feed after the production-readiness pass
- Share page/column/screenshot layout classes between the feed page and
  its loading state, and merge the scattered desktop media queries.
- Remove leftovers from earlier iterations: stale comments, an unused data
  attribute and class, redundant CSS and a ref mirroring state.
- Pass `focused` to pages explicitly and memoize SkinPage so a swipe only
  re-renders the pages whose focus changed.
- Read Webamp's own closed state instead of tracking it separately.
- Reuse existing helpers: screenshotUrlFromHash, the shared GraphQL client
  for feedback, and one FEED_COLUMN_RATIO constant.
- Record a page of skins in the session with one insert, and load each
  skin's details in parallel.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 16:57:13 -07:00
Jordan EldredgeandClaude Opus 5.5 05689f5cec Get the /scroll skin museum experience production ready
Feed:
- Keep a single Webamp instance alive across skins, swapping skins and
  resetting window layout/shade/open state, so music keeps playing.
- Hide a skin's screenshot once Webamp has loaded over it.
- Stop fetch retry loops, dedupe skins, and await session bookkeeping.
- Log skin views and session end accurately (sendBeacon on page hide).
- Report NSFW for human review via Discord, like the legacy site, instead
  of marking skins NSFW directly.
- Show the full readme clipped under the skin; tap to slide it up.
- Title above the skin; action icons in a row on mobile and a column
  beside the skin on desktop; compact like counts.
- Copy share links on desktop instead of opening the share sheet.
- Replace the bounce scroll hint with a one-time pill on mobile; add
  previous/next buttons and J/K/Space on desktop.
- Stop Webamp swallowing wheel events meant for the feed.
- Ship a smaller first page and a loading state for faster navigation.

Grid:
- Load pages into their own slots so out-of-order loads land correctly.
- Debounced, race-free search with the legacy SQLite fallback, keeping
  old results mounted while a search is pending.

Also: fix the feedback form endpoint, sync the legacy store with
client-side navigation (fixes the Upload button), use dvh units, and
fix the hamburger menu toggle and z-index.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 16:47:54 -07:00
Jordan EldredgeandClaude Opus 4.6 85e830768f Gate Algolia writes behind ALGOLIA_ENABLED env var
Algolia account is blocked, breaking upload processing. Since search
reads already use SQLite FTS, gate all Algolia write operations behind
an opt-in ALGOLIA_ENABLED=true flag so uploads can proceed without it.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-10-14 15:22:05 -07:00
Jordan EldredgeandClaude Opus 5 aac737a237 Cap how many museum chunks fetch at once
Prepared but not deployed, and not the cause of any outage -- this
predates the Algolia swap.

Scrolling the grid quickly queues up many chunks, and the mergeMap that
fetches them had no concurrency limit, so they all went in flight at
once and then shared the visitor's downlink.

Measured against production over a 10 minute window, the three heaviest
clients -- ordinary Chrome users scrolling, not scrapers -- accounted for
55% of all large GraphQL responses and saw medians of 5713ms, 1547ms and
690ms on 26KB chunks, an implied ~8KB/s. Over the same window the server
answered that identical query in ~50ms, and each of those clients' own
fastest request was ~50ms. Meanwhile 79 other client IPs sat at 43-75ms.
So the server was never the slow part; the requests were starving each
other for bandwidth.

Capping in-flight chunk fetches at 3 keeps prefetch useful while letting
each chunk land at close to server speed.

Worth knowing: this makes the aggregate POST /graphql p50 in the access
log (~900ms through this window) misleading as a measure of how the site
feels. It was dominated by a handful of self-congesting clients, and the
typical visitor was always getting tens of milliseconds.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Ex3RJHBceDNPsHZpaFtn9V
2026-10-12 18:19:41 -07:00
Jordan EldredgeandClaude Opus 5 f99e823e52 Make the search debounce configurable at runtime
Prepared but not deployed; the museum is comfortably within capacity as
of this commit.

The debounce is the main lever we have on how much search traffic
reaches the server, and right now changing it means rebuilding. That's
the expensive way to change a number: the build needs ~2GB of heap,
which this server can only just manage.

So read SEARCH_DEBOUNCE_MS in the root layout, which is a server
component on a force-dynamic route, and hand the value to the client
through the store provider. Being resolved per request rather than
inlined at build time is the whole point -- a NEXT_PUBLIC_ var would be
baked into the bundle and still need a rebuild. This way tuning it is:

    sudo systemctl set-environment  # or edit the unit's Environment=
    sudo systemctl restart skin-database-{blue,green}

which is seconds, with no build.

Details worth knowing:

- The epic reads the value per search (debounce + timer) rather than
  once when it's constructed, so the result doesn't depend on the store
  being created after the config is set.
- Values are clamped to 50-2000ms. Below ~50ms the debounce isn't doing
  anything and above a couple of seconds search feels broken, so a value
  outside that is more likely a typo in the service config than an
  intent.
- An unset or unparseable value falls back to the current 400ms rather
  than to zero, so a fat-fingered env var can't turn every keystroke
  into a query.

Verified against a local production build: SEARCH_DEBOUNCE_MS=1200
reaches the client as 1200, 99999 clamps to 2000, and "soon" and unset
both fall back to the default.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Ex3RJHBceDNPsHZpaFtn9V
2026-10-12 17:40:47 -07:00
Jordan EldredgeandClaude Opus 5 79117c66d7 Fall back to SQLite FTS for museum search
We're out of Algolia credits, so flip the museum's search over to the
SQLite FTS index we already maintain (the `skin_search` table and its
triggers), via the graphqlSearch fallback that was already sitting in
legacy-client/src/algolia.js unused.

That fallback wasn't usable as-is: it passed the raw user input straight
into an FTS5 MATCH expression, and FTS5 reads a lot of punctuation as
operators. On prod today, "rock's", "drum-n-bass", "C++", a bare "AND"
and an unbalanced quote all returned an "Unexpected error" rather than
results. So add ftsMatchExpression(), which tokenizes the input the way
FTS5's unicode61 tokenizer does and quotes each token as a phrase, and
run every FTS search through it.

Two other things that fall out of no longer having Algolia:

- Prefix match the last token, as Algolia did, so results still appear
  while you're typing. Searching "metall" went from 6 hits to 100+.
  Single character tokens are left unprefixed: "a"* matches a large
  share of the index and takes ~350ms to rank, versus ~10ms for a
  realistic query.
- Order by rank. The old query had no ORDER BY at all, so hits came back
  in rowid order, which is to say arbitrary.

An empty expression now returns no results instead of raising, which is
what the museum sends when a query is nothing but `filter:` tokens.

Also bump the search debounce 300ms -> 400ms, since these queries now
land on our own box rather than Algolia's, and stop showing the "Search
by Algolia" badge while Algolia isn't what's answering.

Known gap: the `filter:nsfw` / `has:likes>5` query operators are
Algolia-only and silently match nothing on this path.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Ex3RJHBceDNPsHZpaFtn9V
2026-10-12 17:23:03 -07:00
Jordan Eldredge 143ce4db06 Add accessible names to buttons without a title (#1366)
The equalizer's On/Auto toggles and the shade/close buttons of the equalizer,
playlist and Milkdrop windows had no title, so screen readers announced them
as unnamed buttons. Name them with aria-label (rather than title, which would
add tooltips for pointer users), matching the main window's existing titles.
2026-10-12 13:14:11 -07:00
Jordan Eldredge f8f7e87f51 Make WinampButton keyboard and screen reader operable (#1365)
When a WinampButton has an onClick handler, render it with role="button" and
tabIndex=0, and activate it with Enter or Space. WinampButtons used as plain
containers (no onClick) are unchanged.

ClickedDiv now also sets its "clicked" state on click, since keyboard
activation does not fire pointerdown.

The Milkdrop window binds Space to "next preset" via a native listener that
runs before React's, so ignore Space there when it targets a button.
2026-10-12 12:00:03 -07:00
Jordan Eldredge bbae589323 Give the Milkdrop window a stable id (#1364)
The main, equalizer and playlist windows have #main-window, #equalizer-window
and #playlist-window. Add #milkdrop-window for parity so embedders and tests
can target it.
2026-10-12 11:53:28 -07:00
Jordan Eldredge a286400162 Mark emitted type declarations as ESM (#1363)
The package is not "type": "module", so under moduleResolution node16/nodenext
TypeScript interpreted the emitted .d.ts files as CommonJS. Each entrypoint's
default export was then seen as a namespace, making `new Webamp()` a type error.
Writing a nested built/types/package.json with "type": "module" after tsc
fixes this without changing how the JavaScript bundles are resolved.
2026-10-12 11:50:14 -07:00
Jordan Eldredge 0c894a2feb Fix types path for webamp/butterchurn entrypoint (#1362)
The ./butterchurn export pointed its types at built/types/js/butterchurn.d.ts,
but tsc emits built/types/js/webampWithButterchurn.d.ts (named after the source
file). As a result `import Webamp from "webamp/butterchurn"` failed to find
type declarations.
2026-10-12 11:40:43 -07:00
Jordan EldredgeandClaude Opus 4.6 e6283c0eab Move dependency overrides under "pnpm" so pnpm actually applies them
The top-level "overrides" field is npm's; pnpm 9 only reads
"pnpm.overrides", so both the existing graphql pin and the new
prebuild-install 7.1.3 pin (added to fix installs on Node 22.12+) were
silently ignored. The lockfile confirms it: it still resolved
[email protected].0 and [email protected].

Moving them under "pnpm" makes the overrides take effect and dedupes
graphql to 16.8.1 and prebuild-install to 7.1.3 in the lockfile.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-10-10 14:30:36 -07:00
Jordan Eldredge 999f567614 Override prebuild-install to 7.1.3 to fix installs on Node 22.12+ (#1360) (#1361) 2026-10-10 11:22:01 -07:00
Jordan Eldredge 88ed5815d9 Fix three GraphQL skins bugs reported by API consumer
- MUSEUM sort's `count` returned the total classic-skin count (~102K)
  but pagination stopped at museum_sort_order's row count (~93K).
  Switch count() to a new getMuseumPageCount() that matches what's
  actually paginable.
- Orphaned museum_sort_order rows (skin deleted, MSO entry left) made
  specific offsets throw `path.basename(null)`. Use INNER JOIN so those
  rows are skipped.
- `filter + sort: MUSEUM` threw a plain Error, which graphql-yoga masked
  as "Unexpected error." Throw GraphQLError so the real message reaches
  the client.
2026-08-23 13:18:28 -07:00
David Mason 5f56a5369c added link to bamp to In The Wild, resolves #1353 (#1354) 2026-07-31 08:20:41 -07:00
Jordan EldredgeandClaude Opus 4.6 0882aa7a31 Remove diagnostic logging from NSFW review flow (#1352)
Reverts the temporary diagnostic logging added in 86588f56,
which introduced prettier formatting violations that broke CI.

Co-authored-by: Claude Opus 4.6 <[email protected]>
2026-07-02 19:46:10 -07:00
Jordan EldredgeandClaude Opus 4.6 317b768c02 Add HN discussion to Skin Museum press links
Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-07-02 19:38:40 -07:00
Jordan Eldredge 86588f56e6 Add diagnostic logging to NSFW review flow
NSFW reports posted via the website-side GraphQL mutation aren't
triggering reviews when moderators react in Discord, but the bot-command
path (!review) works. Adds structured logging to disambiguate the two
paths and pinpoint which step in the path-B (graphql) flow is failing:

- DiscordEventHandler: client lifecycle events (ready, error, disconnect,
  reconnecting, shardError), login failures, top-level
  messageReactionAdd listener so we can see whether the Next.js-side
  gateway receives reactions at all
- DiscordEventHandler.handle: entry log + try/catch so swallowed errors
  surface (notify is fire-and-forget)
- requestReview: entry, resolved tweetStatus, explicit "skipping
  already-reviewed" log
- postSkin: new optional source param so logs can distinguish the bot
  command from the graphql mutation; logs at entry, after send, on bot
  reaction success/failure, awaiting reactions, awaitReactions resolved
- loggingFilter wrapping the awaitReactions filter to log every observed
  reaction with pass/fail and any filter errors
2026-06-27 17:00:11 -07:00
Jordan EldredgeandClaude Opus 4.6 646ee1cb7f Add local search to docs site
Uses @easyops-cn/docusaurus-search-local to provide client-side
full-text search with no external service dependency.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-06-15 21:08:34 -07:00
Jordan EldredgeandClaude Opus 4.6 b0982a085d Update changelog for v2.3.1
Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-06-15 21:03:05 -07:00
Jordan EldredgeandClaude Opus 4.6 4a5783ae51 Fix CI: allow same version in npm version for tagged releases
Co-Authored-By: Claude Opus 4.6 <[email protected]>
v2.3.1
2026-06-15 20:50:03 -07:00
Jordan EldredgeandClaude Opus 4.6 5de9496010 Bump version to 2.3.1
Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-06-15 20:43:55 -07:00
Jordan EldredgeandClaude Opus 4.6 6c3dd2a793 Update docs, examples, and version for v2.3.0
- Bump package.json and Webamp.VERSION to 2.3.0
- Move changelog Unreleased items into 2.3.0 section
- Update "unreleased" references in docs to v2.3.0
- Switch all examples from renderWhenReady to renderInto
- Update multipleMilkdropPresets to use stable @^2 instead of @next
- Update lazy example to webamp@^2.3.0
- Update release skill with version bump and changelog steps

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-06-15 20:42:10 -07:00
Jordan EldredgeandClaude Opus 4.6 d7b0cee444 Revert version bump — CI sets version from tag
Co-Authored-By: Claude Opus 4.6 <[email protected]>
v2.3.0
2026-06-15 20:15:47 -07:00
Jordan EldredgeandClaude Opus 4.6 8585e9616d Bump webamp to v2.3.0
Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-06-15 20:10:19 -07:00
Jordan EldredgeandClaude Opus 4.6 6a45429e39 Fix scroll offset in contained mode (renderInto)
centerWindows() was unconditionally adding window.scrollX/Y, which
incorrectly offset window positions when using renderInto with a scrolled
page. Move scroll offset responsibility to callers so each can decide:
- contained (renderInto): no scroll offset needed
- non-contained (renderWhenReady): add scroll offset to viewport coords
- body fallback (centerWindowsInView): add scroll offset only for body

Also make parentDomNode required in browserWindowSizeChanged,
ensureWindowsAreOnScreen, and centerWindowsInView since the serialized
state code (the only caller without a parentDomNode) was removed.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-06-15 20:07:00 -07:00
Jordan EldredgeandClaude Opus 4.6 f973319e3e Remove serialized state infrastructure
The serialized state feature (LOAD_SERIALIZED_STATE action, SerializedStateV1
types, getSerializedState/getSerlializedState functions) was unused external
API surface. Removing it simplifies the codebase and eliminates the only
caller of ensureWindowsAreOnScreen() without a parentDomNode argument.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-06-15 20:07:00 -07:00
Jordan EldredgeandClaude Sonnet 4.6 d99b8c6b3d Remove unused RichText import from bluesky.ts to fix CI lint failure (#1347)
The RichText import from @atproto/api was unused after refactoring the
Bluesky posting logic to use manual facets instead of RichText helpers.
This caused the ESLint no-unused-vars rule to fail in CI.

Co-authored-by: Claude Sonnet 4.6 <[email protected]>
2026-06-15 19:09:44 -07:00
Jordan EldredgeandClaude Opus 4.6 a0c018ac0f Fix incorrect method name in renderInto docs example
The code example for renderInto was calling renderWhenReady instead.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-06-15 10:32:05 -07:00