Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add default labels visibility in dataset app config + selective labels rendering optimization #5356

Merged
merged 80 commits into from
Feb 7, 2025

Conversation

sashankaryal
Copy link
Contributor

@sashankaryal sashankaryal commented Jan 8, 2025

What changes are proposed in this pull request?

This PR adds a new dataset app config for configuring default visibility of labels. If app_config.default_visibility_labels is not set, all labels are not rendered by default.

Also adds an optimization of not rendering labels if it's unchecked in the sidebar.

Notes to the reviewer

The most relevant files on the app side are:
- useSelect.ts
- labelsVisibility.ts
- useDetectNewActiveLabelFields.ts
- lookers/abstract.ts
- worker/index.ts
- worker/async-labels-rendering-manager.test.ts

Known Issues:

  • Recoloring samples too fast has a race condition (data clone error in postMessage). This doesn't crash the app, but might lead to confusion. I think it's safe to fix it in upcoming PRs.

How is this patch tested? If it is not, please explain why.

Unit tests where applicable. E2E tests.

For local smoke testing, use following script:

import fiftyone as fo
import fiftyone.zoo as foz

dataset = foz.load_zoo_dataset("coco-2014", split="validation")

dataset.app_config.default_visibility_labels = {"include": ["detections"]}
dataset.save()

Place a debugger in painter.ts for Segmentations and observe it doesn't get called. Scroll through the grid and check/uncheck fields to see different behavior.

Release Notes

Is this a user-facing change that should be mentioned in the release notes?

  • No. You can skip the rest of this section.
  • Yes. Give a description of this change to be included in the release
    notes for FiftyOne users.

What areas of FiftyOne does this PR affect?

  • App: FiftyOne application changes
  • Build: Build and test infrastructure changes
  • Core: Core fiftyone Python library changes
  • Documentation: FiftyOne documentation changes
  • Other

Summary by CodeRabbit

  • New Features
    • Introduced customizable default visibility settings for datasets, allowing users to refine which labels are displayed by default.
    • Added new hooks for selective rendering of modals based on active label fields and current frame data for different types of lookers.
    • Implemented methods for refreshing overlays and samples in the ImaVidLooker class to enhance user interaction with video data.
    • Added a new function to synchronize label fields for lookers, improving the management of active labels.
    • Enhanced error messaging in the UI to provide more detailed feedback during issues.
  • Improvements
    • Enhanced overlay rendering and sample refresh behavior for a smoother, more reliable viewing experience.
    • Optimized media viewer components (image and video) for improved responsiveness and performance.
  • Tests
    • Expanded test coverage to ensure robust label processing and media interaction, contributing to overall system stability.

@sashankaryal sashankaryal requested a review from a team January 8, 2025 00:21
@sashankaryal sashankaryal self-assigned this Jan 8, 2025
Copy link
Contributor

coderabbitai bot commented Jan 8, 2025

Warning

Rate limit exceeded

@sashankaryal has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 16 minutes and 26 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between b175c04 and 5d577ff.

📒 Files selected for processing (2)
  • app/packages/looker/src/overlays/detection.ts (3 hunks)
  • app/packages/looker/src/worker/disk-overlay-decoder.ts (3 hunks)

Walkthrough

This pull request introduces extensive modifications across multiple modules. The changes include updates to configuration files, component imports, and state management. Enhancements in overlay processing, asynchronous label rendering, and modal selective rendering are implemented. New types, selectors, and GraphQL fields for label visibility have been added, while test suites and e2e cases have been updated. Several functions now include refined parameter lists and cleanup logic, improving resource management. Overall, the modifications focus on improved modularity and control flow across lookers, overlays, workers, and UI components.

Changes

File(s) Change Summary
.gitignore Added .env entry to ignore environment files.
app/packages/core/src/components/Dataset.tsx Reordered import statements (moved useTrackEvent below EventTracker and re-added EventTracker).
app/packages/core/src/components/Grid/useRefreshers.ts Added import for gridActivePathsLUT, updated useEffect for proper unsubscribe, and modified dispose signature to accept an id parameter.
app/packages/looker/src/lookers/abstract.ts Added constant UPDATING_SAMPLES_IDS, introduced refreshSample method, updated cleanOverlays signature (added boolean parameter), made state public, and added asyncLabelsRenderingManager property.
app/packages/looker/src/overlays/{base.ts,detection.ts,heatmap.ts,segmentation.ts} Updated cleanup method signatures to accept a boolean parameter; added new properties (mask_path, renderStatus) and refined validation logic in constructors and drawing methods.
app/packages/looker/src/worker/{disk-overlay-decoder.ts,index.ts,painter.ts,resolve-color.ts,shared.ts} Updated imports and added usage of DenseLabelRenderStatus, introduced new property assignments (e.g. setting renderStatus to "decoded"/"painting"/"painted"), added new interface ResolveColor, and modified various function signatures to include an activePaths parameter.
app/packages/looker/src/worker/tests/indexed-png-decoder.test.ts Deleted obsolete test file for PNG decoding.
app/packages/relay/src/fragments/datasetAppConfigFragment.ts Added new field defaultVisibilityLabels with subfields include and exclude.
app/packages/state/src/recoil/{schema.ts,selectors.ts,types.ts} Added new selectors (defaultVisibleLabels, defaultVisibilityLabels), new type DefaultVisibilityLabelsConfig, and updated DatasetAppConfig to include default visibility settings.
app/packages/utilities/src/schema.ts Added new constants for embedded doc types, updated the getFieldsWithEmbeddedDocType function, and introduced getDenseLabelNames function.
app/schema.graphql Introduced new type FieldVisibilityConfig and integrated it into DatasetAppConfig.
fiftyone/{core/odm/dataset.py,server/query.py} Added or updated the default_visibility_labels field for dataset configuration and GraphQL query support.
app/packages/looker/src/lookers/{frame-reader.ts,video.ts} Updated interfaces and method signatures to include an activePaths parameter for lookers.
app/packages/core/src/components/Grid/Grid.tsx Added getCurrentActiveLabelFields function and updated cleanup logic to remove entries from gridActivePathsLUT.
app/packages/core/src/components/Grid/useSelect.ts Integrated new hook useDetectNewActiveLabelFields to refresh overlays based on active label fields.
e2e-pw/src/oss/specs/{overlays/detection-mask.spec.ts,sidebar/sidebar.spec.ts,smoke-tests/tagger.spec.ts} Updated dataset configuration and test cases to focus on ground truth labels and include sidebar parameter handling in setup.
app/packages/state/src/{labelsVisibility.test.ts,labelsVisibility.ts} Introduced tests for and implementation of computeDefaultVisibleLabels to determine default label visibility based on include/exclude config.
app/packages/looker/src/processOverlays.ts Enhanced overlay processing with additional validation checks for segmentation and heatmap overlays.
app/packages/core/src/components/Sidebar/{syncAndGetNewLabels.test.ts,syncAndGetNewLabels.ts} Added a function to synchronize and retrieve new active label fields for lookers along with corresponding tests.
app/packages/looker/src/worker/{async-labels-rendering-manager.ts,async-labels-rendering-manager.test.ts} Added new types, constants, and methods to manage asynchronous label rendering jobs and their worker communications.
app/packages/looker/src/util.ts Updated createWorker to return null if web worker support is not available.
e2e-pw/src/oss/fixtures/loader.ts Modified waitUntilGridVisible to include retry logic and options for page navigation.
e2e-pw/src/shared/network-utils/port.ts Added logging to indicate when a port is available.
app/packages/core/src/components/Sidebar/useDetectNewActiveLabelFields.ts New hook file added to detect new active label fields, maintaining lookup tables for modal and grid contexts.
e2e-pw/src/oss/fixtures/foServer.ts Appended --clean_start to the command-line arguments for starting the web server.
e2e-pw/src/oss/poms/modal/imavid-controls.ts Updated modal controls with new properties (lookerControls, settingsButton) and methods (toggleSettings, setLooping), and renamed a frame-waiting method.
e2e-pw/src/oss/specs/groups/{ima-vid.spec.ts,nested-dynamic-groups.spec.ts} Adjusted test sequences to incorporate new modal settings and looping configurations.
app/packages/looker/src/state.ts Updated the Sample type to include an optional frames property.
app/packages/core/src/components/Modal/{ImaVidLooker.tsx,ModalLooker.tsx,VideoLooker.tsx} Updated component type annotations, integrated new hooks (useImavidModalSelectiveRendering, useVideoModalSelectiveRendering), and refined modal rendering flow.
app/packages/core/src/components/Modal/use-modal-selective-rendering.ts Added three custom hooks for selective modal rendering for images, ImaVid, and video lookers.
app/packages/state/src/hooks/useLookerStore.ts Expanded the Lookers union type to include ImaVidLooker.
app/packages/looker/src/lookers/imavid/index.ts Introduced a refreshSample method to update samples using asynchronous label painting jobs.

Sequence Diagram(s)

sequenceDiagram
  participant Client as AsyncLabelsRenderingManager
  participant Queue as JobQueue
  participant Worker as FreeWorker
  participant System as WorkerResponse Handler

  Client->>Queue: enqueueLabelPaintingJob(sample, labels)
  Queue->>Worker: assignJob(sample, labels)
  Worker->>Worker: Process label painting job
  Worker->>Queue: Send processed job result
  Queue->>Client: Resolve job and update sample state
  Client->>System: Trigger overlay updater
Loading
sequenceDiagram
  participant Modal as Modal Component
  participant Hook as useImageModalSelectiveRendering
  participant Detector as useDetectNewActiveLabelFields
  participant Looker as Looker Instance

  Modal->>Hook: Call useImageModalSelectiveRendering(id, looker)
  Hook->>Detector: Retrieve new active label fields
  Detector->>Hook: Return new fields (or null)
  alt New fields detected
    Hook->>Looker: Invoke refreshSample(newFields) to update sample
  else No new fields
    Hook->>Looker: Maintain current sample state
  end
Loading

Suggested labels

app, documentation

Suggested reviewers

  • benjaminpkane

Poem

Hey there, I'm a bunny on the run,
Hopping through code changes in the sun.
With overlays, hooks, and labels in play,
I nibble on commits all through the day.
Bugs hide away, as my paws tap and code—🐰💻
Celebrating tweaks with a hop and a joyful ode!


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@sashankaryal sashankaryal changed the title Feat/app config default labels visibility add default labels visibility in dataset app config + selective labels rendering optimization Jan 8, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (15)
app/packages/relay/src/fragments/datasetAppConfigFragment.ts (1)

8-11: Add type definitions for include/exclude fields

While the structure is logical for visibility filtering, consider adding type definitions for the include and exclude fields to ensure type safety and better IDE support.

    defaultVisibilityLabels {
-     include
-     exclude
+     include: [String!]
+     exclude: [String!]
    }
app/packages/looker/src/worker/shared.ts (1)

3-3: Add JSDoc comments for the render status type

Consider adding documentation to explain the purpose and meaning of each render status state for better maintainability.

+/**
+ * Represents the rendering lifecycle of a dense label
+ * @type {null} Initial state before rendering begins
+ * @type {"painting"} Label is currently being rendered
+ * @type {"painted"} Label has been rendered successfully
+ * @type {"decoded"} Label data has been decoded but not yet rendered
+ */
export type DenseLabelRenderStatus = null | "painting" | "painted" | "decoded";
app/packages/looker/src/worker/resolve-color.ts (1)

33-42: Enhance promise error handling

The promise creation lacks error handling. Consider adding error handling and timeout mechanism.

-          seedPromises[key] = new Promise((resolve) => {
+          seedPromises[key] = new Promise((resolve, reject) => {
+            const timeout = setTimeout(() => {
+              delete seedRequests[key];
+              delete seedPromises[key];
+              reject(new Error('Color resolution timeout'));
+            }, 5000);
             seedRequests[key] = resolve;
             postMessage({
               method: "requestColor",
               key,
               seed,
               pool,
             });
+            return () => clearTimeout(timeout);
           });
app/packages/core/src/components/Sidebar/useOnSidebarSelectionChange.ts (2)

23-60: Optimize effect performance and add cleanup

The effect has several optimization opportunities:

  1. The Set operations could be memoized
  2. Missing cleanup function could lead to memory leaks
  3. Consider using useMemo for currentActiveLabelFields
+ const currentActiveLabelFields = useMemo(
+   () => new Set(activeLabelFieldsValue),
+   [activeLabelFieldsValue]
+ );

  useEffect(() => {
    const thisPageActiveFields = sidebarTracker.get(gridPageValue);

-   const currentActiveLabelFields = new Set(activeLabelFieldsValue);

    if (currentActiveLabelFields.size === 0) {
      return;
    }

    // ... rest of the effect

+   return () => {
+     // Cleanup any subscriptions or pending state updates
+   };
  }, [activeLabelFieldsValue, sidebarTracker]);

15-17: Consider using useLayoutEffect for ref updates

Updating refs in render phase could lead to inconsistencies. Consider moving the ref update to useLayoutEffect.

  const gridPageValueRef = useRef(gridPageValue);

- gridPageValueRef.current = gridPageValue;
+ useLayoutEffect(() => {
+   gridPageValueRef.current = gridPageValue;
+ }, [gridPageValue]);
app/packages/utilities/src/schema.ts (1)

70-92: Add JSDoc documentation for the enhanced function signature.

The function has been enhanced with new parameters, but lacks documentation explaining their purpose and usage.

Add this documentation above the function:

+/**
+ * Returns fields with specified embedded document type(s).
+ * @param schema The schema to search through
+ * @param embeddedDocType Single or array of embedded document types to match
+ * @param shouldRecurse Whether to recursively search nested fields (default: true)
+ * @returns Array of matching fields
+ */
 export function getFieldsWithEmbeddedDocType(
app/packages/core/src/components/Grid/useRefreshers.ts (1)

100-109: Consider debouncing the refresh operation.

Refreshing all lookers on every label toggle could impact performance if there are many lookers. Consider implementing debouncing to prevent rapid consecutive refreshes.

+import { debounce } from 'lodash';
+
 const gridLabelsToggleTracker = useOnSidebarSelectionChange({ modal: false });
+const debouncedRefresh = useMemo(
+  () =>
+    debounce(() => {
+      lookerStoreRef.current?.forEach((looker) => {
+        looker.refreshSample();
+      });
+    }, 250),
+  []
+);
 
 useEffect(() => {
-  lookerStoreRef.current?.forEach((looker) => {
-    looker.refreshSample();
-  });
+  debouncedRefresh();
+  return () => {
+    debouncedRefresh.cancel();
+  };
 }, [gridLabelsToggleTracker]);
app/packages/looker/src/overlays/base.ts (1)

77-77: Add JSDoc documentation for the cleanup method.

The new setTargetsToNull parameter needs documentation to explain its purpose and when it should be set to true/false.

Add this documentation above the method:

+/**
+ * Cleans up resources used by the overlay.
+ * @param setTargetsToNull When true, sets target references to null for garbage collection
+ */
 cleanup?(setTargetsToNull: boolean): void;
app/packages/looker/src/worker/disk-overlay-decoder.ts (1)

134-135: Consider using proper type definition instead of type assertion.

The type assertion to DenseLabelRenderStatus suggests that the label object's type definition might need improvement.

-  label.renderStatus = "decoded" as DenseLabelRenderStatus;
+  label.renderStatus = "decoded";

And update the label parameter type:

-  label: Record<string, any>,
+  label: Record<string, any> & { renderStatus?: DenseLabelRenderStatus },
app/packages/looker/src/overlays/detection.ts (1)

264-269: LGTM! Consider adding JSDoc.

The implementation is correct and maintains backward compatibility. Consider adding JSDoc to document the purpose of the setTargetsToNull parameter.

+/**
+ * Cleans up resources associated with the detection overlay.
+ * @param setTargetsToNull - When true, sets the label mask to null after cleanup
+ */
 public cleanup(setTargetsToNull = false): void {
app/packages/looker/src/worker/painter.ts (1)

33-34: LGTM! Consider using an enum for render status.

The implementation of render status tracking is consistent across all methods. For better type safety and maintainability, consider using an enum for the render status values.

enum RenderStatus {
  PAINTING = "painting",
  PAINTED = "painted",
  DECODED = "decoded",
  NONE = null
}

Also applies to: 146-147, 162-163, 193-194, 274-275, 407-408

app/packages/state/src/recoil/selectors.ts (1)

145-152: LGTM! Consider handling potential undefined values.

The selector implementation is clean and type-safe. Consider handling the case where datasetAppConfig might be undefined.

 export const defaultVisibilityLabels =
   selector<State.DefaultVisibilityLabelsConfig>({
     key: "defaultVisibilityLabels",
     get: ({ get }) => {
-      return get(datasetAppConfig)
+      const config = get(datasetAppConfig);
+      return config
         .defaultVisibilityLabels as State.DefaultVisibilityLabelsConfig | null;
     },
   });
app/packages/looker/src/worker/index.ts (1)

Line range hint 310-323: Consider documenting the 3D labels exception.

The code correctly processes all 3D labels regardless of active paths, but this behavior should be documented.

-    // we process all 3d labels regardless of active paths
+    // We process all 3D labels regardless of active paths because
+    // 3D label rendering has different optimization constraints
+    // and dependencies compared to 2D labels.
     process3DLabels(schema, sample);
fiftyone/core/odm/dataset.py (1)

518-519: Documentation needs clarification.

The docstring for default_visibility_labels should specify the expected dictionary structure (include/exclude lists) to match the GraphQL schema.

-        default_visibility_labels (None): config with include and exclude lists
-            of labels to render by default in the App
+        default_visibility_labels (None): a dictionary containing 'include' and 'exclude'
+            lists of label field names to control their default visibility in the App.
+            Example::
+                {
+                    "include": ["detections", "classifications"],
+                    "exclude": ["segmentations"]
+                }
app/schema.graphql (1)

332-335: LGTM! Consider adding descriptions.

The FieldVisibilityConfig type is well-structured. Consider adding descriptions to document the purpose of each field.

 type FieldVisibilityConfig {
+  """
+  List of label field names to show by default
+  """
   include: [String!]
+  """
+  List of label field names to hide by default
+  """
   exclude: [String!]
 }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c5d3bf5 and a814e18.

⛔ Files ignored due to path filters (3)
  • app/packages/app/src/pages/datasets/__generated__/DatasetPageQuery.graphql.ts is excluded by !**/__generated__/**, !**/__generated__/**
  • app/packages/relay/src/fragments/__generated__/datasetAppConfigFragment.graphql.ts is excluded by !**/__generated__/**, !**/__generated__/**
  • app/packages/relay/src/queries/__generated__/datasetQuery.graphql.ts is excluded by !**/__generated__/**, !**/__generated__/**
📒 Files selected for processing (25)
  • .gitignore (1 hunks)
  • app/packages/core/src/components/Dataset.tsx (1 hunks)
  • app/packages/core/src/components/Grid/useRefreshers.ts (3 hunks)
  • app/packages/core/src/components/Modal/Modal.tsx (2 hunks)
  • app/packages/core/src/components/Sidebar/useOnSidebarSelectionChange.ts (1 hunks)
  • app/packages/looker/src/lookers/abstract.ts (7 hunks)
  • app/packages/looker/src/overlays/base.ts (1 hunks)
  • app/packages/looker/src/overlays/detection.ts (1 hunks)
  • app/packages/looker/src/overlays/heatmap.ts (1 hunks)
  • app/packages/looker/src/overlays/segmentation.ts (1 hunks)
  • app/packages/looker/src/worker/disk-overlay-decoder.ts (2 hunks)
  • app/packages/looker/src/worker/index.ts (11 hunks)
  • app/packages/looker/src/worker/painter.ts (8 hunks)
  • app/packages/looker/src/worker/resolve-color.ts (1 hunks)
  • app/packages/looker/src/worker/shared.ts (1 hunks)
  • app/packages/looker/src/worker/tests/indexed-png-decoder.test.ts (0 hunks)
  • app/packages/relay/src/fragments/datasetAppConfigFragment.ts (1 hunks)
  • app/packages/state/src/recoil/schema.ts (4 hunks)
  • app/packages/state/src/recoil/selectors.ts (1 hunks)
  • app/packages/state/src/recoil/sidebar.ts (2 hunks)
  • app/packages/state/src/recoil/types.ts (2 hunks)
  • app/packages/utilities/src/schema.ts (3 hunks)
  • app/schema.graphql (2 hunks)
  • fiftyone/core/odm/dataset.py (2 hunks)
  • fiftyone/server/query.py (2 hunks)
💤 Files with no reviewable changes (1)
  • app/packages/looker/src/worker/tests/indexed-png-decoder.test.ts
✅ Files skipped from review due to trivial changes (2)
  • .gitignore
  • app/packages/core/src/components/Dataset.tsx
🧰 Additional context used
📓 Path-based instructions (19)
app/packages/looker/src/worker/shared.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/state/src/recoil/selectors.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/looker/src/overlays/base.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/relay/src/fragments/datasetAppConfigFragment.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/state/src/recoil/types.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/looker/src/overlays/segmentation.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/looker/src/overlays/heatmap.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/looker/src/overlays/detection.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/core/src/components/Modal/Modal.tsx (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/state/src/recoil/sidebar.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/core/src/components/Sidebar/useOnSidebarSelectionChange.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/core/src/components/Grid/useRefreshers.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/state/src/recoil/schema.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/looker/src/worker/disk-overlay-decoder.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/looker/src/worker/painter.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/looker/src/worker/index.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/looker/src/worker/resolve-color.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/looker/src/lookers/abstract.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/utilities/src/schema.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

🪛 Biome (1.9.4)
app/packages/state/src/recoil/schema.ts

[error] 372-381: This code is unreachable

... because either this statement, ...

... this statement, ...

... this statement, ...

... or this statement will return from the function beforehand

(lint/correctness/noUnreachable)

⏰ Context from checks skipped due to timeout of 90000ms (8)
  • GitHub Check: test / test-python (ubuntu-latest-m, 3.11)
  • GitHub Check: test / test-python (ubuntu-latest-m, 3.10)
  • GitHub Check: test / test-python (ubuntu-latest-m, 3.9)
  • GitHub Check: test / test-app
  • GitHub Check: e2e / test-e2e
  • GitHub Check: lint / eslint
  • GitHub Check: build / build
  • GitHub Check: build
🔇 Additional comments (23)
app/packages/utilities/src/schema.ts (2)

1-12: LGTM! Well-structured constants declaration.

The constants are well-organized, properly namespaced, and follow TypeScript naming conventions.


116-124: Document the rationale for non-recursive search.

The function explicitly sets shouldRecurse to false when calling getFieldsWithEmbeddedDocType. Please document why recursive search is not needed for dense labels.

Let's verify if dense labels can appear in nested fields:

app/packages/core/src/components/Grid/useRefreshers.ts (2)

68-81: LGTM! Proper cleanup implementation.

The subscription cleanup follows React best practices by properly unsubscribing when the component unmounts.


97-99: LGTM! Proper use of useRef for mutable references.

The implementation correctly uses useRef to maintain a reference to the looker store without triggering re-renders.

app/packages/looker/src/worker/disk-overlay-decoder.ts (1)

2-8: LGTM! Clean import organization.

Unused imports have been removed and new imports are properly organized.

app/packages/state/src/recoil/types.ts (2)

24-27: LGTM! Well-structured type definition.

The DefaultVisibilityLabelsConfig type is well-defined with clear optional properties for including and excluding labels.


127-127: LGTM! Interface extension is appropriate.

The addition of defaultVisibilityLabels to DatasetAppConfig aligns with the PR objectives for configuring default label visibility.

app/packages/looker/src/overlays/heatmap.ts (1)

209-214: LGTM! Cleanup method enhancement.

The cleanup method now provides more control over target cleanup through the optional parameter, maintaining backward compatibility with the default false value.

app/packages/looker/src/overlays/segmentation.ts (1)

264-269: LGTM! Consistent cleanup implementation.

The cleanup method follows the same pattern as the heatmap overlay, providing consistent behavior across overlay types.

app/packages/core/src/components/Modal/Modal.tsx (1)

182-186: Consider race condition mitigation.

While the implementation correctly refreshes the sample on sidebar selection changes, be aware of the potential race condition mentioned in the PR objectives when recoloring samples too quickly.

Consider implementing a debounce mechanism to prevent rapid consecutive refreshes:

+import { debounce } from 'lodash';
+
 const modalLabelsToggleTracker = useOnSidebarSelectionChange({ modal: true });

 useEffect(() => {
-  activeLookerRef.current?.refreshSample();
+  const debouncedRefresh = debounce(() => {
+    activeLookerRef.current?.refreshSample();
+  }, 100);
+  debouncedRefresh();
+  return () => debouncedRefresh.cancel();
 }, [modalLabelsToggleTracker]);
✅ Verification successful

Race condition mitigation is warranted

The review comment is valid. The refreshSample operation involves async sample loading and overlay processing, which could be resource-intensive when triggered rapidly by sidebar selection changes. The suggested debounce implementation aligns with existing patterns in the codebase and provides proper cleanup.

🏁 Scripts executed

The following scripts were executed for the analysis:

Error: No shell scripts found


Script:

#!/bin/bash
# Search for refreshSample implementation
ast-grep --pattern 'refreshSample() {
  $$$
}'

# Look for existing debounce usage
rg "debounce" -A 2

# Search for similar useEffect patterns with refresh
rg "useEffect.*refresh" -A 2

Length of output: 27337


Script:

#!/bin/bash
# Search for refreshSample method with context
rg "refreshSample" -A 5 -B 5

# Look for Looker class implementation
ast-grep --pattern 'class $_ {
  $$$
  refreshSample($$$) {
    $$$
  }
  $$$
}'

Length of output: 2627

app/packages/looker/src/worker/index.ts (4)

Line range hint 52-61: Documentation accurately reflects the implementation.

The updated documentation clearly explains the processing steps and aligns well with the implementation.


174-191: LGTM! Consistent handling of active paths.

The implementation correctly applies the same active paths check for painting operations, maintaining consistency with the decoding phase.


Line range hint 213-217: Good use of render status for optimization.

The bitmap collection is properly gated by the render status check, ensuring only fully painted labels are processed.


96-121: Verify the impact of skipping inactive paths.

The conditional processing of labels based on activePaths is a good optimization. However, we should verify that skipping inactive paths doesn't cause issues with label dependencies.

✅ Verification successful

Skipping inactive paths is safe

The conditional processing of labels based on activePaths is a safe optimization. Each label maintains its own independent rendering state, and there are no cross-label dependencies that could be affected by async processing of inactive paths.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for potential label dependencies across paths
ast-grep --pattern 'label.renderStatus = $status'

Length of output: 51


Script:

#!/bin/bash
# Search for label-related patterns
rg -A 5 "label\.renderStatus" --type ts

# Look for label processing and dependencies
ast-grep --pattern 'function $name($_) {
  $$$
  label.renderStatus
  $$$
}'

# Find async label processing
rg -A 10 "async.*label" --type ts

Length of output: 6460

fiftyone/server/query.py (2)

62-66: LGTM! Well-structured GraphQL type definition.

The FieldVisibilityConfig class is properly defined with optional fields and appropriate type annotations for GraphQL integration.


219-219: LGTM! Clean integration of visibility configuration.

The default_visibility_labels field is properly added to DatasetAppConfig with the correct type annotation.

app/packages/looker/src/lookers/abstract.ts (5)

49-50: LGTM! Good concurrency control.

The UPDATING_SAMPLES_IDS Set effectively prevents concurrent updates of the same sample.


523-530: LGTM! Robust sample update handling.

The implementation effectively handles concurrent sample updates by:

  1. Checking if a sample is already being updated
  2. Cleaning up overlays
  3. Queueing a microtask for retry

534-536: LGTM! Clean refresh implementation.

The refreshSample method provides a convenient way to refresh the current sample.


719-722: LGTM! Enhanced cleanup flexibility.

The cleanOverlays method now supports optional target nullification through the setTargetsToNull parameter.


316-319: Address the TODO comment for mouse overlay detection.

The commented code for mouse overlay detection needs to be fixed or removed.

Run the following script to check for any existing implementations or related code:

✅ Verification successful

Uncomment the mouse overlay detection code

The commented implementation is valid and consistent with the codebase's overlay detection patterns. The TODO comment appears to be outdated.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Search for mouse overlay detection implementations
# Test: Look for similar implementations of mouse overlay detection
rg -A 5 "mouseIsOnOverlay|containsPoint"

Length of output: 9329

app/packages/state/src/recoil/sidebar.ts (2)

75-76: LGTM! Clear type definitions.

The types GridPageNumber and SidebarEntriesSet are well-defined and serve clear purposes.


912-918: LGTM! Well-structured state management.

The labelsToggleTracker atom family effectively manages label toggle states across grid pages using appropriate types and a sensible default value.

app/packages/looker/src/worker/resolve-color.ts Outdated Show resolved Hide resolved
app/packages/state/src/recoil/schema.ts Outdated Show resolved Hide resolved
fiftyone/core/odm/dataset.py Show resolved Hide resolved
@kaixi-wang
Copy link
Contributor

If the default is not set, does that mean previously rendered things will suddenly not be visible unless someone explicitly sets it?

Considering the multi-user case, Should this be set at the dataset level for all users or per dataset per user?

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
app/packages/looker/src/lookers/abstract.ts (1)

721-725: Enhance clarity of the setTargetsToNull parameter

The parameter name setTargetsToNull in the cleanOverlays method might be unclear to other developers. Consider renaming it to something more descriptive, such as disposeOverlays or resetOverlayTargets, to better convey its purpose and improve code readability.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a814e18 and 7689416.

📒 Files selected for processing (7)
  • app/packages/core/src/components/Sidebar/useOnSidebarSelectionChange.ts (1 hunks)
  • app/packages/looker/src/lookers/abstract.ts (5 hunks)
  • app/packages/looker/src/lookers/frame-reader.ts (3 hunks)
  • app/packages/looker/src/lookers/video.ts (1 hunks)
  • app/packages/looker/src/worker/index.ts (18 hunks)
  • app/packages/looker/src/worker/resolve-color.ts (1 hunks)
  • app/packages/state/src/recoil/selectors.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • app/packages/state/src/recoil/selectors.ts
  • app/packages/core/src/components/Sidebar/useOnSidebarSelectionChange.ts
🧰 Additional context used
📓 Path-based instructions (5)
app/packages/looker/src/lookers/video.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/looker/src/lookers/frame-reader.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/looker/src/worker/resolve-color.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/looker/src/lookers/abstract.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/looker/src/worker/index.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

🪛 Biome (1.9.4)
app/packages/looker/src/worker/resolve-color.ts

[error] 15-15: Don't use '{}' as a type.

Prefer explicitly define the object shape. '{}' means "any non-nullable value".

(lint/complexity/noBannedTypes)

⏰ Context from checks skipped due to timeout of 90000ms (7)
  • GitHub Check: build / build
  • GitHub Check: test / test-python (ubuntu-latest-m, 3.11)
  • GitHub Check: test / test-python (ubuntu-latest-m, 3.10)
  • GitHub Check: test / test-python (ubuntu-latest-m, 3.9)
  • GitHub Check: lint / eslint
  • GitHub Check: test / test-app
  • GitHub Check: build
🔇 Additional comments (6)
app/packages/looker/src/lookers/frame-reader.ts (1)

35-35: LGTM! Clean implementation of activePaths.

The addition of activePaths to the interface and its usage in setStream is well-structured and aligns with the PR's objective of selective label rendering.

Also applies to: 92-92, 135-135

app/packages/looker/src/lookers/video.ts (1)

260-260: LGTM! Proper integration of activePaths.

The addition of activePaths from state options is consistent with the frame-reader implementation.

app/packages/looker/src/worker/index.ts (4)

Line range hint 52-60: LGTM! Well-documented label processing implementation.

The comment clearly explains the processing steps and the recursive nature of the implementation.


310-310: Verify the decision to process all 3D labels.

The code processes all 3D labels regardless of activePaths. Please confirm if this is intentional and document the reasoning.


96-121: LGTM! Clean implementation of selective label processing.

The conditional processing of dense labels based on activePaths is well-implemented, with proper handling of the render status.


175-191: LGTM! Consistent implementation of painter factory integration.

The activePaths check is consistently applied before invoking the painter factory.

app/packages/looker/src/worker/resolve-color.ts Outdated Show resolved Hide resolved
app/packages/looker/src/worker/resolve-color.ts Outdated Show resolved Hide resolved
app/packages/looker/src/lookers/abstract.ts Outdated Show resolved Hide resolved
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
app/packages/state/src/recoil/schema.ts (1)

325-378: Well-structured implementation of label visibility logic.

The selector efficiently handles different visibility configurations using Set operations. However, the Set operations could be optimized further.

Consider this optimization for the Set operations:

-    const includeList = new Set(defaultVisibleLabelsConfig.include);
-    const excludeList = new Set(defaultVisibleLabelsConfig.exclude);
-    // resolved = set(include) - set(exclude)
-    const resolved = new Set(
-      [...includeList].filter((x) => !excludeList.has(x))
-    );
-    return allLabels.filter((label) => resolved.has(label));
+    // Pre-filter allLabels against include list first
+    const excludeSet = new Set(defaultVisibleLabelsConfig.exclude);
+    return allLabels
+      .filter((label) => defaultVisibleLabelsConfig.include.includes(label))
+      .filter((label) => !excludeSet.has(label));

This optimization:

  1. Reduces memory usage by creating only one Set
  2. Avoids unnecessary Set creation for the include list
  3. Maintains the same functionality with better performance
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ea50110 and f53f0fc.

📒 Files selected for processing (1)
  • app/packages/state/src/recoil/schema.ts (4 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
app/packages/state/src/recoil/schema.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: test / test-python (ubuntu-latest-m, 3.10)
  • GitHub Check: build / build
  • GitHub Check: test / test-app
  • GitHub Check: e2e / test-e2e
  • GitHub Check: lint / eslint
  • GitHub Check: build
🔇 Additional comments (2)
app/packages/state/src/recoil/schema.ts (2)

26-26: LGTM! Clean import organization.

The new imports are well-organized and properly grouped with related imports.

Also applies to: 35-35


573-573: LGTM! Proper fallback to default visible labels.

The selector correctly uses defaultVisibleLabels as a fallback when no active fields are stored, which aligns with the PR objectives.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (9)
app/packages/core/src/components/Sidebar/useShouldReloadSample.ts (1)

9-9: Consider memory management for gridActivePathsLUT

The global Map instance could potentially grow unbounded as new Looker IDs are added. Consider implementing a cleanup mechanism or using a size-limited LRU cache.

-export const gridActivePathsLUT = new Map<LookerId, CachedLabels>();
+import LRU from "lru-cache";
+export const gridActivePathsLUT = new LRU<LookerId, CachedLabels>({
+  max: 1000, // Adjust based on expected concurrent Looker instances
+});
app/packages/core/src/components/Sidebar/syncAndCheckRefreshNeeded.ts (1)

44-48: Consider optimizing Set creation

The spread operator with Set creation could be optimized for performance.

-    new Set([...(thisLookerActiveFields ?? []), ...currentActiveLabelFields])
+    new Set(function*() {
+      if (thisLookerActiveFields) yield* thisLookerActiveFields;
+      yield* currentActiveLabelFields;
+    }())
app/packages/core/src/components/Grid/useSelect.ts (1)

38-41: Enhance comment clarity

The current comment could be more descriptive about why this refresh is necessary.

-        // rerender looker if active fields have changed and have never been rendered before
+        // Trigger a looker refresh when newly activated label fields are detected,
+        // ensuring they are properly rendered in the viewport
app/packages/core/src/components/Sidebar/syncAndCheckRefreshNeeded.test.ts (2)

17-18: Fix typo in comment

There's a typo in the comment.

-    // lut shoiuldn't be modified
+    // lut shouldn't be modified

4-96: LGTM! Comprehensive test coverage

The test suite is well-structured and covers important edge cases:

  • Empty fields
  • New looker scenarios
  • Subset of fields
  • Repeated calls

Consider adding a test case for concurrent modifications to ensure thread safety.

app/packages/core/src/components/Grid/Grid.tsx (2)

Line range hint 179-179: Remove debug console.log statement

The console.log statement should be removed before merging to production.

-        console.log(">>>painting ", `${prefix ?? ""}${field}`);

50-57: Add dependencies array to useRecoilCallback

The useRecoilCallback hook should include a dependencies array as the second argument to prevent unnecessary recreations of the callback.

  const getCurrentActiveLabelFields = useRecoilCallback(
    ({ snapshot }) =>
      () => {
        return snapshot
          .getLoadable(fos.activeLabelFields({ modal: false }))
          .getValue();
      }
-  );
+  , []);
app/packages/looker/src/worker/index.ts (1)

Line range hint 773-783: Improve error handling in worker message posting

The catch block could be more robust by logging the error before retrying.

    } catch (error) {
      // rarely we'll get a DataCloneError
      // if one of the buffers is detached and we didn't catch it
      // try again without transferring the buffers (copying them)
      if (error.name === "DataCloneError") {
+       console.warn("DataCloneError encountered, retrying without buffer transfer", error);
        labelsWorker.postMessage(workerArgs);
      } else {
+       console.error("Unexpected error in worker message posting", error);
        throw error;
      }
    }
app/packages/looker/src/lookers/abstract.ts (1)

536-543: Improve the TODO comment with more context

The TODO comment should provide more context about the issue and potential solutions.

-  // todo: sometimes instance in spotlight?.updateItems() is defined but has no ref to sample
-  // this crashes the app. this is a bug and should be fixed
+  /**
+   * TODO: Fix crash in spotlight?.updateItems()
+   * Issue: Sometimes the instance exists but has no reference to sample
+   * Potential fixes:
+   * 1. Add null checks in updateItems
+   * 2. Ensure sample is always set before calling updateItems
+   * 3. Add error boundary to handle the crash gracefully
+   */
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f53f0fc and dd60641.

📒 Files selected for processing (9)
  • app/packages/core/src/components/Grid/Grid.tsx (4 hunks)
  • app/packages/core/src/components/Grid/useRefreshers.ts (2 hunks)
  • app/packages/core/src/components/Grid/useSelect.ts (3 hunks)
  • app/packages/core/src/components/Modal/Modal.tsx (2 hunks)
  • app/packages/core/src/components/Sidebar/syncAndCheckRefreshNeeded.test.ts (1 hunks)
  • app/packages/core/src/components/Sidebar/syncAndCheckRefreshNeeded.ts (1 hunks)
  • app/packages/core/src/components/Sidebar/useShouldReloadSample.ts (1 hunks)
  • app/packages/looker/src/lookers/abstract.ts (5 hunks)
  • app/packages/looker/src/worker/index.ts (18 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/packages/core/src/components/Grid/useRefreshers.ts
🧰 Additional context used
📓 Path-based instructions (8)
app/packages/core/src/components/Grid/useSelect.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/core/src/components/Sidebar/useShouldReloadSample.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/looker/src/lookers/abstract.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/core/src/components/Sidebar/syncAndCheckRefreshNeeded.test.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/looker/src/worker/index.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/core/src/components/Sidebar/syncAndCheckRefreshNeeded.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/core/src/components/Modal/Modal.tsx (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/core/src/components/Grid/Grid.tsx (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

📓 Learnings (1)
app/packages/looker/src/lookers/abstract.ts (1)
Learnt from: sashankaryal
PR: voxel51/fiftyone#5356
File: app/packages/looker/src/lookers/abstract.ts:49-50
Timestamp: 2025-01-09T23:46:50.918Z
Learning: The `UPDATING_SAMPLES_IDS` set in `app/packages/looker/src/lookers/abstract.ts` is intentionally module-scoped to prevent concurrent updates of the same sample across different instances of AbstractLooker.
🔇 Additional comments (6)
app/packages/core/src/components/Sidebar/useShouldReloadSample.ts (1)

11-30: LGTM! Well-structured React hook

The hook follows React best practices:

  • Proper use of useCallback for memoization
  • Dependency array is correctly specified
  • Clear type definitions
app/packages/core/src/components/Sidebar/syncAndCheckRefreshNeeded.ts (1)

3-11: LGTM! Excellent documentation

The JSDoc comments are clear, comprehensive, and properly document all parameters and return values.

app/packages/core/src/components/Grid/Grid.tsx (1)

74-74: Well-implemented active paths tracking

The implementation properly manages the lifecycle of active paths:

  • Cleans up paths when destroying Spotlight instances
  • Initializes paths when rendering new instances
  • Safely checks for existence before initialization

Also applies to: 116-126

app/packages/core/src/components/Modal/Modal.tsx (1)

181-185: Well-implemented modal refresh on sidebar changes

The implementation properly handles sidebar selection changes:

  • Uses a dedicated tracker for modal context
  • Refreshes the sample when selections change
  • Correctly scopes the effect with proper dependency
app/packages/looker/src/worker/index.ts (1)

96-121: Excellent optimization of label processing

The implementation efficiently processes only the active labels:

  • Checks if the field is in activePaths before processing
  • Sets renderStatus to null for inactive labels
  • Prevents unnecessary processing and memory usage
app/packages/looker/src/lookers/abstract.ts (1)

522-532: Well-implemented concurrent update handling

The implementation properly prevents concurrent updates of the same sample:

  • Uses a shared Set to track updating samples
  • Queues updates using queueMicrotask
  • Properly cleans up state after updates

@sashankaryal sashankaryal force-pushed the feat/app-config-default-labels-visibility branch from dd60641 to 4630ad4 Compare January 11, 2025 02:22
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

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 (8)
app/packages/core/src/components/Grid/Grid.tsx (1)

115-126: Consider memoizing the active label fields initialization.

While the implementation is correct, consider memoizing the currentActiveLabelFields value to prevent unnecessary recomputation during frequent renders.

+ const memoizedActiveLabelFields = useMemo(
+   () => getCurrentActiveLabelFields(),
+   [getCurrentActiveLabelFields]
+ );

  // initialize active paths tracker
- const currentActiveLabelFields = getCurrentActiveLabelFields();
  if (
-   currentActiveLabelFields &&
+   memoizedActiveLabelFields &&
    !gridActivePathsLUT.has(id.description)
  ) {
    gridActivePathsLUT.set(
      id.description,
-     new Set(currentActiveLabelFields)
+     new Set(memoizedActiveLabelFields)
    );
  }
app/packages/utilities/src/schema.ts (2)

70-92: Consider adding type guards for the embeddedDocType parameter.

The function now accepts both string and string[] types, but there's no type guard to ensure type safety at runtime.

 export function getFieldsWithEmbeddedDocType(
   schema: Schema,
-  embeddedDocType: string | string[],
+  embeddedDocType: string | readonly string[],
   shouldRecurse = true
 ): Field[] {
   const result: Field[] = [];
+  const types = Array.isArray(embeddedDocType) ? embeddedDocType : [embeddedDocType];
 
   function recurse(schema: Schema) {
     for (const field of Object.values(schema ?? {})) {
-      if (Array.isArray(embeddedDocType)) {
-        if (embeddedDocType.includes(field.embeddedDocType)) {
-          result.push(field);
-        }
-      } else if (field.embeddedDocType === embeddedDocType) {
+      if (types.includes(field.embeddedDocType)) {
         result.push(field);
       }
       if (field.fields && shouldRecurse) {
         recurse(field.fields);
       }
     }
   }
 
   recurse(schema);
   return result;
 }

116-124: Add JSDoc comments for the new function.

The function's purpose and return value should be documented for better maintainability.

+/**
+ * Returns an array of field names that represent dense labels in the schema.
+ * Dense labels are fields with embedded document types defined in DENSE_LABEL_EMBEDDED_DOC_TYPES.
+ * @param schema - The schema to search for dense labels
+ * @returns An array of field names representing dense labels
+ */
 export function getDenseLabelNames(schema: Schema): string[] {
   const denseLabels = getFieldsWithEmbeddedDocType(
     schema,
     DENSE_LABEL_EMBEDDED_DOC_TYPES,
     false
   );
 
   return denseLabels.map((label) => label.name);
 }
app/packages/state/src/recoil/schema.ts (1)

325-378: Optimize Set operations for better performance.

The implementation is logically correct but could be optimized when handling include/exclude lists.

 // is in both include and exclude
-const includeList = new Set(defaultVisibleLabelsConfig.include);
-const excludeList = new Set(defaultVisibleLabelsConfig.exclude);
-// resolved = set(include) - set(exclude)
-const resolved = new Set(
-  [...includeList].filter((x) => !excludeList.has(x))
-);
-return allLabels.filter((label) => resolved.has(label));
+const excludeSet = new Set(defaultVisibleLabelsConfig.exclude);
+return allLabels.filter(
+  (label) =>
+    defaultVisibleLabelsConfig.include.includes(label) && !excludeSet.has(label)
+);
app/packages/looker/src/worker/resolve-color.ts (1)

19-20: Consider using Map for better type safety.

The use of plain objects for storing requests and promises could benefit from using Map for better type safety and built-in key management.

-  const requests = {};
-  const promises = {};
+  const requests = new Map<number, Record<string | number, (color: string) => void>>();
+  const promises = new Map<number, Record<string | number, Promise<string>>>();
app/packages/looker/src/worker/index.ts (1)

311-311: Document 3D labels processing behavior.

Add a comment explaining why 3D labels are processed regardless of activePaths status.

-    // we process all 3d labels regardless of active paths
+    // 3D labels are always processed regardless of active paths because they are
+    // essential for the scene geometry and cannot be selectively rendered
app/packages/looker/src/worker/disk-overlay-decoder.ts (1)

134-135: Consider improving type safety and error handling.

The current implementation has a few areas for improvement:

  1. Type casting could be replaced with proper type definition
  2. Consider adding error handling for the race condition mentioned in the PR objectives

Apply this diff to improve type safety:

-  label.renderStatus = "decoded" as DenseLabelRenderStatus;
+  label.renderStatus = "decoded";
+  type LabelWithRenderStatus = typeof label & { renderStatus: DenseLabelRenderStatus };

Also, consider adding a check to prevent race conditions:

+  if (label.renderStatus === "painting") {
+    // Skip updating if already being processed
+    return;
+  }
   label.renderStatus = "decoded" as DenseLabelRenderStatus;
app/packages/looker/src/lookers/abstract.ts (1)

49-50: Add documentation for UPDATING_SAMPLES_IDS.

While the module-scoped Set is intentional (as per retrieved learning), adding documentation would help future maintainers understand its purpose and scope.

+/**
+ * A shared Set to track sample IDs that are currently being updated across all instances
+ * of AbstractLooker. This is intentionally module-scoped to prevent concurrent updates
+ * of the same sample across different instances.
+ */
 const UPDATING_SAMPLES_IDS = new Set();
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dd60641 and 4630ad4.

⛔ Files ignored due to path filters (3)
  • app/packages/app/src/pages/datasets/__generated__/DatasetPageQuery.graphql.ts is excluded by !**/__generated__/**, !**/__generated__/**
  • app/packages/relay/src/fragments/__generated__/datasetAppConfigFragment.graphql.ts is excluded by !**/__generated__/**, !**/__generated__/**
  • app/packages/relay/src/queries/__generated__/datasetQuery.graphql.ts is excluded by !**/__generated__/**, !**/__generated__/**
📒 Files selected for processing (30)
  • .gitignore (1 hunks)
  • app/packages/core/src/components/Dataset.tsx (1 hunks)
  • app/packages/core/src/components/Grid/Grid.tsx (4 hunks)
  • app/packages/core/src/components/Grid/useRefreshers.ts (2 hunks)
  • app/packages/core/src/components/Grid/useSelect.ts (3 hunks)
  • app/packages/core/src/components/Modal/Modal.tsx (2 hunks)
  • app/packages/core/src/components/Sidebar/syncAndCheckRefreshNeeded.test.ts (1 hunks)
  • app/packages/core/src/components/Sidebar/syncAndCheckRefreshNeeded.ts (1 hunks)
  • app/packages/core/src/components/Sidebar/useShouldReloadSample.ts (1 hunks)
  • app/packages/looker/src/lookers/abstract.ts (5 hunks)
  • app/packages/looker/src/lookers/frame-reader.ts (3 hunks)
  • app/packages/looker/src/lookers/video.ts (1 hunks)
  • app/packages/looker/src/overlays/base.ts (1 hunks)
  • app/packages/looker/src/overlays/detection.ts (1 hunks)
  • app/packages/looker/src/overlays/heatmap.ts (1 hunks)
  • app/packages/looker/src/overlays/segmentation.ts (1 hunks)
  • app/packages/looker/src/worker/disk-overlay-decoder.ts (2 hunks)
  • app/packages/looker/src/worker/index.ts (18 hunks)
  • app/packages/looker/src/worker/painter.ts (8 hunks)
  • app/packages/looker/src/worker/resolve-color.ts (1 hunks)
  • app/packages/looker/src/worker/shared.ts (1 hunks)
  • app/packages/looker/src/worker/tests/indexed-png-decoder.test.ts (0 hunks)
  • app/packages/relay/src/fragments/datasetAppConfigFragment.ts (1 hunks)
  • app/packages/state/src/recoil/schema.ts (4 hunks)
  • app/packages/state/src/recoil/selectors.ts (1 hunks)
  • app/packages/state/src/recoil/types.ts (2 hunks)
  • app/packages/utilities/src/schema.ts (3 hunks)
  • app/schema.graphql (2 hunks)
  • fiftyone/core/odm/dataset.py (2 hunks)
  • fiftyone/server/query.py (2 hunks)
💤 Files with no reviewable changes (1)
  • app/packages/looker/src/worker/tests/indexed-png-decoder.test.ts
🚧 Files skipped from review as they are similar to previous changes (20)
  • .gitignore
  • app/packages/looker/src/worker/shared.ts
  • app/packages/core/src/components/Dataset.tsx
  • app/packages/relay/src/fragments/datasetAppConfigFragment.ts
  • app/packages/state/src/recoil/selectors.ts
  • app/packages/core/src/components/Grid/useSelect.ts
  • app/schema.graphql
  • app/packages/looker/src/overlays/heatmap.ts
  • app/packages/looker/src/overlays/detection.ts
  • app/packages/state/src/recoil/types.ts
  • fiftyone/server/query.py
  • fiftyone/core/odm/dataset.py
  • app/packages/looker/src/overlays/base.ts
  • app/packages/looker/src/overlays/segmentation.ts
  • app/packages/looker/src/worker/painter.ts
  • app/packages/core/src/components/Grid/useRefreshers.ts
  • app/packages/looker/src/lookers/video.ts
  • app/packages/core/src/components/Sidebar/useShouldReloadSample.ts
  • app/packages/core/src/components/Sidebar/syncAndCheckRefreshNeeded.ts
  • app/packages/core/src/components/Sidebar/syncAndCheckRefreshNeeded.test.ts
🧰 Additional context used
📓 Path-based instructions (9)
app/packages/looker/src/lookers/frame-reader.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/core/src/components/Modal/Modal.tsx (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/core/src/components/Grid/Grid.tsx (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/state/src/recoil/schema.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/looker/src/worker/resolve-color.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/looker/src/worker/disk-overlay-decoder.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/looker/src/lookers/abstract.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/looker/src/worker/index.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/utilities/src/schema.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

📓 Learnings (1)
app/packages/looker/src/lookers/abstract.ts (1)
Learnt from: sashankaryal
PR: voxel51/fiftyone#5356
File: app/packages/looker/src/lookers/abstract.ts:49-50
Timestamp: 2025-01-09T23:46:50.918Z
Learning: The `UPDATING_SAMPLES_IDS` set in `app/packages/looker/src/lookers/abstract.ts` is intentionally module-scoped to prevent concurrent updates of the same sample across different instances of AbstractLooker.
⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: test / test-python (ubuntu-latest-m, 3.10)
  • GitHub Check: test / test-app
  • GitHub Check: build / build
  • GitHub Check: e2e / test-e2e
  • GitHub Check: lint / eslint
  • GitHub Check: build
🔇 Additional comments (18)
app/packages/core/src/components/Grid/Grid.tsx (3)

13-13: LGTM! Import statements are properly organized.

The new imports are correctly added and align with the implementation requirements.

Also applies to: 16-16


50-57: LGTM! Well-implemented Recoil callback.

The function correctly uses useRecoilCallback to access the Recoil state and follows React/Recoil best practices.


74-74: LGTM! Proper cleanup implementation.

The addition of gridActivePathsLUT cleanup in the destroy callback prevents potential memory leaks.

app/packages/utilities/src/schema.ts (1)

1-12: LGTM! Well-structured constants for embedded document types.

The constants are clearly named and organized, making the code more maintainable.

app/packages/state/src/recoil/schema.ts (1)

573-573: LGTM! Proper fallback to default visible labels.

The modification correctly uses the new defaultVisibleLabels selector as a fallback when no active fields are set.

app/packages/looker/src/worker/resolve-color.ts (2)

15-17: LGTM! Good cache configuration.

The LRU cache is well-configured with a reasonable max size limit of 1000 entries, which helps prevent memory issues.


31-56: LGTM! Robust promise handling.

The promise resolution logic is well-implemented with proper error handling and cache management.

app/packages/looker/src/lookers/frame-reader.ts (2)

35-35: LGTM! Clear interface extension.

The addition of activePaths to AcquireReaderOptions is well-documented and non-breaking.


92-92: LGTM! Consistent parameter propagation.

The activePaths parameter is correctly propagated through setStream and included in the worker message.

Also applies to: 135-135

app/packages/looker/src/worker/index.ts (1)

96-121: LGTM! Well-structured conditional processing.

The label processing logic correctly handles activePaths with proper null checks and conditional rendering.

app/packages/looker/src/worker/disk-overlay-decoder.ts (2)

1-8: LGTM! Clean import statements.

The removal of unused imports and addition of DenseLabelRenderStatus improves code maintainability.


Line range hint 20-138: Verify error handling in multi-user scenarios.

Based on the PR comments from kaixi-wang about multi-user implications, we should verify the error handling and state management in concurrent scenarios.

Let's check for potential race conditions in the codebase:

✅ Verification successful

Error handling and concurrency implementation verified

The implementation shows proper error isolation and state management for multi-user scenarios. Failed operations gracefully exit without corrupting state, and the worker-based architecture naturally handles concurrent access through message passing and atomic state transitions.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Look for concurrent access patterns and error handling
# Search for places where renderStatus is modified
rg "renderStatus.*=.*" -A 5

# Search for error handling patterns around label processing
rg "catch.*\{" -A 5 "app/packages/looker"

# Look for other worker files that might interact with this one
fd "worker" --type f "app/packages/looker"

Length of output: 9630

app/packages/core/src/components/Modal/Modal.tsx (2)

181-182: LGTM!

The modalLabelsToggleTracker is well-named and correctly initialized with modal-specific context.


183-186: LGTM!

The effect is well-implemented with proper null checks and dependency tracking, ensuring samples are refreshed when label visibility changes.

app/packages/looker/src/lookers/abstract.ts (4)

522-532: LGTM!

The enhanced updateSample method effectively prevents concurrent updates of the same sample through:

  • Proper ID handling with null coalescing
  • Clean overlay cleanup and retry mechanism using queueMicrotask

536-544: Address the TODO comment regarding spotlight?.updateItems() bug.

The comment indicates a potential crash when the instance is defined but has no reference to the sample. This should be investigated and fixed.

Would you like me to create a GitHub issue to track this bug? The issue would help track the investigation and fix for the spotlight?.updateItems() crash scenario.


726-729: LGTM!

The enhanced cleanOverlays method provides flexible cleanup behavior through the optional setTargetsToNull parameter while maintaining safe cleanup calls.


751-752: LGTM!

The cleanup of sample ID from UPDATING_SAMPLES_IDS is properly implemented with consistent ID field handling.

app/packages/looker/src/worker/index.ts Outdated Show resolved Hide resolved
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (4)
app/packages/core/src/components/Modal/use-looker.ts (1)

59-67: Consider debouncing the refresh operation.

While the implementation is correct, rapid changes to lookerOptions.activePaths could trigger multiple refreshes in quick succession. This might be related to the race condition mentioned in the PR objectives regarding sample recoloring.

Consider adding debounce:

  useEffect(() => {
    if (!looker) {
      return;
    }

+   const debouncedRefresh = debounce(() => {
      if (shouldRefresh(id)) {
        looker?.refreshSample();
      }
+   }, 150);
+
+   debouncedRefresh();
+   return () => debouncedRefresh.cancel();
-   if (shouldRefresh(id)) {
-     looker?.refreshSample();
-   }
  }, [id, lookerOptions.activePaths, looker]);
app/packages/core/src/components/Sidebar/useShouldReloadSample.ts (3)

6-10: Consider enhancing type safety.

While the types are correctly defined, they could be more specific:

-export type LookerId = string;
+export type LookerId = string & { readonly __brand: unique symbol };

This would prevent accidental assignment of any string as a LookerId.


12-28: Document the hook's behavior and return value.

The implementation is solid, but missing JSDoc documentation would help other developers understand the hook's purpose and usage.

Add documentation:

+/**
+ * Hook to manage and track changes in label visibility for samples
+ * @param {Object} params - Hook parameters
+ * @param {boolean} params.modal - Whether the hook is used in modal context
+ * @returns {Function} shouldRefresh - Function to check if a sample needs refreshing
+ */
export const useShouldReloadSampleOnActiveFieldsChange = ({
  modal,
}: {
  modal: boolean;
}) => {

33-41: Consider adding error handling for cleanup.

The cleanup logic is correct but could benefit from error handling to ensure robustness.

  useEffect(() => {
    return () => {
+     try {
        if (modal) {
          modalActivePathsLUT.clear();
        } else {
          gridActivePathsLUT.clear();
        }
+     } catch (error) {
+       console.error('Failed to clear lookup table:', error);
+     }
    };
  }, []);
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between acbf069 and 9e59b90.

📒 Files selected for processing (2)
  • app/packages/core/src/components/Modal/use-looker.ts (2 hunks)
  • app/packages/core/src/components/Sidebar/useShouldReloadSample.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
app/packages/core/src/components/Sidebar/useShouldReloadSample.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/core/src/components/Modal/use-looker.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

⏰ Context from checks skipped due to timeout of 90000ms (7)
  • GitHub Check: lint / eslint
  • GitHub Check: test / test-python (ubuntu-latest-m, 3.11)
  • GitHub Check: build / changes
  • GitHub Check: build / build
  • GitHub Check: test / test-python (ubuntu-latest-m, 3.10)
  • GitHub Check: test / test-app
  • GitHub Check: build
🔇 Additional comments (1)
app/packages/core/src/components/Modal/use-looker.ts (1)

55-57: LGTM! Clear configuration of reload hook.

The hook configuration with modal: true correctly identifies this as a modal context, aligning with the PR's objective of selective label rendering.

@sashankaryal sashankaryal force-pushed the feat/app-config-default-labels-visibility branch 2 times, most recently from 65def39 to 9eeac97 Compare January 14, 2025 20:52
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (12)
app/packages/app/src/components/Analytics.tsx (1)

46-54: LGTM! Well-documented test environment handling.

The conditional check effectively disables analytics during Playwright tests, with clear comments explaining this exceptional case. The approach is pragmatic for improving test performance.

However, consider adding a type definition for the IS_PLAYWRIGHT window property to improve type safety.

+// Add to a global.d.ts file
+interface Window {
+  IS_PLAYWRIGHT?: boolean;
+}
e2e-pw/src/oss/fixtures/loader.ts (1)

130-135: LGTM! Clean implementation of test environment flag.

The initialization script is properly placed to ensure early setup of the IS_PLAYWRIGHT flag.

Consider adding a type definition instead of using @ts-ignore:

+// Add to a global.d.ts file
+interface Window {
+  IS_PLAYWRIGHT?: boolean;
+}
app/packages/core/src/components/QueryPerformanceToast.tsx (1)

80-88: LGTM, but consider adding type safety for the global variable.

The conditional check effectively prevents test delays. However, the window.IS_PLAYWRIGHT property should be properly typed.

Add type declaration for the global property:

declare global {
  interface Window {
    IS_PLAYWRIGHT?: boolean;
  }
}
app/packages/state/src/recoil/schema.ts (2)

325-378: Well-structured implementation with room for performance optimization.

The selector efficiently handles different visibility configurations using Set operations. However, we can optimize the array spread operations.

Consider this optimization:

-    const denseLabels = [...denseLabelsSamples, ...denseLabelsFrames];
-    const allLabels = [...allSampleLabels, ...allFrameLabels];
+    const denseLabels = denseLabelsSamples.concat(denseLabelsFrames);
+    const allLabels = allSampleLabels.concat(allFrameLabels);

325-378: Enhance code documentation for better maintainability.

While the logic is clear, adding JSDoc comments would improve maintainability.

Consider adding documentation:

+/**
+ * Selector that computes the list of visible labels based on configuration.
+ * 
+ * @returns {string[]} Array of visible label paths
+ * 
+ * Configuration cases:
+ * 1. No config: Hide dense labels
+ * 2. Include only: Show only included labels
+ * 3. Exclude only: Show all except excluded
+ * 4. Both: Show (included - excluded)
+ */
 export const defaultVisibleLabels = selector({
app/packages/core/src/components/Sidebar/useShouldReloadSample.ts (1)

12-44: Add TypeScript return type annotation to the hook.

The hook implementation follows React best practices, but could benefit from explicit TypeScript return type annotation for better type safety and documentation.

-export const useShouldReloadSampleOnActiveFieldsChange = ({
+export const useShouldReloadSampleOnActiveFieldsChange = ({
   modal,
 }: {
   modal: boolean;
-}) => {
+}): (id: string) => boolean => {
app/packages/looker/src/worker/index.ts (2)

Line range hint 52-71: Enhance documentation for the activePaths parameter.

While the function steps are well documented, consider adding a description of the activePaths parameter and its role in the processing logic.


96-121: Refactor duplicated active paths check into a helper function.

The active paths check logic is duplicated. Consider extracting it into a helper function to improve maintainability and reduce code duplication.

+const isFieldActive = (field: string, prefix: string | null, activePaths: string[]): boolean => {
+  return activePaths.length && activePaths.includes(`${prefix ?? ""}${field}`);
+};

 // Then use it in both places:
-if (
-  activePaths.length &&
-  activePaths.includes(`${prefix ?? ""}${field}`)
-) {
+if (isFieldActive(field, prefix, activePaths)) {

Also applies to: 174-191

app/packages/looker/src/lookers/abstract.ts (4)

49-50: Add documentation for the module-scoped Set.

While the module-scoped Set is intentional (as confirmed by retrieved learning), consider adding documentation to explain its purpose in preventing concurrent updates across different instances.

+/**
+ * A shared Set to track sample IDs that are currently being updated across all instances
+ * of AbstractLooker. This is intentionally module-scoped to prevent concurrent updates
+ * of the same sample across different instances, avoiding potential race conditions.
+ */
 const UPDATING_SAMPLES_IDS = new Set();

522-532: Add error handling for sample updates.

Consider adding error handling and a maximum retry count to prevent infinite retry loops in case of persistent issues.

+private maxRetries = 3;
+private retryCount = new Map<string, number>();

 updateSample(sample: Sample) {
   const id = sample.id ?? sample._id;

   if (UPDATING_SAMPLES_IDS.has(id)) {
+    const currentRetries = this.retryCount.get(id) ?? 0;
+    if (currentRetries >= this.maxRetries) {
+      console.error(`Max retries reached for sample ${id}`);
+      this.retryCount.delete(id);
+      return;
+    }
+    this.retryCount.set(id, currentRetries + 1);
     UPDATING_SAMPLES_IDS.delete(id);
     this.cleanOverlays(true);
     queueMicrotask(() => this.updateSample(sample));
     return;
   }

+  this.retryCount.delete(id);
   UPDATING_SAMPLES_IDS.add(id);

536-543: Address the TODO comment regarding potential crashes.

The TODO comment indicates a bug where the instance in spotlight?.updateItems() might have an undefined sample reference, leading to crashes.

Would you like me to help investigate this issue and propose a solution? I can help create a GitHub issue to track this bug.


726-729: Add JSDoc for the setTargetsToNull parameter.

Consider adding documentation to explain the purpose and impact of the setTargetsToNull parameter.

+/**
+ * Cleans up overlays and optionally nullifies their targets.
+ * @param setTargetsToNull When true, overlay targets will be set to null during cleanup
+ */
 protected cleanOverlays(setTargetsToNull = false) {
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9e59b90 and 9eeac97.

⛔ Files ignored due to path filters (3)
  • app/packages/app/src/pages/datasets/__generated__/DatasetPageQuery.graphql.ts is excluded by !**/__generated__/**, !**/__generated__/**
  • app/packages/relay/src/fragments/__generated__/datasetAppConfigFragment.graphql.ts is excluded by !**/__generated__/**, !**/__generated__/**
  • app/packages/relay/src/queries/__generated__/datasetQuery.graphql.ts is excluded by !**/__generated__/**, !**/__generated__/**
📒 Files selected for processing (36)
  • .gitignore (1 hunks)
  • app/packages/app/src/components/Analytics.tsx (1 hunks)
  • app/packages/core/src/components/Dataset.tsx (1 hunks)
  • app/packages/core/src/components/Grid/Grid.tsx (4 hunks)
  • app/packages/core/src/components/Grid/useRefreshers.ts (2 hunks)
  • app/packages/core/src/components/Grid/useSelect.ts (3 hunks)
  • app/packages/core/src/components/Modal/use-looker.ts (2 hunks)
  • app/packages/core/src/components/QueryPerformanceToast.tsx (1 hunks)
  • app/packages/core/src/components/Sidebar/syncAndCheckRefreshNeeded.test.ts (1 hunks)
  • app/packages/core/src/components/Sidebar/syncAndCheckRefreshNeeded.ts (1 hunks)
  • app/packages/core/src/components/Sidebar/useShouldReloadSample.ts (1 hunks)
  • app/packages/looker/src/lookers/abstract.ts (5 hunks)
  • app/packages/looker/src/lookers/frame-reader.ts (3 hunks)
  • app/packages/looker/src/lookers/video.ts (1 hunks)
  • app/packages/looker/src/overlays/base.ts (1 hunks)
  • app/packages/looker/src/overlays/detection.ts (1 hunks)
  • app/packages/looker/src/overlays/heatmap.ts (1 hunks)
  • app/packages/looker/src/overlays/segmentation.ts (1 hunks)
  • app/packages/looker/src/worker/disk-overlay-decoder.ts (2 hunks)
  • app/packages/looker/src/worker/index.ts (18 hunks)
  • app/packages/looker/src/worker/painter.ts (8 hunks)
  • app/packages/looker/src/worker/resolve-color.ts (1 hunks)
  • app/packages/looker/src/worker/shared.ts (1 hunks)
  • app/packages/looker/src/worker/tests/indexed-png-decoder.test.ts (0 hunks)
  • app/packages/relay/src/fragments/datasetAppConfigFragment.ts (1 hunks)
  • app/packages/state/src/recoil/schema.ts (4 hunks)
  • app/packages/state/src/recoil/selectors.ts (1 hunks)
  • app/packages/state/src/recoil/types.ts (2 hunks)
  • app/packages/utilities/src/schema.ts (3 hunks)
  • app/schema.graphql (2 hunks)
  • e2e-pw/src/oss/fixtures/loader.ts (1 hunks)
  • e2e-pw/src/oss/specs/overlays/detection-mask.spec.ts (1 hunks)
  • e2e-pw/src/oss/specs/sidebar/sidebar.spec.ts (4 hunks)
  • e2e-pw/src/oss/specs/smoke-tests/tagger.spec.ts (1 hunks)
  • fiftyone/core/odm/dataset.py (2 hunks)
  • fiftyone/server/query.py (2 hunks)
💤 Files with no reviewable changes (1)
  • app/packages/looker/src/worker/tests/indexed-png-decoder.test.ts
🚧 Files skipped from review as they are similar to previous changes (23)
  • app/packages/core/src/components/Dataset.tsx
  • app/packages/state/src/recoil/selectors.ts
  • .gitignore
  • app/packages/looker/src/overlays/base.ts
  • app/packages/core/src/components/Modal/use-looker.ts
  • app/packages/relay/src/fragments/datasetAppConfigFragment.ts
  • app/packages/core/src/components/Grid/useSelect.ts
  • app/packages/looker/src/lookers/frame-reader.ts
  • app/packages/looker/src/worker/shared.ts
  • app/packages/looker/src/lookers/video.ts
  • app/packages/looker/src/overlays/segmentation.ts
  • fiftyone/server/query.py
  • app/packages/core/src/components/Grid/Grid.tsx
  • app/packages/state/src/recoil/types.ts
  • fiftyone/core/odm/dataset.py
  • app/packages/looker/src/worker/painter.ts
  • app/packages/looker/src/worker/resolve-color.ts
  • app/packages/core/src/components/Sidebar/syncAndCheckRefreshNeeded.test.ts
  • app/packages/looker/src/worker/disk-overlay-decoder.ts
  • app/packages/looker/src/overlays/detection.ts
  • app/packages/looker/src/overlays/heatmap.ts
  • app/packages/core/src/components/Grid/useRefreshers.ts
  • app/packages/core/src/components/Sidebar/syncAndCheckRefreshNeeded.ts
🧰 Additional context used
📓 Path-based instructions (11)
e2e-pw/src/oss/specs/overlays/detection-mask.spec.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/app/src/components/Analytics.tsx (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

e2e-pw/src/oss/fixtures/loader.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/core/src/components/QueryPerformanceToast.tsx (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/state/src/recoil/schema.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

e2e-pw/src/oss/specs/smoke-tests/tagger.spec.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/looker/src/lookers/abstract.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/looker/src/worker/index.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/core/src/components/Sidebar/useShouldReloadSample.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

e2e-pw/src/oss/specs/sidebar/sidebar.spec.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/utilities/src/schema.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

📓 Learnings (1)
app/packages/looker/src/lookers/abstract.ts (1)
Learnt from: sashankaryal
PR: voxel51/fiftyone#5356
File: app/packages/looker/src/lookers/abstract.ts:49-50
Timestamp: 2025-01-09T23:46:50.918Z
Learning: The `UPDATING_SAMPLES_IDS` set in `app/packages/looker/src/lookers/abstract.ts` is intentionally module-scoped to prevent concurrent updates of the same sample across different instances of AbstractLooker.
⏰ Context from checks skipped due to timeout of 90000ms (8)
  • GitHub Check: test / test-python (ubuntu-latest-m, 3.11)
  • GitHub Check: test / test-python (ubuntu-latest-m, 3.10)
  • GitHub Check: test / test-python (ubuntu-latest-m, 3.9)
  • GitHub Check: test / test-app
  • GitHub Check: lint / eslint
  • GitHub Check: e2e / test-e2e
  • GitHub Check: build / build
  • GitHub Check: build
🔇 Additional comments (14)
e2e-pw/src/oss/specs/overlays/detection-mask.spec.ts (1)

93-95: LGTM! Proper test setup for default label visibility.

The configuration correctly demonstrates the new default visibility labels feature, properly including both "ground_truth" and "prediction" labels.

e2e-pw/src/oss/specs/smoke-tests/tagger.spec.ts (1)

37-40: LGTM! Proper test setup for label visibility.

The changes ensure a consistent test state by explicitly setting visibility for required labels.

e2e-pw/src/oss/specs/sidebar/sidebar.spec.ts (1)

36-37: LGTM! Consistent updates across test cases.

The changes consistently update all test cases to show only ground truth labels, aligning with the PR objectives for selective label rendering.

Also applies to: 92-93, 150-151, 208-209

app/packages/utilities/src/schema.ts (3)

1-12: LGTM! Well-organized constants for label types.

The constants are clearly defined and logically grouped into the DENSE_LABEL_EMBEDDED_DOC_TYPES array.


70-92: LGTM! Enhanced function with improved flexibility.

The getFieldsWithEmbeddedDocType function now supports:

  • Array of embedded doc types
  • Optional recursion control

116-124: LGTM! Clean utility function implementation.

The getDenseLabelNames function is focused and effectively reuses existing functionality.

app/schema.graphql (2)

333-336: LGTM! Well-structured visibility configuration type.

The FieldVisibilityConfig type clearly defines the include/exclude pattern with non-nullable arrays.


250-250: LGTM! Clean integration with DatasetAppConfig.

The defaultVisibilityLabels field is properly added to the DatasetAppConfig type.

app/packages/state/src/recoil/schema.ts (2)

26-26: LGTM! Clean import additions.

The new imports are well-organized and properly aligned with the feature requirements.

Also applies to: 35-35


573-573: LGTM! Clean integration of default visibility.

The fallback to defaultVisibleLabels is properly implemented and aligns with the feature requirements.

app/packages/core/src/components/Sidebar/useShouldReloadSample.ts (3)

1-5: LGTM!

The imports are well-organized and include all necessary dependencies for the hook's functionality.


6-8: LGTM!

The type definitions are clear, descriptive, and appropriately typed.


9-10: LGTM!

The lookup tables are well-structured with appropriate types and clear naming conventions.

app/packages/looker/src/worker/index.ts (1)

33-33: LGTM!

Good refactoring to move color resolution logic into a separate module, improving code organization and maintainability.

Also applies to: 45-45

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (4)
app/packages/state/src/labelsVisibility.ts (3)

27-27: Remove console.log statement.

Production code should not contain console.log statements.

-    console.log("all", all);

19-57: Optimize label filtering using Set operations.

The current implementation uses array operations which have O(n²) complexity. Using Set operations would improve performance.

-  const allLabels = [...allSampleLabels, ...allFrameLabels];
+  const allLabelsSet = new Set([...allSampleLabels, ...allFrameLabels]);
+  const denseLabelsSet = new Set(denseLabels);
 
   if (
     !defaultVisibleLabelsConfig?.include &&
     !defaultVisibleLabelsConfig?.exclude
   ) {
-    const all = allLabels.filter((label) => !denseLabels.includes(label));
-    return all;
+    return [...allLabelsSet].filter((label) => !denseLabelsSet.has(label));
   }
 
   if (
     defaultVisibleLabelsConfig?.include &&
     !defaultVisibleLabelsConfig?.exclude
   ) {
-    return allLabels.filter((label) =>
-      defaultVisibleLabelsConfig.include.includes(label)
-    );
+    const includeSet = new Set(defaultVisibleLabelsConfig.include);
+    return [...allLabelsSet].filter((label) => includeSet.has(label));
   }
 
   if (
     !defaultVisibleLabelsConfig?.include &&
     defaultVisibleLabelsConfig?.exclude
   ) {
-    return allLabels.filter(
-      (label) => !defaultVisibleLabelsConfig.exclude.includes(label)
-    );
+    const excludeSet = new Set(defaultVisibleLabelsConfig.exclude);
+    return [...allLabelsSet].filter((label) => !excludeSet.has(label));
   }

3-12: Add JSDoc documentation.

Add comprehensive documentation to explain the function's purpose, parameters, and return value.

+/**
+ * Computes the list of labels that should be visible based on the provided schemas and configuration.
+ * @param sampleSchema - The schema for sample labels
+ * @param frameSchema - The schema for frame labels
+ * @param allSampleLabels - Array of all sample label names
+ * @param allFrameLabels - Array of all frame label names
+ * @param defaultVisibleLabelsConfig - Optional configuration for label visibility
+ * @param defaultVisibleLabelsConfig.include - Optional array of labels to include
+ * @param defaultVisibleLabelsConfig.exclude - Optional array of labels to exclude
+ * @returns Array of label names that should be visible
+ */
 export const computeDefaultVisibleLabels = (
app/packages/state/src/labelsVisibility.test.ts (1)

40-54: Enhance test coverage with edge cases.

While the current test cases cover the main scenarios, consider adding tests for:

  1. Empty arrays in include/exclude configs
  2. Non-existent labels in include/exclude configs
  3. Overlapping labels in include/exclude configs
  4. Empty input label arrays

Here's an example test case for empty configs:

it("handles empty include/exclude arrays", () => {
  const allSampleLabels = ["detection", "classification"];
  const allFrameLabels = ["frames.detection"];

  const result = computeDefaultVisibleLabels(
    sampleSchemaMock,
    frameSchemaMock,
    allSampleLabels,
    allFrameLabels,
    { include: [], exclude: [] }
  );

  expect(result).toEqual([]);
});

Also applies to: 56-69, 71-84, 86-102

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9eeac97 and 5919203.

📒 Files selected for processing (3)
  • app/packages/state/src/labelsVisibility.test.ts (1 hunks)
  • app/packages/state/src/labelsVisibility.ts (1 hunks)
  • app/packages/state/src/recoil/schema.ts (3 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
app/packages/state/src/labelsVisibility.test.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/state/src/labelsVisibility.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/state/src/recoil/schema.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: test / test-app
  • GitHub Check: build / build
  • GitHub Check: build
🔇 Additional comments (1)
app/packages/state/src/recoil/schema.ts (1)

325-344: Clean implementation of label visibility logic.

The selector efficiently handles different visibility configurations and integrates well with the existing state management pattern.

Also applies to: 539-539

@sashankaryal sashankaryal force-pushed the feat/app-config-default-labels-visibility branch from 560c340 to fc65aff Compare February 3, 2025 19:38
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (8)
app/packages/core/src/components/Modal/use-looker.ts (1)

55-67: Consider debouncing the sample refresh calls.

The effect could trigger multiple rapid refreshes when activePaths changes frequently, potentially causing performance issues or race conditions. Consider implementing debouncing to coalesce multiple refresh calls.

Here's a suggested implementation using debounce:

+import { debounce } from "lodash";
+
 const shouldRefresh = useShouldReloadSampleOnActiveFieldsChange({
   modal: true,
 });
 
+const debouncedRefresh = React.useCallback(
+  debounce((looker: L) => {
+    looker.refreshSample();
+  }, 250),
+  []
+);
+
 useEffect(() => {
   if (!looker) {
     return;
   }
 
   if (shouldRefresh(id)) {
-    looker?.refreshSample();
+    debouncedRefresh(looker);
   }
+
+  return () => {
+    debouncedRefresh.cancel();
+  };
 }, [id, lookerOptions.activePaths, looker]);

This change will:

  1. Prevent rapid consecutive refreshes
  2. Improve performance by reducing unnecessary refreshes
  3. Help mitigate potential race conditions
app/packages/state/src/labelsVisibility.ts (2)

27-27: Remove debug console.log statement.

The console.log statement should be removed from production code.

-    console.log("all", all);
+    return all;

51-56: Consider using Set operations for better performance.

When both include and exclude are defined, you're using Set operations efficiently. Consider using the same approach for the default case to maintain consistency and potentially improve performance with large label lists.

-  const resolved = new Set([...includeList].filter((x) => !excludeList.has(x)));
+  const resolved = new Set(
+    [...includeList].filter((x) => !excludeList.has(x))
+  );
app/packages/state/src/labelsVisibility.test.ts (1)

40-103: Add test cases for edge cases and error conditions.

While the current test coverage is good, consider adding these test cases:

  1. Empty arrays for allSampleLabels and allFrameLabels
  2. Empty arrays for include and exclude
  3. Invalid input validation (e.g., undefined schemas)

Example additional test cases:

it("handles empty label arrays", () => {
  const result = computeDefaultVisibleLabels(
    sampleSchemaMock,
    frameSchemaMock,
    [],
    [],
    undefined
  );
  expect(result).toEqual([]);
});

it("handles empty include/exclude arrays", () => {
  const result = computeDefaultVisibleLabels(
    sampleSchemaMock,
    frameSchemaMock,
    ["detection"],
    [],
    { include: [], exclude: [] }
  );
  expect(result).toEqual([]);
});
app/packages/looker/src/worker/resolve-color.ts (2)

15-17: Consider adding cache eviction callback.

The LRU cache is well-configured with a reasonable size limit, but consider adding a dispose callback to clean up any resources when entries are evicted.

 const cache = new LRUCache<number, Record<string | number, string>>({
   max: MAX_COLOR_CACHE_SIZE,
+  dispose: (key, value) => {
+    // Clean up any resources associated with the evicted entry
+  },
 });

19-20: Consider using Map for type safety.

Using plain objects for storing requests and promises could lead to prototype pollution. Consider using Map for better type safety.

-  const requests = {};
-  const promises = {};
+  const requests = new Map();
+  const promises = new Map();
app/packages/looker/src/processOverlays.ts (1)

36-45: Consider consolidating overlay validation logic.

The validation logic for Segmentation and Heatmap overlays is duplicated. Consider extracting it into a shared validation function.

+const validateOverlayData = (overlay, requiredField, pathField) => {
+  return overlay[pathField] && !overlay[requiredField];
+};

-if (
-  overlay instanceof SegmentationOverlay &&
-  overlay.label.mask_path &&
-  !overlay.label.mask
-) {
+if (overlay instanceof SegmentationOverlay && 
+    validateOverlayData(overlay.label, 'mask', 'mask_path')) {
   continue;
 }

-if (
-  overlay instanceof HeatmapOverlay &&
-  overlay.label.map_path &&
-  !overlay.label.map
-) {
+if (overlay instanceof HeatmapOverlay && 
+    validateOverlayData(overlay.label, 'map', 'map_path')) {
   continue;
 }

Also applies to: 47-53

app/packages/looker/src/worker/index.ts (1)

96-121: Consider adding early return for inactive paths.

The nested conditions could be simplified using an early return pattern.

-if (
-  activePaths.length &&
-  activePaths.includes(`${prefix ?? ""}${field}`)
-) {
+const isActivePath = activePaths.length && activePaths.includes(`${prefix ?? ""}${field}`);
+if (!isActivePath) {
+  label.renderStatus = null;
+  continue;
+}

 maskPathDecodingPromises.push(
   decodeOverlayOnDisk(
     // ... existing code
   )
 );

 if (cls in DeserializerFactory) {
   DeserializerFactory[cls](label, maskTargetsBuffers);
   label.renderStatus = "decoded";
 }
-} else {
-  label.renderStatus = null;
-}
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 560c340 and fc65aff.

⛔ Files ignored due to path filters (3)
  • app/packages/app/src/pages/datasets/__generated__/DatasetPageQuery.graphql.ts is excluded by !**/__generated__/**, !**/__generated__/**
  • app/packages/relay/src/fragments/__generated__/datasetAppConfigFragment.graphql.ts is excluded by !**/__generated__/**, !**/__generated__/**
  • app/packages/relay/src/queries/__generated__/datasetQuery.graphql.ts is excluded by !**/__generated__/**, !**/__generated__/**
📒 Files selected for processing (36)
  • .gitignore (1 hunks)
  • app/packages/core/src/components/Dataset.tsx (1 hunks)
  • app/packages/core/src/components/Grid/Grid.tsx (4 hunks)
  • app/packages/core/src/components/Grid/useRefreshers.ts (2 hunks)
  • app/packages/core/src/components/Grid/useSelect.ts (3 hunks)
  • app/packages/core/src/components/Modal/use-looker.ts (2 hunks)
  • app/packages/core/src/components/Sidebar/syncAndCheckRefreshNeeded.test.ts (1 hunks)
  • app/packages/core/src/components/Sidebar/syncAndCheckRefreshNeeded.ts (1 hunks)
  • app/packages/core/src/components/Sidebar/useShouldReloadSample.ts (1 hunks)
  • app/packages/looker/src/lookers/abstract.ts (5 hunks)
  • app/packages/looker/src/lookers/frame-reader.ts (3 hunks)
  • app/packages/looker/src/lookers/video.ts (1 hunks)
  • app/packages/looker/src/overlays/base.ts (1 hunks)
  • app/packages/looker/src/overlays/detection.ts (1 hunks)
  • app/packages/looker/src/overlays/heatmap.ts (1 hunks)
  • app/packages/looker/src/overlays/segmentation.ts (1 hunks)
  • app/packages/looker/src/processOverlays.ts (1 hunks)
  • app/packages/looker/src/worker/disk-overlay-decoder.ts (2 hunks)
  • app/packages/looker/src/worker/index.ts (18 hunks)
  • app/packages/looker/src/worker/painter.ts (8 hunks)
  • app/packages/looker/src/worker/resolve-color.ts (1 hunks)
  • app/packages/looker/src/worker/shared.ts (1 hunks)
  • app/packages/looker/src/worker/tests/indexed-png-decoder.test.ts (0 hunks)
  • app/packages/relay/src/fragments/datasetAppConfigFragment.ts (1 hunks)
  • app/packages/state/src/labelsVisibility.test.ts (1 hunks)
  • app/packages/state/src/labelsVisibility.ts (1 hunks)
  • app/packages/state/src/recoil/schema.ts (3 hunks)
  • app/packages/state/src/recoil/selectors.ts (1 hunks)
  • app/packages/state/src/recoil/types.ts (2 hunks)
  • app/packages/utilities/src/schema.ts (3 hunks)
  • app/schema.graphql (2 hunks)
  • e2e-pw/src/oss/specs/overlays/detection-mask.spec.ts (1 hunks)
  • e2e-pw/src/oss/specs/sidebar/sidebar.spec.ts (4 hunks)
  • e2e-pw/src/oss/specs/smoke-tests/tagger.spec.ts (1 hunks)
  • fiftyone/core/odm/dataset.py (2 hunks)
  • fiftyone/server/query.py (2 hunks)
💤 Files with no reviewable changes (1)
  • app/packages/looker/src/worker/tests/indexed-png-decoder.test.ts
🚧 Files skipped from review as they are similar to previous changes (26)
  • app/packages/looker/src/worker/shared.ts
  • app/packages/state/src/recoil/selectors.ts
  • .gitignore
  • app/packages/core/src/components/Dataset.tsx
  • app/schema.graphql
  • app/packages/relay/src/fragments/datasetAppConfigFragment.ts
  • app/packages/core/src/components/Grid/useSelect.ts
  • e2e-pw/src/oss/specs/overlays/detection-mask.spec.ts
  • app/packages/looker/src/overlays/segmentation.ts
  • app/packages/looker/src/overlays/heatmap.ts
  • app/packages/looker/src/lookers/frame-reader.ts
  • app/packages/state/src/recoil/types.ts
  • app/packages/looker/src/overlays/detection.ts
  • app/packages/looker/src/worker/painter.ts
  • app/packages/core/src/components/Grid/Grid.tsx
  • app/packages/looker/src/lookers/video.ts
  • e2e-pw/src/oss/specs/sidebar/sidebar.spec.ts
  • fiftyone/server/query.py
  • app/packages/core/src/components/Grid/useRefreshers.ts
  • e2e-pw/src/oss/specs/smoke-tests/tagger.spec.ts
  • app/packages/looker/src/overlays/base.ts
  • app/packages/looker/src/worker/disk-overlay-decoder.ts
  • app/packages/core/src/components/Sidebar/syncAndCheckRefreshNeeded.ts
  • app/packages/core/src/components/Sidebar/useShouldReloadSample.ts
  • app/packages/core/src/components/Sidebar/syncAndCheckRefreshNeeded.test.ts
  • fiftyone/core/odm/dataset.py
🧰 Additional context used
📓 Path-based instructions (9)
app/packages/state/src/labelsVisibility.test.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/core/src/components/Modal/use-looker.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/state/src/recoil/schema.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/looker/src/processOverlays.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/looker/src/worker/resolve-color.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/looker/src/lookers/abstract.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/state/src/labelsVisibility.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/utilities/src/schema.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/looker/src/worker/index.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

📓 Learnings (1)
app/packages/looker/src/lookers/abstract.ts (1)
Learnt from: sashankaryal
PR: voxel51/fiftyone#5356
File: app/packages/looker/src/lookers/abstract.ts:49-50
Timestamp: 2025-01-09T23:46:50.918Z
Learning: The `UPDATING_SAMPLES_IDS` set in `app/packages/looker/src/lookers/abstract.ts` is intentionally module-scoped to prevent concurrent updates of the same sample across different instances of AbstractLooker.
⏰ Context from checks skipped due to timeout of 90000ms (7)
  • GitHub Check: test / test-python (ubuntu-latest-m, 3.11)
  • GitHub Check: test / test-python (ubuntu-latest-m, 3.10)
  • GitHub Check: test / test-app
  • GitHub Check: test / test-python (ubuntu-latest-m, 3.9)
  • GitHub Check: e2e / test-e2e
  • GitHub Check: build / build
  • GitHub Check: build
🔇 Additional comments (13)
app/packages/core/src/components/Modal/use-looker.ts (1)

6-6: LGTM!

Clean import statement following the project's import conventions.

app/packages/utilities/src/schema.ts (3)

1-12: LGTM! Well-structured constants and exports.

The constants are properly defined and exported, following TypeScript best practices.


68-95: LGTM! Clean function modification.

The changes to getFieldsWithEmbeddedDocType maintain backward compatibility while adding new functionality. The code is well-structured and handles both string and array inputs efficiently.


116-124: LGTM! Clean utility function implementation.

The getDenseLabelNames function follows single responsibility principle and effectively uses the modified getFieldsWithEmbeddedDocType function.

app/packages/state/src/recoil/schema.ts (2)

325-344: LGTM! Clean Recoil selector implementation.

The defaultVisibleLabels selector is well-implemented:

  1. Proper memoization through Recoil's selector
  2. Clean integration with existing selectors
  3. Follows Recoil best practices

533-548: LGTM! Clean integration with existing functionality.

The changes to activeFields selector cleanly integrate the new default visibility functionality while maintaining existing behavior.

app/packages/looker/src/worker/resolve-color.ts (1)

5-9: LGTM! Well-defined interface.

The ResolveColor interface clearly defines the contract for color resolution with appropriate types.

app/packages/looker/src/processOverlays.ts (2)

34-35: LGTM! Good validation.

The field validation ensures that only valid overlays are processed.


106-126: LGTM! Well-structured filter function.

The filter function effectively validates overlays and follows the same pattern as the main processing logic.

app/packages/looker/src/worker/index.ts (2)

33-45: LGTM! Clean color resolution integration.

The color resolution is well-integrated with the worker system.


396-398: LGTM! Proper type extension.

The FrameChunkResponse interface is correctly extended with the activePaths property.

app/packages/looker/src/lookers/abstract.ts (2)

49-50: LGTM! Well-documented shared state.

The module-scoped UPDATING_SAMPLES_IDS set is intentionally shared to prevent concurrent updates across instances.


726-730: LGTM! Clean overlay cleanup.

The cleanOverlays method properly handles the new setTargetsToNull parameter.

app/packages/looker/src/lookers/abstract.ts Outdated Show resolved Hide resolved
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (11)
app/packages/core/src/components/Sidebar/useShouldReloadSample.ts (1)

12-44: Consider adding error boundaries for Map operations.

While the implementation is solid, Map operations could potentially throw if the lookerId is invalid.

 const getNewFields = useCallback(
   (id: string) => {
+    try {
       return syncAndGetNewLabels(
         id,
         modal ? modalActivePathsLUT : gridActivePathsLUT,
         new Set(activeLabelFieldsValue)
       );
+    } catch (error) {
+      console.error('Error syncing label fields:', error);
+      return null;
+    }
   },
   [activeLabelFieldsValue]
 );
app/packages/core/src/components/Sidebar/syncAndGetNewLabels.ts (1)

12-48: LGTM! Well-implemented synchronization logic.

The function is well-documented, has proper error handling, and follows best practices for performance optimization with early returns.

Consider adding debug logging for easier troubleshooting:

 export const syncAndGetNewLabels = (
   lookerId: string,
   lut: Map<LookerId, CachedLabels>,
   currentActiveLabelFields: Set<string>
 ): string[] | null => {
+  const debug = (msg: string, ...args: any[]) => console.debug(`[syncAndGetNewLabels] ${msg}`, ...args);
+
   if (currentActiveLabelFields.size === 0) {
+    debug('No active label fields');
     return null;
   }
app/packages/core/src/components/Sidebar/syncAndGetNewLabels.test.ts (1)

4-87: LGTM! Comprehensive test coverage.

The test suite is well-structured and covers all important scenarios including edge cases.

Consider adding tests for error cases:

+  it("handles invalid looker IDs gracefully", () => {
+    const id = "";
+    const currentActiveLabelFields = new Set(["field1"]);
+    
+    const result = syncAndGetNewLabels(id, lut, currentActiveLabelFields);
+    expect(result).not.toBeUndefined();
+  });
app/packages/core/src/components/Modal/use-looker.ts (1)

55-69: Consider memoizing getNewFields callback.

The effect implementation is correct, but the getNewFields callback could be memoized to prevent unnecessary re-renders.

-  const getNewFields = useShouldReloadSampleOnActiveFieldsChange({
+  const getNewFields = React.useMemo(
+    () => useShouldReloadSampleOnActiveFieldsChange({
     modal: true,
-  });
+    }),
+    []
+  );
app/packages/looker/src/worker/async-labels-rendering-manager.ts (1)

29-38: Consider a more efficient strategy for large queues

This loop removes jobs while iterating over jobQueue, which may become inefficient for large queues. You could optimize this logic by using a dedicated queue structure (e.g., a linked list or a lightweight FIFO queue) to reduce time complexity for removals at arbitrary positions.

app/packages/looker/src/worker/async-labels-rendering-manager.test.ts (2)

23-40: Potential test flakiness from fixed timeouts

The setTimeout(..., 10) usage could lead to test flakiness in slower environments. Consider using mock timers or other deterministic synchronization methods to ensure consistent results.


111-129: Error scenario coverage

The test clarifies that worker errors bubble up as promises being rejected. You may consider adding an assertion to ensure partial updates do not remain in a corrupted state, but otherwise this coverage looks solid.

app/packages/looker/src/worker/index.ts (3)

33-46: Centralize color resolution initialization for clarity

Using [requestColor, resolveColor] = colorResolve(); is concise. Consider adding a short inline doc to clarify how color resolution is injected and used, especially since it’s central to label rendering.


51-74: Fix typographical error in comment

“precendence” should be “precedence”. Additionally, consider clarifying how the renderStatus overrides activePaths to make the logic explicit.

- // it means this label was processed before and we should re-render it
- // it takes precendence over activePaths
+ // It means this label was processed before and we should re-render it
+ // It takes precedence over activePaths

602-603: New property in SetStream

Adding activePaths to the stream configuration is an effective way to control label processing. Ensure the upstream usage sets a default (e.g., an empty array) when not explicitly provided.

app/packages/looker/src/lookers/abstract.ts (1)

110-169: Consider removing the type cast in the constructor.

The type cast this as unknown as Lookers might be unnecessary. Consider extending the class from Lookers or updating the AsyncLabelsRenderingManager constructor to accept AbstractLooker.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a0633a4 and 1332d83.

📒 Files selected for processing (10)
  • app/packages/core/src/components/Grid/useSelect.ts (3 hunks)
  • app/packages/core/src/components/Modal/use-looker.ts (2 hunks)
  • app/packages/core/src/components/Sidebar/syncAndGetNewLabels.test.ts (1 hunks)
  • app/packages/core/src/components/Sidebar/syncAndGetNewLabels.ts (1 hunks)
  • app/packages/core/src/components/Sidebar/useShouldReloadSample.ts (1 hunks)
  • app/packages/looker/src/lookers/abstract.ts (7 hunks)
  • app/packages/looker/src/worker/async-labels-rendering-manager.test.ts (1 hunks)
  • app/packages/looker/src/worker/async-labels-rendering-manager.ts (1 hunks)
  • app/packages/looker/src/worker/index.ts (18 hunks)
  • app/packages/looker/src/worker/painter.ts (8 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • app/packages/core/src/components/Grid/useSelect.ts
  • app/packages/looker/src/worker/painter.ts
🧰 Additional context used
📓 Path-based instructions (8)
app/packages/core/src/components/Modal/use-looker.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/core/src/components/Sidebar/useShouldReloadSample.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/looker/src/worker/index.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/looker/src/worker/async-labels-rendering-manager.test.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/looker/src/lookers/abstract.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/core/src/components/Sidebar/syncAndGetNewLabels.test.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/looker/src/worker/async-labels-rendering-manager.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/core/src/components/Sidebar/syncAndGetNewLabels.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

📓 Learnings (1)
app/packages/looker/src/lookers/abstract.ts (1)
Learnt from: sashankaryal
PR: voxel51/fiftyone#5356
File: app/packages/looker/src/lookers/abstract.ts:49-50
Timestamp: 2025-01-09T23:46:50.918Z
Learning: The `UPDATING_SAMPLES_IDS` set in `app/packages/looker/src/lookers/abstract.ts` is intentionally module-scoped to prevent concurrent updates of the same sample across different instances of AbstractLooker.
⏰ Context from checks skipped due to timeout of 90000ms (5)
  • GitHub Check: test / test-python (ubuntu-latest-m, 3.10)
  • GitHub Check: test / test-app
  • GitHub Check: build / build
  • GitHub Check: e2e / test-e2e
  • GitHub Check: build
🔇 Additional comments (12)
app/packages/core/src/components/Sidebar/useShouldReloadSample.ts (1)

6-10: LGTM! Well-defined types and clear exports.

The type definitions are clear and the Map declarations follow TypeScript best practices.

app/packages/core/src/components/Modal/use-looker.ts (1)

1-6: LGTM! Clean import organization.

The imports are well-organized and the new hook import is properly placed.

app/packages/looker/src/worker/async-labels-rendering-manager.ts (4)

49-65: Ensure active job removal logic is robust

Inside the while loop, you remove a job from jobQueue and break. This is correct for immediate processing, but be mindful of potential edge cases where multiple jobs might share the same sample reference. A robust approach would confirm that subsequent jobs referencing the same sample are appropriately updated or merged.


103-107: Validate keys filtering logic

When filtering out keys that are not in job.labels, confirm that no vital metadata is accidentally removed from the sample object (e.g., metadata necessary for error handling or logging).


137-150: Check concurrency around shared references

The code pushes new jobs into the global queue and updates shared structures (pendingJobs, processingSamples). Although this runs in a single thread in most workers, confirm that external race conditions or re-entrant calls (e.g., from message event handlers) won’t lead to inconsistent states.


172-178: Good testing utility for state reset

The _internal_resetForTests method is a clean choice to ensure consistent state between tests. No issues observed here.

app/packages/looker/src/worker/async-labels-rendering-manager.test.ts (2)

86-99: Meaningful assertion on merged sample

The test assertion thoroughly checks that the original field (“b”) remains intact while “a” and “c” are transformed. This is a clear demonstration of the merge behavior; nice work!


101-109: Good coverage of processing state checks

Verifying isProcessing() across asynchronous transitions is important. Overall logic appears correct.

app/packages/looker/src/worker/index.ts (2)

95-96: Include usage checks for new parameters

The new schema and activePaths parameters add complexity to downstream calls. Confirm that all call sites provide valid data, especially if activePaths can be empty or undefined.


205-212: Confirm painting logic syncs with decoding phase

shouldProcessLabel is invoked again before painting. Ensure that if a label was not decoded (e.g., renderStatus = null), you do not attempt to paint it prematurely.

app/packages/looker/src/lookers/abstract.ts (2)

48-51: LGTM!

The module-scoped UPDATING_SAMPLES_IDS set is intentionally shared across instances to prevent concurrent updates of the same sample.


758-761: LGTM!

The setTargetsToNull parameter is well-typed and properly used in the cleanup method.

app/packages/looker/src/lookers/abstract.ts Outdated Show resolved Hide resolved
app/packages/looker/src/lookers/abstract.ts Outdated Show resolved Hide resolved
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
app/packages/looker/src/lookers/abstract.ts (2)

48-51: LGTM! Consider adding documentation for UPDATING_SAMPLES_IDS.

The module-scoped Set is intentionally used to prevent concurrent updates of the same sample across different instances of AbstractLooker.

Add this documentation:

+/**
+ * A shared set to track sample IDs that are currently being updated across all instances
+ * of AbstractLooker. This is intentionally module-scoped to prevent concurrent updates
+ * of the same sample across different instances, avoiding potential race conditions
+ * when the same sample is being processed by multiple lookers.
+ */
 const UPDATING_SAMPLES_IDS = new Set();

532-542: Add timeout and error handling for concurrent updates.

While the concurrent update prevention is good, consider these improvements:

  1. Add a timeout to prevent deadlock if an error occurs
  2. Add error handling in the microtask

Apply this diff:

 const id = sample.id ?? sample._id;
+const UPDATE_TIMEOUT = 5000; // 5 seconds
 
 if (UPDATING_SAMPLES_IDS.has(id)) {
   UPDATING_SAMPLES_IDS.delete(id);
   this.cleanOverlays(true);
-  queueMicrotask(() => this.updateSample(sample));
+  const timeoutId = setTimeout(() => {
+    UPDATING_SAMPLES_IDS.delete(id);
+  }, UPDATE_TIMEOUT);
+  queueMicrotask(() => {
+    try {
+      clearTimeout(timeoutId);
+      this.updateSample(sample);
+    } catch (error) {
+      UPDATING_SAMPLES_IDS.delete(id);
+      this.updater({ error });
+    }
+  });
   return;
 }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1332d83 and 486041b.

📒 Files selected for processing (1)
  • app/packages/looker/src/lookers/abstract.ts (7 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
app/packages/looker/src/lookers/abstract.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

📓 Learnings (1)
app/packages/looker/src/lookers/abstract.ts (1)
Learnt from: sashankaryal
PR: voxel51/fiftyone#5356
File: app/packages/looker/src/lookers/abstract.ts:49-50
Timestamp: 2025-01-09T23:46:50.918Z
Learning: The `UPDATING_SAMPLES_IDS` set in `app/packages/looker/src/lookers/abstract.ts` is intentionally module-scoped to prevent concurrent updates of the same sample across different instances of AbstractLooker.
⏰ Context from checks skipped due to timeout of 90000ms (7)
  • GitHub Check: test / test-python (ubuntu-latest-m, 3.11)
  • GitHub Check: lint / eslint
  • GitHub Check: test / test-python (ubuntu-latest-m, 3.10)
  • GitHub Check: build / build
  • GitHub Check: test / test-python (ubuntu-latest-m, 3.9)
  • GitHub Check: test / test-app
  • GitHub Check: build
🔇 Additional comments (3)
app/packages/looker/src/lookers/abstract.ts (3)

110-113: LGTM! The instance properties and initialization look good.

The state property is correctly marked as internal, and the asyncLabelsRenderingManager is properly initialized in the constructor.

Also applies to: 167-169


759-762: LGTM! The cleanOverlays method changes look good.

The addition of the setTargetsToNull parameter provides better control over the cleanup behavior.


546-576: 🛠️ Refactor suggestion

Add comprehensive error handling and simplify the promise chain.

The method could be improved with better error handling and a simpler promise chain.

Apply this diff:

 refreshSample(renderLabels: string[] | null) {
-  // todo: sometimes instance in spotlight?.updateItems() is defined but has no ref to sample
-  // this crashes the app. this is a bug and should be fixed
   if (!this.sample) {
+    console.warn('Attempted to refresh undefined sample');
     return;
   }
 
   if (!renderLabels?.length) {
-    this.updateSample(this.sample);
+    try {
+      this.updateSample(this.sample);
+    } catch (error) {
+      this.updater({ error });
+    }
+    return;
   }
 
-  this.asyncLabelsRenderingManager
-    .enqueueLabelPaintingJob({
+  return this.asyncLabelsRenderingManager
+    .enqueueLabelPaintingJob({
       sample: this.sample,
       labels: renderLabels,
     })
     .then(({ sample, coloring }) => {
       this.sample = sample;
       this.state.options.coloring = coloring;
       this.loadOverlays(sample);
 
-      // to run looker reconciliation
       this.updater({
         overlaysPrepared: true,
       });
     })
     .catch((error) => {
       this.updater({ error });
+      throw error; // Re-throw to allow caller to handle the error
     });
 }

Likely invalid or redundant comment.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

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 (4)
app/packages/core/src/components/Grid/useSelect.ts (2)

38-69: Add missing dependency to useEffect and consider refactoring for maintainability.

The label rendering logic implementation looks solid, but there are a few improvements to consider:

  1. The getNewFields function is missing from the useEffect dependency array on line 77.
  2. The complex logic could be extracted into separate functions for better maintainability.

Consider refactoring the code like this:

+ const handleNewFields = (instance, newFields, overlays) => {
+   const thisInstanceOverlays = overlays.filter(
+     (o) => o.field && newFields.includes(o.field)
+   );
+
+   thisInstanceOverlays?.forEach((o) => {
+     if (o.label) {
+       o.label.renderStatus = "pending";
+     }
+   });
+
+   if (thisInstanceOverlays?.length > 0) {
+     instance.refreshSample(newFields);
+   }
+ };
+
+ const handlePendingLabels = (instance, overlays) => {
+   const pending = overlays.filter(
+     (o) => o.field && o.label && o.label.renderStatus === "pending"
+   );
+
+   if (pending?.length > 0) {
+     const rerenderFields = pending.map((o) => o.field!);
+     instance.refreshSample(rerenderFields);
+   }
+ };

  const newFieldsIfAny = getNewFields(id.description);
  const overlays = instance.getSampleOverlays() ?? [];

  if (newFieldsIfAny) {
-   const thisInstanceOverlays = overlays.filter(
-     (o) => o.field && newFieldsIfAny.includes(o.field)
-   );
-
-   thisInstanceOverlays?.forEach((o) => {
-     if (o.label) {
-       o.label.renderStatus = "pending";
-     }
-   });
-
-   if (thisInstanceOverlays?.length > 0) {
-     instance.refreshSample(newFieldsIfAny);
-   }
+   handleNewFields(instance, newFieldsIfAny, overlays);
  } else {
-   const pending = overlays.filter(
-     (o) => o.field && o.label && o.label.renderStatus === "pending"
-   );
-
-   if (pending?.length > 0) {
-     const rerenderFields = pending.map((o) => o.field!);
-     instance.refreshSample(rerenderFields);
-   }
+   handlePendingLabels(instance, overlays);
  }

Also update the dependency array:

- }, [deferred, getFontSize, options, selected, spotlight, store]);
+ }, [deferred, getFontSize, getNewFields, options, selected, spotlight, store]);

50-52: Document the potential race condition.

The comment explains the "pending" status, but it would be helpful to document the known race condition mentioned in the PR objectives.

Add a note about the race condition:

  // "pending" means we're marking this label for rendering / painting
  // even if it's interrupted, say by unchecking sidebar
+ // Note: There's a known race condition that may occur if recoloring happens too quickly
app/packages/looker/src/lookers/abstract.ts (2)

51-51: LGTM! Consider adding documentation.

The module-scoped set is intentionally designed to prevent concurrent updates of the same sample across different instances.

Add this documentation to clarify the intention:

+/**
+ * A shared set to track sample IDs that are currently being updated across all instances
+ * of AbstractLooker. This is intentionally module-scoped to prevent concurrent updates
+ * of the same sample across different instances, avoiding potential race conditions.
+ */
const UPDATING_SAMPLES_IDS = new Set();

199-201: Add JSDoc documentation.

Consider adding documentation to clarify the purpose and return type of this method.

Add this documentation:

+/**
+ * Returns the current sample overlays.
+ * @returns {Overlay<State>[]} Array of overlays for the current sample
+ */
 getSampleOverlays(): Overlay<State>[] {
   return this.sampleOverlays;
 }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 486041b and 64df37a.

📒 Files selected for processing (5)
  • app/packages/core/src/components/Grid/useSelect.ts (3 hunks)
  • app/packages/looker/src/lookers/abstract.ts (9 hunks)
  • app/packages/looker/src/overlays/base.ts (3 hunks)
  • app/packages/looker/src/worker/index.ts (18 hunks)
  • app/packages/looker/src/worker/shared.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • app/packages/looker/src/worker/shared.ts
  • app/packages/looker/src/overlays/base.ts
  • app/packages/looker/src/worker/index.ts
🧰 Additional context used
📓 Path-based instructions (2)
app/packages/core/src/components/Grid/useSelect.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/looker/src/lookers/abstract.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

📓 Learnings (1)
app/packages/looker/src/lookers/abstract.ts (1)
Learnt from: sashankaryal
PR: voxel51/fiftyone#5356
File: app/packages/looker/src/lookers/abstract.ts:49-50
Timestamp: 2025-01-09T23:46:50.918Z
Learning: The `UPDATING_SAMPLES_IDS` set in `app/packages/looker/src/lookers/abstract.ts` is intentionally module-scoped to prevent concurrent updates of the same sample across different instances of AbstractLooker.
⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: test / test-app
  • GitHub Check: build / build
  • GitHub Check: e2e / test-e2e
  • GitHub Check: build
🔇 Additional comments (5)
app/packages/core/src/components/Grid/useSelect.ts (1)

6-6: LGTM! Clean hook initialization.

The import and hook initialization follow React best practices and align with the PR objectives for selective label rendering.

Also applies to: 16-18

app/packages/looker/src/lookers/abstract.ts (4)

110-111: Verify the necessity of making state public.

While the @internal tag indicates this is for internal use, making it public could lead to unintended external modifications. Consider keeping it protected unless there's a specific need for public access.

Could you explain why this property needs to be public? If it's only needed by specific internal modules, consider using TypeScript's module system to control access instead.


778-781: LGTM! Clean implementation of the cleanup flag.

The addition of the setTargetsToNull parameter provides better control over the cleanup process.


536-561: LGTM! Robust implementation of concurrent update prevention.

The changes effectively prevent concurrent updates of the same sample while adding proper error handling and deadlock prevention.


565-595: ⚠️ Potential issue

Add error handling and improve promise chain.

The method has several issues that need to be addressed:

  1. The TODO comment indicates a known crash bug
  2. Missing error handling when renderLabels is empty
  3. Promise chain could be improved

Apply this diff to fix the issues:

 refreshSample(renderLabels: string[] | null = null) {
-  // todo: sometimes instance in spotlight?.updateItems() is defined but has no ref to sample
-  // this crashes the app. this is a bug and should be fixed
   if (!this.sample) {
+    console.warn('Attempted to refresh undefined sample');
     return;
   }
 
   if (!renderLabels?.length) {
-    this.updateSample(this.sample);
+    try {
+      this.updateSample(this.sample);
+    } catch (error) {
+      this.updater({ error });
+    }
     return;
   }
 
-  this.asyncLabelsRenderingManager
+  return this.asyncLabelsRenderingManager
     .enqueueLabelPaintingJob({
       sample: this.sample,
       labels: renderLabels,
     })
     .then(({ sample, coloring }) => {
       this.sample = sample;
       this.state.options.coloring = coloring;
       this.loadOverlays(sample);
 
-      // to run looker reconciliation
       this.updater({
         overlaysPrepared: true,
       });
     })
     .catch((error) => {
       this.updater({ error });
+      throw error; // Re-throw to allow caller to handle the error
     });
 }

Likely invalid or redundant comment.

app/packages/looker/src/lookers/abstract.ts Outdated Show resolved Hide resolved
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🔭 Outside diff range comments (3)
app/packages/core/src/components/Modal/ImaVidLooker.tsx (3)

308-318: Replace polling with event-based updates

Using setInterval to poll for frame count is inefficient and can cause unnecessary re-renders.

Consider implementing an event-based system:

-    let intervalId = setInterval(() => {
-      const totalFrameCount =
-        imaVidLookerRef.current.frameStoreController.totalFrameCount;
-      if (totalFrameCount) {
-        setTotalFrameCount(totalFrameCount);
-        clearInterval(intervalId);
-      }
-    }, 10);
-
-    return () => clearInterval(intervalId);
+    const handleFrameCountUpdate = (count: number) => {
+      setTotalFrameCount(count);
+    };
+    
+    imaVidLookerRef.current.frameStoreController.on('frameCountUpdate', handleFrameCountUpdate);
+    
+    return () => {
+      imaVidLookerRef.current.frameStoreController.off('frameCountUpdate', handleFrameCountUpdate);
+    };

230-241: Fix potential memory leak in readyWhen hook

The interval in readyWhen might not be cleared if the component unmounts before totalFrameCount is resolved.

 const readyWhen = useCallback(async () => {
   return new Promise<void>((resolve) => {
-    let intervalId;
+    let intervalId: NodeJS.Timeout;
     intervalId = setInterval(() => {
       if (totalFrameCountRef.current) {
         clearInterval(intervalId);
         resolve();
       }
     }, 10);
+    
+    // Ensure cleanup after 5 seconds to prevent infinite polling
+    setTimeout(() => {
+      clearInterval(intervalId);
+      resolve();
+    }, 5000);
   });
 }, []);

217-219:

🧩 Analysis chain

Address TODO comments

There are multiple TODO comments indicating incomplete implementation of frame count updates.

Please verify if these TODOs are still relevant and if they should be tracked as issues:


🏁 Script executed:

#!/bin/bash
# Search for related frame count implementation in the codebase
rg -A 5 "frameCount|totalFrames" --type ts

Length of output: 37429

Frame count update mechanism in ImaVidLooker is still incomplete

  • The TODO remains in place in ImaVidLooker.tsx indicating that the mechanism for updating the frame count asynchronously is not fully implemented.
  • Unlike other parts of the codebase (e.g., in VideoLooker and various looker modules) where total frame counts are calculated and passed consistently, the ImaVidLooker component still expects an event-based update that has not been wired up.
  • This discrepancy confirms that the TODO is still relevant and should be tracked as a known issue for follow-up.
🧹 Nitpick comments (1)
app/packages/core/src/components/Modal/use-modal-selective-rendering.ts (1)

17-21: Add type safety for newFieldsIfAny

The newFieldsIfAny variable lacks type information, which could lead to type-related issues.

-    const newFieldsIfAny = getNewFields(id);
+    const newFieldsIfAny: string[] | undefined = getNewFields(id);

     if (newFieldsIfAny) {
-      looker?.refreshSample(newFieldsIfAny);
+      looker.refreshSample(newFieldsIfAny);
     }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 87f425c and 432306f.

📒 Files selected for processing (4)
  • app/packages/core/src/components/Modal/ImaVidLooker.tsx (5 hunks)
  • app/packages/core/src/components/Modal/use-looker.ts (2 hunks)
  • app/packages/core/src/components/Modal/use-modal-selective-rendering.ts (1 hunks)
  • app/packages/state/src/hooks/useLookerStore.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/packages/core/src/components/Modal/use-looker.ts
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.{ts,tsx}`: Review the Typescript and React code for co...

**/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

  • app/packages/state/src/hooks/useLookerStore.ts
  • app/packages/core/src/components/Modal/use-modal-selective-rendering.ts
  • app/packages/core/src/components/Modal/ImaVidLooker.tsx
⏰ Context from checks skipped due to timeout of 90000ms (5)
  • GitHub Check: test / test-python (ubuntu-latest-m, 3.10)
  • GitHub Check: test / test-app
  • GitHub Check: build / build
  • GitHub Check: e2e / test-e2e
  • GitHub Check: build
🔇 Additional comments (2)
app/packages/state/src/hooks/useLookerStore.ts (1)

1-11: LGTM! Clean type extension.

The addition of ImaVidLooker to the Lookers type union is well-structured and maintains type safety.

app/packages/core/src/components/Modal/ImaVidLooker.tsx (1)

320-320: LGTM! Good integration of selective rendering.

The integration of useModalSelectiveRendering is clean and follows the component's lifecycle management pattern.

Comment on lines +546 to +549
// to prevent deadlock, we'll remove the id from the set after a timeout
const timeoutId = setTimeout(() => {
UPDATING_SAMPLES_IDS.delete(id);
}, updateTimeoutMs);
Copy link
Contributor

@benjaminpkane benjaminpkane Feb 5, 2025

Choose a reason for hiding this comment

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

I don't see how this has an effect, noting that id is deleted on 542, as well.

Can UPDATING_SAMPLES_IDS be replaced with state internal to Looker? Like isBatching? Introducing a global store feels somewhat inflexible

Copy link
Contributor Author

@sashankaryal sashankaryal Feb 6, 2025

Choose a reason for hiding this comment

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

Can UPDATING_SAMPLES_IDS be replace with state internal to Looker?

Yes. lookers and samples have 1:1 relationship, so makes sense 100%. Will attempt in subsequent follow-up PR when I tackle data clone bug, which is related

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (1)
app/packages/core/src/components/Modal/use-modal-selective-rendering.ts (1)

13-23: ⚠️ Potential issue

Add missing dependency to useEffect

The getNewFields function is used inside useEffect but not included in the dependencies array.

-  }, [id, lookerOptions.activePaths, looker]);
+  }, [id, lookerOptions.activePaths, looker, getNewFields]);
🧹 Nitpick comments (4)
app/packages/core/src/components/Modal/ImaVidLooker.tsx (1)

305-318: Replace polling with event-based approach

The current implementation uses polling to check for frame count, which is not optimal. Consider implementing an event-based system as noted in the TODO comments.

-    useEffect(() => {
-      let intervalId = setInterval(() => {
-        const totalFrameCount =
-          imaVidLookerRef.current.frameStoreController.totalFrameCount;
-        if (totalFrameCount) {
-          setTotalFrameCount(totalFrameCount);
-          clearInterval(intervalId);
-        }
-      }, 10);
-
-      return () => clearInterval(intervalId);
-    }, [looker]);
+    useEffect(() => {
+      const handleFrameCountUpdate = (count: number) => {
+        setTotalFrameCount(count);
+      };
+      
+      imaVidLookerRef.current.frameStoreController.on('frameCountUpdate', handleFrameCountUpdate);
+      
+      return () => {
+        imaVidLookerRef.current.frameStoreController.off('frameCountUpdate', handleFrameCountUpdate);
+      };
+    }, [looker]);
app/packages/looker/src/lookers/imavid/index.ts (1)

208-213: Add type safety for frameNumber parameter.

The frameNumber parameter's type is not specified, and the null check could be more explicit.

-    const sampleId =
-      this.frameStoreController.store.frameIndex.get(frameNumber);
-
-    if (!sampleId) {
-      return;
-    }
+    const sampleId = typeof frameNumber === 'number' 
+      ? this.frameStoreController.store.frameIndex.get(frameNumber)
+      : null;
+
+    if (sampleId === null || sampleId === undefined) {
+      console.warn(`No sample found for frame number: ${frameNumber}`);
+      return;
+    }
app/packages/looker/src/overlays/detection.ts (2)

19-21: Add JSDoc comments for mask-related properties.

The mask and mask_path properties lack documentation explaining their purpose and relationship.

 export interface DetectionLabel extends RegularLabel {
+  /** Bitmap mask data for the detection */
   mask?: LabelMask;
+  /** Path to the mask resource */
   mask_path?: string;

46-50: Optimize containsPoint check for performance.

The mask check could be optimized by avoiding unnecessary property access.

-    if ((this.label.mask || this.label.mask_path) && this.label.mask?.data) {
+    const { mask, mask_path } = this.label;
+    if ((mask?.data || mask_path) && mask?.data) {
       return CONTAINS.NONE;
     }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 432306f and 9c98e52.

📒 Files selected for processing (5)
  • app/packages/core/src/components/Modal/ImaVidLooker.tsx (5 hunks)
  • app/packages/core/src/components/Modal/use-modal-selective-rendering.ts (1 hunks)
  • app/packages/looker/src/lookers/abstract.ts (8 hunks)
  • app/packages/looker/src/lookers/imavid/index.ts (1 hunks)
  • app/packages/looker/src/overlays/detection.ts (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/packages/looker/src/lookers/abstract.ts
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.{ts,tsx}`: Review the Typescript and React code for co...

**/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

  • app/packages/core/src/components/Modal/use-modal-selective-rendering.ts
  • app/packages/looker/src/lookers/imavid/index.ts
  • app/packages/looker/src/overlays/detection.ts
  • app/packages/core/src/components/Modal/ImaVidLooker.tsx
⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: test / test-python (ubuntu-latest-m, 3.11)
  • GitHub Check: test / test-python (ubuntu-latest-m, 3.10)
  • GitHub Check: test / test-python (ubuntu-latest-m, 3.9)
  • GitHub Check: e2e / test-e2e
  • GitHub Check: build / build
  • GitHub Check: build
🔇 Additional comments (3)
app/packages/core/src/components/Modal/use-modal-selective-rendering.ts (1)

26-58: Well-implemented subscription pattern with proper cleanup!

The hook follows React best practices:

  • Proper subscription cleanup in useEffect
  • Correct dependency array
  • Efficient frame update handling
app/packages/core/src/components/Modal/ImaVidLooker.tsx (2)

61-66: Improved type safety with direct ImaVidLooker typing

The removal of type casting through unknown and use of direct ImaVidLooker type improves type safety and code clarity.

Also applies to: 77-77


320-320: Good integration of selective rendering hook!

The hook is properly integrated at the component level, enhancing the modal's rendering performance.

app/packages/looker/src/lookers/imavid/index.ts Outdated Show resolved Hide resolved
app/packages/looker/src/overlays/detection.ts Outdated Show resolved Hide resolved
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
app/packages/looker/src/overlays/segmentation.ts (1)

268-273: Add JSDoc documentation for the cleanup method.

While the implementation is correct, please add JSDoc documentation explaining the purpose of the setTargetsToNull parameter and when it should be used.

Add this documentation above the method:

+/**
+ * Cleans up resources associated with this overlay.
+ * @param setTargetsToNull - When true, clears the targets array in addition to closing the bitmap.
+ *                          Set to false to retain targets for potential reuse.
+ */
 public cleanup(setTargetsToNull = false): void {
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9c98e52 and a84f781.

📒 Files selected for processing (5)
  • app/packages/core/src/components/Modal/use-modal-selective-rendering.ts (1 hunks)
  • app/packages/looker/src/lookers/imavid/index.ts (2 hunks)
  • app/packages/looker/src/overlays/detection.ts (3 hunks)
  • app/packages/looker/src/overlays/heatmap.ts (4 hunks)
  • app/packages/looker/src/overlays/segmentation.ts (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • app/packages/looker/src/overlays/heatmap.ts
  • app/packages/looker/src/overlays/detection.ts
  • app/packages/core/src/components/Modal/use-modal-selective-rendering.ts
  • app/packages/looker/src/lookers/imavid/index.ts
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.{ts,tsx}`: Review the Typescript and React code for co...

**/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

  • app/packages/looker/src/overlays/segmentation.ts
⏰ Context from checks skipped due to timeout of 90000ms (7)
  • GitHub Check: test / test-python (ubuntu-latest-m, 3.11)
  • GitHub Check: test / test-python (ubuntu-latest-m, 3.10)
  • GitHub Check: test / test-python (ubuntu-latest-m, 3.9)
  • GitHub Check: test / test-app
  • GitHub Check: build / build
  • GitHub Check: e2e / test-e2e
  • GitHub Check: build
🔇 Additional comments (3)
app/packages/looker/src/overlays/segmentation.ts (3)

49-49: LGTM! Improved null safety check in constructor.

The enhanced null check using optional chaining prevents potential runtime errors by ensuring both mask and its data exist.


65-67: LGTM! Added defensive null check in containsPoint.

Early return with CONTAINS.NONE when mask data is missing prevents potential runtime errors.


84-84: LGTM! Enhanced bitmap validation in draw method.

The improved check ensures bitmap is valid by verifying its width property, preventing attempts to draw invalid bitmaps.

Comment on lines +38 to +39
? instance.pluckedOverlays
: instance.sampleOverlays;
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think exposing these is necessary. refreshSample could capture the overlay filtering logic happening below

export type AsyncLabelsRenderingJob = {
sample: Sample;
labels: string[];
lookerRef: Lookers;
Copy link
Contributor

@benjaminpkane benjaminpkane Feb 6, 2025

Choose a reason for hiding this comment

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

I think this ref could be replaced with a function that returns the required options. This would remove the need to expose the internal state

Copy link
Contributor

@benjaminpkane benjaminpkane left a comment

Choose a reason for hiding this comment

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

A couple nits that are probably worth following up on, but once video is working this LGTM!

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a84f781 and d71d8b5.

📒 Files selected for processing (3)
  • app/packages/core/src/components/Modal/ModalLooker.tsx (2 hunks)
  • app/packages/core/src/components/Modal/VideoLooker.tsx (2 hunks)
  • app/packages/core/src/components/Modal/use-modal-selective-rendering.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.{ts,tsx}`: Review the Typescript and React code for co...

**/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

  • app/packages/core/src/components/Modal/VideoLooker.tsx
  • app/packages/core/src/components/Modal/ModalLooker.tsx
  • app/packages/core/src/components/Modal/use-modal-selective-rendering.ts
⏰ Context from checks skipped due to timeout of 90000ms (8)
  • GitHub Check: test / test-python (ubuntu-latest-m, 3.11)
  • GitHub Check: test / test-python (ubuntu-latest-m, 3.10)
  • GitHub Check: test / test-python (ubuntu-latest-m, 3.9)
  • GitHub Check: test / test-app
  • GitHub Check: e2e / test-e2e
  • GitHub Check: build / build
  • GitHub Check: lint / eslint
  • GitHub Check: build
🔇 Additional comments (4)
app/packages/core/src/components/Modal/ModalLooker.tsx (1)

9-9: LGTM! The selective rendering implementation looks good.

The changes correctly implement the selective rendering optimization by:

  1. Adding the necessary hook import
  2. Extracting the looker from useLooker
  3. Using the hook with proper dependencies

Also applies to: 31-33

app/packages/core/src/components/Modal/use-modal-selective-rendering.ts (2)

16-26: Add missing dependency to useEffect

The getNewFields function is used inside useEffect but not included in the dependencies array.

-  }, [id, lookerOptions.activePaths, looker]);
+  }, [id, lookerOptions.activePaths, looker, getNewFields]);

81-83: Consider implementing granular refreshing for video looker

The TODO comment indicates missing functionality for granular refreshing in video lookers. This could impact performance.

Would you like me to help create an issue to track the implementation of granular refreshing for video lookers?

app/packages/core/src/components/Modal/VideoLooker.tsx (1)

12-12: LGTM! The video modal selective rendering is properly integrated.

The hook is imported and used correctly with the required dependencies.

Also applies to: 26-26

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
app/packages/core/src/components/Modal/use-modal-selective-rendering.ts (1)

16-26: 🛠️ Refactor suggestion

Add missing dependency and remove unnecessary optional chaining.

The hook has two issues:

  1. Missing dependency in useEffect (getNewFields)
  2. Unnecessary optional chaining on looker.refreshSample since looker is already null-checked
-      looker?.refreshSample(newFieldsIfAny);
+      looker.refreshSample(newFieldsIfAny);
   }
-  }, [id, lookerOptions.activePaths, looker]);
+  }, [id, lookerOptions.activePaths, looker, getNewFields]);
🧹 Nitpick comments (3)
app/packages/core/src/components/Modal/use-modal-selective-rendering.ts (1)

6-9: Consider making the looker parameter non-nullable.

Since the hook requires a valid looker to function, consider making it non-nullable to improve type safety.

-  looker: Lookers
+  looker: NonNullable<Lookers>
app/packages/core/src/components/Sidebar/useDetectNewActiveLabelFields.ts (1)

63-71: Consider using a single cleanup effect.

The component has two separate effects for cleanup: one for dataset changes (lines 31-37) and one for unmounting. These could be combined into a single effect to reduce code duplication.

  useEffect(() => {
+   // reset when dataset changes or component unmounts
    if (modal) {
      modalActivePathsLUT.clear();
    } else {
      gridActivePathsLUT.clear();
    }
+   return () => {
+     if (modal) {
+       modalActivePathsLUT.clear();
+     } else {
+       gridActivePathsLUT.clear();
+     }
+   };
-  }, [datasetNameValue]);
-
-  useEffect(() => {
-    return () => {
-      if (modal) {
-        modalActivePathsLUT.clear();
-      } else {
-        gridActivePathsLUT.clear();
-      }
-    };
-  }, [modal]);
+  }, [datasetNameValue, modal]);
app/packages/looker/src/lookers/imavid/index.ts (1)

32-36: Add a comment explaining the thumbnail check.

The early return based on thumbnail configuration could use a more descriptive comment explaining why this check is necessary.

-   // we have other mechanism for the modal
+   // Skip frame number subscription for modal views as they handle updates differently
    if (!this.state.config.thumbnail) {
      return;
    }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d71d8b5 and b297586.

📒 Files selected for processing (4)
  • app/packages/core/src/components/Grid/useSelect.ts (3 hunks)
  • app/packages/core/src/components/Modal/use-modal-selective-rendering.ts (1 hunks)
  • app/packages/core/src/components/Sidebar/useDetectNewActiveLabelFields.ts (1 hunks)
  • app/packages/looker/src/lookers/imavid/index.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/packages/core/src/components/Grid/useSelect.ts
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.{ts,tsx}`: Review the Typescript and React code for co...

**/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

  • app/packages/core/src/components/Sidebar/useDetectNewActiveLabelFields.ts
  • app/packages/looker/src/lookers/imavid/index.ts
  • app/packages/core/src/components/Modal/use-modal-selective-rendering.ts
⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: e2e / test-e2e
  • GitHub Check: test / test-python (ubuntu-latest-m, 3.11)
  • GitHub Check: test / test-python (ubuntu-latest-m, 3.10)
  • GitHub Check: test / test-python (ubuntu-latest-m, 3.9)
  • GitHub Check: build / build
  • GitHub Check: build
🔇 Additional comments (6)
app/packages/core/src/components/Modal/use-modal-selective-rendering.ts (2)

1-5: LGTM! Clean import organization.

Imports are well-organized, following the convention of external dependencies first, followed by internal modules.


41-65: ⚠️ Potential issue

Add safety checks and missing dependencies.

The useEffect has several issues:

  1. Missing null check on looker before subscription
  2. Potential race condition in subscription callback
  3. Missing dependency lookerOptions.activePaths
 useEffect(() => {
+  if (!looker) {
+    return;
+  }
+
   const unsub = looker.subscribeToState(
     "currentFrameNumber",
     (currentFrameNumber: number) => {
-      if (!looker.thisFrameSample?.sample) {
+      if (!looker?.thisFrameSample?.sample) {
         return;
       }

       const thisFrameId = `${id}-${currentFrameNumber}`;
       const newFieldsIfAny = getNewFields(thisFrameId);

       if (newFieldsIfAny) {
         looker.refreshSample(newFieldsIfAny, currentFrameNumber);
       } else {
         looker.refreshOverlaysToCurrentFrame();
       }
     }
   );

   return () => {
     unsub();
   };
-  }, [getNewFields, looker]);
+  }, [getNewFields, looker, lookerOptions.activePaths]);

Likely invalid or redundant comment.

app/packages/core/src/components/Sidebar/useDetectNewActiveLabelFields.ts (2)

6-10: LGTM! Well-defined types and clear exports.

The type definitions are clear and the lookup tables are properly typed with generics.


39-48: Add modal to useCallback dependencies.

The getNewFields callback uses the modal parameter but doesn't include it in the dependencies array. This could lead to stale closures.

  const getNewFields = useCallback(
    (id: string) => {
      return syncAndGetNewLabels(
        id,
        modal ? modalActivePathsLUT : gridActivePathsLUT,
        new Set(activeLabelFieldsValue)
      );
    },
-   [activeLabelFieldsValue]
+   [activeLabelFieldsValue, modal]
  );
app/packages/looker/src/lookers/imavid/index.ts (2)

227-231: Convert TODO comment into proper error handling.

The TODO comment indicates a potential crash scenario. This should be properly handled with error boundaries or defensive programming.

-    // todo: sometimes instance in spotlight?.updateItems() is defined but has no ref to sample
-    // this crashes the app. this is a bug and should be fixed
+    if (!this.sample) {
+      console.warn('Invalid sample reference detected in refreshSample');
+      return;
+    }

252-276: ⚠️ Potential issue

Implement proper error handling and race condition prevention.

The async label painting job could lead to race conditions if multiple refreshes are triggered in quick succession. Additionally, error handling could be more informative.

+    let isLatestRequest = true;
+    const requestId = Date.now();
+    this.latestRequestId = requestId;
+
     this.asyncLabelsRenderingManager
       .enqueueLabelPaintingJob({
         sample: sample as Sample,
         labels: renderLabels,
       })
       .then(({ sample, coloring }) => {
+        if (!isLatestRequest || this.latestRequestId !== requestId) {
+          console.debug('Skipping stale label painting result');
+          return;
+        }
         if (sampleIdFromFramesStore) {
           this.frameStoreController.store.updateSample(
             sampleIdFromFramesStore,
             sample
           );
         } else {
           this.sample = sample;
         }
         this.state.options.coloring = coloring;
         this.loadOverlays(sample);

         // to run looker reconciliation
         this.updater({
           overlaysPrepared: true,
         });
       })
       .catch((error) => {
-        this.updater({ error });
+        console.error('Label painting failed:', error);
+        this.updater({ 
+          error: new Error(`Failed to paint labels: ${error.message}`),
+          overlaysPrepared: false 
+        });
       });

Likely invalid or redundant comment.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (3)
app/packages/looker/src/lookers/imavid/index.ts (1)

246-297: ⚠️ Potential issue

Improve error handling and prevent race conditions.

The function has several issues:

  1. The TODO comment indicates a potential crash that needs to be fixed.
  2. No race condition prevention for concurrent label painting jobs.
  3. Minimal error handling in the promise chain.

The TODO comment about potential crashes has been previously addressed. Additionally, apply this diff to improve error handling and prevent race conditions:

 refreshSample(renderLabels: string[] | null = null, frameNumber?: number) {
   if (!this.sample) {
+    console.warn('Attempted to refresh undefined sample');
     return;
   }

   if (!renderLabels?.length) {
-    this.updateSample(this.sample);
+    try {
+      this.updateSample(this.sample);
+    } catch (error) {
+      console.error('Failed to update sample:', error);
+      this.updater({ error });
+    }
     return;
   }

+  let isLatestRequest = true;
+  const requestId = Date.now();
+  this.latestRequestId = requestId;
+
   const sampleIdFromFramesStore =
     this.frameStoreController.store.frameIndex.get(frameNumber);

   let sample: Sample;

   // if sampleIdFromFramesStore is not found, it means we're in grid thumbnail view
   if (sampleIdFromFramesStore) {
     const { image: _cachedImage, ...sampleWithoutImage } =
       this.frameStoreController.store.samples.get(sampleIdFromFramesStore);
     sample = sampleWithoutImage.sample;
   } else {
     sample = this.sample;
   }

   this.asyncLabelsRenderingManager
     .enqueueLabelPaintingJob({
       sample: sample as Sample,
       labels: renderLabels,
     })
     .then(({ sample, coloring }) => {
+      if (!isLatestRequest || this.latestRequestId !== requestId) {
+        console.debug('Skipping stale label painting result');
+        return;
+      }
       if (sampleIdFromFramesStore) {
         this.frameStoreController.store.updateSample(
           sampleIdFromFramesStore,
           sample
         );
       } else {
         this.sample = sample;
       }
       this.state.options.coloring = coloring;
-      this.loadOverlays(sample);
+      try {
+        this.loadOverlays(sample);
+      } catch (error) {
+        console.error('Failed to load overlays:', error);
+        this.updater({ error });
+        return;
+      }

       // to run looker reconciliation
       this.updater({
         overlaysPrepared: true,
       });
     })
     .catch((error) => {
-      this.updater({ error });
+      console.error('Label painting failed:', error);
+      this.updater({ 
+        error: new Error(`Failed to paint labels: ${error.message}`),
+        overlaysPrepared: false 
+      });
     });
 }
app/packages/looker/src/lookers/abstract.ts (2)

534-568: ⚠️ Potential issue

Improve error handling and timeout management.

The function has several areas for improvement:

  1. The TODO comment indicates a potential crash that needs to be fixed.
  2. The timeout duration is hardcoded.
  3. The error handling in the microtask could be more informative.

The TODO comment about potential crashes has been previously addressed. Additionally, apply this diff to improve error handling and timeout management:

+const SAMPLE_UPDATE_TIMEOUT_MS = 10000;
+
 updateSample(sample: Sample) {
   if (!this.sample) {
+    console.warn('Attempted to update undefined sample');
     return;
   }

   const id = sample.id ?? sample._id;
-  const updateTimeoutMs = 10000;

   if (UPDATING_SAMPLES_IDS.has(id)) {
     UPDATING_SAMPLES_IDS.delete(id);
     this.cleanOverlays(true);

     // to prevent deadlock, we'll remove the id from the set after a timeout
     const timeoutId = setTimeout(() => {
+      console.warn(`Sample update timed out after ${SAMPLE_UPDATE_TIMEOUT_MS}ms`);
       UPDATING_SAMPLES_IDS.delete(id);
-    }, updateTimeoutMs);
+    }, SAMPLE_UPDATE_TIMEOUT_MS);

     queueMicrotask(() => {
       try {
         this.updateSample(sample);
         clearTimeout(timeoutId);
       } catch (e) {
         UPDATING_SAMPLES_IDS.delete(id);
-        this.updater({ error: e });
+        const error = e instanceof Error ? e : new Error('Failed to update sample');
+        console.error('Sample update failed:', error);
+        this.updater({ 
+          error,
+          overlaysPrepared: false 
+        });
       }
     });
     return;
   }

   UPDATING_SAMPLES_IDS.add(id);

-  this.loadSample(sample, retrieveArrayBuffers(this.sampleOverlays));
+  try {
+    this.loadSample(sample, retrieveArrayBuffers(this.sampleOverlays));
+  } catch (error) {
+    UPDATING_SAMPLES_IDS.delete(id);
+    console.error('Failed to load sample:', error);
+    this.updater({ 
+      error: new Error(`Failed to load sample: ${error.message}`),
+      overlaysPrepared: false 
+    });
+  }
 }

570-600: ⚠️ Potential issue

Improve error handling and prevent race conditions.

The function has several issues:

  1. The TODO comment indicates a potential crash that needs to be fixed.
  2. No race condition prevention for concurrent label painting jobs.
  3. Minimal error handling in the promise chain.

The TODO comment about potential crashes has been previously addressed. Additionally, apply this diff to improve error handling and prevent race conditions:

 refreshSample(renderLabels: string[] | null = null) {
   if (!this.sample) {
+    console.warn('Attempted to refresh undefined sample');
     return;
   }

   if (!renderLabels?.length) {
-    this.updateSample(this.sample);
+    try {
+      this.updateSample(this.sample);
+    } catch (error) {
+      console.error('Failed to update sample:', error);
+      this.updater({ error });
+    }
     return;
   }

+  let isLatestRequest = true;
+  const requestId = Date.now();
+  this.latestRequestId = requestId;
+
   this.asyncLabelsRenderingManager
     .enqueueLabelPaintingJob({
       sample: this.sample,
       labels: renderLabels,
     })
     .then(({ sample, coloring }) => {
+      if (!isLatestRequest || this.latestRequestId !== requestId) {
+        console.debug('Skipping stale label painting result');
+        return;
+      }
       this.sample = sample;
       this.state.options.coloring = coloring;
-      this.loadOverlays(sample);
+      try {
+        this.loadOverlays(sample);
+      } catch (error) {
+        console.error('Failed to load overlays:', error);
+        this.updater({ error });
+        return;
+      }

       // to run looker reconciliation
       this.updater({
         overlaysPrepared: true,
       });
     })
     .catch((error) => {
-      this.updater({ error });
+      console.error('Label painting failed:', error);
+      this.updater({ 
+        error: new Error(`Failed to paint labels: ${error.message}`),
+        overlaysPrepared: false 
+      });
     });
 }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b297586 and b506bbb.

📒 Files selected for processing (2)
  • app/packages/looker/src/lookers/abstract.ts (8 hunks)
  • app/packages/looker/src/lookers/imavid/index.ts (3 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.{ts,tsx}`: Review the Typescript and React code for co...

**/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

  • app/packages/looker/src/lookers/imavid/index.ts
  • app/packages/looker/src/lookers/abstract.ts
📓 Learnings (1)
app/packages/looker/src/lookers/abstract.ts (1)
Learnt from: sashankaryal
PR: voxel51/fiftyone#5356
File: app/packages/looker/src/lookers/abstract.ts:49-50
Timestamp: 2025-01-09T23:46:50.918Z
Learning: The `UPDATING_SAMPLES_IDS` set in `app/packages/looker/src/lookers/abstract.ts` is intentionally module-scoped to prevent concurrent updates of the same sample across different instances of AbstractLooker.
🪛 Biome (1.9.4)
app/packages/looker/src/lookers/imavid/index.ts

[error] 237-237: Unsafe usage of optional chaining.

If it short-circuits with 'undefined' the evaluation will throw TypeError here:

(lint/correctness/noUnsafeOptionalChaining)

⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: test / test-python (ubuntu-latest-m, 3.11)
  • GitHub Check: test / test-python (ubuntu-latest-m, 3.10)
  • GitHub Check: test / test-python (ubuntu-latest-m, 3.9)
  • GitHub Check: e2e / test-e2e
  • GitHub Check: build / build
  • GitHub Check: build
🔇 Additional comments (2)
app/packages/looker/src/lookers/abstract.ts (2)

783-787: LGTM!

The function correctly handles the new setTargetsToNull parameter and safely calls cleanup on each overlay.


809-809: LGTM!

The function correctly cleans up the UPDATING_SAMPLES_IDS set after loading the sample.

app/packages/looker/src/lookers/imavid/index.ts Outdated Show resolved Hide resolved
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (4)
app/packages/core/src/components/Grid/useSelect.ts (1)

48-89: Consider adding debounce to prevent potential race conditions.

While the code correctly handles the rendering status updates, rapid changes to the active fields could lead to race conditions. Consider adding debounce to the refresh operations.

Apply this diff to add debounce:

+import { debounce } from "lodash";

 export default function useSelect(
   getFontSize: () => number,
   options: ReturnType<typeof fos.useLookerOptions>,
   store: LRUCache<string, fos.Lookers>,
   spotlight?: Spotlight<number, fos.Sample>
 ) {
   const { init, deferred } = fos.useDeferrer();
+  const debouncedRefresh = debounce(
+    (instance: fos.Lookers, fields: string[]) => {
+      instance.refreshSample(fields);
+    },
+    300
+  );

   // ... rest of the code ...

-            instance.refreshSample(newFieldsIfAny);
+            debouncedRefresh(instance, newFieldsIfAny);

   // ... rest of the code ...

-            instance.refreshSample(rerenderFields);
+            debouncedRefresh(instance, rerenderFields);
app/packages/looker/src/worker/index.ts (3)

57-81: Consider caching the path check result.

The shouldProcessLabel function is well-structured, but the path check could be optimized for repeated calls.

 const shouldProcessLabel = ({
   field,
   label,
   activePaths,
   prefix,
 }: {
   prefix: string;
   field: string;
   label: any;
   activePaths: string[];
 }) => {
+  const path = `${prefix ?? ""}${field}`;
   const currentLabelRenderStatus = label?.renderStatus;
 
   if (
     currentLabelRenderStatus === RENDER_STATUS_PAINTED ||
     currentLabelRenderStatus === RENDER_STATUS_DECODED ||
     currentLabelRenderStatus === RENDER_STATUS_PENDING
   ) {
     return true;
   }
 
-  return activePaths?.includes(`${prefix ?? ""}${field}`);
+  return activePaths?.includes(path);
 };

351-352: Document the rationale for processing all 3D labels.

The comment indicates that 3D labels are processed regardless of active paths. Consider documenting why this exception exists to help future maintainers understand the design decision.

-    // we process all 3d labels regardless of active paths
+    // Process all 3D labels regardless of active paths because they require special handling
+    // for proper rendering in the 3D viewer and don't benefit from selective rendering

254-256: Consider using the RENDER_STATUS constant.

For consistency with the rest of the codebase, use the RENDER_STATUS_PAINTED constant instead of the string literal.

-      if (label?.renderStatus !== "painted") {
+      if (label?.renderStatus !== RENDER_STATUS_PAINTED) {
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b506bbb and f741c7f.

📒 Files selected for processing (5)
  • app/packages/core/src/components/Grid/useSelect.ts (3 hunks)
  • app/packages/looker/src/worker/disk-overlay-decoder.ts (2 hunks)
  • app/packages/looker/src/worker/index.ts (18 hunks)
  • app/packages/looker/src/worker/painter.ts (8 hunks)
  • app/packages/looker/src/worker/shared.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • app/packages/looker/src/worker/disk-overlay-decoder.ts
  • app/packages/looker/src/worker/painter.ts
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.{ts,tsx}`: Review the Typescript and React code for co...

**/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

  • app/packages/looker/src/worker/shared.ts
  • app/packages/core/src/components/Grid/useSelect.ts
  • app/packages/looker/src/worker/index.ts
⏰ Context from checks skipped due to timeout of 90000ms (5)
  • GitHub Check: test / test-python (ubuntu-latest-m, 3.11)
  • GitHub Check: build / build
  • GitHub Check: test / test-python (ubuntu-latest-m, 3.10)
  • GitHub Check: test / test-app
  • GitHub Check: build
🔇 Additional comments (7)
app/packages/looker/src/worker/shared.ts (2)

3-6: Consider using shorter constant names for better readability.

The constants could be more concise by removing the RENDER_STATUS_ prefix, as suggested in a previous review.

Apply this diff to make the constants more concise:

-export const RENDER_STATUS_PENDING = "pending";
-export const RENDER_STATUS_PAINTING = "painting";
-export const RENDER_STATUS_PAINTED = "painted";
-export const RENDER_STATUS_DECODED = "decoded";
+export const PENDING = "pending";
+export const PAINTING = "painting";
+export const PAINTED = "painted";
+export const DECODED = "decoded";

8-13: LGTM!

The type definition is well-structured and follows TypeScript best practices by using typeof to reference the constants.

app/packages/core/src/components/Grid/useSelect.ts (3)

1-1: LGTM!

The imports are well-organized and properly scoped.

Also applies to: 7-8


18-20: LGTM!

The hook is properly initialized with the correct configuration.


35-40: LGTM!

The code properly handles different looker types and extracts overlays accordingly.

app/packages/looker/src/worker/index.ts (2)

33-51: LGTM! Good refactoring of color resolution logic.

The extraction of color resolution logic into a separate module improves code organization and maintainability.


408-427: Consider handling Promise rejection.

The Promise.all() call could fail if any of the bitmap promises reject. Consider adding error handling to ensure graceful degradation.

-  Promise.all(imageBitmapPromises).then((bitmaps) => {
+  Promise.all(imageBitmapPromises).then((bitmaps) => {
     const flatBitmaps = bitmaps.flat() ?? [];
     const flatMaskTargetsBuffers = maskTargetsBuffers.flat() ?? [];
     const transferables = [...flatBitmaps, ...flatMaskTargetsBuffers];
     postMessage(
       {
         method: "processSample",
         sample,
         coloring,
         uuid,
         customizeColorSetting,
         colorscale,
         labelTagColors,
         selectedLabelTags,
       },
       // @ts-ignore
       transferables
     );
+  }).catch((error) => {
+    postMessage({
+      error: {
+        cls: error.constructor.name,
+        message: error.message,
+      },
+    });
   });

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🔭 Outside diff range comments (2)
app/packages/looker/src/overlays/index.ts (1)

56-56: Fix typo in POINTS_FROM_FO key name.

The key Poylines should be Polylines to match the corresponding key in FROM_FO.

-  Poylines: (label) => getPolylinePoints(label?.polylines ?? []),
+  Polylines: (label) => getPolylinePoints(label?.polylines ?? []),
app/packages/looker/src/lookers/utils.ts (1)

13-60: Consider adding error handling for DataCloneError.

The code mentions potential DataCloneError when switching colors too fast, but doesn't explicitly handle it.

 export const retrieveTransferables = <State extends BaseState>(
   overlays?: Overlay<State>[]
-) => {
+): Transferable[] => {
   const transferables: Transferable[] = [];
+  try {
     for (const overlay of overlays ?? []) {
       // ... existing code ...
     }
     return transferables;
+  } catch (error) {
+    if (error instanceof DOMException && error.name === 'DataCloneError') {
+      console.warn('Buffer transfer failed, likely due to rapid color changes');
+      return [];
+    }
+    throw error;
+  }
 };
🧹 Nitpick comments (4)
app/packages/looker/src/overlays/index.ts (2)

87-136: Consider enhancing type safety and documentation.

While the implementation is solid, consider these improvements for better maintainability:

  1. Add JSDoc comments describing the function's purpose and parameters
  2. Consider making depth parameter more type-safe by using a literal union type
  3. Add return type documentation explaining the structure of classifications and overlays
+/**
+ * Accumulates overlays and classifications from a data object based on schema.
+ * @param data - The data object containing label information
+ * @param schema - The schema defining the structure of the data
+ * @param prefix - Optional path prefix for nested fields
+ * @param depth - Maximum recursion depth for nested documents (default: 1)
+ * @returns Object containing accumulated classifications and overlays
+ */
-export const accumulateOverlays = <State extends BaseState>(
+export const accumulateOverlays = <State extends BaseState, Depth extends 0 | 1>(
   data: {
     [key: string]: any;
   },
   schema: Schema,
   prefix = [],
-  depth = 1
+  depth: Depth = 1 as Depth
 ): {
   classifications: Labels<TemporalDetectionLabel | ClassificationLabel>;
   overlays: Overlay<State>[];
 } => {

81-85: Consider using TypeScript's as const for immutable sets.

Using as const would provide better type safety for these constant sets.

-const TAGS = new Set(LABEL_TAGS_CLASSES);
+const TAGS = new Set(LABEL_TAGS_CLASSES) as const;

-const EMBEDDED_FIELDS = Object.freeze(
-  new Set([EMBEDDED_DOCUMENT_FIELD, DYNAMIC_EMBEDDED_DOCUMENT_FIELD])
-);
+const EMBEDDED_FIELDS = new Set([
+  EMBEDDED_DOCUMENT_FIELD,
+  DYNAMIC_EMBEDDED_DOCUMENT_FIELD
+]) as const;
app/packages/core/src/components/Modal/use-modal-selective-rendering.ts (1)

37-56: Consider memoizing the coloring hash map.

The coloring hash generation could be optimized by memoizing the WeakMap lookups to prevent unnecessary recalculations.

+  const memoizedColoringHash = useMemo(() => {
+    if (!coloringIds.current.has(lookerOptions.coloring)) {
+      coloringIds.current.set(
+        lookerOptions.coloring,
+        `id_${coloringIdCounter.current++}`
+      );
+    }
+    return coloringIds.current.get(lookerOptions.coloring);
+  }, [lookerOptions.coloring]);
+
-  const getColoringHash = useCallback(() => {
-    if (!coloringIds.current.has(lookerOptions.coloring)) {
-      coloringIds.current.set(
-        lookerOptions.coloring,
-        `id_${coloringIdCounter.current++}`
-      );
-    }
-
-    return coloringIds.current.get(lookerOptions.coloring);
-  }, [lookerOptions.coloring]);
app/packages/looker/src/lookers/utils.ts (1)

54-56: Consider adding bitmap null check.

While the code checks for bitmap dimensions, an explicit null check would provide better safety.

-    if (bitmap?.width || bitmap?.height) {
+    if (bitmap && (bitmap.width || bitmap.height)) {
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f741c7f and 3ec947c.

📒 Files selected for processing (8)
  • app/packages/core/src/components/Modal/use-modal-selective-rendering.ts (1 hunks)
  • app/packages/looker/src/elements/common/error.ts (1 hunks)
  • app/packages/looker/src/lookers/abstract.ts (8 hunks)
  • app/packages/looker/src/lookers/imavid/index.ts (3 hunks)
  • app/packages/looker/src/lookers/utils.test.ts (3 hunks)
  • app/packages/looker/src/lookers/utils.ts (2 hunks)
  • app/packages/looker/src/overlays/index.ts (1 hunks)
  • app/packages/looker/src/worker/async-labels-rendering-manager.ts (1 hunks)
👮 Files not reviewed due to content moderation or server errors (4)
  • app/packages/looker/src/elements/common/error.ts
  • app/packages/looker/src/worker/async-labels-rendering-manager.ts
  • app/packages/looker/src/lookers/imavid/index.ts
  • app/packages/looker/src/lookers/abstract.ts
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.{ts,tsx}`: Review the Typescript and React code for co...

**/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

  • app/packages/looker/src/overlays/index.ts
  • app/packages/looker/src/elements/common/error.ts
  • app/packages/looker/src/lookers/utils.test.ts
  • app/packages/looker/src/lookers/utils.ts
  • app/packages/looker/src/lookers/imavid/index.ts
  • app/packages/core/src/components/Modal/use-modal-selective-rendering.ts
  • app/packages/looker/src/lookers/abstract.ts
  • app/packages/looker/src/worker/async-labels-rendering-manager.ts
📓 Learnings (1)
app/packages/looker/src/lookers/abstract.ts (1)
Learnt from: sashankaryal
PR: voxel51/fiftyone#5356
File: app/packages/looker/src/lookers/abstract.ts:49-50
Timestamp: 2025-01-09T23:46:50.918Z
Learning: The `UPDATING_SAMPLES_IDS` set in `app/packages/looker/src/lookers/abstract.ts` is intentionally module-scoped to prevent concurrent updates of the same sample across different instances of AbstractLooker.
🪛 Biome (1.9.4)
app/packages/looker/src/lookers/imavid/index.ts

[error] 240-240: Unsafe usage of optional chaining.

If it short-circuits with 'undefined' the evaluation will throw TypeError here:

(lint/correctness/noUnsafeOptionalChaining)

⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: test / test-python (ubuntu-latest-m, 3.11)
  • GitHub Check: test / test-python (ubuntu-latest-m, 3.10)
  • GitHub Check: e2e / test-e2e
  • GitHub Check: test / test-python (ubuntu-latest-m, 3.9)
  • GitHub Check: build / build
  • GitHub Check: build
🔇 Additional comments (7)
app/packages/looker/src/overlays/index.ts (1)

87-87: LGTM! Making accumulateOverlays public aligns with PR objectives.

The change to export this function supports the new selective label rendering feature by allowing external modules to accumulate overlays based on visibility settings.

app/packages/core/src/components/Modal/use-modal-selective-rendering.ts (3)

1-5: LGTM! Clean imports with proper type declarations.

All necessary imports are present and properly organized.


24-24: Remove unnecessary optional chaining.

The looker is already null-checked at line 17, making the optional chaining on refreshSample redundant.

-      looker?.refreshSample(newFieldsIfAny);
+      looker.refreshSample(newFieldsIfAny);

103-106: Remove unnecessary optional chaining and track TODO.

The hook has two issues:

  1. Unnecessary optional chaining on looker.refreshSample since looker is already null-checked
  2. Missing functionality for granular refreshing

Would you like me to create an issue to track the implementation of granular refreshing for the video looker?

app/packages/looker/src/lookers/utils.test.ts (1)

10-10: LGTM! Function rename consistently applied.

The test cases have been correctly updated to use the new function name retrieveTransferables while maintaining the same test coverage and assertions.

Also applies to: 29-29, 33-33, 37-37, 43-43, 47-47, 59-59, 65-65

app/packages/looker/src/lookers/utils.ts (2)

13-18: LGTM! Type changes align with transferable objects.

The function signature and return type have been correctly updated to handle Transferable[] instead of ArrayBuffer[], which better represents the actual data being transferred.


35-52: Verify browser compatibility for buffer.detached API.

The code includes a new browser API check for buffer.detached. While there's a fallback mechanism, we should verify browser support to ensure wide compatibility.

✅ Verification successful

🌐 Web query:

What browsers support ArrayBuffer.prototype.detached?

💡 Result:

The `ArrayBuffer.prototype.detached` property is supported in modern browsers as of early 2025:

**Current browser support:**
- **Firefox**: Supported since v118[7]
- **Chrome/Chromium**: Supported since v123[7]
- **Safari**: Supported since v17.4[7]
- **Edge**: Supported since v123[7]
- **Opera**: Supported since v109[7]

**Unsupported browsers:**
- Internet Explorer (all versions)[7]
- Legacy mobile browsers not updated since 2023

This property was standardized in ECMAScript 2024[2][4] and allows checking if an ArrayBuffer has been transferred/detached via methods like `structuredClone()` or `transfer()`[2][4]. Detached buffers become inaccessible and throw errors when accessed[1][2].

Implementation details vary slightly between browsers:
- Firefox changed behavior to throw errors on detached buffer access[1]
- Chrome/Safari implementations align with updated WebIDL standards[1]
- All modern engines now return `true` for `.detached` after transfers[2][4]

Citations:

- 1: https://github.com/tc39/ecma262/issues/678
- 2: https://2ality.com/2024/06/array-buffers-es2024.html
- 3: https://tc39.es/ecma262/multipage/structured-data.html
- 4: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer?retiredLocale=pl
- 5: https://caniuse.com
- 6: https://github.com/es-shims/ArrayBuffer.prototype.detached
- 7: https://caniuse.com/mdn-javascript_builtins_arraybuffer_detached

Browser Compatibility Verified for buffer.detached Checks

The web query confirms that the ArrayBuffer.prototype.detached property is supported in all major modern browsers—Firefox (v118+), Chrome/Chromium (v123+), Safari (v17.4+), Edge (v123+), and Opera (v109+). Legacy browsers like Internet Explorer remain unsupported, but the fallback in your code (checking for buffer.byteLength) appropriately covers that case.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

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 (4)
app/packages/core/src/components/Modal/use-modal-selective-rendering.ts (4)

6-9: Add return type annotation for better type safety.

The hook's return type should be explicitly defined for better TypeScript type safety.

-export const useImageModalSelectiveRendering = (
+export const useImageModalSelectiveRendering = (
   id: string,
   looker: Lookers
-) => {
+): void => {

16-26: Remove unnecessary optional chaining.

The looker is already null-checked at the start of the effect, making the optional chaining redundant.

   if (newFieldsIfAny) {
-    looker?.refreshSample(newFieldsIfAny);
+    looker.refreshSample(newFieldsIfAny);
   }

29-32: Add return type annotation for better type safety.

The hook's return type should be explicitly defined for better TypeScript type safety.

-export const useImavidModalSelectiveRendering = (
+export const useImavidModalSelectiveRendering = (
   id: string,
   looker: ImaVidLooker
-) => {
+): void => {

76-79: Add return type annotation for better type safety.

The hook's return type should be explicitly defined for better TypeScript type safety.

-export const useVideoModalSelectiveRendering = (
+export const useVideoModalSelectiveRendering = (
   id: string,
   looker: VideoLooker
-) => {
+): void => {
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3ec947c and b175c04.

📒 Files selected for processing (2)
  • app/packages/core/src/components/Modal/use-modal-selective-rendering.ts (1 hunks)
  • app/packages/looker/src/lookers/imavid/index.ts (3 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.{ts,tsx}`: Review the Typescript and React code for co...

**/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

  • app/packages/looker/src/lookers/imavid/index.ts
  • app/packages/core/src/components/Modal/use-modal-selective-rendering.ts
🪛 Biome (1.9.4)
app/packages/looker/src/lookers/imavid/index.ts

[error] 244-244: Unsafe usage of optional chaining.

If it short-circuits with 'undefined' the evaluation will throw TypeError here:

(lint/correctness/noUnsafeOptionalChaining)

⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: test / test-python (ubuntu-latest-m, 3.11)
  • GitHub Check: test / test-python (ubuntu-latest-m, 3.10)
  • GitHub Check: test / test-python (ubuntu-latest-m, 3.9)
  • GitHub Check: e2e / test-e2e
  • GitHub Check: build / build
  • GitHub Check: build
🔇 Additional comments (6)
app/packages/core/src/components/Modal/use-modal-selective-rendering.ts (3)

1-5: LGTM! Clean and well-organized imports.

The imports follow a good organization pattern, separating external dependencies from internal modules.


94-97: Remove unnecessary optional chaining and track TODO.

The hook has two issues:

  1. Unnecessary optional chaining on looker.refreshSample since looker is already null-checked
  2. Missing functionality for granular refreshing
-      looker?.refreshSample();
+      looker.refreshSample();

Would you like me to create an issue to track the implementation of granular refreshing for the video looker?


49-73: ⚠️ Potential issue

Add null check and protect against looker changes.

The effect needs a null check for looker and should clean up properly if looker changes.

 useEffect(() => {
+  if (!looker) {
+    return;
+  }
+
   const unsub = looker.subscribeToState(
     "currentFrameNumber",
     (currentFrameNumber: number) => {
-      if (!looker.thisFrameSample?.sample) {
+      if (!looker?.thisFrameSample?.sample) {
         return;
       }

       const thisFrameId = `${id}-${currentFrameNumber}-${getColoringHash()}`;
       const newFieldsIfAny = getNewFields(thisFrameId);

       if (newFieldsIfAny) {
         looker.refreshSample(newFieldsIfAny, currentFrameNumber);
       } else {
         looker.refreshOverlaysToCurrentFrame();
       }
     }
   );

   return () => {
     unsub();
   };
 }, [getNewFields, getColoringHash, looker]);

Likely invalid or redundant comment.

app/packages/looker/src/lookers/imavid/index.ts (3)

231-234: LGTM!

The thumbnail-specific logic is well-placed and correctly handles the modal case through useImavidModalSelectiveRendering.


240-251: ⚠️ Potential issue

Add error handling and improve type safety.

The function has potential runtime issues:

  1. Optional chaining on line 244 could lead to TypeError if getSampleAtFrame returns undefined
  2. No error handling for loadOverlays call

Apply this diff to improve error handling and type safety:

 refreshOverlaysToCurrentFrame() {
+  try {
+    const frameSample = this.frameStoreController.store.getSampleAtFrame(
+      this.frameNumber
+    );
+
+    if (!frameSample) {
+      console.debug('No sample found for current frame, using current sample');
+      this.loadOverlays(this.sample);
+      return;
+    }
+
     let { image: _cachedImage, ...thisFrameSample } =
-      this.frameStoreController.store.getSampleAtFrame(
-        this.frameNumber
-      )?.sample;
+      frameSample.sample;

     if (!thisFrameSample) {
       thisFrameSample = this.sample;
     }

     this.loadOverlays(thisFrameSample);
+  } catch (error) {
+    console.error('Failed to load overlays:', error);
+    this.updater({ error });
+  }
 }

Likely invalid or redundant comment.

🧰 Tools
🪛 Biome (1.9.4)

[error] 244-244: Unsafe usage of optional chaining.

If it short-circuits with 'undefined' the evaluation will throw TypeError here:

(lint/correctness/noUnsafeOptionalChaining)


34-71: 🛠️ Refactor suggestion

Improve error handling and prevent race conditions in frame number subscription.

The frame number subscription callback is complex and lacks proper error handling. Consider:

  1. Race conditions when multiple frame updates occur rapidly
  2. Potential errors when accessing this.thisFrameSample?.sample

Apply this diff to improve the implementation:

 init() {
   if (!this.state.config.thumbnail) {
     return;
   }

+  let isProcessing = false;
+  let latestFrameNumber: number | null = null;
+
   this.unsubscribe = this.subscribeToState(
     "currentFrameNumber",
     (currentFrameNumber: number) => {
+      if (isProcessing) {
+        latestFrameNumber = currentFrameNumber;
+        return;
+      }
+      isProcessing = true;
+
       try {
         const thisFrameId = `${
           this.uuid
         }-${currentFrameNumber}-${this.state.options.coloring.targets.join(
           "-"
         )}`;

         if (
           gridActivePathsLUT.has(thisFrameId) &&
           this.thisFrameSample?.sample
         ) {
           this.refreshOverlaysToCurrentFrame();
         } else {
           const newFieldsIfAny = syncAndGetNewLabels(
             thisFrameId,
             gridActivePathsLUT,
             new Set(this.options.activePaths)
           );

           if (newFieldsIfAny && currentFrameNumber > 0) {
             this.refreshSample(newFieldsIfAny, currentFrameNumber);
           } else {
             this.refreshSample();
           }
         }
+      } catch (error) {
+        console.error('Frame update failed:', error);
+        this.updater({ error });
+      } finally {
+        isProcessing = false;
+        if (latestFrameNumber !== null && latestFrameNumber !== currentFrameNumber) {
+          const nextFrameNumber = latestFrameNumber;
+          latestFrameNumber = null;
+          this.subscribeToState("currentFrameNumber", nextFrameNumber);
+        }
+      }
     }
   );
 }

Likely invalid or redundant comment.

Copy link
Contributor

@benjaminpkane benjaminpkane left a comment

Choose a reason for hiding this comment

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

LGTM

@sashankaryal sashankaryal merged commit 9396ee8 into develop Feb 7, 2025
14 checks passed
@sashankaryal sashankaryal deleted the feat/app-config-default-labels-visibility branch February 7, 2025 16:13
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.

3 participants