Commit Graph

195 Commits

Author SHA1 Message Date
LyAhn 8f424773d2 refactor(folder-picker): modularize component
Break down the monolithic FolderPickerModal component into smaller, highly cohesive modules to improve maintainability and separate concerns.

- Extract UI sub-components into dedicated files (`FolderRow`, `StagedFoldersPanel`, and `StatusLine`).
- Move path manipulation, string formatting, and breadcrumb utilities into a dedicated `pathUtils.ts` file.
- Abstract complex local state, API calls, and event listeners into a custom `useFolderPicker` hook.
- Clean up `FolderPickerModal` to act as a lightweight presentation layer.
2026-07-04 19:14:32 +01:00
LyAhn 2149b4cad5 refactor(bg tasks): modularize component
Break down the monolithic BackgroundTasks component into smaller, highly cohesive modules to improve maintainability and readability.

- Extract complex UI components into dedicated files (`BackgroundTaskSummary`, `ExpandedTaskPanel`, `TaskStagePill`, `TaskProgressBar`, `BackgroundTaskActions`, and `FailedWorkerItemRow`).
- Move task construction, duplicate scan formatting, and progress calculation logic into a dedicated `taskModel.ts` utility file.
- Relocate shared interfaces and constants to `types.ts` to cleanly decouple data structures from the presentation layer.
2026-07-04 19:04:06 +01:00
LyAhn 2901425f42 refactor(sidebar): modularize Sidebar
Break down the monolithic Sidebar component into smaller, maintainable modules to improve readability and separation of concerns.

- Extract `NavItem`, `FolderItem`, and `AlbumItem` into dedicated UI components.
- Modularize domain logic by introducing `LibrarySection` and `AlbumSection`.
- Abstract complex drag-and-drop and ordering state into custom `useFolderOrdering` and `useAlbumOrdering` hooks.
- Move shared constants and types to a dedicated `types.ts` file.
2026-07-04 18:57:36 +01:00
LyAhn 1074c875a3 fix(menus): prevent menu label text from being selectable 2026-07-04 18:30:25 +01:00
LyAhn 58ecb03070 refactor(explore): extract components from ExploreView into src/components/explore/
To de-godify \ExploreView.tsx\, the inline components \ClusterCloud\, \ExploreLoadingPanel\, \TagAtlas\, and \TagManageList\, along with shared constants in \layout.ts\, have been extracted into independent files. This improves modularity and maintainability.
2026-07-04 18:09:41 +01:00
LyAhn 2c0b928bf5 refactor(ui): split SettingsModal into feature slices under src/components/settings/
Extracts the various settings sections (General, Media, Updates, Storage, AI Workspace) from the massive SettingsModal.tsx into distinct sub-components. Introduces a shared.tsx file for common settings UI primitives (SettingsGroup, SettingsItem, StatusPill, etc.) to improve code maintainability and separation of concerns.
2026-07-04 16:46:41 +01:00
LyAhn fe312e7678 refactor(lightbox): split Lightbox into smaller hooks and components
Deconstruct the Lightbox.tsx god component into feature-specific hooks (slideshow, region selection, navigation, media details) and separate UI components (viewport, slideshow view, details panel). Extracted shared types and formatting logic into utility files.
2026-07-04 16:33:54 +01:00
LyAhn 52ac2543ec refactor(store): split store.ts into feature slices under src/store/
Break the 3,244-line monolithic store into a Zustand slice-per-feature
layout (types, helpers, librarySlice, gallerySlice, searchSlice,
exploreSlice, albumSlice, duplicateSlice, taggerSlice, captionSlice,
settingsSlice, appSlice, events) combined in index.ts. Components keep
calling useGalleryStore(s => s.field) against the same flat state
object — no component changes required. Completes the de-godify effort
started with the menu/Dropdown/Sidebar extractions and icons.tsx.
2026-07-04 15:53:52 +01:00
LyAhn fbf8a7878c refactor(ui): extract shared icons into icons.tsx
The ten most-duplicated inline SVGs (Check, Close, ChevronDown/Right,
Plus, Photo, Folder, Warning, Star, Play) become shared components --
34+ copies across 15 files replaced. Stroke icons take a per-site
strokeWidth since weights legitimately differ by context; icons that
appear once or use variant path data stay inline.
2026-07-04 12:29:14 +01:00
LyAhn 4d41f3744f fix(duplicates): key duplicate tiles on the outermost mapped element
The key sat on the button nested inside the Tooltip wrapper, so React
warned about missing keys for every duplicate group tile.
2026-07-04 12:21:04 +01:00
LyAhn c27662dd74 refactor(ui): extract AlbumPicker from BulkActionBar
The album popover body (album list + create-new-album form) moves to
its own component. The host supplies onPick, which both an existing
album row and a freshly created album route through -- BulkActionBar
adds the selection and closes the panel. Ready for reuse anywhere else
an album needs picking (e.g. the context menu submenu later).
2026-07-04 12:11:36 +01:00
LyAhn ee2a1b204e refactor(sidebar): extract InlineRename, InlineConfirm, and NavItem
FolderItem and AlbumItem each carried their own copy of the in-place
rename input (state, select-on-open effect, commit/cancel handling) and
the Confirm/Cancel pair for destructive actions. Both are now shared
components: InlineRename mounts fresh per rename (an external name
change mid-rename no longer clobbers typing), InlineConfirm is the
compact red/gray pair. The four copy-pasted nav rows collapse into a
local NavItem component.
2026-07-04 12:11:26 +01:00
LyAhn 6806703363 fix(dev): clone mock IPC results to match real invoke semantics
Mock handlers return references straight into the in-memory db (e.g.
`return db.albums`), so store updates like set({ albums }) kept the same
array identity across loads and Zustand never notified subscribers --
the sidebar album list froze after creating an album from the bulk bar.
Real invoke() deserializes fresh JSON per call, so production was never
affected. structuredClone in the shim restores that fidelity for every
mock command at once.
2026-07-04 12:11:00 +01:00
LyAhn 90fd6f4fed chore(ui): remove dead MenuBar component
MenuBar has had no importers since the Toolbar superseded it -- its
last real touch was the multi-folder picker work. Delete it and update
the CLAUDE.md component list (which also drifted: Timeline/ExploreView
existed but were missing, TagCloud never split out) plus a pointer to
the shared menu primitives.
2026-07-04 07:22:32 +01:00
LyAhn 053a2bd846 refactor(ui): consolidate dropdowns into one shared Dropdown
ThemedDropdown, Toolbar''s local SortDropdown, and FolderScopeDropdown
were three hand-rolled implementations of the same select pattern. They
are replaced by a single generic Dropdown (src/components/menu/) built
on MenuPanel/MenuItem, with solid/ghost/compact trigger variants and
Object.is value comparison so number|null folder scopes work alongside
string unions. Call sites drop their `value as X` casts.

MenuItem gains an `active` state plus stable menu-panel/menu-item class
hooks, and the subtle-light CSS that previously dressed only the folder
scope dropdown (feature-scope-*) now themes every menu surface --
dropdowns, context menus, and submenus alike.
2026-07-04 07:22:22 +01:00
LyAhn 54016df830 refactor(ui): adopt useDismissable in ColorFilter and BulkActionBar
Replaces the hand-rolled outside-pointerdown listeners with the shared
hook. Both popovers now also close on Escape, and listeners only attach
while a panel is actually open.
2026-07-04 07:22:09 +01:00
LyAhn 5d46ee5b94 feat(gallery): add-to-album submenu on the image right-click menu
Right-clicking an image in the Gallery or Timeline now offers an
"Add to Album" submenu listing all albums with their counts — the first
use of the new SubMenu primitive. Filing a single image away no longer
requires starting a multi-select.
2026-07-03 23:58:18 +01:00
LyAhn 83081928f6 refactor(ui): unify context menus into a shared menu system
Five hand-rolled context menu implementations (image tiles, sidebar
folders, sidebar albums, title bar theme switcher, plus duplicated
close-listener effects in Gallery/Timeline) are replaced by shared
primitives in src/components/menu/:

- useDismissable: one outside-pointerdown + Escape dismissal hook
- Menu.tsx: MenuPanel chrome, MenuItem (danger/disabled/checked/hint),
  MenuSeparator, MenuLabel, and SubMenu with viewport edge-flipping
- ContextMenu: portal-rendered wrapper that measures and clamps to the
  viewport, fixing menus rendering off-screen and the latent
  fixed-inside-transform bug under framer-motion Reorder items

The image right-click menu moves to ImageContextMenu.tsx, shared by
Gallery and Timeline.
2026-07-03 23:54:28 +01:00
LyAhn e374ff6b02 fix(ui): fix tile tooltip + folder picker
- Tooltips now shows their correct px size on hovering

- Remove trailing `\` from drive letter in folder picker UI
2026-07-03 23:27:14 +01:00
LyAhn 3ab9357d6f perf(explore): reduce tag cloud refresh pressure
github/actions/ci GitHub Actions CI finished: success
Debounce Explore tag refreshes while AI tagging is active so the tag cloud catches up after worker activity settles instead of continuously re-querying.

Optimize the tag cloud aggregate query by fetching representative thumbnails in one pass and adding a supporting tag/image index.
2026-07-03 22:43:41 +01:00
LyAhn fe65bc6f38 feat(settings): reorganize preferences pages
Split Settings into General, Media, Updates & Setup, Storage, and AI Workspace pages so update/setup and maintenance controls are easier to reach.

Move runtime check results beside the model runtime controls instead of under model location.
2026-07-03 22:43:41 +01:00
LyAhn 68932b55c5 fix(tagger): separate JoyTag confidence threshold
Store confidence thresholds per tagger model so JoyTag no longer inherits WD tuning. Refresh the active threshold when switching models, guard stale threshold saves, and keep UI Lab mocks in sync.

Also tightens the onboarding model selector so the segmented control no longer stretches across the row.
2026-07-03 22:43:41 +01:00
LyAhn f1116c6c26 feat(onboarding): choose AI tagger model
Add WD and JoyTag selection to the Welcome Tour AI step so users can choose the model before downloading it.

Share tagger model metadata with Settings and keep the Settings close button anchored to the modal chrome.
2026-07-03 22:43:41 +01:00
LyAhn b92b850d02 Merge AI tagger readiness fixes
Refresh selected tagger readiness at startup so the lightbox AI tags action reflects installed model state without needing a Settings refresh.

Add UI Lab coverage for uninstalled WD and JoyTag scenarios, and include the toolbar clear-button alignment follow-up.
2026-07-02 20:59:48 +01:00
LyAhn bf04df7484 fix(toolbar): center search field overlay buttons
The clear button and command-prefix chip sat a few pixels high: their
inline-flex Tooltip wrappers created a line box with descender space
below, making the positioned div taller than the button it centers.
Making the wrappers flex containers collapses them to the button height
so the translate centering lands correctly.
2026-07-02 20:47:22 +01:00
LyAhn d29a779c13 test(ui-lab): add tagger readiness scenarios
Add UI Lab scenarios for uninstalled WD and JoyTag tagger states so the lightbox and AI Workspace readiness flows can be exercised directly.

Make the Settings title-bar button accessible by label to support reliable UI Lab automation.
2026-07-02 20:22:06 +01:00
LyAhn b7e82dbf91 fix(ai-tags): refresh tagger readiness for lightbox
Load the selected tagger model and model status during app startup so the lightbox AI tags action does not stay disabled until Settings refreshes the state.

Refresh readiness after tagger downloads complete and replace stale WD-specific unavailable copy with generic AI tagger wording.
2026-07-02 20:13:45 +01:00
LyAhn 749b23723a Merge lightbox slideshow mode
github/actions/ci GitHub Actions CI finished: success
Add fullscreen slideshow playback for image-only lightbox sessions, including sequential or random order, idle controls, and slideshow settings.

Include the gentle motion transition polish and no-repeat random navigation refinements from the feature branch.
2026-07-02 17:52:30 +01:00
LyAhn 68d19d219e feat(lightbox): add gentle slideshow motion
Add a selectable slideshow transition setting with a gentle motion option, keep the default soft fade, and make random slideshow navigation avoid repeats until the current image pool is exhausted.

Split the slideshow crossfade layer from the slow image drift so transitions overlap cleanly without black frames or lingering stale slide layers.
2026-07-02 11:39:19 +01:00
LyAhn 31b46327fd feat(lightbox): add slideshow mode
Adds a fullscreen image-only slideshow from the current lightbox collection, with pause, keyboard navigation, hidden idle controls, and polished image transitions.

Adds slideshow duration and playback order settings, including random order support.
2026-07-02 08:19:13 +01:00
LyAhn 29d9106039 Merge AI tag maintenance tools
github/actions/ci GitHub Actions CI finished: failure
Add an AI tag reset flow across Settings and Explore so AI-generated tags, AI tagging metadata, and stale tagging jobs can be cleared without affecting user tags or other media data.

Also add subtle AI source indicators in the tag manager and an Extreme UI Lab scenario for stress-testing large tag/library counts.
2026-07-01 11:38:51 +01:00
LyAhn a78111c8d4 test(ui-lab): add extreme mock scenario
Add an extreme UI Lab scenario with virtual-scale library, album, cluster, and tag counts while keeping the rendered media fixture set manageable.

Return the full extreme tag set from the mock backend so tag manager layouts can be stress-tested with 10k-100k tag counts.
2026-07-01 10:31:59 +01:00
LyAhn 4cdbc54d18 feat(ai-tags): add reset flow
Add a reset_ai_tags backend command that removes AI-generated tag rows, clears AI tagging metadata, cancels active tagging jobs, and drops queued or failed tagging jobs in the selected scope.

Expose reset actions in AI Workspace and the Explore tag manager, refresh gallery/progress/tag state after reset, and add subtle AI source indicators to tag manager rows.
2026-07-01 10:31:22 +01:00
LyAhn d5b93b2e21 feat(folder-picker): add editable address navigation
github/actions/ci GitHub Actions CI finished: success
Add a File Explorer-style dual-mode path bar to the folder picker so users can navigate with breadcrumbs or switch into an editable address field for pasted paths. Also allow staging the current or typed path directly and show friendlier feedback for missing folders.
2026-07-01 01:00:18 +01:00
LyAhn 257b2b54e7 chore: update changelog 2026-06-30 23:43:17 +01:00
LyAhn d619b01f2e refactor(ui): replace native app tooltips
github/actions/ci GitHub Actions CI finished: success
Wrap app controls with the shared Tooltip component and use cursor-positioned tooltip placement for icon buttons, chips, path labels, and media controls.

Leave native title attributes on the window chrome buttons so Minimize, Maximize, and Close keep platform-style behavior.
2026-06-30 23:25:43 +01:00
LyAhn 1a971899d1 feat(search): filter tag results by colour
Pass the active colour filter through tag searches and apply the existing palette match inside the tag query and count paths.

Update the UI Lab mock backend so colour filtering behaves the same way when testing tag search results.
2026-06-30 23:24:25 +01:00
LyAhn 8fe5daf25d Dev/codex cloud setup (#9)
## Summary
- Adds `scripts/codex-cloud-setup.sh` for Codex Cloud environment setup
- Installs Linux/Tauri native dependencies, Node/pnpm, Rust tooling, JS dependencies, and Rust crates
- Uses CPU-safe Rust checks with `--no-default-features` because Phokus enables CUDA by default
- Adds UI Lab/browser guidance for `pnpm dev:ui` on port `1422`

## Notes
- The script is intended to be pasted into the Codex Cloud setup field or run from the repo root.
- It avoids full release builds during setup to keep Codex cache warm without making environment creation too heavy.

Reviewed-on: #9
2026-06-30 14:35:20 +00:00
LyAhn 619bd0c9d2 Merge: rename 'tag cloud' to 'visual clusters' across the stack
github/actions/ci GitHub Actions CI finished: success
Disambiguate the Explore feature naming: the visual k-means cluster view was
named tag_cloud/tagCloud/TagCloud everywhere while the actual tag list is
explore_tags, which was easy to confuse. The cluster side is now
visual_cluster/visualCluster/VisualCluster (command, types, store, DB cache
table, and the ExploreView component); the tags side and the user-facing
"Tag Cloud" label are unchanged. Includes a mock-fixture fix so the dev
"huge" scenario surfaces a realistic field of clusters.
2026-06-30 10:01:25 +01:00
LyAhn 996bb71375 fix(mock): scale visual-cluster fixtures to the scenario
The mock Explore clusters were a fixed 10 regardless of scenario, so the "huge"
dev scenario looked sparse next to its large tag vocabulary. Mirror the backend's
k = (n / 20).clamp(5, 30): the huge scenario now surfaces ~30 clusters with a
long-tailed, big-library-sized count distribution, so Explore looks realistic.
2026-06-30 09:59:50 +01:00
LyAhn 0d9229635b refactor(explore): rename misnamed "tag cloud" to "visual clusters"
The visual k-means cluster feature was confusingly named tag_cloud / tagCloud /
TagCloud across the whole stack, while the actual tag list is explore_tags — the
two were trivially easy to mix up (and did cause confusion). Rename the cluster
side to visual_cluster / visualCluster / VisualCluster everywhere: command
get_tag_cloud -> get_visual_clusters (+ lib.rs registration and the invoke
string), VisualClusterEntry, the store fields/actions/tokens, and the mock
backend. Old names are retired rather than reused, so any missed reference fails
loudly instead of silently resolving to the wrong concept.

The tags side keeps its accurate explore_tags naming, and the user-facing
"Tag Cloud" UI label is unchanged.

Also rename the SQLite tag_cloud_cache table -> visual_cluster_cache (the old
table is dropped during schema setup — it is a disposable cache already
invalidated by the clustering version bump) and the TagCloud.tsx component file
-> ExploreView.tsx, since it is the Explore container hosting both the cluster
and tag views.
2026-06-30 09:48:38 +01:00
LyAhn cdb8aa20b9 Merge: faster Explore visual clustering on large libraries
Sampled, parallel k-means with density-aware k-means++ seeding makes first-time
visual clustering on large libraries fast, without a single cluster swallowing
tens of thousands of generic images. Together with the earlier cache-hit
optimization, the Explore tab no longer stalls for several seconds on 80k+
libraries.
2026-06-30 09:10:15 +01:00
LyAhn d2af84d9e8 perf(explore): sampled, parallel visual clustering for large libraries
Computing visual clusters was O(n·k·dim) per Lloyd iteration over the whole
library, single-threaded — several seconds on an 80k-image library on first
view. Find centroids on a deterministic, evenly-strided sample (<=3000
embeddings) and then assign every image to its nearest centroid in one parallel
rayon pass. Libraries at or below the sample cap are unchanged.

Replace the greedy farthest-point seeding (which seeds outliers, leaving the
dense core under-represented on a sample so one centroid absorbed tens of
thousands of generic images) with proper density-aware k-means++ D² seeding,
made deterministic via a small fixed-seed SplitMix64 PRNG. This keeps clusters
balanced on large libraries.

A CLUSTER_CACHE_VERSION is folded into the tag-cloud cache key so existing
caches computed by the old algorithm are invalidated and recomputed. The
clustering timing line and the cache-write failure now go through the `log`
facade (debug/warn) instead of eprintln.
2026-06-30 09:08:48 +01:00
LyAhn 68a9df5ab3 feat(lightbox): two-column metadata layout
github/actions/ci GitHub Actions CI finished: failure
Lay the lightbox info-panel metadata out in a two-column grid: paired fields
(Dimensions/Duration, Video codec/Audio codec, Type/File size) sit side by side
while Rating, Modified, and Embedding span the full width. More compact panel
with less scrolling. Tags and EXIF sections are unchanged.
2026-06-29 20:35:13 +01:00
LyAhn 79ce458fd5 feat(tags): open the tag manager from Settings
Add an "Open tag manager" button under a new Tag library group in Settings →
AI Workspace. It closes Settings and jumps to Explore's tag Manage mode.

To make manage mode reachable from outside the Explore view, lift its flag out
of TagCloud's local state into the store (tagManagerOpen / setTagManagerOpen)
behind an openTagManager() action. Manage mode is reset whenever Explore is
entered normally or the visual-cluster view is selected, so openTagManager()
stays the only path that opens it programmatically.
2026-06-29 20:26:49 +01:00
LyAhn a9a8f8422e feat(ui): quick theme switch from the settings cog
Right-clicking the settings cog in the title bar opens a small theme menu
(Phokus / Subtle Light / Conventional Dark) with the active theme checked,
anchored under the cog and dismissed on outside-click or Escape. Left-click
still opens Settings. Keeps theme switching one gesture away without cluttering
the title bar with another icon.
2026-06-29 20:26:08 +01:00
LyAhn ab7022e118 perf(explore): instant tag-cloud cache hits + fix stale-on-switch loading
The get_tag_cloud cache key was built by loading and hashing every embedding
blob for the scope *before* checking the cache, so even a cache hit re-read
hundreds of MB on large libraries and stalled Explore for several seconds.
Validate the cache from a lightweight image-ID-set signature plus the embedding
revision instead, so a hit never loads embeddings. The ID-set hash keeps the key
membership-sensitive (add/remove/move between folders) and the revision covers
an image being re-embedded in place. Cache write failures are now logged rather
than silently ignored.

On the frontend, switching folders (or re-entering Explore) no longer leaves the
previous folder's clusters/tags on screen with no loading indicator:
loadTagCloud/loadExploreTags clear stale entries on a real folder switch. The
displayed folder is tracked separately (exploreTagsShownFolderId) from the
cache-dirty marker so a same-folder invalidation (tag edits, new AI tags) does
not masquerade as a switch and wipe the visible list mid-refresh.
2026-06-29 20:25:38 +01:00
LyAhn 23e9850c7a Merge feat/tagging-ux: tagging model choice, related tags, tag manager, and UX polish
github/actions/ci GitHub Actions CI finished: failure
- JoyTag added as a second selectable tagger alongside WD; model switches on demand, tags attributed per-model

- Related tags atlas in Explore: clicking a tag shows co-occurring tags with animated connection lines and image counts

- Persist worker pauses across restarts via a Settings toggle

- Tag manager gains live filter, sort (most-used / least-used / A–Z / Z–A), and virtualisation for large libraries

- Tooltip portal with anchorToCursor mode for precise hover positioning

- Noisy AI tags (e.g. generic background descriptors) filtered at store time; existing tags cleaned on migration

- Fix: tag cloud hover glow and atlas gradient now adapt to Subtle Light theme

- Fix: AI Workspace 'Selected Folders' scope no longer pre-selects the first folder

- Fix: changelog version lookup strips build suffixes so What's New works in UI Lab
2026-06-29 18:36:01 +01:00
LyAhn c111032d99 fix(changelog): strip build suffixes before version lookup
UI-lab builds append a suffix to the version string (e.g.
"0.1.1-ui") which caused getChangelogForVersion to find no match and
fall back to the "not available in-app" message. A regex now strips any
hyphen-and-letter suffix before the lookup so the What's New modal
renders correctly in all build modes.
2026-06-29 18:06:45 +01:00
LyAhn c13f78c68b docs(changelog): add unreleased entries for feat/tagging-ux
Adds the user-facing changes introduced on this branch that were missing
from the [Unreleased] section:

Added:
- Related tags in Explore (tag atlas connection lines + co-occurrence counts)
- Persist worker pauses across restarts (Settings toggle)

Changed:
- Tag manager search/sort/virtualisation

Fixed:
- Noisy AI tags filtered automatically (removal list applied at store time)
- Explore Tag Cloud hover glow and atlas gradient in Subtle Light theme
- AI Workspace "Selected Folders" scope no longer pre-selects a folder
2026-06-29 17:23:20 +01:00