Skip to content

Conversation

@imanjra
Copy link
Contributor

@imanjra imanjra commented Aug 28, 2025

What changes are proposed in this pull request?

Add a utility to easily register and trigger JS panel events from Python panel events

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

Using a hybrid python panel

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.

Add a utility to easily register and trigger JS panel events from Python panel events. See usage for more details

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

Usage

# Python
class PanelOne(Panel):
    @property
    def config(self):
        return PanelConfig(
            name="panel_one",
            label="Panel One",
            icon="looks_one",
        )

    def set_samples_count(self, ctx):
        params = {"count":  ctx.view.count()}
        ctx.panel.trigger("set_samples_count", params)

    def render(self, ctx):
        panel = types.Object()
        return types.Property(
            panel,
            view=types.View(
                component="CustomViewOne",
                composite_view=True,
                set_samples_count=self.set_samples_count,
            ),
        )
// JavaScript
export default function Four(props) {
  const { set_samples_count } = props.schema.view;

  const handleEvent = usePanelEvent();
  const panelId = usePanelId();
  const { register } = usePanelClientEvent();
  const [count, setCount] = useState()

  register("set_samples_count", (params) => {
    setCount(params.count)
  });

  return (
    <Box>
      <Button
        onClick={() => {
          handleEvent(panelId, { operator: my_py_event });
        }}
      >
        Set samples count
      </Button>
      <Typography>Samples count: {count}</Typography>
    </Box>
  );
}

Summary by CodeRabbit

  • New Features

    • Add a hook to register, trigger, and unregister client-side panel events.
    • Introduce a built-in operator to trigger panel client events.
    • Expose panel hooks through the package public API for simpler importing.
    • Add Python SDK methods to trigger panel client events, including from panel references.
  • Chores

    • Remove an unused icon import.

@imanjra imanjra requested review from a team as code owners August 28, 2025 15:36
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 28, 2025

Walkthrough

Adds a client-side per-panel event system: a React hook for registering/triggering panel callbacks, a built-in operator to invoke those hooks, public re-exports, and Python APIs to request client-side panel event triggers via the operator execution path.

Changes

Cohort / File(s) Summary
Panel Hooks
app/packages/operators/src/Panel/hooks/index.ts, app/packages/operators/src/Panel/hooks/usePanelClientEvent.tsx
Adds usePanelClientEvent (default export re-exported) implementing an in-memory per-panel event registry with register, trigger, and unregister; exposes hook via hooks index.
Operators Integration
app/packages/operators/src/built-in-operators.ts
Adds TriggerPanelClientEvent built-in operator that obtains trigger from the hook and invokes trigger(event, params, panel_id); registers the operator; updates imports.
Operators Public API
app/packages/operators/src/index.ts
Re-exports ./Panel/hooks to surface the hook from the package public API.
Python Backend API
fiftyone/operators/operations.py, fiftyone/operators/panel.py
Adds Operations.trigger_panel_client_event(id, event, params=None) and PanelRef.trigger(event, params=None) to invoke the operator execution path with panel_id, event, and params.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Py as Python: PanelRef
  participant Ops as Python: Operations
  participant Ctx as ExecContext
  participant FE as Frontend: Built-in Operator
  participant Hook as Frontend: usePanelClientEvent
  participant Panel as Client Panel Callback

  Py->>Ops: trigger(event, params)
  Ops->>Ctx: execute("trigger_panel_client_event", {panel_id, event, params})
  Ctx->>FE: run TriggerPanelClientEvent with payload
  FE->>Hook: call trigger(event, params, panel_id)
  alt callback registered
    Hook->>Panel: invoke callback(params)
  else no callback
    Note right of Hook: no-op
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

A rabbit tapped keys with nimble feet,
Wired tiny signals — soft and fleet.
From Python burrow to React glen,
Events hop through the meadow again.
Carrots cheer: trigger, receive, repeat. 🥕

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/js-event-from-py

🪧 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.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbit in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbit 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:
    • @coderabbit gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbit read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbit help to get the list of available commands.

Other keywords and placeholders

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

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • 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.

@imanjra imanjra changed the title trigger client side panel event from python [Proposal] trigger client side panel event from python Aug 28, 2025
@imanjra imanjra requested a review from ritch August 28, 2025 15:37
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 (4)
fiftyone/operators/operations.py (1)

706-717: Add ctx fallback when id is None to match other panel ops

Brings parity with _create_panel_params behavior and avoids forcing callers to always pass id.

 def trigger_panel_client_event(self, id, event, params=None):
     """Triggers a panel client-side event.
@@
-        return self._ctx.trigger(
+        # allow using ctx.panel_id if id is None (parity with other panel ops)
+        if id is None:
+            id = self._ctx.params.get("panel_id", None)
+        return self._ctx.trigger(
             "trigger_panel_client_event",
             {"panel_id": id, "event": event, "params": params},
         )
fiftyone/operators/panel.py (1)

375-384: Validate event name before triggering

Guard against falsy/empty event names to prevent silent no-ops.

 def trigger(self, event, params=None):
     """Triggers a client-side event of a panel.
@@
-        self._ctx.ops.trigger_panel_client_event(
+        if not event or not isinstance(event, str):
+            raise ValueError("event must be a non-empty string")
+        self._ctx.ops.trigger_panel_client_event(
             id=self.id, event=event, params=params
         )
app/packages/operators/src/Panel/hooks/usePanelClientEvent.tsx (2)

5-8: Align function name with file/export for clarity

Rename the default-exported hook to usePanelClientEvent to match filename and public API, improving discoverability and stack traces.

-export default function usePanelEvents(panelId?: string) {
+export default function usePanelClientEvent(panelId?: string) {
   const id = usePanelId();
   const computedPanelId = panelId ?? id;

21-27: Add guard and return delivery status from trigger()

Helps callers detect missed deliveries and avoids "undefined" bucket usage.

-  function trigger(event: string, params: unknown, panelId?: string) {
+  function trigger(event: string, params?: unknown, panelId?: string): boolean {
     const triggerPanelId = panelId ?? computedPanelId;
+    if (!triggerPanelId) {
+      if (process.env.NODE_ENV !== "production") {
+        console.warn("usePanelClientEvent.trigger called without panelId in a non-panel context");
+      }
+      return false;
+    }
     const callback = events[triggerPanelId]?.[event];
     if (callback) {
       callback(params);
-    }
+      return true;
+    }
+    return false;
   }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 2c90638 and 5cf3098.

📒 Files selected for processing (6)
  • app/packages/operators/src/Panel/hooks/index.ts (1 hunks)
  • app/packages/operators/src/Panel/hooks/usePanelClientEvent.tsx (1 hunks)
  • app/packages/operators/src/built-in-operators.ts (3 hunks)
  • app/packages/operators/src/index.ts (1 hunks)
  • fiftyone/operators/operations.py (1 hunks)
  • fiftyone/operators/panel.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}

⚙️ CodeRabbit configuration file

Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

Files:

  • app/packages/operators/src/index.ts
  • app/packages/operators/src/Panel/hooks/index.ts
  • app/packages/operators/src/built-in-operators.ts
  • app/packages/operators/src/Panel/hooks/usePanelClientEvent.tsx
🧠 Learnings (3)
📓 Common learnings
Learnt from: imanjra
PR: voxel51/fiftyone#4373
File: app/packages/operators/src/built-in-operators.ts:983-983
Timestamp: 2024-07-27T04:14:08.421Z
Learning: The `registerPanel` function in `app/packages/operators/src/Panel/register.tsx` extracts parameters from the `ctx` object internally and does not require explicit parameter passing when called.
Learnt from: imanjra
PR: voxel51/fiftyone#4373
File: app/packages/operators/src/built-in-operators.ts:983-983
Timestamp: 2024-10-08T15:36:21.356Z
Learning: The `registerPanel` function in `app/packages/operators/src/Panel/register.tsx` extracts parameters from the `ctx` object internally and does not require explicit parameter passing when called.
📚 Learning: 2024-10-08T15:36:21.356Z
Learnt from: imanjra
PR: voxel51/fiftyone#4373
File: app/packages/operators/src/built-in-operators.ts:983-983
Timestamp: 2024-10-08T15:36:21.356Z
Learning: The `registerPanel` function in `app/packages/operators/src/Panel/register.tsx` extracts parameters from the `ctx` object internally and does not require explicit parameter passing when called.

Applied to files:

  • app/packages/operators/src/index.ts
  • app/packages/operators/src/Panel/hooks/index.ts
  • app/packages/operators/src/built-in-operators.ts
  • app/packages/operators/src/Panel/hooks/usePanelClientEvent.tsx
📚 Learning: 2025-04-23T15:22:03.452Z
Learnt from: benjaminpkane
PR: voxel51/fiftyone#5732
File: app/packages/core/src/components/Filters/use-query-performance-icon.tsx:26-38
Timestamp: 2025-04-23T15:22:03.452Z
Learning: React hooks (including useRecoilValue and other state management hooks) must be called unconditionally at the top level of a component or custom hook. They cannot be placed inside conditional statements, loops, or nested functions, as this violates React's Rules of Hooks and leads to unpredictable behavior.

Applied to files:

  • app/packages/operators/src/built-in-operators.ts
🧬 Code graph analysis (4)
fiftyone/operators/operations.py (1)
fiftyone/operators/panel.py (2)
  • id (325-327)
  • trigger (375-384)
fiftyone/operators/panel.py (3)
fiftyone/operators/executor.py (3)
  • trigger (93-108)
  • trigger (937-965)
  • ops (828-832)
app/packages/operators/src/operators.ts (2)
  • trigger (63-66)
  • trigger (165-172)
fiftyone/operators/operations.py (1)
  • trigger_panel_client_event (706-717)
app/packages/operators/src/built-in-operators.ts (2)
app/packages/operators/src/operators.ts (6)
  • Operator (313-402)
  • OperatorConfig (253-311)
  • trigger (63-66)
  • trigger (165-172)
  • ExecutionContext (104-187)
  • _registerBuiltInOperator (429-432)
fiftyone/operators/panel.py (1)
  • trigger (375-384)
app/packages/operators/src/Panel/hooks/usePanelClientEvent.tsx (2)
fiftyone/operators/panel.py (2)
  • id (325-327)
  • trigger (375-384)
app/packages/spaces/src/hooks.ts (1)
  • usePanelId (283-287)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
  • GitHub Check: test-windows / test-python (windows-latest, 3.12)
  • GitHub Check: test / test-python (ubuntu-latest-m, 3.12)
  • GitHub Check: test / test-python (ubuntu-latest-m, 3.11)
  • GitHub Check: test-windows / test-python (windows-latest, 3.11)
  • GitHub Check: test / test-python (ubuntu-latest-m, 3.9)
  • GitHub Check: test-windows / test-python (windows-latest, 3.10)
  • GitHub Check: test-windows / test-python (windows-latest, 3.9)
  • GitHub Check: test / test-python (ubuntu-latest-m, 3.10)
  • GitHub Check: e2e / test-e2e
  • GitHub Check: build / build
  • GitHub Check: build
🔇 Additional comments (5)
app/packages/operators/src/index.ts (1)

32-32: Re-export looks good; verify no name collisions with existing hooks

usePanelEvent (singular) already exists; adding usePanelClientEvent is fine. Ensure docs/examples distinguish both to avoid confusion.

app/packages/operators/src/Panel/hooks/index.ts (1)

1-1: LGTM

Public re-export matches the new hook. Keep the symbol name consistent with the implementation (see rename suggestion).

app/packages/operators/src/built-in-operators.ts (3)

1584-1584: Registration looks good

Operator is properly registered in registerBuiltInOperators().


26-26: No action needed—exports and dependency graph are correct
usePanelClientEvent is re-exported from app/packages/operators/src/index.ts via export * from "./Panel/hooks", and index.ts does not import built-in-operators, so there’s no circular dependency.


1504-1525: Fix id vs panel_id mismatch, mark unlisted, add validation & fallback
Python code invokes trigger_panel_client_event(id=…) but the current hook only reads panel_id, dropping the panel target.

  • In get config(), add unlisted: true
  • In useHooks(ctx), accept both panel_id and id, validate that event is a non-empty string, then compute
    const targetId = p.panel_id ?? p.id ?? ctx.getCurrentPanelId();
  • Rename the returned method to triggerEvent and update execute() to call ctx.hooks.triggerEvent(ctx.params)
    Ensure that ExecutionContext.getCurrentPanelId() exists or supply an alternative fallback.

@imanjra imanjra force-pushed the feat/js-event-from-py branch from 5cf3098 to 7c71670 Compare August 28, 2025 15:59
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 (2)
app/packages/operators/src/Panel/hooks/usePanelClientEvent.tsx (2)

31-37: Past feedback addressed: unregister now exists

The unregister API addition resolves earlier leak/stale-handler concerns.


31-37: Remove empty panel buckets to prevent unbounded growth

Clean up panel map when the last event is removed.

   function unregister(event: string, panelId?: string) {
     const unregisterPanelId = panelId ?? computedPanelId;
     assertPanelId(unregisterPanelId);
     if (events[unregisterPanelId]) {
       delete events[unregisterPanelId][event];
+      if (!Object.keys(events[unregisterPanelId]).length) {
+        delete events[unregisterPanelId];
+      }
     }
   }
🧹 Nitpick comments (3)
app/packages/operators/src/Panel/hooks/usePanelClientEvent.tsx (3)

3-3: Harden maps against prototype collisions

Use Object.create(null) for maps to avoid accidental prototype key collisions. Optional if you adopt Map<K,V> later.

-const events: PanelsEvents = {};
+const events: PanelsEvents = Object.create(null);
-    if (!events[registerPanelId]) {
-      events[registerPanelId] = {};
-    }
+    if (!events[registerPanelId]) {
+      events[registerPanelId] = Object.create(null);
+    }

Also applies to: 16-18


5-5: Align exported name with filename for clarity

Minor: rename the function to match the file/export name.

-export default function usePanelEvents(panelId?: string) {
+export default function usePanelClientEvent(panelId?: string) {

42-45: Improve error message for developer ergonomics

Clarify remediation steps when panelId is missing.

-  if (!panelId) {
-    throw new Error("Panel ID is required");
-  }
+  if (!panelId) {
+    throw new Error(
+      "usePanelClientEvent: panelId is required (provide panelId or call within a Panel context)"
+    );
+  }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 5cf3098 and 7c71670.

📒 Files selected for processing (6)
  • app/packages/operators/src/Panel/hooks/index.ts (1 hunks)
  • app/packages/operators/src/Panel/hooks/usePanelClientEvent.tsx (1 hunks)
  • app/packages/operators/src/built-in-operators.ts (3 hunks)
  • app/packages/operators/src/index.ts (1 hunks)
  • fiftyone/operators/operations.py (1 hunks)
  • fiftyone/operators/panel.py (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • app/packages/operators/src/Panel/hooks/index.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • fiftyone/operators/operations.py
  • app/packages/operators/src/index.ts
  • fiftyone/operators/panel.py
  • app/packages/operators/src/built-in-operators.ts
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}

⚙️ CodeRabbit configuration file

Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

Files:

  • app/packages/operators/src/Panel/hooks/usePanelClientEvent.tsx
🧠 Learnings (2)
📓 Common learnings
Learnt from: imanjra
PR: voxel51/fiftyone#4373
File: app/packages/operators/src/built-in-operators.ts:983-983
Timestamp: 2024-07-27T04:14:08.421Z
Learning: The `registerPanel` function in `app/packages/operators/src/Panel/register.tsx` extracts parameters from the `ctx` object internally and does not require explicit parameter passing when called.
Learnt from: imanjra
PR: voxel51/fiftyone#4373
File: app/packages/operators/src/built-in-operators.ts:983-983
Timestamp: 2024-10-08T15:36:21.356Z
Learning: The `registerPanel` function in `app/packages/operators/src/Panel/register.tsx` extracts parameters from the `ctx` object internally and does not require explicit parameter passing when called.
📚 Learning: 2024-07-27T04:14:08.421Z
Learnt from: imanjra
PR: voxel51/fiftyone#4373
File: app/packages/operators/src/built-in-operators.ts:983-983
Timestamp: 2024-07-27T04:14:08.421Z
Learning: The `registerPanel` function in `app/packages/operators/src/Panel/register.tsx` extracts parameters from the `ctx` object internally and does not require explicit parameter passing when called.

Applied to files:

  • app/packages/operators/src/Panel/hooks/usePanelClientEvent.tsx
🧬 Code graph analysis (1)
app/packages/operators/src/Panel/hooks/usePanelClientEvent.tsx (2)
fiftyone/operators/panel.py (2)
  • id (325-327)
  • trigger (375-384)
app/packages/spaces/src/hooks.ts (1)
  • usePanelId (283-287)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (12)
  • GitHub Check: test-windows / test-python (windows-latest, 3.9)
  • GitHub Check: test-windows / test-python (windows-latest, 3.12)
  • GitHub Check: test / test-python (ubuntu-latest-m, 3.12)
  • GitHub Check: test-windows / test-python (windows-latest, 3.11)
  • GitHub Check: test / test-python (ubuntu-latest-m, 3.11)
  • GitHub Check: test-windows / test-python (windows-latest, 3.10)
  • 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

Comment on lines +16 to +20
if (!events[registerPanelId]) {
events[registerPanelId] = {};
}
events[registerPanelId][event] = callback;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Allow multiple handlers per event and return a boolean from trigger

Currently, registering the same event twice overwrites the previous handler. Support a Set of handlers per event and have trigger return whether anything ran.

Apply these diffs:

-    if (!events[registerPanelId]) {
-      events[registerPanelId] = {};
-    }
-    events[registerPanelId][event] = callback;
+    if (!events[registerPanelId]) {
+      events[registerPanelId] = {};
+    }
+    if (!events[registerPanelId][event]) {
+      events[registerPanelId][event] = new Set<PanelEventHandler>();
+    }
+    events[registerPanelId][event].add(callback);
-  function trigger(event: string, params: unknown, panelId?: string) {
+  function trigger(event: string, params: unknown, panelId?: string): boolean {
     const triggerPanelId = panelId ?? computedPanelId;
     assertPanelId(triggerPanelId);
-    const callback = events[triggerPanelId]?.[event];
-    if (callback) {
-      callback(params);
-    }
+    const callbacks = events[triggerPanelId]?.[event];
+    if (callbacks?.size) {
+      for (const cb of callbacks) cb(params);
+      return true;
+    }
+    return false;
   }
 type PanelEventHandler = (params: unknown) => void;
-type PanelEvents = {
-  [key: string]: PanelEventHandler;
-};
-type PanelsEvents = {
-  [key: string]: PanelEvents;
-};
+type PanelEvents = {
+  [event: string]: Set<PanelEventHandler>;
+};
+type PanelsEvents = {
+  [panelId: string]: PanelEvents;
+};

Also applies to: 22-29, 49-55

🤖 Prompt for AI Agents
In app/packages/operators/src/Panel/hooks/usePanelClientEvent.tsx around lines
16-20, 22-29 and 49-55, event registration currently stores a single callback
per event which overwrites previous handlers and trigger does not return whether
any handlers ran; change the event storage to use a Set of handlers for each
event (initialize events[registerPanelId][event] as a Set and add callbacks to
the Set instead of replacing), update removal to delete from the Set, update
trigger to iterate over the Set invoking each handler (catching errors if
desired) and have trigger return a boolean indicating whether at least one
handler was invoked.

Comment on lines +39 to +40
return { register, trigger, unregister };
}
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Expose a non-hook trigger for non-React call sites (operators, services)

Triggers don’t require React state; calling this hook from non-React code would violate Rules of Hooks. Provide a pure helper and prefer it in operator code paths.

Add this helper near the top-level (outside the hook):

export function triggerPanelEvent(panelId: string, event: string, params?: unknown): boolean {
  if (!panelId) return false;
  const bucket = events[panelId]?.[event];
  if (!bucket) return false;
  if (bucket instanceof Set) {
    if (!bucket.size) return false;
    for (const cb of bucket) cb(params);
    return true;
  }
  // Back-compat if single-handler shape remains somewhere
  (bucket as unknown as (p: unknown) => void)(params);
  return true;
}

Verification (scan for hook usage outside components/hooks):


🏁 Script executed:

#!/bin/bash
# Find direct invocations of the hook; inspect contexts for non-React usage
rg -n -C2 -S '\b(usePanelClientEvent|usePanelEvents)\s*\(' app/packages

Length of output: 940


Extract trigger logic into a standalone helper and update operator call sites

• In app/packages/operators/src/Panel/hooks/usePanelClientEvent.tsx, add above the hook definition:

export function triggerPanelEvent(
  panelId: string,
  event: string,
  params?: unknown
): boolean {
  if (!panelId) return false;
  const bucket = events[panelId]?.[event];
  if (!bucket) return false;
  if (bucket instanceof Set) {
    if (!bucket.size) return false;
    for (const cb of bucket) cb(params);
    return true;
  }
  (bucket as unknown as (p: unknown) => void)(params);
  return true;
}

• In app/packages/operators/src/built-in-operators.ts (line 1513), replace:

const { trigger } = usePanelClientEvent();

with:

import { triggerPanelEvent } from './Panel/hooks/usePanelClientEvent';

const trigger = (params) => triggerPanelEvent(panelId, eventName, params);
🤖 Prompt for AI Agents
In app/packages/operators/src/Panel/hooks/usePanelClientEvent.tsx around lines
39-40, extract the trigger logic into a standalone exported helper named
triggerPanelEvent that accepts (panelId: string, event: string, params?:
unknown) and performs the same null checks, bucket lookup, Set iteration or
single-callback invocation, and boolean return as in the suggested snippet;
place this exported function above the hook definition. Then in
app/packages/operators/src/built-in-operators.ts at ~line 1513, replace the
hook-derived trigger by importing triggerPanelEvent from the Panel/hooks file
and define const trigger = (params) => triggerPanelEvent(panelId, eventName,
params) so operator call sites use the new helper.

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.

2 participants