- Fix ESLint errors in compare.ts: remove unused params, use toSorted(),
restructure negated conditions, move imgTag to module scope, replace
process.exit with throw
- Fix ESLint errors in analyze-deps.ts: use toSorted(), wrap callback
in arrow function, replace process.exit with throw
- Run Prettier on compare.ts, run-scenarios.ts, and navigation-bar.svelte
https://claude.ai/code/session_01XSTqDJXuR4jaLN7SGm3uES
- Fix blank base screenshots by aggressively killing the preview server
between PR and base steps (fuser -k on port 4173) and clearing the
SvelteKit build cache before rebuilding the base version
- Replace git branch push with GitHub Actions artifact upload:
- Upload full screenshots as a zipped artifact for download
- Upload an HTML report with embedded base64 images as a non-zipped
artifact (archive: false) for direct browser viewing
- Update compare.ts to generate both a text-only markdown summary
(for the PR comment) and a self-contained HTML visual comparison
- Downgrade permissions from contents:write to contents:read since
we no longer push to the repository
https://claude.ai/code/session_01XSTqDJXuR4jaLN7SGm3uES
The workspace has verifyDepsBeforeRun: install which triggers pnpm install
before every pnpm exec/run. After checking out the base web/SDK code, the
package.json files no longer match the lockfile, causing pnpm to re-install
and corrupt the workspace state. This broke both the base build and the
compare step.
- Set PNPM_VERIFY_DEPS_BEFORE_RUN=false on all steps after the base checkout
- Add a "Restore PR source" step to reset web/SDK to the PR version before
running compare, so subsequent pnpm commands work normally
https://claude.ai/code/session_01XSTqDJXuR4jaLN7SGm3uES
- Remove pnpm install from base build steps since it modifies the workspace
lockfile and can break tsx/other deps used by later steps
- Just run pnpm build using the existing node_modules from the PR install
- Ensure screenshot directories exist before compare step runs
https://claude.ai/code/session_01XSTqDJXuR4jaLN7SGm3uES
- Drop --frozen-lockfile for base builds since the PR's lockfile may not
match the base branch's package.json
- Add continue-on-error and step dependencies so the workflow continues
gracefully when base builds fail
- Add catch-all /api/** mock to return empty JSON for unmocked endpoints
- Block socket.io WebSocket connections that prevent networkidle
- Add networkidle timeout fallback in screenshot runner
https://claude.ai/code/session_01XSTqDJXuR4jaLN7SGm3uES
Base screenshots showed loading spinners or were missing entirely because:
1. Unmocked API calls (e.g. /api/people, /api/search/explore) hit the static
preview server which returns HTML instead of JSON, preventing networkidle
2. Socket.io WebSocket connections never complete handshake, blocking networkidle
Add a catch-all /api/** mock (registered first, so specific mocks take priority)
that returns empty JSON for any unmocked endpoint. Block socket.io connections.
Also add a networkidle timeout fallback in run-scenarios.ts so screenshots are
still captured even if networkidle doesn't resolve within 15s.
https://claude.ai/code/session_01XSTqDJXuR4jaLN7SGm3uES
- Increase waitForSelector timeout from 5s to 15s for slower page loads
- Add explicit wait for loading spinners to disappear before screenshot
- Push screenshot images to a temporary branch for inline display
- Read report.md from compare.ts with raw.githubusercontent.com URLs
- Accept optional image base URL in compare.ts CLI
- Upgrade contents permission to write for pushing screenshot branch
https://claude.ai/code/session_01XSTqDJXuR4jaLN7SGm3uES
- Use pulls.listFiles API instead of git diff to detect changed web files
(resolves issue where git diff returned empty due to checkout strategy)
- Replace mshick/add-pr-comment with actions/github-script for all PR
comments (fixes "Unexpected input 'github-token'" warning)
- Skip pnpm/playwright setup when no web changes detected (faster skip)
- Add diagnostic logging for changed file detection
https://claude.ai/code/session_01XSTqDJXuR4jaLN7SGm3uES
- Add tsx as an e2e devDependency (was missing, causing silent failures)
- Replace `npx tsx` with `pnpm exec tsx` throughout
- Move `pnpm install` before `playwright install` (correct ordering)
- Remove `2>/dev/null` that was hiding analyzer errors
- Add debug logging to route analysis step
- Set default working-directory for the job
https://claude.ai/code/session_01XSTqDJXuR4jaLN7SGm3uES
Moves all ${{ }} expressions out of `run:` blocks and into `env:` to
prevent potential code injection via template expansion (zizmor finding).
Also switches the initial PR comment to use github-script with env vars
instead of add-pr-comment with inline template interpolation.
https://claude.ai/code/session_01XSTqDJXuR4jaLN7SGm3uES
Adds a red background tint and [VISUAL TEST] label to the navigation bar.
This commit is intended to be reverted after testing the visual-review
workflow — it exercises the dependency analyzer by changing a shared
component used across many pages.
https://claude.ai/code/session_01XSTqDJXuR4jaLN7SGm3uES
Adds a label-triggered GitHub Actions workflow that automatically generates
before/after screenshots when web UI changes are made in a PR. Uses smart
dependency analysis to only screenshot pages affected by the changed files.
Key components:
- Reverse dependency analyzer: traces changed files through the import graph
to find which +page.svelte routes are affected
- Screenshot scenarios: Playwright tests using existing mock-network infrastructure
(no Docker/backend needed) for fast, deterministic screenshots
- Pixel comparison: generates diff images highlighting changed pixels
- GitHub Actions workflow: triggered by 'visual-review' label, posts results
as a PR comment with change percentages per page
https://claude.ai/code/session_01XSTqDJXuR4jaLN7SGm3uES
* feat: add playbackStyle to local asset entity and related database schema
* implement conversion function for playbackStyle in local sync service
* implement conversion function for playbackStyle in local sync service
* refactor: remove deducedPlaybackStyle from TrashedLocalAssetEntityData
* add playbackStyle column to trashed local asset entity
* make playbackStyle non-nullable across the mobile codebase
* Streamline playbackStyle backfill:
- only backfill local assets playbackStyle in flutter/dart code
- only update trashed local assets in db migration
* bump target database version to 23 and update migration logic for playbackStyle
* set playback_style to 0 in merged_asset.drift as its a getter in base asset
* run make pigeon
* Populate playbackStyle for trashed assets during native migration
* feat(mobile): add support for encoded image requests in local and remote image APIs
* fix(mobile): handle memory cleanup for cancelled image requests
* refactor(mobile): simplify memory management and response handling for encoded image requests
* fix(mobile): correct formatting in cancellation check for image requests
* Apply suggestion from @mertalev
Co-authored-by: Mert <101130780+mertalev@users.noreply.github.com>
* refactor(mobile): rename 'encoded' parameter to 'preferEncoded' for clarity in image request APIs
* fix(mobile): ensure proper resource cleanup for cancelled image requests
* refactor(mobile): streamline codec handling by removing unnecessary descriptor disposal in loadCodec request
---------
Co-authored-by: Mert <101130780+mertalev@users.noreply.github.com>
* feat(mobile): add playbackStyle to native sync API
Adds a `playbackStyle` field to `PlatformAsset` in the pigeon sync API so
native platforms can communicate the asset's playback style (image, video,
animated, livePhoto) to Flutter during sync.
- Add `playbackStyleValue` computed property to `PHAsset` extension (iOS)
- Populate `playbackStyle` in `toPlatformAsset()` and the full-sync path
- Update generated Dart/Kotlin/Swift files
* fix(tests): add playbackStyle to local asset test cases
* fix(tests): update playbackStyle to use integer values in local sync tests
* feat(mobile): extend playbackStyle enum to include videoLooping
* Update PHAssetExtensions.swift
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* fix(playback): simplify playbackStyleValue implementation by removing iOS version check
* feat(android): implement proper playbackStyle detection
* add PlatformAssetPlaybackStyle enum
* linting
---------
Co-authored-by: Mert <101130780+mertalev@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
The changes in #25952 inadvertently removed an optimisation which
prevents the video player from being recreated when the tree changed.
This happens surprisingly often, namely when the hero animation
finishes. The widget is particularly expensive, so recreating it 2-3 in
a short period not only feels sluggish, but also causes the video to
hitch and restart.
The solution is to bring the global key back for the native video
player. Unlike before, we are using a custom global key which compares
the values of hero tags directly. This means we don't need to maintain a
map of hero tags to global keys in the state, and also means we don't
have to pass the global key down multiple layers.
This also fixes#25981.
Asset details are prematurely hidden when a drag ends if the simulation
shows that it will close given its current velocity. It makes for a much
more responsible feeling UI. However, this behaviour conflicts with the
logic which determines whether details are showing based on the current
offset. The result is that the details are hidden, then immediately
shown again, and then hidden once it passes the min snap distance
threshold.
This can be fixed by only evaluating the position based logic when a
drag is active, and then inferring upcoming state with a simulation.
* ScrollDatePicker defaults maximumDate to DateTime.now(). When no birthday exists, the picker starts at today (Feb 2026) with max also Feb 2026 — so only Jan–Feb are available for the current year.
Fix applied: Added maximumDate: DateTime(DateTime.now().year, 12, 31) at person_edit_birthday_modal.widget.dart:93, allowing all 12 months to be selected while still preventing future-year birthdays.
* fix(mobile): initialize birthday picker to past date to prevent future birthdays
When no birthday exists, initialize to 30 years ago instead of today.
This allows all 12 months to be selectable while keeping maximumDate
as DateTime.now() to prevent future birthday selection.
Fixes issue where only current months were available due to maxDate constraint.
---------
Co-authored-by: socksprox <info@shadowfly.net>