Skip to content

feat: lig-9585 Introduce changes in hooks required to render categorical metadata in distribution panel - #1796

Open
ikondrat wants to merge 7 commits into
mainfrom
feature/lig-9585-categorical-metadata-distribution.hooks
Open

feat: lig-9585 Introduce changes in hooks required to render categorical metadata in distribution panel#1796
ikondrat wants to merge 7 commits into
mainfrom
feature/lig-9585-categorical-metadata-distribution.hooks

Conversation

@ikondrat

@ikondrat ikondrat commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

What has changed and why?

This PR introduces changes required to render dataset distribution panel with categorical metadata values

How has it been tested?

Accompanied by unit tests

Did you update CHANGELOG.md?

  • Yes
  • Not needed (internal change)

Summary by CodeRabbit

  • New Features
    • Added categorical metadata filter chips alongside numeric range chips, including toggle/clear behavior, formatted categorical labels, and remembered selections.
    • Enabled categorical filtering end-to-end across image, video, and frame filter flows, with dimensions and metadata scoped to their respective derived collection IDs.
  • Bug Fixes
    • Missing categorical values are now represented as null in filter predicates and chip labels.
    • Improved dimension-bounds loading validation with clearer errors for incomplete responses.
  • Tests
    • Expanded unit coverage for chip rendering, categorical filter request construction, query-key generation, and selection persistence.

@ikondrat
ikondrat requested a review from a team as a code owner July 29, 2026 10:35
@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Categorical metadata selections are supported in filter chips, propagated through image, frame, infinite-image, and video filter construction, and scoped through derived collection IDs. Dimension store handling and response validation are also updated.

Changes

Metadata filtering and collection scoping

Layer / File(s) Summary
Categorical chip state and interactions
lightly_studio_view/src/lib/components/MetadataFilterChips/*
Filter chips now represent numeric or categorical metadata, remember categorical selections, handle toggle and clear operations, and format categorical values including missing-value variants.
Categorical filter propagation and coverage
lightly_studio_view/src/lib/hooks/useFramesFilter/..., useImageFilters/..., useImagesInfinite/..., useMetadataFilters/..., useVideoFilters/...
Shared metadata types and tests cover categorical selections, null values, generated metadata filters, and query keys across filter paths.
Collection-scoped metadata and dimension inputs
lightly_studio_view/src/routes/datasets/...
New helpers derive metadata and dimensions collection IDs, and the route layout passes them to the corresponding hooks.
Dimension store updates and response validation
lightly_studio_view/src/lib/hooks/useDimensions/..., lightly_studio_view/src/lib/services/loadDimensionBounds.*
Readable collection IDs trigger dimension loading with explicit cleanup, while dimension-bound responses are structurally validated and tested.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant MetadataFilterChips
  participant useMetadataFilterChips
  participant useMetadataFilters
  participant FilterBuilder
  participant SampleFilter
  MetadataFilterChips->>useMetadataFilterChips: toggle categorical chip
  useMetadataFilterChips->>useMetadataFilters: update categorical metadata values
  useMetadataFilters->>FilterBuilder: create metadata filters
  FilterBuilder->>SampleFilter: add metadata_filters
  SampleFilter-->>MetadataFilterChips: updated filter state
Loading

Possibly related PRs

Suggested reviewers: leonardorosaa

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and clearly describes the main change: adding hook support for categorical metadata in the distribution panel.
Description check ✅ Passed The description matches the template and covers change summary, testing, and changelog status, though the testing details are brief.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/lig-9585-categorical-metadata-distribution.hooks

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@ikondrat
ikondrat force-pushed the feature/lig-9585-categorical-metadata-distribution.hooks branch from e3017e5 to 82dc719 Compare July 29, 2026 10:37

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e3017e5b4d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@lightly-fast-track-bot

lightly-fast-track-bot Bot commented Jul 29, 2026

Copy link
Copy Markdown

❌  Fast Track: checks did not pass

Guardrail Result Message
dummy Always passes.
frontend/complexity lightly_studio_view/src/lib/hooks/useFramesFilter/frameFilter.ts:23 — Arrow function has a complexity of 25. Maximum allowed is 10. lightly_studio_view/src/lib/hooks/useVideoFilters/useVideoFilters.ts:25 — Arrow function has a complexity of 30. Maximum allowed is 10. lightly_studio_view/src/routes/datasets/[dataset_id]/[collection_type]/[collection_id]/+layout.svelte:171 — Function 'handleSelectAllKeydown' has a complexity of 11. Maximum allowed is 10. lightly_studio_view/src/routes/datasets/[dataset_id]/[collection_type]/[collection_id]/+layout.svelte:404 — Arrow function has a complexity of 11. Maximum allowed is 10.
backend/complexity 0 file(s) checked.
backend/coverage 0 file(s) checked.
diff-size PR adds 461 line(s), which exceeds the limit of 215.
frontend/coverage [FAIL] lightly_studio_view/src/lib/hooks/useFrames/useFrames.ts: no test file found [FAIL] lightly_studio_view/src/lib/components/MetadataFilterChips/MetadataFilterChips.svelte: coverage data not found [FAIL] lightly_studio_view/src/lib/components/MetadataFilterChips/useMetadataFilterChips.svelte.ts: coverage data not found [FAIL] lightly_studio_view/src/lib/hooks/useDimensions/useDimensions.ts: coverage data not found [FAIL] lightly_studio_view/src/lib/hooks/useFramesFilter/frameFilter.ts: coverage data not found [FAIL] lightly_studio_view/src/lib/hooks/useVideoFilters/useVideoFilters.ts: coverage data not found [FAIL] lightly_studio_view/src/lib/services/loadDimensionBounds.ts: coverage data not found [FAIL] lightly_studio_view/src/routes/datasets/[dataset_id]/[collection_type]/[collection_id]/+layout.svelte: coverage data not found [FAIL] lightly_studio_view/src/routes/datasets/[dataset_id]/[collection_type]/[collection_id]/getDimensionsCollectionId.ts: coverage data not found [FAIL] lightly_studio_view/src/routes/datasets/[dataset_id]/[collection_type]/[collection_id]/getMetadataCollectionId.ts: coverage data not found

View the guardrail run

To run the guardrails locally, from fast_track/ run make install once, then make run-guardrails (or GUARDRAILS=<name1>,<name2> make run-guardrails for some guardrails).

Reflects 899b048.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (5)
lightly_studio_view/src/lib/components/MetadataFilterChips/useMetadataFilterChips.svelte.ts (1)

114-130: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Categorical toggles skip PostHog tracking and hinge on lastCategoricalValues membership.

Two things in the categorical branch:

  1. trackFilterChanged is never called, so metadata_filter_changed events are only emitted for numeric chips — the analytics for categorical filters will silently be missing.
  2. Using lastCategoricalValues[key] as the "is categorical" discriminator is fragile: a categorical key whose selection was never recorded (empty selection, or effect not yet flushed) falls through to the numeric path and no-ops. Checking the store (key in categoricalStore.current) or passing chip.kind from the caller is more direct.
♻️ Sketch
-        if (lastCategoricalValues[key]) {
+        if (key in categoricalStore.current || lastCategoricalValues[key]) {
             updateCategoricalMetadataValues({
                 ...categoricalStore.current,
-                [key]: checked ? lastCategoricalValues[key] : []
+                [key]: checked ? (lastCategoricalValues[key] ?? []) : []
             });
+            trackFilterChanged(key, checked ? 'enabled' : 'disabled');
             return;
         }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@lightly_studio_view/src/lib/components/MetadataFilterChips/useMetadataFilterChips.svelte.ts`
around lines 114 - 130, Update handleToggle so categorical detection uses key
membership in categoricalStore.current rather than lastCategoricalValues[key],
preserving categorical handling even when no prior selection exists. Emit
trackFilterChanged for categorical toggles as well, using the same
enabled/disabled status, while keeping the numeric range behavior unchanged.
lightly_studio_view/src/lib/components/DatasetDistributionPanel/DatasetDistributionPanel.svelte (2)

366-376: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Inline retry diverges from the full-state retry control.

The stale-data retry is a bare <button class="underline"> with no data-testid, while the empty-state retry at Line 448 uses the Button component with data-testid="metadata-categorical-retry". Reusing the same component/test id keeps styling consistent and makes this path assertable (the test at DatasetDistributionPanel.test.ts Lines 462-496 currently only checks the alert text, never the click).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@lightly_studio_view/src/lib/components/DatasetDistributionPanel/DatasetDistributionPanel.svelte`
around lines 366 - 376, The stale-data retry in DatasetDistributionPanel should
use the same Button component and data-testid="metadata-categorical-retry" as
the empty-state retry, replacing the bare button while preserving
onCategoricalRetry and the existing Retry label. Update the related test to
query this test id and verify clicking it invokes the retry handler.

184-192: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Key categorical configs by source + group id.

categoricalConfigs is keyed on activeGroup.id alone. Group ids are only unique within a source, so two sources exposing the same group id (e.g. city) would share orientation/top-N state. Composing the key avoids the collision.

♻️ Proposed change
-    let categoricalConfigs = $state<Record<string, DistributionConfig>>({});
+    let categoricalConfigs = $state<Record<string, DistributionConfig>>({});
+    const categoricalConfigKey = $derived(
+        activeGroup ? `${activeSource.id}:${activeGroup.id}` : undefined
+    );
     const categoricalConfig = $derived<DistributionConfig>(
-        activeGroup
-            ? (categoricalConfigs[activeGroup.id] ?? {
+        categoricalConfigKey
+            ? (categoricalConfigs[categoricalConfigKey] ?? {
                   ...defaultCategoricalConfig,
                   n: Math.max(categoricalData.length, 1)
               })
             : defaultCategoricalConfig
     );

setCategoricalConfig needs the same key.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@lightly_studio_view/src/lib/components/DatasetDistributionPanel/DatasetDistributionPanel.svelte`
around lines 184 - 192, Update categoricalConfigs and its related accessors to
key entries by both source and group identity rather than activeGroup.id alone,
preventing collisions between sources with the same group id. Apply the
identical composite-key construction in setCategoricalConfig so reads and writes
use the same key.
lightly_studio_view/src/lib/components/DatasetDistributionPanel/DatasetDistributionPanel.test.ts (1)

418-423: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Scope the Missing lookup to the dialog.

getAllByText('Missing') searches the whole document, so it also picks up anything the categorical filter or sr-only summary renders with that exact text — the toHaveLength(2) assertion and the [1] index then depend on unrelated markup. Query within the dialog instead.

♻️ Proposed change
         await fireEvent.click(screen.getByRole('tab', { name: 'Manual' }));
-        const missingOptions = screen.getAllByText('Missing');
+        const missingOptions = within(screen.getByRole('dialog')).getAllByText('Missing');
         expect(missingOptions).toHaveLength(2);

Add within to the @testing-library/svelte import.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@lightly_studio_view/src/lib/components/DatasetDistributionPanel/DatasetDistributionPanel.test.ts`
around lines 418 - 423, Scope the `Missing` text lookup in the test around the
dialog opened by `dataset-distribution-configure` to avoid unrelated document
content. Add the `within` testing-library helper to the existing import, obtain
the dialog element, and use it for the lookup while preserving the expected
count and selected option.
lightly_studio_view/src/lib/components/DatasetDistributionPanel/MetadataCategoricalFilter.svelte (1)

54-74: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a short comment explaining the Missing/Other disambiguation rules (P2 style).

The four-branch disambiguation logic (literal 'Missing'/'Other' values vs. semantic missing/aggregate buckets) is correct but non-obvious from the code alone. A one-line comment per branch would help future readers avoid re-deriving the intent.

As per coding guidelines, "focus on code style, use succinct comments, assign style comments priority P2, and ensure the pull request follows the ai_guidelines documentation."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@lightly_studio_view/src/lib/components/DatasetDistributionPanel/MetadataCategoricalFilter.svelte`
around lines 54 - 74, Add succinct comments to the branches in optionLabel
explaining the disambiguation between literal “Missing”/“Other” values and
semantic missing/aggregate buckets; preserve the existing conditions and labels
without changing behavior.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@lightly_studio_view/src/lib/components/DatasetDistributionPanel/MetadataCategoricalFilter.svelte`:
- Around line 109-112: Update the trigger button’s accessible name in
MetadataCategoricalFilter so it includes the dynamic loading or selection
summary instead of overriding it with the static “Select metadata values” label.
Preserve the visible span’s existing loading and summary behavior while ensuring
screen readers receive the current state.

---

Nitpick comments:
In
`@lightly_studio_view/src/lib/components/DatasetDistributionPanel/DatasetDistributionPanel.svelte`:
- Around line 366-376: The stale-data retry in DatasetDistributionPanel should
use the same Button component and data-testid="metadata-categorical-retry" as
the empty-state retry, replacing the bare button while preserving
onCategoricalRetry and the existing Retry label. Update the related test to
query this test id and verify clicking it invokes the retry handler.
- Around line 184-192: Update categoricalConfigs and its related accessors to
key entries by both source and group identity rather than activeGroup.id alone,
preventing collisions between sources with the same group id. Apply the
identical composite-key construction in setCategoricalConfig so reads and writes
use the same key.

In
`@lightly_studio_view/src/lib/components/DatasetDistributionPanel/DatasetDistributionPanel.test.ts`:
- Around line 418-423: Scope the `Missing` text lookup in the test around the
dialog opened by `dataset-distribution-configure` to avoid unrelated document
content. Add the `within` testing-library helper to the existing import, obtain
the dialog element, and use it for the lookup while preserving the expected
count and selected option.

In
`@lightly_studio_view/src/lib/components/DatasetDistributionPanel/MetadataCategoricalFilter.svelte`:
- Around line 54-74: Add succinct comments to the branches in optionLabel
explaining the disambiguation between literal “Missing”/“Other” values and
semantic missing/aggregate buckets; preserve the existing conditions and labels
without changing behavior.

In
`@lightly_studio_view/src/lib/components/MetadataFilterChips/useMetadataFilterChips.svelte.ts`:
- Around line 114-130: Update handleToggle so categorical detection uses key
membership in categoricalStore.current rather than lastCategoricalValues[key],
preserving categorical handling even when no prior selection exists. Emit
trackFilterChanged for categorical toggles as well, using the same
enabled/disabled status, while keeping the numeric range behavior unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: cc4b08d1-11cd-4397-bafe-72db88d0a23c

📥 Commits

Reviewing files that changed from the base of the PR and between 60e1646 and e3017e5.

📒 Files selected for processing (28)
  • lightly_studio_view/src/lib/components/BarChart/BarChart.svelte
  • lightly_studio_view/src/lib/components/DatasetDistributionPanel/DatasetDistributionPanel.svelte
  • lightly_studio_view/src/lib/components/DatasetDistributionPanel/DatasetDistributionPanel.test.ts
  • lightly_studio_view/src/lib/components/DatasetDistributionPanel/DistributionConfigDialog/DistributionConfigDialog.svelte
  • lightly_studio_view/src/lib/components/DatasetDistributionPanel/DistributionConfigDialog/DistributionConfigDialog.test.ts
  • lightly_studio_view/src/lib/components/DatasetDistributionPanel/ExpandDialog/ExpandDialog.svelte
  • lightly_studio_view/src/lib/components/DatasetDistributionPanel/ExpandDialog/ExpandDialog.test.ts
  • lightly_studio_view/src/lib/components/DatasetDistributionPanel/MetadataCategoricalFilter.svelte
  • lightly_studio_view/src/lib/components/DatasetDistributionPanel/MetadataCategoricalFilter.test.ts
  • lightly_studio_view/src/lib/components/DatasetDistributionPanel/PanelHeader/PanelHeader.svelte
  • lightly_studio_view/src/lib/components/DatasetDistributionPanel/PanelHeader/PanelHeader.test.ts
  • lightly_studio_view/src/lib/components/DatasetDistributionPanel/selectVisibleCounts.test.ts
  • lightly_studio_view/src/lib/components/DatasetDistributionPanel/selectVisibleCounts.ts
  • lightly_studio_view/src/lib/components/DatasetDistributionPanel/types.ts
  • lightly_studio_view/src/lib/components/Histogram/buildHistogramOption.test.ts
  • lightly_studio_view/src/lib/components/Histogram/buildHistogramOption.ts
  • lightly_studio_view/src/lib/components/MetadataFilterChips/MetadataFilterChips.svelte
  • lightly_studio_view/src/lib/components/MetadataFilterChips/MetadataFilterChips.test.ts
  • lightly_studio_view/src/lib/components/MetadataFilterChips/useMetadataFilterChips.svelte.ts
  • lightly_studio_view/src/lib/components/MetadataFilterChips/useMetadataFilterChips.test.ts
  • lightly_studio_view/src/lib/hooks/useFramesFilter/frameFilter.test.ts
  • lightly_studio_view/src/lib/hooks/useFramesFilter/frameFilter.ts
  • lightly_studio_view/src/lib/hooks/useImageFilters/useImageFilters.test.ts
  • lightly_studio_view/src/lib/hooks/useImagesInfinite/buildRequestBody.test.ts
  • lightly_studio_view/src/lib/hooks/useImagesInfinite/createImagesInfiniteOptions.test.ts
  • lightly_studio_view/src/lib/hooks/useMetadataFilters/useMetadataFilters.test.ts
  • lightly_studio_view/src/lib/hooks/useVideoFilters/useVideoFilters.test.ts
  • lightly_studio_view/src/lib/hooks/useVideoFilters/useVideoFilters.ts

@ikondrat ikondrat changed the title Feature/lig 9585 categorical metadata distribution.hooks feat: lig-9585 Introduce changes in hooks required to render categorical metadata in distribution panel Jul 29, 2026
@ikondrat

Copy link
Copy Markdown
Contributor Author

/review

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lightly_studio_view/src/lib/hooks/useDimensions/useDimensions.ts`:
- Around line 56-72: Update the collectionId subscription in the readable
returned by the dimensions hook to handle falsy values by invoking the existing
reset helper that clears dimensionsBounds and dimensionsValues and resets
lastCollectionId. Apply the same reset handling in the no-collection-ID path
before returning source, ensuring undefined invalidates prior collection state
and prevents stale responses from repopulating it.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 61b25f0a-4fbf-4cc7-a1af-2c2e364ddbd2

📥 Commits

Reviewing files that changed from the base of the PR and between 82dc719 and 54efea0.

📒 Files selected for processing (11)
  • lightly_studio_view/src/lib/components/MetadataFilterChips/MetadataFilterChips.svelte
  • lightly_studio_view/src/lib/components/MetadataFilterChips/MetadataFilterChips.test.ts
  • lightly_studio_view/src/lib/hooks/useDimensions/useDimensions.test.ts
  • lightly_studio_view/src/lib/hooks/useDimensions/useDimensions.ts
  • lightly_studio_view/src/lib/services/loadDimensionBounds.test.ts
  • lightly_studio_view/src/lib/services/loadDimensionBounds.ts
  • lightly_studio_view/src/routes/datasets/[dataset_id]/[collection_type]/[collection_id]/+layout.svelte
  • lightly_studio_view/src/routes/datasets/[dataset_id]/[collection_type]/[collection_id]/getDimensionsCollectionId.test.ts
  • lightly_studio_view/src/routes/datasets/[dataset_id]/[collection_type]/[collection_id]/getDimensionsCollectionId.ts
  • lightly_studio_view/src/routes/datasets/[dataset_id]/[collection_type]/[collection_id]/getMetadataCollectionId.test.ts
  • lightly_studio_view/src/routes/datasets/[dataset_id]/[collection_type]/[collection_id]/getMetadataCollectionId.ts

Comment on lines +56 to +72
if (!isReadableStore<string | undefined>(collectionId)) {
loadInitialDimensionBounds(collectionId);
return source;
}

return derived([source, collectionId], ([$source, $collectionId]) => {
if ($collectionId) {
loadInitialDimensionBounds($collectionId);
}
return readable(get(source), (set) => {
const unsubscribeSource = source.subscribe(set);
const unsubscribeCollectionId = collectionId.subscribe(($collectionId) => {
if ($collectionId) {
loadInitialDimensionBounds($collectionId);
}
});

return $source;
return () => {
unsubscribeSource();
unsubscribeCollectionId();
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Clear dimension state when the readable collection ID becomes undefined.

getDimensionsCollectionId intentionally emits undefined for views without an image-backed collection, but this subscriber silently ignores that transition. The module-level dimensionsBounds and dimensionsValues therefore retain the previous image collection’s range, allowing stale dimensions to affect later filter/count requests. Clear both stores and reset lastCollectionId on the falsy transition so in-flight responses cannot repopulate stale values. Based on the supplied collection-scoping contract, undefined is a valid state that must invalidate the previous collection.

Suggested reset handling
         const unsubscribeCollectionId = collectionId.subscribe(($collectionId) => {
             if ($collectionId) {
                 loadInitialDimensionBounds($collectionId);
+            } else {
+                lastCollectionId.set(null);
+                dimensionsBounds.set(null);
+                dimensionsValues.set(null);
             }
         });

Apply the same reset helper to the no-collection-ID path.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!isReadableStore<string | undefined>(collectionId)) {
loadInitialDimensionBounds(collectionId);
return source;
}
return derived([source, collectionId], ([$source, $collectionId]) => {
if ($collectionId) {
loadInitialDimensionBounds($collectionId);
}
return readable(get(source), (set) => {
const unsubscribeSource = source.subscribe(set);
const unsubscribeCollectionId = collectionId.subscribe(($collectionId) => {
if ($collectionId) {
loadInitialDimensionBounds($collectionId);
}
});
return $source;
return () => {
unsubscribeSource();
unsubscribeCollectionId();
};
if (!isReadableStore<string | undefined>(collectionId)) {
loadInitialDimensionBounds(collectionId);
return source;
}
return readable(get(source), (set) => {
const unsubscribeSource = source.subscribe(set);
const unsubscribeCollectionId = collectionId.subscribe(($collectionId) => {
if ($collectionId) {
loadInitialDimensionBounds($collectionId);
} else {
lastCollectionId.set(null);
dimensionsBounds.set(null);
dimensionsValues.set(null);
}
});
return () => {
unsubscribeSource();
unsubscribeCollectionId();
};
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lightly_studio_view/src/lib/hooks/useDimensions/useDimensions.ts` around
lines 56 - 72, Update the collectionId subscription in the readable returned by
the dimensions hook to handle falsy values by invoking the existing reset helper
that clears dimensionsBounds and dimensionsValues and resets lastCollectionId.
Apply the same reset handling in the no-collection-ID path before returning
source, ensuring undefined invalidates prior collection state and prevents stale
responses from repopulating it.

ikondrat added 4 commits July 29, 2026 16:40
…s' of github.com:lightly-ai/lightly-studio into feature/lig-9585-categorical-metadata-distribution.hooks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant