mirror of
https://github.com/spacedriveapp/spacedrive.git
synced 2026-04-20 14:38:58 -04:00
* fix(search): apply TagFilter in search.files query Tags were silently ignored by FilterBuilder. Adds find_entry_ids_for_tag() (batch lookup via user_metadata_tag → user_metadata → entry, handles both entry_uuid and content_identity_uuid paths) and resolve_tag_filter() (AND logic for include, OR for exclude). Applied in both execute_fast_search_no_fts() and execute_fast_search() FTS path. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(tags): implement tags.by_id, tags.ancestors, tags.children, files.by_tag Fixes "Tag not found" when clicking a tag in the sidebar, and shows the actual tagged files in the tag view. Backend (register_library_query via inventory): - tags.by_id: find tag by UUID via TagManager::get_tags_by_ids - tags.ancestors: get ancestor tags via TagManager::get_ancestors - tags.children: get descendant tags via TagManager::get_descendants - files.by_tag: find entries via user_metadata_tag → user_metadata → entry (handles both entry_uuid and content_identity_uuid paths) Frontend: - TagView: replace ExplorerView (used global Explorer context, ignored files.by_tag results) with a direct file list rendered from TaggedFileSummary TODOs for tag feature follow-up: - files.by_tag: return full File objects with sd_path so files are clickable/actionable (currently: id, name, extension, size only) - tags.related handler (sidebar shows related tags) - "Filters" button in TagView: secondary filters (type/date/size) within tagged files - tags.children in TagView currently uses get_descendants (all levels); should use get_direct_children for the quick-filter chips - DEL key binding for removing a tag from a file (#21 dependency resolved) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(tags): prevent duplicate tag applications on the same file The apply_tags_to_metadata() relied on catching a unique constraint error to detect duplicates, but no such constraint existed — so every call to tags.apply would silently create a new row. - Migration m20260125: deduplicates existing rows (keeps MIN(id) per pair), then adds UNIQUE INDEX(user_metadata_id, tag_id) - apply_tags_to_metadata(): explicit check-before-insert (upsert pattern), independent of DB constraint Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(tags,ui): make tag view files navigable and wire Overview search button - files.by_tag query now joins directory_paths/volumes/devices to build SdPath::Physical, enabling navigation from tag view to explorer - Tag view: double-click navigates to parent folder (files) or into directory; use react-router navigate() instead of window.location.href - Overview: search button now navigates to /explorer instead of no-op * feat(tags): render tag view using standard explorer with full File objects Backend: - files.by_tag now returns Vec<File> (full domain objects with File::from_entity_model) instead of lightweight TaggedFileSummary, matching the same data format as directory_listing and search.files Frontend: - Add tag mode to explorer context (ENTER_TAG_MODE/EXIT_TAG_MODE) - useExplorerFiles supports tag source via files.by_tag query - Tag route activates tag mode and renders ExplorerView directly, giving tagged files the same UI as normal file browsing (list/grid views, thumbnails, selection, context menus, keyboard shortcuts) - Fix ExplorerView empty state guard to allow tag/recents/search modes without requiring a currentPath Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(tags): add unapply/delete actions, fix tag sync and Inspector UX Backend: - Add tags.unapply action: remove tags from files by entry UUID, resolves via both entry_uuid and content_identity_uuid paths - Add tags.delete action: delete a tag and all its relationships via TagManager::delete_tag() - Add EntryUuid variant to TagTargets and ApplyToTargets to accept frontend UUIDs (fixes parseInt(uuid) bug that tagged wrong files) - files.by_tag: batch load tags for returned files (same pattern as directory_listing) so Inspector shows tags in tag view - navigateToPath exits tag mode to prevent empty directory on nav Frontend: - Tag primitive: add onRemove prop with X button for inline removal - FileInspector: optimistic tag updates via updateSelectedFileTags, refetchQueries with correct query keys (query:files.by_tag prefix) - TagsGroup: right-click delete with confirmation, active state - useFileContextMenu: "Remove tag" option when in tag mode - TagSelector: fix create+apply with EntryUuid fallback - Generated types: add DeleteTagInput/Output, UnapplyTagsInput/Output, EntryUuid variant to TagTargets and ApplyToTargets * refactor: extract shared useRefetchTagQueries hook Extract duplicated refetchQueries calls from FileInspector, useFileContextMenu, TagsGroup, and TagSelector into a single useRefetchTagQueries hook. Removes direct useQueryClient usage from those files. * fix(core): use current device slug instead of \"unknown-device\" fallback When the SQL join to devices table returns no result (volume_id or device_id NULL), fall back to get_current_device_slug() instead of the hardcoded \"unknown-device\" string. The previous fallback caused SdPath::is_local() to return false, breaking ephemeral indexing when navigating to directories from the tag view. Fixed in both files.by_tag and search.files queries. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(media): replace broken useJobDispatch with direct mutations Context menu used useJobDispatch/jobs.dispatch which has no backend handler, causing all media processing (thumbnail, OCR, transcribe, thumbstrip, proxy) to fail from the context menu. - Replace all 15 runJob() calls with direct useLibraryMutation calls (media.thumbnail.regenerate, media.ocr.extract, etc.) - Add forEachTarget helper for batch operations - Add mime_from_extension() fallback in RegenerateThumbnailAction for indexed files where content_identity MIME lookup fails - useJobDispatch.ts is now dead code (no remaining imports) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(tags): address CodeRabbit review findings on tag system - TOCTOU race: replace check-then-insert with atomic ON CONFLICT upsert in metadata manager (prevents duplicate tag applications under concurrency) - children query: use get_direct_children (depth=1) instead of get_descendants (entire subtree) for tags.children endpoint - delete atomicity: wrap tag cascade deletion in a transaction (relationships, closure, applications, usage patterns, tag) - files_by_tag: implement include_children and min_confidence filters (were declared in input but ignored) - files_by_tag: map content_id from SQL result instead of fabricating None - files_by_tag: merge entry-scoped and content-scoped tags with dedup (previously content-scoped tags were silently dropped) - unapply: emit resource events for all entries sharing content, not just the directly specified entries - frontend: derive tagModeActive from mode.type instead of storing separately (prevents state desynchronization) - Document sync deletion gaps with TODO(sync) comments Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(migration): keep newest row (MAX id) when deduplicating tag applications The dedup query before creating the unique index on (user_metadata_id, tag_id) was keeping MIN(id) — the oldest row. Since user_metadata_tag rows carry mutable state (version, updated_at, device_uuid), keeping MAX(id) preserves the most recent state. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * revert(tags): restore independent tagModeActive state The previous commit incorrectly derived tagModeActive from mode.type, conflating two separate concepts: - mode: {type: "tag"} = viewing files by tag (sidebar navigation) - tagModeActive = bulk tag assignment UI bar These are independent: clicking a tag in the sidebar should show tagged files without opening the assignment bar. Reverts the context.tsx portion of 04a181535. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(tags): address second round of CodeRabbit review - Increment version on ON CONFLICT update path so sync detects changes - Only report/notify entries that actually lost a tag (skip when 0 rows deleted) - Exit tag mode on all navigation paths (navigateToView, goBack, goForward) to prevent tag mode leaking through non-path navigation Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(tags): skip rows with undecodable required fields instead of fabricating entry_id=0 and modified_at=now() hide real decode failures. Required fields (entry_id, entry_name, created_at, modified_at) now skip the row with a warning log. Optional/numeric fields (size, child_count) keep sensible defaults since 0 is a valid value. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(tags): remove broken optimistic update and alert() dialog - FileInspector: remove updateSelectedFileTags() which mutated selectedFiles while the pane renders from file.tags — the refetch on mutation success is what actually updates the UI - TagsGroup: remove alert() on delete error (console.error suffices, alert() breaks platform-agnostic design) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(tags): emit file events on tag delete, refetch files.by_id for inspector - delete/action.rs: collect affected entry UUIDs before deleting the tag, then emit "file" resource events so the explorer grid updates (removes tag dots). Follows the same pattern as apply and unapply actions. - useRefetchTagQueries: add files.by_id to the refetch list so the Inspector panel updates immediately after tag mutations. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(tags): add extension to root-level file paths, validate entry UUIDs - files_by_tag.rs: root-level files (no parent_path) were missing their extension in the constructed path — now uses the same name+ext logic as the parent_path branch - apply/action.rs: validate that entry UUIDs exist in the entry table before creating user_metadata rows, since there is no FK constraint at the SQLite level on user_metadata.entry_uuid — prevents orphaned metadata from invalid UUIDs Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(tags): pre-index content rows to avoid O(n²) tag merge, require entry_kind - load_tags_for_entries: pre-build HashMap<content_uuid, Vec<entry_uuid>> from rows in a single pass, then lookup in the content-scoped branch instead of rescanning all rows per metadata record - entry_kind treated as required field (skip row with warning instead of silently defaulting to 0, which would misclassify directories as files) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(tags): secure FTS5 escaping, batch entry lookups for performance - FTS5 search: wrap each query token in double quotes to prevent operator injection (AND, OR, NOT, -, *, etc.) - apply/action.rs: replace per-entry UUID lookups with batch query (Entry branch: single WHERE IN instead of N round trips) - apply/action.rs: replace per-entry existence validation with batch query (EntryUuid branch: single WHERE IN instead of N round trips) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(tags): remove redundant inline sea_orm imports ColumnTrait, EntityTrait, QueryFilter are already imported at top-level. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(tags): validate entry UUIDs in create action before applying * fix: address code review feedback from CodeRabbit - Resolve merge conflict markers in TagSelector.tsx - Add type: 'all' to refetchQueries for inactive cache refresh - Replace browser confirm() with useContextMenu in TagsGroup - Add onSuccess refetch to createTag mutation in TagsGroup - Remove unnecessary 'as any' casts in OverviewTopBar - Replace alert() with toast.error() in useFileContextMenu - Remove 'any' casts in useExplorerFiles tag/directory queries - Add nil UUID rejection in tag apply input validation - Propagate SeaORM errors in thumbnail MIME lookup - Wrap tag upsert loop in DB transaction for atomicity - Fix tab indentation in migration mod.rs * fix: validate create tag targets, confirm before delete, escape LIKE wildcards, remove dead code * fix: prevent tagging ephemeral files and improve empty tag view UX * fix: platform confirm, parameterized SQL, remove dead code, handle serialization errors * fix: use native Tauri dialog for confirm on Windows (WebView2 broken) * fix: prevent double callback in platform.confirm and distinguish tag error types * fix: separate missing-target, null-UUID, and execution errors in tag apply --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: James Pine <ijamespine@me.com>