412 Commits

Author SHA1 Message Date
Jason Rasmussen
1ba0989e15
refactor: auth manager (#27638) 2026-04-14 08:49:24 -04:00
Min Idzelis
de9ec95db1
fix(web): handle unhandled promise rejection in CancellableTask (#27553)
When a concurrent caller awaits `this.complete` inside `execute()` and
`cancel()` is called, the promise rejects with `undefined` outside of any
try/catch, causing "Uncaught (in promise) undefined" console spam during
rapid timeline scrolling.

- Wrap the `await this.complete` path in try/catch, returning 'CANCELED'
- Guard the `finally` block to only null `cancelToken` if it still belongs
  to this call, preventing a race condition with `cancel()` to `init()`

Change-Id: I65764dd664eb408433fc6e5fc2be4df56a6a6964
2026-04-07 09:22:29 -05:00
Min Idzelis
649d14822a
refactor(web): rename MonthGroup to TimelineMonth (#27447)
Rename MonthGroup class to TimelineMonth to better convey that it represents a single month within the timeline. Updates the file, class, and all references across 16 files.

Change-Id: Id50fd6d4b7d0e431571b67c0f81c0e316a6a6964
2026-04-03 13:27:12 -04:00
Min Idzelis
2166f07b1f
refactor(web): rename DayGroup to TimelineDay (#27446)
Rename DayGroup class to TimelineDay to better convey that it represents
a single day within the timeline. Updates the file, class, and all
references across 13 files.

Change-Id: I9faef9bad73cd5b11f40daaf5eb140dd6a6a6964
2026-04-01 19:30:54 -04:00
Min Idzelis
18201a26d9
feat(web): OCR overlay interactivity during zoom (#27039)
Change-Id: Id62e1a0264df2de0f3177a59b24bc5176a6a6964
2026-03-30 19:19:53 -05:00
Jason Rasmussen
17d4941089
refactor: asset select manager (#27330) 2026-03-30 15:45:57 +01:00
Jason Rasmussen
14cce0cba3
refactor: asset select manager (#27327) 2026-03-27 13:48:51 -04:00
Jason Rasmussen
9b80ffd9c6
refactor: selection mananger (#27325) 2026-03-27 12:41:52 -04:00
Phlogi
8c6adf7157
feat(server): resolve duplicates (#25316)
* feat(web): Synchronize information from deduplicated images

* Added new settings menu to the the deduplication tab.
* The toggable options in the settings are synchronization of: albums, favorites, ratings, description, visibility and location.
* When synchronizing the albums, the resolved images will be added to all albums of the duplicates.
* When synchronizing the favorite status, the resolved images will be marked as favorite, if at least one selectable image is marked as favorite.
* When synchronizing the ratings, the highest rating from the selectable images will be applied to the resolved image.
* When synchronizing the description, all descriptions from the selectable images will be merged into one description for the resolved image.
* When synchronizing the visibility, the most restrictive visibility setting from the selectable images will be applied to the resolved image.
* When synchronizing the location, if exactly one unique location exists among the selectable images, this location will be applied to the resolved image.
* There is no additional UI for these settings to keep the visual clutter minimal. The settings are applied automatically based on the user's preferences.

* Replace addAssetToAlbums with copyAsset

* fix linter

* feat(web): add duplicate sync fields and fix typo

* feat(web): add tag sync and enhance duplicate resolution

This update introduces tag synchronization for duplicate resolution,
ensuring all unique tag IDs from duplicates are applied to kept assets.
The visibility sync logic is updated to use a simplified ordering, as the hidden status items will never show up in a duplicate set.
Album synchronization now merges albums directly via addAssetsToAlbums; as the approach with copyAsset API endpoint was ineffiecient.
Description, rating, and location sync logic is improved for correctness.
and deduplication. i18n strings were added / updated.

* feat(server): move duplicate resolution to backend with sync and stacking

Moves duplicate metadata synchronization from frontend to backend, enabling robust
batch operations and proper validation. This is an improved refactor of PR #13851.

New endpoints:
- POST /duplicates/resolve - batch resolve with configurable metadata sync
- POST /duplicates/stack - create stacks from duplicate groups
- GET /duplicates - now includes suggestedKeepAssetIds based on file size and EXIF

Key changes:
- Move sync logic (albums, tags, favorites, ratings, descriptions, location, visibility) to server
- Add server-side metadata merge policies with proper conflict resolution
- Replace client-side resolution logic with new backend endpoints
- Add comprehensive E2E tests (70+ test cases) and unit tests
- Update OpenAPI specs and TypeScript SDK

No breaking changes - only additions to existing API.

* feat(preferences): enable all duplicate sync settings by default

* chore: clean up

* chore: clean up

* refactor: rename & clean up

* fix: preference upgrade

* chore: linting

* refactor(e2e): use updateAssets API for setAssetDuplicateId

* fix: visibility sync logic in duplicate resolution

* fix(duplicate): write description to exifUpdate

Previously the duplicate resolution populated assetUpdate.description even
though description belongs to exif info.

* fix(duplicate): remove redundant updateLockedColumns wrapper

updateAllExif already computes lockedProperties via distinctLocked
using Object.keys(options). The wrapper added a lockedProperties key
to the options object, causing the spurious string 'lockedProperties'
to be stored in the lockedProperties array.

* fix(duplicate): write merged tags to asset_exif to survive metadata re-extraction

During duplicate resolution, replaceAssetTags correctly wrote merged tag
IDs to the tag_asset table, but never updated asset_exif.tags or locked
the tags property. The subsequent SidecarWrite → AssetExtractMetadata
chain calls applyTagList, which destructively replaces tag_asset rows
with whatever is in asset_exif.tags — still the original per-asset tags,
not the merged set.

Write merged tag values to asset_exif.tags via updateAllExif (which also
locks the property via distinctLocked), and queue SidecarWrite when tags
change so they persist to the sidecar file.

* docs(duplicates): clarify location and tag sync behavior

* refactor(duplicate): remove sync settings, always sync all metadata on resolve

Remove DuplicateSyncSettingsDto and the per-field sync toggles
(albums, favorites, rating, description, visibility, location, tags).
Duplicate resolution now unconditionally syncs all metadata from
trashed assets to kept assets.

- Remove DuplicateSyncSettingsDto and settings field from DuplicateResolveDto
- Update DuplicateService to always run all sync logic without conditionals
- Delete DuplicateSettingsModal.svelte and settings gear button from UI
- Remove DuplicateSettings type and duplicateSettings persisted store
- Update unit and e2e tests to remove settings from resolve requests

* docs: update duplicates utility to reflect automatic metadata sync

* docs(web): replace duplicates info modal with link to documentation

* chore: clean up

* fix: add missing type cast to jsonAgg in duplicate repository getAll

* fix: skip persisting rating=0 in duplicate merge to avoid unnecessary sidecar write

---------

Co-authored-by: Toni <51962051+EinToni@users.noreply.github.com>
Co-authored-by: Jason Rasmussen <jason@rasm.me>
Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>
2026-03-26 18:33:55 +00:00
Daniel Dietzler
389356149a
refactor: actionable toasts (#27203) 2026-03-26 18:18:06 +01:00
Jason Rasmussen
813d684aaa
fix: shared link add to album (#27063) 2026-03-20 13:14:07 -05:00
Jason Rasmussen
29000461c2
fix: ignore errors deleting untitled album (#27020) 2026-03-19 12:55:43 -04:00
Jason Rasmussen
48e6e17829
feat: primary notifications (#26988) 2026-03-17 17:54:11 -04:00
Min Idzelis
48fe111daa
feat(web): improve OCR overlay text fitting, reactivity, and accessibility (#26678)
- Precise font sizing using canvas measureText instead of character-count heuristic
- Fix overlay repositioning on viewport resize by computing metrics from reactive state instead of DOM reads
- Fix animation delay on resize by using transition-colors instead of transition-all
- Add keyboard accessibility: OCR boxes are focusable via Tab with reading-order sort
- Show text on focus (same styling as hover) with proper ARIA attributes
2026-03-13 22:04:55 -05:00
Min Idzelis
8764a1894b
feat: adaptive progressive image loading for photo viewer (#26636)
* feat(web): adaptive progressive image loading for photo viewer

Replace ImageManager with a new AdaptiveImageLoader that progressively
loads images through quality tiers (thumbnail → preview → original).

New components and utilities:
- AdaptiveImage: layered image renderer with thumbhash, thumbnail,
  preview, and original layers with visibility managed by load state
- AdaptiveImageLoader: state machine driving the quality progression
  with per-quality callbacks and error handling
- ImageLayer/Image: low-level image elements with load/error lifecycle
- PreloadManager: preloads adjacent assets for instant navigation
- AlphaBackground/DelayedLoadingSpinner: loading state UI

Zoom is handled via a derived CSS transform applied to the content
wrapper in AdaptiveImage, with the zoom library (zoomTarget: null)
only tracking state without manipulating the DOM directly.

Also adds scaleToCover to container-utils and getAssetUrls to utils.

* fix: don't partially render images in firefox

* add passive loading indicator to asset-viewer

---------

Co-authored-by: Alex <alex.tran1502@gmail.com>
2026-03-11 09:48:46 -05:00
Michel Heusschen
45eff1c663
fix(web): prevent unrelated assets from appearing in tag view (#26816) 2026-03-10 17:43:30 +01:00
Michel Heusschen
a47b232235
fix(web): refresh recent albums sidebar after album changes (#26757) 2026-03-09 17:11:32 +01:00
Nicolò Maria Semprini
5532f669eb
feat: improve HEIC, HEIF and JPEG XL browser support detection (#26122)
feat: improve heic, heif and jxl browser support detection
2026-03-03 22:41:51 -05:00
Min Idzelis
4da3d68a67
refactor: use keyed each for face bounding boxes (#26648) 2026-03-02 22:16:13 -06:00
Min Idzelis
20c639e52a
refactor: extract shared ContentMetrics for overlay position calculations (#26310) 2026-03-02 21:49:56 -06:00
Mees Frensel
11e5c42bc9
fix(web): toast warning when trying to upload unsupported file type (#26492) 2026-02-24 15:58:40 -05:00
Jason Rasmussen
1d11106dd0
refactor: add to album (#26366) 2026-02-19 15:27:30 -05:00
Jonathan Jogenfors
b3b9834c00
feat(web): loop chromecast video (#24410) 2026-02-18 20:29:13 -05:00
Mees Frensel
84f7fb63ee
feat(web): show ocr text boxes in panoramas (#25727) 2026-02-18 20:04:18 -05:00
Jorge Montejo
1f8359ead4
fix: Download the edited version when downloading multiple photos (#26259)
* fix: download the edited version when downloading multiple photos

* test: update tests

* chore: clean up

---------

Co-authored-by: Jason Rasmussen <jason@rasm.me>
2026-02-18 21:47:45 +00:00
Michel Heusschen
ff7dca35f5
perf(web): speed up asset selection (#26216) 2026-02-14 15:31:04 -05:00
Jason Rasmussen
5bf4e9595c
refactor: purchase store (#25734) 2026-02-12 13:32:17 -05:00
Jason Rasmussen
72cef8b94b
feat: shared link login (#25678) 2026-02-12 12:08:38 -05:00
Min Idzelis
81f592ca52
feat: remove Cache API, rework preload(), cancel() and fetch() (#25289)
* feat: remove Cache API, rework preload(), cancel() and fetch()

perf - replace broadcast channel with direct postMessage

* remove sw response handling

* review comments
2026-02-12 11:25:20 -05:00
Michel Heusschen
f207f99e86
fix(web): prevent event manager from throwing error (#26156) 2026-02-12 11:09:15 -05:00
Nicolò Maria Semprini
1ddb8f0667
feat: jxl browser support detection (#25599) 2026-02-10 12:49:09 -05:00
Michel Heusschen
a9e0fa43fa
fix: correctly cancel select all assets (#26067) 2026-02-10 11:47:23 -05:00
Jason Rasmussen
e6e56d75e2
fix(web): refresh text (#26071) 2026-02-10 16:42:03 +00:00
Mert
7fa6f617f5
fix(server): thumbnail queueing (#26077)
* fix thumbnail queueing

* add bmp

* other isEdited column
2026-02-10 09:04:03 -06:00
Jason Rasmussen
10b53b525d
refactor: event manager (#25565) 2026-01-29 08:52:18 -05:00
Daniel Dietzler
e50579eefc
fix: album card ranges (#25639) 2026-01-28 14:38:09 -05:00
Brandon Wees
f467a5e2c8
fix(web): edit order handling (#25496)
* fix(web): edit order handling

* chore: tests

* simplify normalization function

* chore: refactor

---------

Co-authored-by: Daniel Dietzler <mail@ddietzler.dev>
Co-authored-by: Alex <alex.tran1502@gmail.com>
2026-01-27 10:55:10 -06:00
Min Idzelis
646bb372ab
feat: add onMany to BaseEventManager (#25492)
Use a map of events instead of array of tuples for better ergonomics.
2026-01-26 10:34:26 -05:00
Jason Rasmussen
4fedae4150
refactor: event manager (#25481)
* refactor: event manager

* fix: broken downloadFile endpoint
2026-01-23 18:02:23 -05:00
Jason Rasmussen
b52e8cd570
refactor: asset navbar (#25480) 2026-01-23 16:19:46 -05:00
Min Idzelis
a96a08939e
refactor: rename preloadManager to imageManager (#25436)
rename: preloadManager to imageManager
2026-01-22 21:11:57 -06:00
Min Idzelis
280f906e4b
feat: handle-error minor improvments (#25288)
* feat: handle-error minor improvments

* review comments

* Update web/src/lib/utils/handle-error.ts

Co-authored-by: Jason Rasmussen <jason@rasm.me>

---------

Co-authored-by: Alex <alex.tran1502@gmail.com>
Co-authored-by: Jason Rasmussen <jason@rasm.me>
2026-01-21 16:46:08 +00:00
Paul Makles
61a9d5cbc7
feat: restore database backups (#23978)
* feat: ProcessRepository#createSpawnDuplexStream

* test: write tests for ProcessRepository#createSpawnDuplexStream

* feat: StorageRepository#createGzip,createGunzip,createPlainReadStream

* feat: backups util (args, create, restore, progress)

* feat: wait on maintenance operation lock on boot

* chore: use backup util from backup.service.ts
test: update backup.service.ts tests with new util

* feat: list/delete backups (maintenance services)

* chore: open api
fix: missing action in cli.service.ts

* chore: add missing repositories to MaintenanceModule

* refactor: move logSecret into module init

* feat: initialise StorageCore in maintenance mode

* feat: authenticate websocket requests in maintenance mode

* test: add mock for new storage fns

* feat: add MaintenanceEphemeralStateRepository
refactor: cache the secret in memory

* test: update service worker tests

* feat: add external maintenance mode status

* feat: synchronised status, restore db action

* test: backup restore service tests

* refactor: DRY end maintenance

* feat: list and delete backup routes

* feat: start action on boot

* fix: should set status on restore end

* refactor: add maintenanceStore to hold writables

* feat: sync status to web app

* feat: web impl.

* test: various utils for testings

* test: web e2e tests

* test: e2e maintenance spec

* test: update cli spec

* chore: e2e lint

* chore: lint fixes

* chore: lint fixes

* feat: start restore flow route

* test: update e2e tests

* chore: remove neon lights on maintenance action pages

* fix: use 'startRestoreFlow' on onboarding page

* chore: ignore any library folder in `docker/`

* fix: load status on boot

* feat: upload backups

* refactor: permit any .sql(.gz) to be listed/restored

* feat: download backups from list

* fix: permit uploading just .sql files

* feat: restore just .sql files

* fix: don't show backups list if logged out

* feat: system integrity check in restore flow

* test: not providing failed backups in API anymore

* test: util should also not try to use failedBackups

* fix: actually assign inputStream

* test: correct test backup prep.

* fix: ensure task is defined to show error

* test: fix docker cp command

* test: update e2e web spec to select next button

* test: update e2e api tests

* test: refactor timeouts

* chore: remove `showDelete` from maint. settings

* chore: lint

* chore: lint

* fix: make sure backups are correctly sorted for clean up

* test: update service spec

* test: adjust e2e timeout

* test: increase web timeouts for ci

* chore: move gitignore changes

* chore: additional filename validation

* refactor: better typings for integrity API

* feat: higher accuracy progress tracking

* chore: delay lock retry

* refactor: remove old maintenance settings

* refactor: clean up tailwind classes

* refactor: use while loop rather than recursive calls

* test: update service specs

* chore: check canParse too

* chore: lint

* fix: logic error causing infinite loop

* refactor: use <ProgressBar /> from ui library

* fix: create or overwrite file

* chore: i18n pass, update progress bar

* fix: wrong translation string

* chore: update colour variables

* test: update web test for new maint. page

* chore: format, fix key

* test: update tests to be more linter complaint & use new routines

* chore: update onClick -> onAction, title -> breadcrumbs

* fix: use wrench icon in admin settings sidebar

* chore: add translation strings to accordion

* chore: lint

* refactor: move maintenance worker init into service

* refactor: `maintenanceStatus` -> `getMaintenanceStatus`
refactor: `integrityCheck` -> `detectPriorInstall`
chore: add `v2.4.0` version
refactor: `/backups/list` -> `/backups`
refactor: use sendFile in download route
refactor: use separate backups permissions
chore: correct descriptions
refactor: permit handler that doesn't return promise for sendfile

* refactor: move status impl into service
refactor: add active flag to maintenance status

* refactor: split into database backup controller

* test: split api e2e tests and passing

* fix: move end button into authed default maint page

* fix: also show in restore flow

* fix: import getMaintenanceStatus

* test: split web e2e tests

* refactor: ensure detect install is consistently named

* chore: ensure admin for detect install while out of maint.

* refactor: remove state repository

* test: update maint. worker service spec

* test: split backup service spec

* refactor: rename db backup routes

* refactor: instead of param, allow bulk backup deletion

* test: update sdk use in e2e test

* test: correct deleteBackup call

* fix: correct type for serverinstall response dto

* chore: validate filename for deletion

* test: wip

* test: backups no longer take path param

* refactor: scope util to database-backups instead of backups

* fix: update worker controller with new route

* chore: use new admin page actions

* chore: remove stray comment

* test: rename outdated test

* refactor: getter pattern for maintenance secret

* refactor: `createSpawnDuplexStream` -> `spawnDuplexStream`

* refactor: prefer `Object.assign`

* refactor: remove useless try {} block

* refactor: prefer `type Props`
refactor: prefer arrow function

* refactor: use luxon API for minutesAgo

* chore: remove change to gitignore

* refactor: prefer `type Props`

* refactor: remove async from onMount

* refactor: use luxon toRelative for relative time

* refactor: duplicate logic check

* chore: open api

* refactor: begin moving code into web//services

* refactor: don't use template string with $t

* test: use dialog role to match prompt

* refactor: split actions into flow/restore

* test: fix action value

* refactor: move more service calls into web//services

* chore: should void fn return

* chore: bump 2.4.0 to 2.5.0 in controller

* chore: bump 2.4.0 to 2.5.0 in controller

* refactor: use events for web//services

* chore: open api

* chore: open api

* refactor: don't await returned promise

* refactor: remove redundant check

* refactor: add `type: command` to actions

* refactor: split backup entries into own component

* refactor: split restore flow into separate components

* refactor(web): split BackupDelete event

* chore: stylings

* chore: stylings

* fix: don't log query failure on first boot

* feat: support pg_dumpall backups

* feat: display information about each backup

* chore: i18n

* feat: rollback to restore point on migrations failure

* feat: health check after restore

* chore: format

* refactor: split health check into separate function

* refactor: split health into repository
test: write tests covering rollbacks

* fix: omit 'health' requirement from createDbBackup

* test(e2e): rollback test

* fix: wrap text in backup entry

* fix: don't shrink context menu button

* fix: correct CREATE DB syntax for postgres

* test: rename backups generated by test

* feat: add filesize to backup response dto

* feat: restore list

* feat: ui work

* fix: e2e test

* fix: e2e test

* pr feedback

* pr feedback

---------

Co-authored-by: Alex <alex.tran1502@gmail.com>
Co-authored-by: Jason Rasmussen <jason@rasm.me>
2026-01-20 09:22:28 -06:00
Jason Rasmussen
4a7c4b6d15
refactor(web): routes (#25365) 2026-01-19 12:07:31 -05:00
Jason Rasmussen
8196bd9bbd
refactor(web): routes (#25313) 2026-01-16 16:11:09 -05:00
Min Idzelis
80a5444bf4
feat: redesign asset-viewer previous/next and hide when nav not possible (#24903) 2026-01-15 12:55:01 +01:00
Jason Rasmussen
38f01a6b7d
fix(web): redirect to login (#25254) 2026-01-13 23:11:14 -05:00
juliancarrivick
6e86697996
fix(web): Handle upload failures from public users (#24826) 2026-01-13 15:15:54 +00:00
Brandon Wees
e8c80d88a5
feat: image editing (#24155) 2026-01-09 17:59:52 -05:00
Jason Rasmussen
fbd49e0b79
refactor: memory lane (#25134) 2026-01-08 12:40:17 -05:00