50 KiB
50 KiB
Changelog
[1.0.3.2] - 2026-08-17
🚀 Features
- Add personOwnerId album filter and opt-in asset IncludeCount
⚡ Performance
- Page cosplayer albums via the album endpoint and skip count queries
- Cache thumbnails and previews in the browser for 24h
- Add filtered IX_Assets_VisibleCreatedAt index for the default listing
- Server-side seeded shuffle, DTO projection, and opt-in count
- Project album assets to DTO in SQL and slim the Find paths
- Push album filter/search/sort/pagination to SQL in person FindVisible
🐛 Bug Fixes
- Add auto-save middleware as a safety net for the Save() pattern
- Desloppify review fixes: PathFromGuid extraction, RegexHighlighter dedup, port default fix
🚜 Refactor
- Quality improvements from the desloppify scan
⚙️ Miscellaneous Tasks
- Update desloppify scorecard and add it to the README
[1.0.3.1] - 2026-07-22
🚀 Features
- Add sliding-window page buffer to AlbumGrid and CosplayerGrid with scroll-based observer reattach
- Embed git version in WASM footer via AssemblyInformationalVersion
🐛 Bug Fixes
- Use status CSS variables for progress bar segment colors
- Eliminate masonry layout race on infinite scroll and banner double-fetch (#146)
- Masonry items stacking on infinite scroll — missing @ on ViewMode binding (#146)
- Prevent infinite scroll cascade by debouncing observer reattach and disabling scroll anchoring
🚜 Refactor
- Replace IntersectionObserver with input-event-based infinite scroll
⚙️ Miscellaneous Tasks
- Scripts and run configs to automate version injection
- Removed outdated agent.md
[1.0.3] - 2026-07-21
🐛 Bug Fixes
- Cropper handle size constant mismatch (14→18 px)
- Jobs past tab zero-based pagination
- Show registration disabled message on 403
- Hide register form when registration is disabled
- Allow manual folder scan when scheduled scan is off
- Prevent masonry layout shift on infinite scroll
- Order album assets by CreatedAt in album detail (#144)
- Replace overflow-y with JS masonry for stable album layout (#146)
- Add aspect-ratio to album cards for stable masonry height (#146)
- Use per-card aspect-ratio from cover dimensions for stable masonry layout (#146)
- Restore masonryLayout.dispose that was lost in reset edit
- Use pre-pagination album count (#143)
- Sort album assets by OriginalFilename instead of CreatedAt (#144)
[1.0.2] - 2026-07-17
🐛 Bug Fixes
- Remove redundant AddJsonFile calls that overrode env vars
⚙️ Miscellaneous Tasks
- Auto-run on tags only, manual dispatch on branches
[1.0.1] - 2026-07-17
🐛 Bug Fixes
- Use explicit domain git.r3d.codes instead of gitea.server_url
- Sanitize tag names for Docker compatibility (replace / with -)
- Lowercase repository name for Docker compatibility
- Match matrix service names to actual directory case (Lactose/MilkStream)
- Remove gha cache config (cache server unreachable from builder)
⚡ Performance
- Add BuildKit cache mounts for NuGet packages in Dockerfiles
⚙️ Miscellaneous Tasks
- Add workflow_dispatch trigger with version input
- Also run on push to master
- Also run on develop and hotfix/* branches
- Pass SixLaborsLicenseKey as build arg for lactose
- Use REGISTRY_TOKEN (PAT) instead of GITEA_TOKEN for registry push
CI
- Added sixlabors license key to the docker file for lactose
[1.0] - 2026-07-16
🚀 Features
- (API) Adds Authentication System (Needs Testing)
- (API) Adds UserRepository.cs
- (API) Adds Password hashing
- (API) Adds Password hashing
- (API) Implements user delete endpoint
- (API) Adds stubs for AssetController.cs
- (API) Adds Album endpoint Stubs
- (API) Adds TagController class
- (API) Adds Tag endpoint implementation
- (API) Adds Repository implementation (not tested yet)
- (API) Implements TagController With Bulk Requests
- (API) Adds person stub endpoint
- (API) Added Resolution, mimetype and extra media data on asset table
- (API) Adds computed file hash field in Asset
- (API) Adds Folder and Asset entities in the DB
- (API) Implemented FolderController and their respective Mappers/DTO/Repository interface and it's own implementation
- [breaking] Upgraded project to .Net 9.0
- (API) Implemented Settings Endpoint
- (API) Implemented Media endpoint and relative Repository and DTO
- (DB) Switched to PostgreSQL connector
- (DB) [breaking] Change to use database pgvecto-rs
- (DB) [breaking] Regenerated migrations for PostgreSQL switch
- Switched to on the fly connection string generation
- (DbContext) Adds Initialization for Development environment
- (API) Adds Authentication Result as DTO for authentication
- (Lactose) Adds RefreshToken
- (Lactose) Adds DB Context Snapshot
- (Lactose) Adds RefreshToken Endpoint
- (DB) Changed Asset table to have OriginalPath as Unique Index
- Added a settings enum + it's own extension class to retrieve interpretation type and string name in DB
- (db) Adds Migration for Settings in LactoseDbContext
- Added disposability to some repositories, changed the FileSystemScanner service to be a singleton.
- (API) Added Access level to the data returned from authentication actions
- (frontend) Upgraded login to new API changes
- (API) Added get-all endpoint for settings controller.
- (frontend) Added settings service
- (API) Added Access level to the data returned from authentication actions
- (frontend) Added login and JWT token auto-refresh
- (frontend) Adds ServiceBase and AuthServiceBase abstract classes
- (frontend) Login and logout now properly work
- (frontend) Added folders service
- (frontend) Nav menu restyle
- (API) Adds method to modify the FileWatcherService: folders, time
- (API) Edits SettingsRepository to manage FileWatcher Service
- (frontend) Editing of settings and addition/editing of folder paths
- (frontend) Added folder removal in settings
- (API) Added get-all endpoint for settings controller.
- (frontend) Added settings service
- (API) Added Access level to the data returned from authentication actions
- (frontend) Added login and JWT token auto-refresh
- (frontend) Adds ServiceBase and AuthServiceBase abstract classes
- (frontend) Login and logout now properly work
- (frontend) Added folders service
- (frontend) Nav menu restyle
- (API) Adds method to modify the FileWatcherService: folders, time
- (API) Edits SettingsRepository to manage FileWatcher Service
- (frontend) Editing of settings and addition/editing of folder paths
- (frontend) Added folder removal in settings
- Migrate MilkStream frontend from Blazor SSR to Blazor WASM
- (cors) Support semicolon-separated CorsAllowedOrigins env var
- Add ParentJobId to JobStatusDto for job hierarchy tracking
- Add ParentJobId to Job base class
- Set ParentJobId on child jobs for ThumbnailJob, FileSystemCrawlJob, PHashJob
- Map ParentJobId in JobMapper
- Add generic reusable components ProgressBar, CollapsibleSection, PollRateSelector
- Add JobRow, JobChildrenSummary, JobSection, JobTree components
- Redesign Jobs page with compact hierarchical layout, poll rate selector, and PHash button; remove JobCard
- Optimize jobs API with pagination, DB persistence, and cleanup
- Add GetAssetsMissingMetadata repository method for metadata job
- Implement MetadataJob for image resolution extraction
- (docker) Add named volumes for thumbnails and previews
- Unify settings pages into sidebar + detail panel with auto-save
- (assets) Add ThumbnailSize column to track generated thumbnail dimensions
- (repository) Add GetAssetsMissingOrWrongThumbnail to interface
- (repository) Implement GetAssetsMissingOrWrongThumbnail query
- (thumbs) Read ThumbnailSize setting, invalidate wrong-sized thumbnails, set size after gen
- (db) Add migration for ThumbnailSize column on Assets
- (jobs) Add CompletedWithErrors status for parents with failed sub-jobs
- (ui) Show CompletedWithErrors as orange and group sub-jobs by status
- (jobs) Apply batching to PHashJob and MetadataJob
- (settings) Add JobBatchSize setting for batch job sizing
- (settings) Add JobBatchSize setting for batch job sizing
- (settings) Add runtime-configurable Po2W slider limits + split thumbnail/preview ranges
- (folders) Add regex pattern field with named groups, syntax highlighting, and sample path preview
- Masonry gallery on homepage with infinite scroll
- Pass JWT token in media URLs for authenticated thumbnail/preview requests
- Make navbar sticky during infinite scroll
- Add PreviewJob and upgrade thumbnails to WebP
- Add CreatePersons and CreateAlbums jobs
- Add stacked progress bar for parent jobs with sub-job breakdown
- Improve logging and progress reporting for album/person jobs
- Add statistics page with aggregate asset/user/system stats via fast DB queries
- Add thousands separators to job status numbers
- Extend AssetPreviewDto with album and cosplayer names
- Add cosplayer and album name overlays on home page
- Add full-size view and download buttons in preview overlay
- Add bidirectional infinite scroll (up and down)
- Implement seed-based deterministic random ordering for home page
- Add data completeness indicators to statistics page
- Add CoverAssetId to albums with resolution data in full DTO
- Add AlbumService and PersonService to WASM client
- Add albums list page and album detail page with inline preview
- Auto-assign album cover from first alphabetically-sorted asset
- Make album names clickable links in home tile hover and preview
- Justified gallery layout for album detail page
- View mode selector for albums page (masonry/grid/list)
- (admin) Add user management page for admins
- (admin) Add inline editing for username, email, and password
- (stats) Add percentage and stale counters to completeness cards
- (backend) Add ProfileAssetId to Person model + migration
- (dtos) Update Person DTOs with ProfileAssetId and album previews
- (backend) Eager-load ProfileAsset and Albums in PersonRepository
- (backend) Add PersonMapper with ToPersonPreviewDto and ToPersonDetailedDto
- (backend) Implement person detail, update, and delete endpoints
- (client) Extract AlbumCard reusable component, update Albums.razor
- (client) Add PersonService client methods and PersonForm component
- (client) Add Cosplayers and CosplayerDetail pages, fix NavMenu, add modularity docs
- (css) Add Cosplayers and CosplayerDetail scoped CSS with banner, grid, and card styles
- (backend) Add ProfileCropX/Y to Person model for profile picture cropping
- (ui) Add circular profile picture cropper with drag-to-pan
- (ui) Redesigned profile cropper with movable circle, dimmed exterior, square boundary, zoom controls; added ProfileCropZoom to model; switched avatars to background-image for zoom support
- (ui) Add New Cosplayer button on list page with create endpoint and form
- (ui) Unassigned album selector in PersonForm, Add Albums button in CosplayerDetail
- (dto) Add RemovePerson flag to AlbumUpdateDto for explicit person unlinking
- Person hard delete with album cascade, AlbumCard select mode, cosplayer list multi-select
- Infinite scroll for albums on cosplayer detail page
- Migrate to UTC timestamps — remove legacy Npgsql timestamp behavior
- Make album name in image previewer an anchor element for native navigation
- Allow users to update own albums and curators to update any album
- Add OldPassword field to UserUpdateDto for self-service password change
- Enforce old-password verification on own password change, allow admin override
- Add password change UI with strength bar and validation on user profile page
- Add server-side password policy validation (admin bypass)
- (ui) Redesign user profile page with avatar initials and edit profile modal
- (ui) Rewrite admin users page with sortable table, search, and modal-based editing
- (ui) Show avatar initial in navbar instead of generic icon
- (ui) Album detail multi-select edit mode, cosplayer link, move edit button (#63)
- (api) Add BulkDeleteAlbumsAsync for bulk album deletion
- (ui) Add album select/edit mode with bulk deletion
- (ui) Redesign jobs page with tabs, search, filters, and mobile cards
- Add LastChange tracking to JobStatus
- Add ModifiedAt to JobRecord with delta query support
- Implement delta-based children endpoint with ?since parameter
- Add delta merge logic to job tree components
- Poll children for all active roots and fix segment progress bar colors
- Return-url login redirects + keepalive + cosplayers fix
- Add password strength validation to registration page
- Hide cosplayers with no visible content from regular users
- (css) SCSS foundation with Bootstrap 5.3.7 source, new dark navy theme, Inter font
- (ui) CSS variables for .razor.css, job type colors, interactive states, gradients
- (jobs) Extract all job-type colors to configurable SCSS variables
- (ui) CosplayerDetail banner empty gradient uses accent theme
- (ui) Aggressive gradients on navbar, buttons, modal headers
- (ui) Page background gradient, status/strength CSS vars, nav/image polish
- (ui) Replace default favicon with MilkyShot logo SVG
- (cropper) Circle handles with accent color, black border, drop shadow
- (gradients) Strengthen all gradients for more aggressive effect
- (ui) Apply metallic gradient to all buttons and surfaces
- (ui) Add eye toggle button to show/hide password on all password fields
- (api) Add sort parameters to pagination DTOs
- (api) Add sorting and unassigned filter to AlbumRepository.SearchQuery
- (api) Pass sort and filter params through AlbumController.Search
- (ui) Add SortFilterBar component and update Albums page with search/sort
- (ui) Extract AlbumGrid to isolate grid re-renders from SortFilterBar
- (api) Add SearchQuery to PersonRepository with sort/search/pagination
- (api) Paginate PersonController.GetAll with sort/search params
- (ui) Convert Cosplayers page to infinite scroll with sort/search toolbar
- (ui) Extract CosplayerGrid to isolate grid re-renders from SortFilterBar
- Add SearchDropdown typeahead component
- Wire SearchDropdown into NavMenu
- Make search dropdown headers selectable with keyboard and mouse
- Parse ?search= query param on Cosplayers and Albums pages
- (cosplayer-detail) Add SortFilterBar for album search and sort
- (butter) Add EVisibility enum, Maintainer access level, and SystemUploaderId setting
- (butter) Update DTOs for visibility, renamed Owner→UploadedBy, removed IsPublic/Owner fields
- (models) Update models for auth redesign — rename Owner→UploadedBy, drop UserOwnerId/IsPubliclyShared, add EVisibility, PersonMaintainer
- (infra) Update DbContext and mappers for auth redesign — new relationships, PersonMaintainer, conditional Visibility
- (repos,controllers) Update repositories, interfaces, controllers for auth redesign — visibility filtering, UploadedBy/Uploader rename, Maintainer support
- (db) Add migration for auth redesign — drop IsPubliclyShared/UserOwnerId/SharedWith, add Visibility, PersonMaintainer, rename OwnerId→UploadedBy
- (auth) Complete Maintainer role — add PersonMaintainer checks, update controller authz, pass uploadedBy search param
- Add MaintainedPersonIds to UserInfoDto with backend plumbing
- Add Maintainer to admin user dropdowns and role badges
- Add Visibility dropdown to album and person create/edit forms
- Add Protected Assets card to stats page
- Expand UI gating to include Maintainer with scope-aware CanEdit
- Maintainer cosplayer assignment in admin user edit modal
- Maintainer assignment UI for cosplayers, maintainer display on user page
- Expand user page edit modal for admins with access level, maintainer assignment, and danger zone
- Make usernames clickable on admin users page, linking to /User/{id}
- Add docker-compose.debug.yml and UseWebAssemblyDebugging for WASM debugging
- Add role-visible colored borders on albums and assets based on item state (#78)
- Add bulk delete and visibility change to AlbumDetail select mode
- Show visibility/deleted breakdown in album header in select mode
- Add Select All / Deselect All button in album select mode
- Add album-level bulk actions to CosplayerDetail page
- Cascade visibility to albums and assets with lower-only rule
- Add multiselect editing controls to Cosplayers page
- Wire up SystemUploaderId setting — seed, UI, and crawl job backfill
- Bulk cosplayer assignment for albums from multiselect mode
- Add reusable ConfirmDialog component
- Add visibility-colored borders to cosplayer cards in selection mode
- Add rate limiting with configurable buckets
- Apply rate-limit buckets to controllers
- Add AlbumMergeDto and PersonMergeDto
- Add MergeAlbums to album repository
- Add MergePeople to person repository
- Add merge endpoints to AlbumController and PersonController
- Add frontend service methods for merge, bulk update, and bulk delete people
- Add MergeModal shared component for merge destination selection
- Add Merge button to Albums.razor with AlbumGrid preview access
- Add Merge button to Cosplayers.razor with CosplayerGrid preview access
- Add Merge Albums button to CosplayerDetail.razor
- Add Unlink cosplayer button to AlbumDetail.razor
- Add UnlinkedAssetGroupDto and extend AssetSearchOptionsDto with unlinked/folder/uploader filters
- Add trigram GIN index on Asset.OriginalFilename for efficient ILike search
- Extend AssetRepository with unassigned, folderId, uploadedBy, search filters and GetUnlinkedGroups
- Extend AssetController with unlinked-groups endpoint and pass new search/unassigned/folder/uploader params
- Extend AssetService with unassigned/folder/uploader/search params and GetUnlinkedGroupsAsync
- Add AlbumAssetPicker modal component with drillable folder sidebar, search, and multi-select
- Integrate AlbumAssetPicker into AlbumDetail with Add Assets button and merge handler
- Add file-browser BrowseUnlinkedAssets endpoint with directory tree from OriginalPath
- Redesign AlbumAssetPicker with breadcrumb navigation, directory drill-down, and file browser UX
- Add collapsible sidebar toggle to AlbumAssetPicker folder browser
- Add list/grid view toggle, bulk delete in maintenance mode, and /maintenance page
- Add visibility and create-album actions, fix embedded viewport sizing
🐛 Bug Fixes
- (API) Split authentication from user endpoint
- (API) Fix retrieve of user authentication data
- (API) Split authentication from user endpoint
- (API) Fixes null exception against anonymous users requests
- (API) Fix exception for UserRepository.cs
- (API) Fix Password hasher dependency resolver
- (API) Adds last login update on login
- (API) Fixes null exception against anonymous users requests
- (API) Fix exception for UserRepository.cs
- (API) Fix Password hasher dependency resolver
- (API) Adds last login update on login
- (API) Remove login permission to Banned or Disabled Users
- (API) User endpoints to be in line with new structure
- (API) Switches tagSearch method from uint to int
- (docker) Fixed Dockerfile stuck on old project name and build configs
- (API) Added support for pagination on Asset GetAll Query
- (API) Added deleted at field in UserInfo mappers
- (API) Implements GetAll tags in tag Repository
- (API) Wrong logger in album controller
- (API) Standardized DTO namespaces
- (API) Added required instead of null initialized strings where it didn't make sense to have a null string in Users DTOs
- (API) Added missing Email field in UserInfoDto
- (API) Marked as required both fields of BulkDto
- (API) Marked as required both fields of credentials DTOs
- (API) Marked as required the tag name in TagCreateDto
- (API) Marked as required all the various required fields in person related DTOs
- (API) Added null checking to folderRepository get operations in FolderController
- (API) Added GetAll method to Folder Repository and controller
- (API) [breaking] Marked required properties in Asset as required
- (API) Added FindByPath in AssetRepository
- (API) Allow Anonymous to use MediaEndpoint
- (docker) Dockerfiles updated to use Net 8 as base images
- (docker) Solved mismatch with base folders in both dockerfile and compose file
- (docker) Applied "Docker" as environment when building the application in a container thus avoiding not getting the correct configuration overloads at run time.
- (Docker) Removes Env var from where is not needed and add it to teh compose
- (Docker) Add internal db port in appsettings.Development.json
- (docker) [breaking] Postgres volume data was not named properly
- (db) Added postgres switch for Legacy Timestamp behaviour
- (API) Correct reference to the return type of result
- (API) Fixes the Db Initializer on Production Environment
- (DB) Added check for already existing assets on DB entry.
- (Services) Added FileSystem scan service to the startup of the webserver.
- (frontend) Adds the new services to the app startup configuration
- (frontend) Added correct headers and response format to LoginService
- (frontend) Adjusted SettingsService to work with List instead of SettingsDto object.
- (API) Added SaveChanges to Settings repository, reflecting changes to he database
- (API) Solved filesystem scanner not scanning
- (frontend) Adds the new services to the app startup configuration
- (frontend) Added correct headers and response format to LoginService
- (frontend) Adjusted SettingsService to work with List instead of SettingsDto object.
- (API) Added SaveChanges to Settings repository, reflecting changes to he database
- (API) Solved filesystem scanner not scanning
- Address code review findings (blocking .Result, async void handlers, email logging)
- Handle stale ProtectedLocalStorage value crashing NavMenu deserialization
- Serve appsettings.json dynamically from MilkStream to fix WASM BaseUrl in Docker
- Support wildcard CORS origin for self-hosted Docker deployments
- Replace CORS wildcard with explicit safe origins per environment
- Add DB healthcheck and service_healthy condition to prevent lactose startup race condition
- Catch OperationCanceledException from Task.Delay to prevent incorrect Completed status on cancellation
- Notify auth info changed after token refresh and handle polling errors
- Change ParentJobId to get/set for WASM compat, remove @key from JobTree, add debug panel
- Handle 401 responses in JwtTokenRefresher by refreshing token and retrying
- Redirect to login when token refresh fails in JwtTokenRefresher
- Handle 401 in JobsService directly with refresh+retry
- Add missing Butter.Types import in AssetRepository for EAssetType
- Remove completed sub-jobs from list so master job can finish
- Prevent master PHashJob from spinning indefinitely on sub-job completion
- Use oninput for live slider value update and widen range layout
- Add minutes unit to Folder Scan Interval description in default settings
- Handle 401 transparently at AuthServiceBase level for all services
- Redirect unauthenticated users from settings page to /login
- Redirect unauthenticated users from jobs page to /login
- (ui) Render child jobs in JobTree by skipping root filter at deeper levels
- (db) Always override ThumbnailPath/PreviewPath values with local paths on init
- (docker) Create thumbnails/previews dirs as app user so subdirs can be created at runtime
- (ui) Restore auth in App.razor before rendering to prevent race redirect to /login
- (phash) Display child jobs grouped by status
- (jobs) Prevent silent crash on large thumbnail jobs and auto-refresh child jobs
- (ui) Apply poll interval to child jobs, add @key for DOM reuse, guard admin pages
- (ui) Submit login form on Enter key press
- (jobs) Fix IntegrityCheckJob conditional + use soft-delete
- (jobs) Add delay to JobManager main loop to prevent CPU spin
- Add DeletedAt filter to asset batch queries to exclude soft-deleted assets
- Add AssetUser many-to-many join table for SharedWith
- Remove AspNetCore.SassCompiler to fix Docker startup crash
- Proper bidirectional SharedWith many-to-many migration
- (Lactose) Admin and Curators media visibility
- Wire MaxConcurrentJobs to database setting on startup and runtime changes
- Propagate cancellation to sub-jobs in batch processing jobs
- Cancel past refresh loop when poll rate is set to Off
- Make job cancellation work for all job states and types
- Per-batch progress messages and counting bug in album/person jobs
- Skip existing AlbumAsset relationships in CreateAlbumsJob to prevent duplicate key violations on re-run
- Add JobBatchSize to DefaultSettings.json to prevent reset on restart
- Populate LastLogin/BannedAt/UpdatedAt in UserInfoDto mapping
- Rewrite TopTags query to avoid EF Core collection navigation translation error
- Remove destructive People soft-delete in IntegrityCheckJob and drop duplicate hash stats
- Move preview info text below image instead of overlaying on bottom border
- Download button now fetches blob and triggers actual file download
- Remove scroll position correction from LoadPreviousPage for smoother scroll-up
- Propagate cancellation tokens through linked child job CTS
- Album controller Save calls, person list endpoint, media empty path guard
- Scoped CSS deep selectors for preview info text and filename binding
- Switch album detail page to row-based flexbox masonry
- Remove preventDefault from album links so they actually navigate
- Remove content-visibility and contain-intrinsic-size from album detail tiles
- Switch album detail grid from flexbox to CSS Grid
- Use flexbox with align-items:flex-start for album detail grid
- Unlock body scroll on navigation, reorder masonry for left-to-right flow
- JS-based masonry for left-to-right item flow with varying heights
- Faulty migration that could cause lactose crash on migration apply
- Use CSS columns for albums page, keep JS masonry only for album detail
- Override masonry inline styles with !important for grid mode
- Force square aspect-ratio in grid mode over inline style
- (controllers) Tighten asset visibility and media access checks
- (AssetController) Correct search visibility filter for #11
- (AlbumController) Exclude deleted assets from search visibility for #11
- (admin) Add confirm/cancel for role change and prevent admin self-demotion
- (user) Hash password in Create endpoint instead of storing raw
- (ui) Remove broken onfocusout handler from role edit dropdown
- (admin) Show inline errors instead of replacing entire page
- (auth) Show ban/disabled message on login and force-logout on refresh
- (auth) Detect banned/deleted user on page load via InitializeAsync
- (frontend) Sorts setting by alphabetical
- (Lactose) Change to use UtcNow, even if alredy working without timeline
- (backend) Add [FromQuery] to person list endpoint to fix 415 error
- (css) Move card styles to AlbumCard.razor.css, use ::deep for grid-to-card selectors
- (ui) Larger cosplayer cards with profile pics, move edit buttons top-right, add masonry/grid toggle
- (auth) Force logout on any refresh failure, redirect to login
- (ui) Add scoped modal CSS to PersonForm for proper overlay rendering
- (ui) Cosplayer cards now match album card style — image fills square, text overlay at bottom
- (js) Set onload before src to handle cached images
- (ui) Remove empty filler cells from banner mosaic, let overlay cover gaps naturally
- (ui) Always render ProfileCropper in tree, use OnParametersSet to trigger JS init
- (ui) Random 3 album covers max in banner, redesigned cropper with drag-to-move and drag-handle resize, image stays static
- (ui) 4 corner handles on square boundary for resizing instead of single circular handle
- (js) Handle positions relative to square, not viewport
- (js) Corner-drag resize with fixed opposite corner, proper visual rate matching
- (css) Show full image in cropper viewport (contain instead of cover)
- (ui) Correct geometry — square outer, circle inscribed inside; image-relative crop coords with background-image viewport; correct display formula for background-position
- (js) Adjust stored cropZoom by image aspect ratio factor so background-size formula matches circle
- (js) Store cropZoom as image-relative fraction (square_side / image_displayed_width), no adjustment needed on save
- (ui) Use pixel-exact transform positioning for avatar display instead of background-size formula
- (js) Use correct opposite corners for TL/TR/BL resize handles
- (js) Skip resize on first mousemove if mouse hasn't moved from mousedown position
- (js) Resize calculations now in viewport pixels to avoid image-relative X/Y scale mismatch on non-square images
- (api) Allow unlinking person from album by directly assigning null PersonOwnerId
- (build) Remove stray semicolon in onclick lambda
- Prevent album title from being overwritten when only setting Person in update DTO
- (pr) Implement BulkUpdate, BulkDelete, revert docker-compose, explain [FromQuery]
- (tests) Fix pre-existing auth route tests and handle empty unassigned albums gracefully
- (test) Use response.body directly instead of JSON.parse (already parsed object)
- (test) Update name assertion to match renamed person
- Create AlbumSearchParametersDto, add album pagination to person detail, fix duplicate XML param
- Cache random banner covers in field, not computed on every render
- (ui) Responsive layout fixes for mobile portrait mode (closes #48)
- (ui) Collapse AdminUsers table columns and actions on mobile (#48)
- (frontend) Removed faulty d-flex tag on main object
- (milkystream) Fix sorted masonry for albums
- (ui) Prevent JobRow horizontal overflow on mobile portrait
- (ui) Change AdminUsers mobile actions dropdown from dropup to dropdown
- (ui) Reduce password input width on AdminUsers to prevent cell overflow
- (ui) Stack setting card description and control vertically on mobile
- (ui) Prevent layout shift from save indicator width change
- (ui) Prevent save indicator from shifting adjacent controls
- (ui) Stack setting card body vertically — description above, control + indicator below
- (ui) Right-align number inputs, let them grow to fill card width
- (ui) Replace inline save indicator with Bootstrap toast notification
- (ui) Restore progress bar visibility on job rows
- Resolve UI issues #61, #62, #66
- (#62) Move mobile modal fix to global MilkyShot.css — scoped ::deep cannot pierce PersonForm root element
- (#62) Use explicit top/left/right/bottom instead of inset, add !important to ensure position:fixed takes effect on mobile
- (#62) Lock body scroll when modal forms open to prevent position:fixed breakage on mobile
- Propagate server-side error messages from UpdateUserAsync to UI
- Auto-close modal on password change success, add page-level success message
- Center profile card on page
- Wire profile icon in navbar to user profile page
- Default sort users table by role descending (admin on top)
- Use bold (700) instead of semi-bold for reliable font rendering
- (mobile) Keep back button inline with title, collapse admin actions into dropdown
- Batch-processing subjobs report correct status on asset failures
- Remove double-count of failed assets in master Done handler
- Metadata icon in dropdown uses text-light for visibility
- Metadata icon visible in job list rows
- Pass null since on first child fetch instead of DateTime.MinValue
- Eagerly fetch children on load and hide sub-jobs placeholder
- Re-render after eager child fetch so icons appear immediately
- Eager-fetch children for all roots including past jobs
- Update home logged-out message to match cosplayers wording
- Update albums logged-out message to match home/cosplayers
- Translate GetAllVisible to client-side eval for SharedWith navigation
- Suppress nullable warnings on Include chain in GetAllVisible
- Remove soft-delete from Person model
- (jobs) Add hover/active variants for all job types via darken()
- (ui) Modal icon order, fill all outline buttons
- (favicon) Render MilkyShot logo as PNG favicon
- (ui) Page bg radial gradient, navbar radial glow, favicon cache
- (cropper) Revert ProfileCropper to inline modal
- (cropper) Restore missing cropper CSS styles
- (cropper) Add pointer-events:auto on handles, fix cursors
- (ui) Fix float values in the cosplayer thumbnail and cosplayer card
- (ui) Fix remaining float culture issues in CSS style attributes
- (ui) Prevent infinite loading loop on cosplayers page with no data
- (ui) Keep header and toolbar visible when albums search returns empty
- (ui) Debounce search input and prevent backspace navigation
- (ui) Refocus search input after debounce triggers reload
- (ui) Move album grid CSS to AlbumGrid.razor.css for scoped isolation
- (ui) Add height:100% to album-card-fallback so icon centers vertically in grid mode
- (ui) Add margin-bottom to SortFilterBar for spacing from grid
- (ui) Add ActionButtons slot to SortFilterBar, move album actions into bar
- (ui) Push ActionButtons to right with ms-auto wrapper
- (ui) Keep header and toolbar visible when cosplayers search returns empty
- (ui) Move cosplayer grid CSS to CosplayerGrid.razor.css for scoped isolation
- (ui) Move cosplayer actions into SortFilterBar, fix sortAsc always sending
- Close outer div and remove invalid @{} blocks in SearchDropdown
- Pressing enter with nothing selected does nothing
- Move query param parsing to OnParametersSet
- Parse search query before await in Albums OnInitializedAsync
- Use case-insensitive ILike search for persons and albums
- Override Bootstrap dropdown-menu display:none
- Return 401 Unauthorized when user data is null in PersonController
- Allow anonymous search for persons and albums
- Materialize person search query before client-side visibility filter
- Restrict user list to admin only
- Add [FromQuery] to TagController.GetAll parameter
- Use zero-based page numbers in AssetController to match API convention
- Guard ILIKE filter in AlbumRepository.SearchQuery against null/empty query
- Restore Include(p => p.Albums) in PersonRepository queries
- Paginate PersonRepository.SearchQuery by ID before loading albums
- Prevent duplicate @key errors across all list renderings
- Filter album count by user visibility in person search
- Restore ToAlbumPreviewDto mapper method per review feedback #91
- Standardize pagination to zero-based across all layers
- Move SharedWith visibility check to client side for translatable queries
- Only reject Page < 0, keep original PageSize validation
- Restore PageSize < 1 validation with upper limit of 250
- (cosplayer-detail) Prevent banner image shuffle on album search/sort
- (cosplayer-detail) Debounce search, keep page intact on filter changes
- (cosplayer-detail) Remove search debounce, instant inline reload
- Replace untranslatable Album navigation in stats queries with direct join table queries
- Replace untranslatable Tags navigation in TopTags stats query with direct join table query
- (albums) Use MaxPageSize constant for unassigned album fetch
- Implement CosplayersMissingProfile stat
- Make card components middle-clickable
- Remove underline from album card person link
- (album-form) Searchable person combobox with modal-safe positioning
- (jobs) Use grey for queued segments in layered progress bar
- (repo) Scope Maintainer to only maintained persons in GetAllVisible and SearchQuery
- (controller) Scope Maintainer album visibility to only maintained persons
- (controller) Scope Maintainer asset access to only maintained cosplayers in Get
- (controller) Add Maintainer scope to Album BulkUpdate, Delete, BulkDelete
- (mapper) Allow Maintainer to see Visibility field in DTO responses
- (media) Replace bool parameter with EAccessLevel, add deleted asset check
- (stats) Add ProtectedAssets count, fix PrivateAssets to count only Private
- (dto) Default AssetCreateDto.Visibility to Private instead of Protected
- (repo) Use PagedParametersDto.MaxPageSize const instead of hardcoded 150
- (migration) Remap existing EAccessLevel values to prevent data corruption
- (migration) Default Visibility to Protected instead of Public, preserve old IsPubliclyShared mapping
- (dto) Make AssetCreateDto.UploadedBy nullable for scanner-created assets
- (model) Add [PrimaryKey] annotation to PersonMaintainer for clarity
- (repo) Scope AlbumRepository.SearchQuery maintainer visibility to maintained persons
- (controller) Scope AssetController maintainer permissions via album→person chain
- (mapper) Replace null-forgiving operator with null-conditional in AlbumMapper
- (controller) Split Maintainer from User in AssetController.GetAll visibility filter
- (media) Allow Maintainer to access media files for cosplayers they maintain
- (mapper) Scope asset counts in album/ person previews to visible assets
- Add missing app.css link to index.html
- Null reference in User page maintainer display
- Remove underline from user links, hide email from non-admin viewers
- Remove underline from maintainer links, load assigned person names in user edit modal
- NRE on user page when user data not yet loaded
- Show email to self-viewing users on user page
- Enforce data visibility per access level with test coverage
- Replace jsonPath with response.body for null-value checks in visibility tests
- Replace remaining jsonPath calls in asset visibility tests
- Allow maintainer assignment for all user roles in UI
- Address PR #127 review issues — fallback, eager load, indentation, Maintainer tests
- Show maintained cosplayers section for all user types in admin edit modal
- Show maintained cosplayers section for all user types in user profile edit modal
- Add entity-level visibility gating for albums and persons
- Update border colors and browse-mode visibility in AlbumDetail
- Show borders for maintainers in select mode
- Prevent jagged wrapping of toolbar and action buttons
- Album-card-select and cosplayer-card-select only on selected cards
- Wrap CosplayerDetail action buttons into mobile dropdown
- Add cascade checkbox to CosplayerDetail album visibility modal
- Cosplayers page bugs
- Prevent private assets with null UploadedBy leaking to anonymous users
- Homepage preview navigation, grid observer crash, and cascade labels
- Label Delete button and cropper modal overlay
- Album detail back button uses history.back() with fallback to /albums
- Soft-delete users in UserController.Delete instead of hard-delete
- Add ConfirmDialog to AdminUsers delete user flow
- Add ConfirmDialog to User delete user flow
- Add ConfirmDialog to AlbumDetail delete flows
- Add ConfirmDialog to CosplayerDetail delete flows
- Add ConfirmDialog to Albums bulk delete flow
- Add ConfirmDialog to Cosplayers bulk delete flow
- Add ConfirmDialog to Settings folder deletion flow
- (UI) Make the delete cosplayer button in cosplayer detail page be just an icon to conform with albums detail page
- Apply Visibility and MaintainerUserIds in PersonController.BulkUpdate
- Split multi-request test blocks into single-request blocks
- Guard against null PersonOwnerId in MergePeople albums query
- Gate registration behind UserRegistrationEnabled setting
- Handle 204/empty responses in AssetService and always reset isLoading in AlbumAssetPicker
- Use Albums.Count==0 instead of !Any() for unlinked filter, add trace logging
- Use OnParametersSetAsync instead of fire-and-forget to ensure Blazor re-renders after init
- Use parameterized SqlQueryRaw instead of unquoted string.Format to prevent SQL error
- Filter directory names to only paths containing a slash after prefix, excluding filenames
- Restrict maintenance page and nav link to admin only
- Add scoped CSS for maintenance page to make embedded picker fill viewport
- Inline all markup in .razor template to restore CSS isolation
- Change LoginService from Singleton to Scoped to match Blazored.LocalStorage lifetime
- Simplify maintenance page CSS to flex-fill container without vh calc
- Adjust embedded picker viewport height offset
- Address PR #142 review issues
🚜 Refactor
- (API) Switch to repository pattern
- (API) Switch to use UserRepository AuthController.cs
- (API) Switch to repository pattern
- (API) Switch to use UserRepository AuthController.cs
- (API) Cleans the code
- (API) Cleans the code
- (API) Extract BulkDtoinseparate file
- (API) Switch to use a class for paged search
- (API) Adds Bulk capabilities
- (API) Move classes and Standardlize
- (API) Rename Asset Dtos
- (API) AlbumController explicitly marked all of the x in lambdas as they were accidentally hiding previous stage, also improved null checking
- Reformatted AssetController and AssetCreateDto files
- (API) Asset controller reformatted params
- Improve readability of CorsAllowedOrigins parsing block
- (jobs) Flatten ThumbnailJob to batch-based sub-jobs
- (jobs) Flatten PHashJob to batch-based sub-jobs
- (jobs) Flatten MetadataJob to batch-based sub-jobs
- Make LoginService singleton, centralize auth state and persistence
- Extract ImagePreview shared component from Home and AlbumDetail
- Remove list view, add grid/masonry toggle to album detail
- (css) Move shared modal styles to global MilkyShot.css/SCSS, remove duplicated scoped CSS
- (css) Remove SCSS, switch to plain CSS with Bootstrap custom properties
- (api) Fold unassigned album filter into search endpoint via query param
- (dto) Move Unassigned filter into PagedSearchParametersDto
- Replace individual albumPage/albumSize params with PagedParametersDto in person detail endpoint
- Replace custom masonry JS with Bootstrap grid + Masonry.js approach
- (auth) Extract AuthServiceBase, replace retry with re-auth on 401
- (auth) Remove SendWithRefreshAsync, rely solely on JwtTokenRefresher
- (ui) Replace inline password form with modal
- (ui) Complete user profile page redesign with card layout
- Move cosplayer link to same line as album name
- Center-align header, album name on top, cosplayer smaller below
- Extract password strength UI into shared PasswordField component
- Replace tuple + custom header with ChildrenResponse DTO
- (ui) ModalFrame + EmptyState components, remove duplicate CSS, remove main border
- (jobs) Use darken() for phash and create-albums hover/active too
- Project PersonPreviewDto directly in repository instead of [NotMapped] entity property
- Project AlbumPreviewDto directly in repository with visibility-aware asset count
- Clean up imports, simplify checks, and add SearchDropdown component
- (albums) Extract visibility-aware AssetCount into AlbumMapper overload
- Replace hardcoded 250 page size limit with PagedParametersDto.MaxPageSize
- Use MaxPageSize constant as default pageSize across all frontend services
- Reuse AlbumGrid in CosplayerDetail via FetchAlbums delegate
- Move all visibility filtering from mappers and controllers to repositories
- Rename AlbumSearchParametersDto.UploadedBy to AssetUploadedBy
- Extract role badge colors to SCSS theme layer
- Generate access level dropdown from enum values
- Extract role checks to LoginService, update all consumers
- Move CanEdit logic to LoginService, remove local page properties
- Extract person search/assign UI into shared SearchAssignBadges component
- Extract VisibilityModal shared component, add editing controls to Albums page
- Centralize cascade visibility logic in AlbumService.CascadeVisibilityToAssetsAsync
- Replace inline visibility modal with shared VisibilityModal in Cosplayers.razor
- Move Unlink cosplayer button from album header to album edit form
- Add Embedded mode, UnlinkedOnly param, rename methods from Unlinked to generic names
- Rename UnlinkedAssetGroupDto to AssetGroupDto
- Replace individual [FromQuery] params with AssetBrowseOptionsDto
📚 Documentation
- Update agents.md for Blazor WASM architecture
- Add CORS configuration section to README
- (readme) Add wildcard CORS warning and MilkStream CORS setup guide
- Add XML documentation to all public APIs across all projects
- Update AGENTS.md with XML doc enforcement and CORS guidance
- Update AGENTS.md with verified infrastructure gotchas
- Add API reuse convention to AGENTS.md — prefer optional query params over new endpoints
- Add Gitea repo owner note to AGENTS.md
- Add granular commit convention to AGENTS.md
- Document REST Client tests in AGENTS.md
- Update AGENTS.md with missing conventions and project info
- Add use case catalog for new auth/permissions model
- Add Gitea label levels note to AGENTS.md
- Add PR workflow section to AGENTS.md
- Reconcile AGENTS.md with current codebase
- (repo) Fix stale pageSize default in IAlbumRepository XML doc
- Add data size estimates to AGENTS.md
- Clarify Curators and Admins can be maintainers for credited attribution
- Fix stale column references and tooling versions in AGENTS.md
- Add visibility rules (R1–R7) to AGENTS.md
⚡ Performance
- Push visibility filter into SQL for PersonRepository queries
- Add partial index on Assets for visibility-aware search queries
- Simplify ORDER BY to total count and add trigram GIN indexes
- Add AsSplitQuery to repository queries with multiple Include calls
- Replace in-memory path parsing with SQL SPLIT_PART aggregation and double-LIKE filter
- Replace in-memory path parsing with SELECT DISTINCT SQL, remove dir count badges
🎨 Styling
- Style cosplayer link with inherited color and person icon
- Match cosplayer link font size to h3, remove icon, keep regular weight
- Consistent inline typography for album title and cosplayer name with dash separator
- Borderless edit pencil with hover reveal on header
- Cosplayer link dims on hover instead of blue/underline
- Replace badge backgrounds with colored icons + matching launch buttons
- Custom job color palette based on purpose, avoid red
- Mobile launch button cyan for better contrast
- Soften accent gradient stops (5%/10% instead of 15%/30%)
- Use rounded-square thumbnails for album results
- Add chevron-right indicator to search dropdown headers
- Use Bootstrap dropdown-menu and dropdown-item classes
- (repo) Convert visibility filters to switch expressions for readability
- (repo) Expand switch expression arms for readability
- (repo) Use explicit named enum cases in PersonRepository switch expressions
- Standardize access level comparison to switch expressions
🧪 Testing
- More unit testing on HTTP client
- (api) Add person endpoint tests to WepApiTest.http
- (api) Add comprehensive http tests for all person and album endpoints
- Add album update test without removePerson field
- Use real album from search for update test, not zero-GUID fallback
- All tests create their own data — create album returns ID, all mutations use self-created resources
- Add endpoint tests for refresh token, albums, curator auth levels
- Add anonymous search tests for persons and albums
- Handle empty person search results gracefully
- Set personId from admin search to ensure it's always populated
- Add user id to profile update request body
- Fix curator user access level from Admin (2) to Curator (1)
- Restructure WepApiTest.http with three clearly separated users
- Add cleanup of regular test user in REST Client suite
- Add page=0&pageSize=5 to paginated list endpoints
- Add merge and bulk visibility tests to WepApiTest.http
- Flatten test numbering to sequential 1–137
- Added rate limiting stress test
⚙️ Miscellaneous Tasks
- Removes auto generated docker-compose file for deployment
- (docker) Removes auto generated docker-compose file for deployment
- (API) Changes the category name of the logger
- Added datagrip files
- (config) Separates connection string defaults for docker and local webserver run
- Removed unused using in Program.cs
- Swapped empty array initializer with empty collection initializer in Asset hash
- Commented out unused logger in Album controller and added a TODO: about adding logging capabilities
- Marked navigation property in folder as nullable
- Commented out logger in FolderController and added a TODO regarding logging
- Removed redundant type cast in tag search
- Removed useless using declarations in TagMapper
- Commented out the logger in tagController and added a TODO regarding logging
- Removed more unused using directives
- (docker) Added NoCache build option for lactose
- (docker) Updated dockerfile to build using dotnet 9.0
- (NuGet) [breaking] Added dependency to Magick.NET-Q16-HDRI-OpenMP-x64
- Updated nuget packages to latest releases supporting Net 8.0, removed ImageMagick package.
- Removed MySql package from required nuget packages
- Refined db connector params
- (API) Removes Redundant Check
- Split TokenGeneratorProvider file in their respective classes
- Moved milkstream services under a services namespace
- (frontend) Switched to a more recent session storage implementation which doesn't have a security vulnerability.
- Updated run profiles
- Moved milkstream services under a services namespace
- (frontend) Switched to a more recent session storage implementation which doesn't have a security vulnerability.
- Updated run profiles
- Remove temporary debug panel from Jobs page
- Remove redundant XML doc comments from ServiceBase
- Upgrade target framework to net10.0
- Add dotnet-tools.json for ef tool
- (dev) Fix ovveride PM Compose deploy
- Remove debug auth logging middleware from Program.cs
- Remove unused banner-cell-empty CSS class
- (test) Remove Swagger test — disabled in container environments
- Added the test images folder to the dockerignore avoiding them being sent every time a docker build starts
- Revert docker-compose.yml to match develop
- Updated packages, removed unused packages. ImageSharp now requires a license
- Commit compiled CSS for role badge classes
- Fix Dockerfiles, add debug compose to solution
- Use record for config classes
- Adjusted rate-limits of thumbs, preview and jobs to avoid triggering them during normal usage
- Add Gitea Actions workflow to build and push containers on version tags
🛡️ Security
- Upgrade to .NET 10 — packages, Docker images, and Swagger fix
◀️ Revert
- Switch album detail back to masonry layout
- Restore docker-compose.yml — testing-only CORS/baseUrl changes committed by accident
- Restore docker-compose.yml LactoseBaseUrl to 192.168.50.100
- Remove AllowAnonymous from AlbumController.Search and PersonController.GetAll
- Change LoginService back to Singleton (DI error was from docker fast mode)
CI
- Added build + push to internal registry action
Fix
- (API) Disables Asset Create
- (API) Refresh token being changed the last 10 minutes instead of every moment
- (API) Refresh token being changed the last 10 minutes instead of every moment