Skip to content

Feat/clean remove panel#6056

Open
theoc0702 wants to merge 17 commits into
OHIF:masterfrom
theoc0702:feat/clean-remove-panel
Open

Feat/clean remove panel#6056
theoc0702 wants to merge 17 commits into
OHIF:masterfrom
theoc0702:feat/clean-remove-panel

Conversation

@theoc0702

@theoc0702 theoc0702 commented Jun 3, 2026

Copy link
Copy Markdown

Context

Currently, there is no reliable, native way to remove a single panel by its ID from the PanelService and guarantee a proper React UI re-render. Developers trying to dynamically hide a panel during a stage change or layout update had to manually fetch the panels, filter them, and attempt to force a reset. This often led to race conditions or silent failures where the panel's state was updated in memory but remained visible on screen.

Changes & Results

  • Added removePanel(panelId: string, options = {}) to platform/core/src/services/PanelService/PanelService.tsx.

What are the effects of this change?

  • Before: Removing a panel required complex workarounds outside the service, often failing to trigger the UI layout redraw.
  • After: The new method retrieves panels across all positions (Left, Right, Bottom), filters out the target panelId, and passes the clean lists directly to this.setPanels(). Because setPanels natively handles this.reset() under the hood, the UI is instantly and cleanly updated.

Testing

  1. Open a study in the viewer.
  2. Ensure a panel (e.g., a custom extension panel or a default one) is active and visible on the right or left.
  3. Call panelService.removePanel('your-panel-id') via a custom command (e.g., during an onLayoutChange event in a Hanging Protocol).
  4. Verify the targeted panel is immediately unmounted and removed from the UI, while the rest of the layout remains stable.

Checklist

PR

  • My Pull Request title is descriptive, accurate and follows the
    semantic-release format and guidelines.

Code

  • My code has been well-documented (function documentation, inline comments,
    etc.)

Public Documentation Updates

  • The documentation page has been updated as necessary for any public API
    additions or removals.

Tested Environment

  • OS: Windows 11 / macOS 14
  • Node version: 18.16.1
  • Browser: Chrome 125.0

Greptile Summary

This PR adds a removePanel method to PanelService so callers can remove a single panel by ID without manually reconstructing the panel list. However, the PR also includes a large set of private @pixilib org-specific configurations across build tooling and default app config that are unrelated to the stated goal and would break builds for any public contributor.

  • PanelService.removePanel: The new method has two functional bugs — a position parameter that is declared but silently ignored, and a Bottom panel wipe-out caused by routing removal through setPanels/reset() without restoring the Bottom position.
  • Org-specific config leak: pluginConfig.json, both webpack configs, default.js, and tailwind.config.js all reference private @pixilib packages that are not published and will cause immediate build failures for anyone without access to that private registry.
  • Production source map: webpack.base.js disables production source maps with a commented-out original value, indicating a local debug tweak that was committed by mistake.

Confidence Score: 1/5

Not safe to merge — private org packages embedded in shared build and default config files will break builds for all public contributors, and the core feature itself has two logic errors.

The build configuration files (pluginConfig.json, webpack configs, default.js) reference unpublished private @pixilib packages. Anyone cloning the public OHIF repo and attempting a build would hit immediate resolution failures. The removePanel implementation also has a dead position parameter and silently drops Bottom panels on every call. The production source-map disable in webpack.base.js appears to be an accidental local commit.

platform/app/pluginConfig.json, platform/app/public/config/default.js, .webpack/webpack.base.js, and platform/app/.webpack/webpack.pwa.js all need the @pixilib entries removed before this can land. PanelService.tsx needs the position parameter usage and Bottom panel handling fixed.

Important Files Changed

Filename Overview
platform/core/src/services/PanelService/PanelService.tsx Adds removePanel method — the position parameter is declared but never used, and calling the method silently wipes all Bottom panels because setPanels/reset() is used without restoring the Bottom position.
platform/app/pluginConfig.json Registers eight @pixilib extensions and two @pixilib modes — private org packages that are unpublished and will break builds for any downstream OHIF developer.
.webpack/webpack.base.js Disables production source maps (devtool: false) and adds a private pixilib node_modules resolver path — both appear to be local development leftovers.
platform/app/.webpack/webpack.pwa.js Adds eight private pixilib module resolver paths that don't exist in the public repository and will cause resolution failures for other contributors.
platform/app/public/config/default.js Changes customizationService to reference private @pixilib modules and adds disableConfirmationPrompts: true — org-specific overrides committed to the shared default config.
platform/app/tailwind.config.js Adds a Tailwind content glob for @pixilib packages and removes the @type JSDoc comment — minor org-specific addition that shouldn't be in upstream.

Comments Outside Diff (1)

  1. platform/app/pluginConfig.json, line 68-141 (link)

    P0 Private @pixilib packages committed to the upstream OHIF config

    This file is the canonical plugin registry for the public OHIF Viewers project. Adding eight @pixilib/* extensions and two @pixilib/* modes (all at "version": "0.0.1") will cause every downstream developer who tries to install or build OHIF to fail, because these packages are not published to any public registry. The same concern applies to the corresponding paths added in .webpack/webpack.base.js, platform/app/.webpack/webpack.pwa.js, platform/app/tailwind.config.js, and platform/app/public/config/default.js. These org-internal entries should be kept out of the upstream repository.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: platform/app/pluginConfig.json
    Line: 68-141
    
    Comment:
    **Private `@pixilib` packages committed to the upstream OHIF config**
    
    This file is the canonical plugin registry for the public OHIF Viewers project. Adding eight `@pixilib/*` extensions and two `@pixilib/*` modes (all at `"version": "0.0.1"`) will cause every downstream developer who tries to install or build OHIF to fail, because these packages are not published to any public registry. The same concern applies to the corresponding paths added in `.webpack/webpack.base.js`, `platform/app/.webpack/webpack.pwa.js`, `platform/app/tailwind.config.js`, and `platform/app/public/config/default.js`. These org-internal entries should be kept out of the upstream repository.
    
    How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
Fix the following 4 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 4
platform/core/src/services/PanelService/PanelService.tsx:146-158
**`position` parameter is declared but never used**

The method signature declares a `position: PanelPosition` argument, but the body ignores it entirely — it always reads and filters both `Left` and `Right` regardless. Any caller who passes a specific position expecting targeted removal will be confused by this silent discard. Either remove the parameter (matching the PR description's `removePanel(panelId, options = {})` signature) or use it to scope the removal to the specified position.

### Issue 2 of 4
platform/core/src/services/PanelService/PanelService.tsx:146-158
**Bottom panels are silently wiped on every `removePanel` call**

`setPanels` internally calls `this.reset()`, which clears all positions — including `Bottom`. The `removePanel` implementation only reconstructs `Left` and `Right` panels before passing them back to `setPanels`; `Bottom` panels are never read and therefore never restored. Any panel currently registered at `PanelPosition.Bottom` will be irreversibly dropped as a side-effect of removing an unrelated Left or Right panel. The fix is to include Bottom in the filter-and-restore logic (or avoid `setPanels`/`reset()` altogether and instead mutate only the target position directly).

### Issue 3 of 4
platform/app/pluginConfig.json:68-141
**Private `@pixilib` packages committed to the upstream OHIF config**

This file is the canonical plugin registry for the public OHIF Viewers project. Adding eight `@pixilib/*` extensions and two `@pixilib/*` modes (all at `"version": "0.0.1"`) will cause every downstream developer who tries to install or build OHIF to fail, because these packages are not published to any public registry. The same concern applies to the corresponding paths added in `.webpack/webpack.base.js`, `platform/app/.webpack/webpack.pwa.js`, `platform/app/tailwind.config.js`, and `platform/app/public/config/default.js`. These org-internal entries should be kept out of the upstream repository.

### Issue 4 of 4
.webpack/webpack.base.js:68
Disabling source maps for production builds (`false`) removes the ability to debug minified production errors. The commented-out value suggests this was a temporary local tweak that was accidentally committed. Restore the original value.

```suggestion
    devtool: isProdBuild ? 'source-map' : 'cheap-module-source-map',
```

Reviews (1): Last reviewed commit: "feat(core): add removePanel method to Pa..." | Re-trigger Greptile

Greptile also left 3 inline comments on this PR.

Summary by CodeRabbit

  • New Features
    • Added support for removing panels from the viewer layout.
  • Bug Fixes
    • Improved DICOMweb thumbnail rendering by switching to the thumbnail pipeline and using a fetch-based request strategy.
    • Enabled wildcard matching for DICOMweb request handling.
    • Adjusted default thumbnail request throttling to better balance thumbnail fetching with other requests.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Claude Code Review

This pull request is from a fork — automated review is disabled. A repository maintainer can comment @claude review to run a one-time review.

@netlify

netlify Bot commented Jun 3, 2026

Copy link
Copy Markdown

Deploy Preview for ohif-dev failed. Why did it fail? →

Name Link
🔨 Latest commit 3d781cc
🔍 Latest deploy log https://app.netlify.com/projects/ohif-dev/deploys/6a44adbcd63ed700083253e9

Comment on lines +146 to +158
public removePanel(panelId: string, position: PanelPosition, options): void {

const leftPanels = this._panelsGroups.get(PanelPosition.Left)?.map((p) => p.id) ?? [];
const rightPanels = this._panelsGroups.get(PanelPosition.Right)?.map((p) => p.id) ?? [];

const newLeftPanels = leftPanels.filter((id) => id !== panelId);
const newRightPanels = rightPanels.filter((id) => id !== panelId);

this.setPanels({
[PanelPosition.Left]: newLeftPanels,
[PanelPosition.Right]: newRightPanels,
} as any, options);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 position parameter is declared but never used

The method signature declares a position: PanelPosition argument, but the body ignores it entirely — it always reads and filters both Left and Right regardless. Any caller who passes a specific position expecting targeted removal will be confused by this silent discard. Either remove the parameter (matching the PR description's removePanel(panelId, options = {}) signature) or use it to scope the removal to the specified position.

Prompt To Fix With AI
This is a comment left during a code review.
Path: platform/core/src/services/PanelService/PanelService.tsx
Line: 146-158

Comment:
**`position` parameter is declared but never used**

The method signature declares a `position: PanelPosition` argument, but the body ignores it entirely — it always reads and filters both `Left` and `Right` regardless. Any caller who passes a specific position expecting targeted removal will be confused by this silent discard. Either remove the parameter (matching the PR description's `removePanel(panelId, options = {})` signature) or use it to scope the removal to the specified position.

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +146 to +158
public removePanel(panelId: string, position: PanelPosition, options): void {

const leftPanels = this._panelsGroups.get(PanelPosition.Left)?.map((p) => p.id) ?? [];
const rightPanels = this._panelsGroups.get(PanelPosition.Right)?.map((p) => p.id) ?? [];

const newLeftPanels = leftPanels.filter((id) => id !== panelId);
const newRightPanels = rightPanels.filter((id) => id !== panelId);

this.setPanels({
[PanelPosition.Left]: newLeftPanels,
[PanelPosition.Right]: newRightPanels,
} as any, options);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Bottom panels are silently wiped on every removePanel call

setPanels internally calls this.reset(), which clears all positions — including Bottom. The removePanel implementation only reconstructs Left and Right panels before passing them back to setPanels; Bottom panels are never read and therefore never restored. Any panel currently registered at PanelPosition.Bottom will be irreversibly dropped as a side-effect of removing an unrelated Left or Right panel. The fix is to include Bottom in the filter-and-restore logic (or avoid setPanels/reset() altogether and instead mutate only the target position directly).

Prompt To Fix With AI
This is a comment left during a code review.
Path: platform/core/src/services/PanelService/PanelService.tsx
Line: 146-158

Comment:
**Bottom panels are silently wiped on every `removePanel` call**

`setPanels` internally calls `this.reset()`, which clears all positions — including `Bottom`. The `removePanel` implementation only reconstructs `Left` and `Right` panels before passing them back to `setPanels`; `Bottom` panels are never read and therefore never restored. Any panel currently registered at `PanelPosition.Bottom` will be irreversibly dropped as a side-effect of removing an unrelated Left or Right panel. The fix is to include Bottom in the filter-and-restore logic (or avoid `setPanels`/`reset()` altogether and instead mutate only the target position directly).

How can I resolve this? If you propose a fix, please make it concise.

Comment thread .webpack/webpack.base.js Outdated
const config = {
mode: isProdBuild ? 'production' : 'development',
devtool: isProdBuild ? 'source-map' : 'cheap-module-source-map',
devtool: isProdBuild ? false /*'source-map'*/ : 'cheap-module-source-map',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Disabling source maps for production builds (false) removes the ability to debug minified production errors. The commented-out value suggests this was a temporary local tweak that was accidentally committed. Restore the original value.

Suggested change
devtool: isProdBuild ? false /*'source-map'*/ : 'cheap-module-source-map',
devtool: isProdBuild ? 'source-map' : 'cheap-module-source-map',
Prompt To Fix With AI
This is a comment left during a code review.
Path: .webpack/webpack.base.js
Line: 68

Comment:
Disabling source maps for production builds (`false`) removes the ability to debug minified production errors. The commented-out value suggests this was a temporary local tweak that was accidentally committed. Restore the original value.

```suggestion
    devtool: isProdBuild ? 'source-map' : 'cheap-module-source-map',
```

How can I resolve this? If you propose a fix, please make it concise.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Default app config now lowers thumbnail request limits, changes DICOMWeb thumbnail rendering and wildcard support, adds commented datasource allowlist examples, updates Tailwind content scan patterns, and adds PanelService.removePanel(...).

Changes

Application configuration and build scanning

Layer / File(s) Summary
DICOMWeb thumbnail config
platform/app/public/config/default.js
The thumbnail request limit is reduced, and the DICOMWeb datasource switches thumbnail rendering to thumbnail, adds thumbnailRequestStrategy: 'fetch', and enables wildcard support.
Datasource allowlist comments
platform/app/public/config/default.js
Commented-out dangerouslyAllowedOriginsForAuthenticatedEnvironments blocks are added under the dicomwebproxy and dicomjson datasource configurations.
Tailwind content globs
platform/app/tailwind.config.js
Tailwind content globs are updated for extensions, modes, and the scanned @ohif/* packages under node_modules.

Panel service API enhancement

Layer / File(s) Summary
Panel removal API
platform/core/src/services/PanelService/PanelService.tsx
PanelService adds removePanel(panelId, position, options) that filters the target panel id from the internal left and right position lists, then delegates to setPanels(...) with the provided options.

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

Suggested reviewers: sedghi

Poem
I hopped through configs, soft and bright,
Thumbnails nibbled down just right.
Tailwind paths went sniff-sniff-scan,
Panels slipped away—goodbye, old plan.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and clearly indicates the main change: cleaning up panel removal in PanelService.
Description check ✅ Passed The PR description matches the template and includes context, changes, testing steps, and a completed checklist.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (1)
platform/app/tailwind.config.js (1)

1-1: 💤 Low value

Consider updating the TODO comment.

The TODO mentions adding external extensions/modes to the Tailwind config, but line 20 already adds @pixilib packages. Consider either:

  • Removing the TODO if it's now complete, or
  • Updating it to be more specific: // TODO: Add a general pattern to automatically include all external npm-installed extensions/modes
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@platform/app/tailwind.config.js` at line 1, Update the TODO comment at the
top of tailwind.config.js to reflect current state: either remove the line if
support for external packages is already implemented (noting that `@pixilib`
packages are included on line referencing '`@pixilib`'), or replace it with a
clearer TODO such as "TODO: Add a general pattern to automatically include all
external npm-installed extensions/modes" so it accurately describes the future
work; locate the top-of-file comment and edit it accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@platform/app/pluginConfig.json`:
- Around line 130-138: pluginConfig.json references two mode packages
("packageName": "`@pixilib/gaelo-mode`" and "`@pixilib/learning-mode`" at "0.0.1")
that are not present in package.json or node_modules; fix by either adding these
packages to your project dependencies (add them to package.json dependencies or
devDependencies with the matching versions and run npm/yarn install) or
removing/replacing these entries from platform/app/pluginConfig.json so the
config only lists modes that actually exist; update whichever path you choose
and re-run install/build to ensure the runtime sees the modules.
- Around line 68-96: pluginConfig.json declares several `@pixilib/`* extensions
(packageName entries like "`@pixilib/custom-tools`", "`@pixilib/e-learning`",
"`@pixilib/gaelo-ohif-forms`", "`@pixilib/gaelo-panels-viewport`",
"`@pixilib/gaelo-tmtv`", "`@pixilib/hanging-protocol-manager`",
"`@pixilib/volume-registration`") but those packages are not present in the repo;
update the project so these extensions are available by either adding them to
the root package.json dependencies (or a workspace manifest) and running the
install/publish step to populate node_modules, or modify the dynamic extension
loader to handle missing modules gracefully (fallback UI/logging and skip
initialization) so runtime import failures do not crash the viewer.

In `@platform/app/public/config/default.js`:
- Around line 9-12: The config's customizationService entries reference module
keys that may not exist at runtime: verify that the Pixilib packages'
getCustomizationModule returns an object with a customizationModule containing
the exact keys "default" (for
"`@pixilib/gaelo-panels-viewport.customizationModule.default`") and "loginRoute"
(for "`@pixilib/gaelo-ohif-forms.customizationModule.loginRoute`"), or update the
strings in customizationService to the actual exported keys; specifically
inspect the Pixilib packages' exports (getCustomizationModule /
customizationModule) and either change the entries in customizationService to
match the real keys or add safeguards around extensionManager.getModuleEntry /
extensionValue.value resolution to avoid throwing when a key is missing.

In `@platform/core/src/services/PanelService/PanelService.tsx`:
- Around line 146-158: The removePanel implementation currently rebuilds only
Left/Right and calls setPanels (which calls reset()), causing Bottom panels to
be lost and the position parameter to be ignored; fix by using the position
argument to remove the panel only from the specified PanelPosition inside
removePanel (or remove the parameter if you truly intend global deletion), i.e.
read the current arrays from this._panelsGroups for all positions (use
this._panelsGroups.get(PanelPosition.Left/Right/Bottom)), filter the target
position's array to exclude panelId, then call setPanels with a complete map
that preserves untouched positions (so reset() inside setPanels won't wipe
Bottom), or alternatively implement targeted mutation like addPanel does to
avoid calling reset; reference symbols: removePanel, setPanels, reset,
PanelPosition.Bottom, addPanel.

---

Nitpick comments:
In `@platform/app/tailwind.config.js`:
- Line 1: Update the TODO comment at the top of tailwind.config.js to reflect
current state: either remove the line if support for external packages is
already implemented (noting that `@pixilib` packages are included on line
referencing '`@pixilib`'), or replace it with a clearer TODO such as "TODO: Add a
general pattern to automatically include all external npm-installed
extensions/modes" so it accurately describes the future work; locate the
top-of-file comment and edit it accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 0631e9cc-29c7-453c-9ea4-5f195b43e62e

📥 Commits

Reviewing files that changed from the base of the PR and between 0885468 and 5731401.

⛔ Files ignored due to path filters (2)
  • .webpack/webpack.base.js is excluded by !**/.webpack/**
  • platform/app/.webpack/webpack.pwa.js is excluded by !**/.webpack/**
📒 Files selected for processing (4)
  • platform/app/pluginConfig.json
  • platform/app/public/config/default.js
  • platform/app/tailwind.config.js
  • platform/core/src/services/PanelService/PanelService.tsx

Comment thread platform/app/pluginConfig.json Outdated
Comment thread platform/app/pluginConfig.json Outdated
Comment thread platform/app/public/config/default.js Outdated
Comment thread platform/core/src/services/PanelService/PanelService.tsx Outdated
Comment thread platform/app/.webpack/webpack.pwa.js Outdated
// Hoisted Yarn Workspace Modules
path.resolve(__dirname, '../../../node_modules'),
SRC_DIR,
path.resolve(__dirname, '../pixilib/modes/gaelo-mode/node_modules'),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is a custom change for your paths/setup - please don't push changes like this not for merge into master.
Also, the right way to add dependencies like this is to add them to pluginConfig.json as references so that you don't need to update webpack.pwa.js

this._broadcastEvent(EVENTS.PANELS_CHANGED, { position, options });
}

public removePanel(panelId: string, position: PanelPosition, options): void {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Address the greptile comment about panelPosition - suggest just removing it.

const newLeftPanels = leftPanels.filter((id) => id !== panelId);
const newRightPanels = rightPanels.filter((id) => id !== panelId);

this.setPanels({

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Probably need to fix setPanels so that it only updates panels actually provided. That will need an update to the reset to work.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
platform/core/src/services/PanelService/PanelService.tsx (1)

146-158: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

removePanel still ignores the Bottom position, so a bottom panel can't be removed.

Only Left and Right lists are reconstructed (Lines 148-152). A panel registered in PanelPosition.Bottom will never be filtered out, contradicting the PR description ("retrieve panels across positions"). This is the same gap raised on a prior commit; the data-loss aspect is now resolved by the setPanels change, but bottom removal is still unhandled.

🐛 Proposed fix to also cover Bottom
   public removePanel(panelId: string, options): void {

     const leftPanels = this._panelsGroups.get(PanelPosition.Left)?.map((p) => p.id) ?? [];
     const rightPanels = this._panelsGroups.get(PanelPosition.Right)?.map((p) => p.id) ?? [];
+    const bottomPanels = this._panelsGroups.get(PanelPosition.Bottom)?.map((p) => p.id) ?? [];

     const newLeftPanels = leftPanels.filter((id) => id !== panelId);
     const newRightPanels = rightPanels.filter((id) => id !== panelId);
+    const newBottomPanels = bottomPanels.filter((id) => id !== panelId);

     this.setPanels({
       [PanelPosition.Left]: newLeftPanels,
       [PanelPosition.Right]: newRightPanels,
+      [PanelPosition.Bottom]: newBottomPanels,
     } as any, options);
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@platform/core/src/services/PanelService/PanelService.tsx` around lines 146 -
158, The removePanel method in PanelService still only rebuilds the Left and
Right panel lists, so Bottom panels are never filtered out and cannot be
removed. Update removePanel to include PanelPosition.Bottom when collecting
current panel ids, filter the target panelId from that list too, and pass the
Bottom result into setPanels alongside Left and Right so removal works
consistently across all positions.
🧹 Nitpick comments (1)
platform/core/src/services/PanelService/PanelService.tsx (1)

154-157: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Type the setPanels parameter as partial to drop the as any cast.

removePanel passes an object with only a subset of positions but setPanels declares { [key in PanelPosition]: string[] }, which requires every position — hence the as any escape hatch on Line 157 that silences all type checking on the payload. Since setPanels already handles a partial map via Object.keys(panels), make the parameter optional to keep type safety at call sites.

♻️ Proposed change
-public setPanels(panels: { [key in PanelPosition]: string[] }, options): void {
+public setPanels(panels: { [key in PanelPosition]?: string[] }, options): void {
     this.setPanels({
       [PanelPosition.Left]: newLeftPanels,
       [PanelPosition.Right]: newRightPanels,
-    } as any, options);
+    }, options);

Also applies to: 168-168

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

In `@platform/core/src/services/PanelService/PanelService.tsx` around lines 154 -
157, `removePanel` is forced to use an `as any` cast because `setPanels` in
`PanelService` is typed as a complete `Record<PanelPosition, string[]>` even
though it only processes provided entries. Update `setPanels` to accept a
partial map of panel positions so callers like `removePanel` can pass only the
changed positions without losing type safety, and then remove the `as any` cast
at the `setPanels` call site; use the existing `setPanels` and `removePanel`
symbols to keep the fix aligned with the current partial-key behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@platform/core/src/services/PanelService/PanelService.tsx`:
- Around line 168-175: `PanelService.setPanels` is deleting a position’s group
and relying on `addPanels`, which only triggers `PANELS_CHANGED` through
`addPanel`, so empty panel lists never notify consumers. Update `setPanels` to
explicitly broadcast after processing each position, including when the incoming
array is empty, so `removePanel`/last-panel removal still causes a re-render.
Keep the fix localized around `setPanels`, `addPanels`, and the existing
`PANELS_CHANGED` dispatch flow.

---

Duplicate comments:
In `@platform/core/src/services/PanelService/PanelService.tsx`:
- Around line 146-158: The removePanel method in PanelService still only
rebuilds the Left and Right panel lists, so Bottom panels are never filtered out
and cannot be removed. Update removePanel to include PanelPosition.Bottom when
collecting current panel ids, filter the target panelId from that list too, and
pass the Bottom result into setPanels alongside Left and Right so removal works
consistently across all positions.

---

Nitpick comments:
In `@platform/core/src/services/PanelService/PanelService.tsx`:
- Around line 154-157: `removePanel` is forced to use an `as any` cast because
`setPanels` in `PanelService` is typed as a complete `Record<PanelPosition,
string[]>` even though it only processes provided entries. Update `setPanels` to
accept a partial map of panel positions so callers like `removePanel` can pass
only the changed positions without losing type safety, and then remove the `as
any` cast at the `setPanels` call site; use the existing `setPanels` and
`removePanel` symbols to keep the fix aligned with the current partial-key
behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e134b1af-852f-42b5-8e74-4dfddbe36103

📥 Commits

Reviewing files that changed from the base of the PR and between 5aa847e and 3d781cc.

📒 Files selected for processing (1)
  • platform/core/src/services/PanelService/PanelService.tsx

Comment on lines +168 to 175
public setPanels(panels: { [key in PanelPosition]: string[] }, options): void {
const positionsToUpdate = Object.keys(panels) as PanelPosition[];

Object.keys(panels).forEach((position: PanelPosition) => {
positionsToUpdate.forEach((position) => {
this._panelsGroups.delete(position);
this.addPanels(position, panels[position], options);
});
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

Critical: removing the last panel of a position no longer emits PANELS_CHANGED, so the UI won't re-render.

setPanels deletes each incoming position's group and then delegates to addPanels, which only broadcasts PANELS_CHANGED from inside addPanel (Line 143) — once per panel actually pushed. When the new list for a position is empty (the common removePanel case where the last panel of a side is removed), addPanels iterates over an empty array and no event fires. The group is silently deleted in state but no consumer is notified, so the closed panel stays visible. This defeats the PR's stated goal of avoiding "panels staying visible after state changes."

Previously reset() guaranteed a broadcast for every cleared position (Lines 189-191); that guarantee is now lost for emptied positions. Broadcast per processed position after updating state.

🐛 Proposed fix to always notify per updated position
 public setPanels(panels: { [key in PanelPosition]: string[] }, options): void {
     const positionsToUpdate = Object.keys(panels) as PanelPosition[];

     positionsToUpdate.forEach((position) => {
       this._panelsGroups.delete(position);
-      this.addPanels(position, panels[position], options);
+      panels[position].forEach(panelId => this.addPanel(position, panelId, options));
+
+      // Ensure emptied positions still notify consumers (addPanel only
+      // broadcasts when a panel is actually added).
+      if (panels[position].length === 0) {
+        this._broadcastEvent(EVENTS.PANELS_CHANGED, { position, options });
+      }
     });
   }
📝 Committable suggestion

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

Suggested change
public setPanels(panels: { [key in PanelPosition]: string[] }, options): void {
const positionsToUpdate = Object.keys(panels) as PanelPosition[];
Object.keys(panels).forEach((position: PanelPosition) => {
positionsToUpdate.forEach((position) => {
this._panelsGroups.delete(position);
this.addPanels(position, panels[position], options);
});
}
public setPanels(panels: { [key in PanelPosition]: string[] }, options): void {
const positionsToUpdate = Object.keys(panels) as PanelPosition[];
positionsToUpdate.forEach((position) => {
this._panelsGroups.delete(position);
panels[position].forEach(panelId => this.addPanel(position, panelId, options));
// Ensure emptied positions still notify consumers (addPanel only
// broadcasts when a panel is actually added).
if (panels[position].length === 0) {
this._broadcastEvent(EVENTS.PANELS_CHANGED, { position, options });
}
});
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@platform/core/src/services/PanelService/PanelService.tsx` around lines 168 -
175, `PanelService.setPanels` is deleting a position’s group and relying on
`addPanels`, which only triggers `PANELS_CHANGED` through `addPanel`, so empty
panel lists never notify consumers. Update `setPanels` to explicitly broadcast
after processing each position, including when the incoming array is empty, so
`removePanel`/last-panel removal still causes a re-render. Keep the fix
localized around `setPanels`, `addPanels`, and the existing `PANELS_CHANGED`
dispatch flow.

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