Landing Page Screenshot Update Guide
The product UI screenshots shown on the documentation site's home page (landing page) are captured by driving the system Chrome with Playwright, automatically logging into a locally running Songloft instance. The script is docs/scripts/capture-screenshots.mjs, and its output lands in docs/public/screenshots/, committed alongside the repository.
When you need to update the UI screenshots (e.g., after a UI redesign), just re-run the script following this guide — no manual screenshotting required.
Prerequisites
Run a Songloft service locally, listening on the default port
58091, with the default credentialsadmin / admin:bashmake run # or start the full build any way you like (requires the bundled web frontend)The screenshots capture the Flutter Web frontend bundled in the full build. The lite build has no frontend and cannot be used for screenshots. It's recommended to have several songs and playlists with covers in your library so the screenshots look good (the script captures the home page playlists, song library, player, etc.).
Install dependencies (in the
docs/directory, only once;playwrightis already indocs/package.json). The script uses the Google Chrome already installed on your system, so no extra browser engine download is needed:bashcd docs && npm install # If your system has no Chrome, use Chromium instead: npx playwright install chromium
Running
Run this in the docs/ directory:
npm run screenshotsYou can override the default address / credentials with environment variables:
SL_URL=http://localhost:58091 SL_USER=admin SL_PASS=admin npm run screenshotsThe script prints the capture progress for each image, and the output overwrites docs/public/screenshots/. When it's done, run cd docs && npm run docs:dev locally to eyeball the landing page, and commit these PNGs only after confirming everything looks right.
Which Images Are Captured
Each page is captured in both light + dark variants; the landing page components switch automatically based on the documentation site's current theme. Naming rule: light has no suffix, dark carries a -dark suffix (e.g., home-desktop.png / home-desktop-dark.png).
File (<page>-<viewport>[-dark].png) | Description |
|---|---|
home-{desktop,mobile} | Home page |
library-{desktop,mobile} | Song library |
playlists-{desktop,mobile} | Playlist list |
playlist-detail-{desktop,mobile} | Playlist detail |
settings-{desktop,mobile} | Settings |
player-mobile | Immersive full-screen player (only mobile has the full-screen Now Playing) |
- Light / dark is switched by injecting the
flutter.theme_modestorage item, and each is captured once. - Desktop viewport
1440×900, device pixel ratio1.5(about 2160px wide); mobile viewport390×844, device pixel ratio2(about 780px wide). - The landing page components use CSS to wrap desktop images in a "browser window frame" (
BrowserFrame.vue) and mobile images in a "phone shell" (DeviceFrame.vue), and automatically load the-darkvariant based onisDark, so the script only needs to store the clean raw screenshots.
How It Works (Why It's Not a Simple Screen Capture)
The frontend is Flutter Web (canvas-rendered). There are no regular DOM form controls on the page, so Playwright's DOM selectors cannot reach the input fields and buttons. The script therefore uses:
- Coordinate + keyboard login: After Flutter's first frame renders, it clicks the username / password fields according to the login page layout and types with the keyboard, then clicks the login button. The coordinates are consolidated in the
LOGINconstant at the top of the script; if the login page is redesigned, they need corresponding fine-tuning. - Reusing the login state: After a successful login,
context.storageState()saves the login info (theflutter.access_tokenetc. inlocalStorage) and injects it into new contexts for the multiple desktop / mobile viewports, so there's no need to align login coordinates separately for each viewport. - Direct hash-route navigation: Each page is navigated directly by routes like
#/library,#/playlists/1for the screenshot (routes taken fromsongloft-player'sAppRoutes). - Light / dark sets: A copy of the login state is made,
flutter.theme_modeis changed todark, and all pages are captured again (the dark files carry the-darksuffix). - Immersive player: On mobile, it enters the playlist detail, taps the first song to start playing, then taps the bottom mini-player to expand to full screen and takes the screenshot.
Where to Change Things When Adjustments Are Needed
The tunable items are consolidated at the top of the script:
LOGIN: login page control coordinates (fine-tune when the login page is redesigned).PAGES: the pages to capture and their corresponding hash routes (add/remove pages).VIEWPORTS/SCALE: viewport sizes and device pixel ratios (resolution, sharpness).- The click coordinates in the immersive player section: fine-tune when the play page layout is redesigned.
Tip: Coordinates depend on the specific layout and are a fragile spot that requires "visual proofreading." Always manually go through the output after a run — if some image landed on a "page not found" or the login page, chances are the coordinates or routes need updating.
