Skip to content

feat(visualization): redesign node context menu - #4492

Merged
ChristianHuehn merged 2 commits into
mainfrom
feature/redesign-context-menu
Jun 12, 2026
Merged

feat(visualization): redesign node context menu#4492
ChristianHuehn merged 2 commits into
mainfrom
feature/redesign-context-menu

Conversation

@ChristianHuehn

@ChristianHuehn ChristianHuehn commented Jun 12, 2026

Copy link
Copy Markdown
Collaborator

Migrate the node context menu from ui/nodeContextMenu (Material, CDK overlay, SCSS) to features/nodeContextMenu following the new feature architecture: standalone OnPush components with signals, DaisyUI templates, ngrx confined to stores/ and selectors/.

  • Header row shows the node name and copies its path without the leading root segment on click
  • New "Show in Explorer" action (map right-clicks only) expands the File Explorer, scrolls to the node, and briefly highlights it via the new ExplorerRevealService
  • rightClickedNodeData state gains an origin field to distinguish map and explorer right-clicks
  • Folder color row keeps the five marking colors, adds a clear button for custom colors, and reuses the inline color picker, which moved to features/shared with a new brush-trigger variant
  • Unflatten entry shows the flatten icon rotated 180 degrees

{Meaningful title}

Please read the CONTRIBUTING.md before opening a PR.

Closes: #

Description

Descriptive pull request text, answering:

  • What problem/issue are you fixing?
  • What does this PR implement and how?

Definition of Done

A PR is only ready for merge once all the following acceptance criteria are fulfilled:

  • Changes have been manually tested
  • All TODOs related to this PR have been closed
  • There are automated tests for newly written code and bug fixes
  • All bugs discovered while working on this PR have been submitted as issues (if not already an open issue)
  • Documentation (GH-pages, analysis/visualization READMEs, parser READMEs, --help, etc.) has been updated (almost always necessary except for bug fixes)
  • CHANGELOG.md has been updated

Screenshots or gifs

Summary by CodeRabbit

  • New Features

    • "Show in Explorer" action: expands, scrolls to, and highlights the revealed node in the sidebar.
    • Copy-on-click for node paths in context menus.
    • Clear button for custom folder color markings; inline custom color picker trigger option.
  • Changed

    • Redesigned node/building/File Explorer context menu with updated layout and interactions.
    • Folder marking refined: five preset colors preserved; behavior and icons adjusted.

Migrate the node context menu from ui/nodeContextMenu (Material, CDK
overlay, SCSS) to features/nodeContextMenu following the new feature
architecture: standalone OnPush components with signals, DaisyUI
templates, ngrx confined to stores/ and selectors/.

- Header row shows the node name and copies its path without the
  leading root segment on click
- New "Show in Explorer" action (map right-clicks only) expands the
  File Explorer, scrolls to the node, and briefly highlights it via
  the new ExplorerRevealService
- rightClickedNodeData state gains an origin field to distinguish
  map and explorer right-clicks
- Folder color row keeps the five marking colors, adds a clear button
  for custom colors, and reuses the inline color picker, which moved
  to features/shared with a new brush-trigger variant
- Unflatten entry shows the flatten icon rotated 180 degrees

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d4933bdd-ebdc-4ba9-82bf-12e2ea8beb07

📥 Commits

Reviewing files that changed from the base of the PR and between 6bd3030 and a7e34ec.

📒 Files selected for processing (4)
  • visualization/CHANGELOG.md
  • visualization/app/codeCharta/codeCharta.component.ts
  • visualization/app/codeCharta/codeCharta.model.ts
  • visualization/app/codeCharta/features/metricsBar/components/colorSettingsPopover/colorBandRow.component.ts
✅ Files skipped from review due to trivial changes (1)
  • visualization/CHANGELOG.md
🚧 Files skipped from review as they are similar to previous changes (3)
  • visualization/app/codeCharta/features/metricsBar/components/colorSettingsPopover/colorBandRow.component.ts
  • visualization/app/codeCharta/codeCharta.component.ts
  • visualization/app/codeCharta/codeCharta.model.ts

📝 Walkthrough

Walkthrough

This PR redesigns the node context menu by migrating from Angular Material/CDK/SCSS in ui/nodeContextMenu to a new signals-based, zoneless component at features/nodeContextMenu with Tailwind/daisyUI styling. The redesign adds origin tracking to right-clicks, extracts the inline color picker to a shared location, implements sidebar reveal-and-highlight behavior, and replaces old overlay-based menu logic with direct template rendering.

Changes

Node Context Menu Redesign

Layer / File(s) Summary
Plan & Domain Model Updates
plans/context-menu/redesign-context-menu.md, visualization/CHANGELOG.md, visualization/app/codeCharta/codeCharta.model.ts
Redesign plan documents the migration goals and clarified behaviors. AppStatus.rightClickedNodeData now includes origin: "codeMap" | "explorer" to track right-click context source.
Shared Inline Color Picker Extraction
visualization/app/codeCharta/features/shared/components/inlineColorPicker/*, visualization/app/codeCharta/features/metricsBar/components/colorSettingsPopover/*.component.ts
InlineColorPickerComponent moved to shared features location and enhanced with optional triggerIcon input for flexible trigger rendering (icon vs. swatch). Both metricsBar components updated to import from shared location. Old template removed from metricsBar.
Right-Click Origin Tracking
visualization/app/codeCharta/ui/codeMap/codeMap.mouseEvent.service.ts, visualization/app/codeCharta/features/sidebarExplorer/components/explorerTreeLevel/explorerTreeLevel.component.ts
Code map and explorer tree level now emit origin: "codeMap" and origin: "explorer" respectively when dispatching right-click events, enabling context-aware menu behavior.
New Node Context Menu Component
visualization/app/codeCharta/features/nodeContextMenu/components/nodeContextMenu/nodeContextMenu.component.ts, nodeContextMenu.component.html, nodeContextMenu.component.spec.ts
New signals-based component with viewport-clamped positioning, global close handlers (outside click/scroll/resize), copy-to-clipboard with transient feedback, conditional action rendering (Show in Explorer hidden for explorer-origin, Focus/Unfocus based on state), and menu closure via RightClickedNodeStore.clear().
Mark Folder Row & Item Components
visualization/app/codeCharta/features/nodeContextMenu/components/nodeContextMenu/contextMenuItem.component.*, markFolderRow.component.ts, markFolderRow.component.html, markFolderRow.component.spec.ts
ContextMenuItemComponent provides reusable button UI with icon, hover hint, and click emission. MarkFolderRowComponent manages preset color marking, custom color via inline picker, and clear-color affordance; tests verify NgRx action dispatches and folderMarked output.
Supporting Stores & Selectors
visualization/app/codeCharta/features/nodeContextMenu/stores/*, selectors/markFolderItems.selector.ts
RightClickedNodeStore wraps right-click node selectors and clear() dispatcher. FocusedNodeStore exposes focus/unfocus action dispatchers. MarkFolderStore provides markFolder/unmarkFolder methods. ContextMenuBlacklistStore wraps flatten/exclude dispatchers. currentMarkColorSelector returns marked color for right-clicked node (supporting parent-child inheritance).
Explorer Reveal Service & Integration
visualization/app/codeCharta/features/sidebarExplorer/services/explorerReveal.service.ts, explorerReveal.service.spec.ts, explorerCollapse.service.ts, explorerTreeLevel.component.ts, explorerTreeLevel.component.html, explorerTreeLevel.component.spec.ts
ExplorerRevealService manages revealedNodePath signal with auto-clear after 1500ms. revealNode() expands explorer, sets path, and schedules clear. ExplorerTreeLevel integrates via new isRevealed computed and effect that opens ancestor folders and scrolls revealed node into view; template adds bg-primary/20 highlight class. Tests cover ancestor opening, prefix rejection, and highlight verification.
Application Wiring
visualization/app/codeCharta/codeCharta.component.ts, codeCharta.component.html, visualization/app/app.config.ts, visualization/app/codeCharta/features/nodeContextMenu/facade.ts, visualization/app/codeCharta/features/sidebarExplorer/facade.ts
NodeContextMenuComponent imported and rendered in root CodeChartaComponent. OpenNodeContextMenuEffect removed from app.config provideEffects. New component and ExplorerRevealService exported via facades.
E2E Tests & Page Objects
visualization/app/codeCharta/features/nodeContextMenu/components/nodeContextMenu/nodeContextMenu.e2e.ts, nodeContextMenu.po.ts, visualization/app/codeCharta/features/sidebarExplorer/components/explorerTreeLevel/explorerTreeLevel.e2e.ts
Playwright tests updated for new component location. New e2e test verifies "Show in Explorer" action is hidden when right-clicking in explorer context. Removed unused clickOnExclude() helper from page object.
Remove Old Material/CDK Implementation
visualization/app/codeCharta/ui/nodeContextMenu/*, visualization/app/codeCharta/state/effects/openNodeContextMenu/*
Deleted old NodeContextMenuService (overlay-based), NodeContextMenuCardComponent, button/focus/flatten/highlight/mark-folder/copy UI components, pipes (LastPartOfNodePath, IsNodeFocused), SCSS styling, and all associated specs. Removed OpenNodeContextMenuEffect and its spec.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly Related PRs

Poem

🐰 I hopped from Material to Tailwind's light,
Signals in my paws, the context menu bright,
Explorer reveals with a flutter and glow,
Colors mark folders in a tidy row,
Old overlays buried — new flows take flight.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is incomplete and contains template boilerplate. It includes useful feature details at the top but lacks a clear problem statement, how-it-works explanation, and the Definition of Done checklist is entirely unchecked. Replace the duplicated template boilerplate with a cohesive description explaining the problem being solved and provide a clear summary of implementation details. Complete or remove the Definition of Done section appropriately.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the primary change: redesigning the node context menu with modern architecture and features.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/redesign-context-menu

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 and usage tips.

@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: 2

🤖 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
`@visualization/app/codeCharta/features/nodeContextMenu/components/nodeContextMenu/markFolderRow.component.ts`:
- Around line 54-60: onCustomColorChange currently calls
this.markFolderStore.markFolder(nodePath, color) but does not emit the
folderMarked event like the other handlers; update onCustomColorChange (in
markFolderRow.component.ts) to emit the same folderMarked event after
successfully marking the folder (e.g., call the component's folderMarked emitter
or emitFolderMarked helper with the nodePath and/or color) so consumers receive
a consistent folderMarked notification.

In
`@visualization/app/codeCharta/features/nodeContextMenu/components/nodeContextMenu/nodeContextMenu.component.ts`:
- Around line 104-115: Wrap the navigator.clipboard.writeText call in copyPath
in a try/catch so clipboard write rejections are handled; only call
this.wasPathCopied.set(true) and start/clear this.pathCopiedTimeout on
successful write, and in the catch block avoid changing the copied state and log
or surface the error (e.g., via console.error or existing logger). Ensure you
still guard for a missing node via menuNode() and keep using
pathWithoutRootSegment(node) and PATH_COPIED_FEEDBACK_DURATION_MS as before.
🪄 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

Run ID: fa056f0f-411f-48eb-a9d6-32b2179fc59f

📥 Commits

Reviewing files that changed from the base of the PR and between 972f1bd and 6bd3030.

⛔ Files ignored due to path filters (4)
  • plans/context-menu/redesign-context-menu.png is excluded by !**/*.png
  • plans/context-menu/verify-clear-custom-color.png is excluded by !**/*.png
  • plans/context-menu/verify-menu-from-explorer.png is excluded by !**/*.png
  • plans/context-menu/verify-show-in-explorer-reveal.png is excluded by !**/*.png
📒 Files selected for processing (71)
  • plans/context-menu/redesign-context-menu.md
  • visualization/CHANGELOG.md
  • visualization/app/app.config.ts
  • visualization/app/codeCharta/codeCharta.component.html
  • visualization/app/codeCharta/codeCharta.component.ts
  • visualization/app/codeCharta/codeCharta.model.ts
  • visualization/app/codeCharta/features/metricsBar/components/colorSettingsPopover/colorBandRow.component.ts
  • visualization/app/codeCharta/features/metricsBar/components/colorSettingsPopover/folderOverrideRow.component.ts
  • visualization/app/codeCharta/features/metricsBar/components/colorSettingsPopover/inlineColorPicker.component.html
  • visualization/app/codeCharta/features/nodeContextMenu/components/nodeContextMenu/contextMenuItem.component.html
  • visualization/app/codeCharta/features/nodeContextMenu/components/nodeContextMenu/contextMenuItem.component.ts
  • visualization/app/codeCharta/features/nodeContextMenu/components/nodeContextMenu/markFolderRow.component.html
  • visualization/app/codeCharta/features/nodeContextMenu/components/nodeContextMenu/markFolderRow.component.spec.ts
  • visualization/app/codeCharta/features/nodeContextMenu/components/nodeContextMenu/markFolderRow.component.ts
  • visualization/app/codeCharta/features/nodeContextMenu/components/nodeContextMenu/nodeContextMenu.component.html
  • visualization/app/codeCharta/features/nodeContextMenu/components/nodeContextMenu/nodeContextMenu.component.spec.ts
  • visualization/app/codeCharta/features/nodeContextMenu/components/nodeContextMenu/nodeContextMenu.component.ts
  • visualization/app/codeCharta/features/nodeContextMenu/components/nodeContextMenu/nodeContextMenu.e2e.ts
  • visualization/app/codeCharta/features/nodeContextMenu/components/nodeContextMenu/nodeContextMenu.po.ts
  • visualization/app/codeCharta/features/nodeContextMenu/facade.ts
  • visualization/app/codeCharta/features/nodeContextMenu/selectors/markFolderItems.selector.spec.ts
  • visualization/app/codeCharta/features/nodeContextMenu/selectors/markFolderItems.selector.ts
  • visualization/app/codeCharta/features/nodeContextMenu/stores/contextMenuBlacklist.store.ts
  • visualization/app/codeCharta/features/nodeContextMenu/stores/focusedNode.store.ts
  • visualization/app/codeCharta/features/nodeContextMenu/stores/markFolder.store.ts
  • visualization/app/codeCharta/features/nodeContextMenu/stores/rightClickedNode.store.ts
  • visualization/app/codeCharta/features/shared/components/inlineColorPicker/inlineColorPicker.component.html
  • visualization/app/codeCharta/features/shared/components/inlineColorPicker/inlineColorPicker.component.spec.ts
  • visualization/app/codeCharta/features/shared/components/inlineColorPicker/inlineColorPicker.component.ts
  • visualization/app/codeCharta/features/sidebarExplorer/components/explorerTreeLevel/explorerTreeLevel.component.html
  • visualization/app/codeCharta/features/sidebarExplorer/components/explorerTreeLevel/explorerTreeLevel.component.spec.ts
  • visualization/app/codeCharta/features/sidebarExplorer/components/explorerTreeLevel/explorerTreeLevel.component.ts
  • visualization/app/codeCharta/features/sidebarExplorer/components/explorerTreeLevel/explorerTreeLevel.e2e.ts
  • visualization/app/codeCharta/features/sidebarExplorer/facade.ts
  • visualization/app/codeCharta/features/sidebarExplorer/services/explorerCollapse.service.ts
  • visualization/app/codeCharta/features/sidebarExplorer/services/explorerReveal.service.spec.ts
  • visualization/app/codeCharta/features/sidebarExplorer/services/explorerReveal.service.ts
  • visualization/app/codeCharta/features/sidebarExplorer/stores/appStatus.store.spec.ts
  • visualization/app/codeCharta/state/effects/openNodeContextMenu/openNodeContextMenu.effect.spec.ts
  • visualization/app/codeCharta/state/effects/openNodeContextMenu/openNodeContextMenu.effect.ts
  • visualization/app/codeCharta/ui/codeMap/codeMap.mouseEvent.service.spec.ts
  • visualization/app/codeCharta/ui/codeMap/codeMap.mouseEvent.service.ts
  • visualization/app/codeCharta/ui/nodeContextMenu/copyButton/copyPathButton.component.html
  • visualization/app/codeCharta/ui/nodeContextMenu/copyButton/copyPathButton.component.spec.ts
  • visualization/app/codeCharta/ui/nodeContextMenu/copyButton/copyPathButton.component.ts
  • visualization/app/codeCharta/ui/nodeContextMenu/excludeButton/excludeButton.component.html
  • visualization/app/codeCharta/ui/nodeContextMenu/excludeButton/excludeButton.component.ts
  • visualization/app/codeCharta/ui/nodeContextMenu/flattenButtons/flattenButtons.component.html
  • visualization/app/codeCharta/ui/nodeContextMenu/flattenButtons/flattenButtons.component.spec.ts
  • visualization/app/codeCharta/ui/nodeContextMenu/flattenButtons/flattenButtons.component.ts
  • visualization/app/codeCharta/ui/nodeContextMenu/focusButtons/focusButtons.component.html
  • visualization/app/codeCharta/ui/nodeContextMenu/focusButtons/focusButtons.component.spec.ts
  • visualization/app/codeCharta/ui/nodeContextMenu/focusButtons/isNodeFocused.pipe.spec.ts
  • visualization/app/codeCharta/ui/nodeContextMenu/focusButtons/isNodeFocused.pipe.ts
  • visualization/app/codeCharta/ui/nodeContextMenu/highlightButtons/highlightButtons.component.html
  • visualization/app/codeCharta/ui/nodeContextMenu/highlightButtons/highlightButtons.component.spec.ts
  • visualization/app/codeCharta/ui/nodeContextMenu/highlightButtons/highlightButtons.component.ts
  • visualization/app/codeCharta/ui/nodeContextMenu/markFolderRow/markFolderRow.component.html
  • visualization/app/codeCharta/ui/nodeContextMenu/markFolderRow/markFolderRow.component.scss
  • visualization/app/codeCharta/ui/nodeContextMenu/markFolderRow/markFolderRow.component.spec.ts
  • visualization/app/codeCharta/ui/nodeContextMenu/markFolderRow/markFolderRow.component.ts
  • visualization/app/codeCharta/ui/nodeContextMenu/markFolderRow/selectors/markFolderItems.selector.spec.ts
  • visualization/app/codeCharta/ui/nodeContextMenu/nodeContextMenu.service.spec.ts
  • visualization/app/codeCharta/ui/nodeContextMenu/nodeContextMenu.service.ts
  • visualization/app/codeCharta/ui/nodeContextMenu/nodeContextMenuButton.component.scss
  • visualization/app/codeCharta/ui/nodeContextMenu/nodeContextMenuCard/lastPartOfNodePath.pipe.spec.ts
  • visualization/app/codeCharta/ui/nodeContextMenu/nodeContextMenuCard/lastPartOfNodePath.pipe.ts
  • visualization/app/codeCharta/ui/nodeContextMenu/nodeContextMenuCard/nodeContextMenuCard.component.html
  • visualization/app/codeCharta/ui/nodeContextMenu/nodeContextMenuCard/nodeContextMenuCard.component.scss
  • visualization/app/codeCharta/ui/nodeContextMenu/nodeContextMenuCard/nodeContextMenuCard.component.spec.ts
  • visualization/app/codeCharta/ui/nodeContextMenu/nodeContextMenuCard/nodeContextMenuCard.component.ts
💤 Files with no reviewable changes (33)
  • visualization/app/codeCharta/ui/nodeContextMenu/nodeContextMenuCard/nodeContextMenuCard.component.scss
  • visualization/app/codeCharta/ui/nodeContextMenu/excludeButton/excludeButton.component.html
  • visualization/app/codeCharta/ui/nodeContextMenu/copyButton/copyPathButton.component.ts
  • visualization/app/codeCharta/ui/nodeContextMenu/nodeContextMenuCard/lastPartOfNodePath.pipe.ts
  • visualization/app/codeCharta/ui/nodeContextMenu/focusButtons/isNodeFocused.pipe.ts
  • visualization/app/codeCharta/ui/nodeContextMenu/flattenButtons/flattenButtons.component.html
  • visualization/app/codeCharta/ui/nodeContextMenu/excludeButton/excludeButton.component.ts
  • visualization/app/codeCharta/ui/nodeContextMenu/nodeContextMenu.service.spec.ts
  • visualization/app/codeCharta/features/metricsBar/components/colorSettingsPopover/inlineColorPicker.component.html
  • visualization/app/codeCharta/ui/nodeContextMenu/copyButton/copyPathButton.component.spec.ts
  • visualization/app/codeCharta/ui/nodeContextMenu/focusButtons/isNodeFocused.pipe.spec.ts
  • visualization/app/codeCharta/ui/nodeContextMenu/nodeContextMenu.service.ts
  • visualization/app/codeCharta/ui/nodeContextMenu/flattenButtons/flattenButtons.component.spec.ts
  • visualization/app/codeCharta/ui/nodeContextMenu/flattenButtons/flattenButtons.component.ts
  • visualization/app/codeCharta/ui/nodeContextMenu/focusButtons/focusButtons.component.spec.ts
  • visualization/app/codeCharta/ui/nodeContextMenu/markFolderRow/markFolderRow.component.ts
  • visualization/app/codeCharta/ui/nodeContextMenu/markFolderRow/selectors/markFolderItems.selector.spec.ts
  • visualization/app/codeCharta/ui/nodeContextMenu/markFolderRow/markFolderRow.component.spec.ts
  • visualization/app/codeCharta/ui/nodeContextMenu/nodeContextMenuCard/nodeContextMenuCard.component.html
  • visualization/app/codeCharta/ui/nodeContextMenu/highlightButtons/highlightButtons.component.spec.ts
  • visualization/app/codeCharta/ui/nodeContextMenu/markFolderRow/markFolderRow.component.scss
  • visualization/app/codeCharta/ui/nodeContextMenu/focusButtons/focusButtons.component.html
  • visualization/app/codeCharta/ui/nodeContextMenu/nodeContextMenuButton.component.scss
  • visualization/app/codeCharta/state/effects/openNodeContextMenu/openNodeContextMenu.effect.spec.ts
  • visualization/app/codeCharta/ui/nodeContextMenu/nodeContextMenuCard/nodeContextMenuCard.component.spec.ts
  • visualization/app/codeCharta/ui/nodeContextMenu/copyButton/copyPathButton.component.html
  • visualization/app/codeCharta/ui/nodeContextMenu/highlightButtons/highlightButtons.component.ts
  • visualization/app/codeCharta/ui/nodeContextMenu/nodeContextMenuCard/nodeContextMenuCard.component.ts
  • visualization/app/app.config.ts
  • visualization/app/codeCharta/state/effects/openNodeContextMenu/openNodeContextMenu.effect.ts
  • visualization/app/codeCharta/ui/nodeContextMenu/markFolderRow/markFolderRow.component.html
  • visualization/app/codeCharta/ui/nodeContextMenu/nodeContextMenuCard/lastPartOfNodePath.pipe.spec.ts
  • visualization/app/codeCharta/ui/nodeContextMenu/highlightButtons/highlightButtons.component.html

Comment on lines +54 to +60
onCustomColorChange(color: string) {
const nodePath = this.nodePath()
if (!nodePath) {
return
}
this.markFolderStore.markFolder(nodePath, color)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Emit folderMarked after custom color selection for consistent action flow.

onCustomColorChange marks the folder but does not emit folderMarked, unlike clear/preset handlers. This creates inconsistent behavior for consumers relying on that event.

Suggested fix
     onCustomColorChange(color: string) {
         const nodePath = this.nodePath()
         if (!nodePath) {
             return
         }
         this.markFolderStore.markFolder(nodePath, color)
+        this.folderMarked.emit()
     }
🤖 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
`@visualization/app/codeCharta/features/nodeContextMenu/components/nodeContextMenu/markFolderRow.component.ts`
around lines 54 - 60, onCustomColorChange currently calls
this.markFolderStore.markFolder(nodePath, color) but does not emit the
folderMarked event like the other handlers; update onCustomColorChange (in
markFolderRow.component.ts) to emit the same folderMarked event after
successfully marking the folder (e.g., call the component's folderMarked emitter
or emitFolderMarked helper with the nodePath and/or color) so consumers receive
a consistent folderMarked notification.

Comment on lines +104 to +115
async copyPath() {
const node = this.menuNode()
if (!node) {
return
}
await navigator.clipboard.writeText(this.pathWithoutRootSegment(node))
this.wasPathCopied.set(true)
if (this.pathCopiedTimeout) {
clearTimeout(this.pathCopiedTimeout)
}
this.pathCopiedTimeout = setTimeout(() => this.wasPathCopied.set(false), PATH_COPIED_FEEDBACK_DURATION_MS)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="visualization/app/codeCharta/features/nodeContextMenu/components/nodeContextMenu/nodeContextMenu.component.ts"
echo "== file = $FILE"
ls -la "$FILE" || true
echo

echo "== lines 70-160"
nl -ba "$FILE" | sed -n '70,160p'
echo

echo "== search for copyPath and clipboard usage"
rg -n "copyPath|clipboard\.writeText|pathCopiedTimeout|PATH_COPIED_FEEDBACK_DURATION_MS|wasPathCopied" "$FILE"
echo

echo "== search project for wasPathCopied / pathCopiedTimeout handling"
rg -n "wasPathCopied|pathCopiedTimeout" visualization/app/codeCharta/features/nodeContextMenu/components/nodeContextMenu -S

Repository: MaibornWolff/codecharta

Length of output: 425


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="visualization/app/codeCharta/features/nodeContextMenu/components/nodeContextMenu/nodeContextMenu.component.ts"

echo "== lines 70-160 (with line numbers)"
cat -n "$FILE" | sed -n '70,160p'

echo
echo "== copyPath + clipboard usage"
rg -n "async copyPath|navigator\.clipboard\.writeText|pathWithoutRootSegment|wasPathCopied|pathCopiedTimeout|PATH_COPIED_FEEDBACK_DURATION_MS" "$FILE"

echo
echo "== project-wide handling of wasPathCopied/pathCopiedTimeout"
rg -n "wasPathCopied|pathCopiedTimeout" visualization/app/codeCharta/features/nodeContextMenu/components/nodeContextMenu -S

Repository: MaibornWolff/codecharta

Length of output: 5585


🏁 Script executed:

#!/bin/bash
set -euo pipefail
BASE="visualization/app/codeCharta/features/nodeContextMenu/components/nodeContextMenu"

echo "== search for copyPath call sites in component folder"
rg -n "copyPath\(" "$BASE" -S

echo
echo "== search for navigator.clipboard usage in repo (quick scope)"
rg -n "navigator\.clipboard\.writeText|navigator\.clipboard" visualization/app -S

Repository: MaibornWolff/codecharta

Length of output: 1222


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== clipboardWriter.ts"
cat -n visualization/app/codeCharta/util/clipboard/clipboardWriter.ts

echo
echo "== clipboardWriter.spec.ts"
cat -n visualization/app/codeCharta/util/clipboard/clipBoardWriter.spec.ts | sed -n '1,120p'

echo
echo "== inspectorHeader.component.ts (clipboard usage)"
cat -n visualization/app/codeCharta/features/sidebarInspector/components/inspectorHeader/inspectorHeader.component.ts | sed -n '1,120p'

echo
echo "== nodeContextMenu.component.html (click handler)"
cat -n visualization/app/codeCharta/features/nodeContextMenu/components/nodeContextMenu/nodeContextMenu.component.html | sed -n '1,80p'

Repository: MaibornWolff/codecharta

Length of output: 9558


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n "checkWriteToClipboardAllowed|clipboardWriter|setToClipboard|try\s*\{[^}]*clipboard|catch\s*\([^)]*\)[^{]*clipboard|navigator\.clipboard" visualization/app/codeCharta/features/nodeContextMenu/components/nodeContextMenu -S
echo "----"
rg -n "checkWriteToClipboardAllowed|setToClipboard" visualization/app/codeCharta -S

Repository: MaibornWolff/codecharta

Length of output: 3046


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="visualization/app/codeCharta/features/viewCubeToolbox/services/screenshot.service.ts"
echo "== screenshot.service.ts"
cat -n "$FILE" | sed -n '1,220p'

echo
echo "== search for try/catch around clipboard in repo"
rg -n "clipboard\.writeText|clipboardWriter|setToClipboard|write\(" visualization/app/codeCharta -S | head -n 50

Repository: MaibornWolff/codecharta

Length of output: 11408


🌐 Web query:

Angular template event binding (click)="asyncMethod()" unhandled promise rejection handling behavior

💡 Result:

In Angular, if you bind an event to an async method in a template—such as (click)="asyncMethod"—the behavior regarding unhandled promise rejections depends heavily on your application's use of Zone.js and your global error configuration [1][2][3]. Angular's built-in ErrorHandler is primarily designed to catch errors that occur synchronously during the framework's execution (e.g., component lifecycle hooks, template expression evaluation) [1][2][3]. Because asynchronous operations (like those inside an async method) often resolve or reject outside the framework's direct control, they are not automatically intercepted by the standard ErrorHandler [1][2]. Key behaviors and handling strategies: 1. Zone.js Environment: When using Zone.js, it monkey-patches asynchronous browser APIs [4][3]. Historically, this provided a "catch-all" that could intercept unhandled promise rejections and forward them to the global ErrorHandler [3]. However, this mechanism is not foolproof and can be brittle; relying on it for critical error handling is discouraged [1][2]. 2. Zoneless Environment / Modern Angular: In modern Angular applications, particularly those running in a "zoneless" configuration, the framework does not implicitly intercept asynchronous errors [3]. The responsibility for handling these errors lies with the developer [2]. 3. Recommended Solution: To ensure unhandled promise rejections are consistently captured and forwarded to your ErrorHandler, you should use the provideBrowserGlobalErrorListeners() provider [2][5][3]. This provider attaches listeners to the browser's global unhandledrejection and error events, ensuring that any unhandled asynchronous errors are forwarded to your application's central ErrorHandler [2][5]. 4. Implementation Best Practices: - Use try/catch blocks locally within your async methods to handle expected failures gracefully, rather than relying on global catchers [2]. - If a promise fails and you want to prevent global error handling (e.g., to display a custom UI state instead), ensure you handle the error locally and avoid re-throwing it [6][7]. - For applications using the AsyncPipe, errors are handled at the stream level [7]. You can use the catchError operator in your observable streams to catch and transform errors into a safe state (e.g., a default value or an error message) for display in the template [6][7]. In summary, Angular does not automatically bridge all promise rejections in event bindings to the ErrorHandler [1][2]. To maintain robust error reporting, explicitly configure global error listeners and manage errors locally within your asynchronous code [2][3].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n "provideBrowserGlobalErrorListeners|unhandledrejection|unhandledRejection|window\.onunhandledrejection|addEventListener\(\"unhandledrejection\"|zoneless|provideZoneChangeDetection|withZone|bootstrapApplication" visualization/app -S

Repository: MaibornWolff/codecharta

Length of output: 402


🏁 Script executed:

#!/bin/bash
set -euo pipefail

MAIN="visualization/app/main.ts"
echo "== main.ts"
cat -n "$MAIN" | sed -n '1,200p'

echo
echo "== find appConfig definition"
rg -n "appConfig" visualization/app -S

echo
echo "== print the file where appConfig is defined (search result first hit)"
APP_CONFIG_FILE=$(rg -n "const appConfig|export const appConfig|appConfig\s*=" visualization/app -S --files-with-matches | head -n 1 || true)
echo "appConfig file: $APP_CONFIG_FILE"
if [ -n "${APP_CONFIG_FILE:-}" ]; then
  cat -n "$APP_CONFIG_FILE" | sed -n '1,240p'
fi

Repository: MaibornWolff/codecharta

Length of output: 5394


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== search for ErrorHandler providers / custom implementations"
rg -n "ErrorHandler|error-handler|provide: ErrorHandler|provideErrorHandler|Uncaught|unhandledrejection|onunhandledrejection" visualization/app -S

echo
echo "== search for global error handling in app/codeCharta"
rg -n "unhandledrejection|unhandledRejection|ErrorHandler" visualization/app/codeCharta -S

Repository: MaibornWolff/codecharta

Length of output: 129


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n "ErrorHandler|provideErrorHandler|error-handler|unhandledrejection|unhandledRejection|onunhandledrejection" . -S || true

echo "----"
rg -n "zone\.js|zone\.js" . -S || true

echo "----"
rg -n "window\.addEventListener\(\s*['\"]unhandledrejection['\"]|addEventListener\(\s*['\"]unhandledrejection['\"]" . -S || true

Repository: MaibornWolff/codecharta

Length of output: 50382


🏁 Script executed:

#!/bin/bash
set -euo pipefail
FILE="visualization/app/codeCharta/features/nodeContextMenu/components/nodeContextMenu/nodeContextMenu.component.ts"
cat -n "$FILE" | sed -n '1,120p'

Repository: MaibornWolff/codecharta

Length of output: 6477


Handle clipboard write failures in copyPath
copyPath awaits navigator.clipboard.writeText(...) without catching rejections; clipboard permission/security-context failures can reject and leave the UI state inconsistent / surface an unhandled async error.

Suggested fix
     async copyPath() {
         const node = this.menuNode()
         if (!node) {
             return
         }
-        await navigator.clipboard.writeText(this.pathWithoutRootSegment(node))
-        this.wasPathCopied.set(true)
-        if (this.pathCopiedTimeout) {
-            clearTimeout(this.pathCopiedTimeout)
-        }
-        this.pathCopiedTimeout = setTimeout(() => this.wasPathCopied.set(false), PATH_COPIED_FEEDBACK_DURATION_MS)
+        try {
+            await navigator.clipboard.writeText(this.pathWithoutRootSegment(node))
+            this.wasPathCopied.set(true)
+            if (this.pathCopiedTimeout) {
+                clearTimeout(this.pathCopiedTimeout)
+            }
+            this.pathCopiedTimeout = setTimeout(() => this.wasPathCopied.set(false), PATH_COPIED_FEEDBACK_DURATION_MS)
+        } catch {
+            this.wasPathCopied.set(false)
+        }
     }
🤖 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
`@visualization/app/codeCharta/features/nodeContextMenu/components/nodeContextMenu/nodeContextMenu.component.ts`
around lines 104 - 115, Wrap the navigator.clipboard.writeText call in copyPath
in a try/catch so clipboard write rejections are handled; only call
this.wasPathCopied.set(true) and start/clear this.pathCopiedTimeout on
successful write, and in the catch block avoid changing the copied state and log
or surface the error (e.g., via console.error or existing logger). Ensure you
still guard for a missing node via menuNode() and keep using
pathWithoutRootSegment(node) and PATH_COPIED_FEEDBACK_DURATION_MS as before.

@sonarqubecloud

Copy link
Copy Markdown

@ChristianHuehn
ChristianHuehn merged commit faea768 into main Jun 12, 2026
8 checks passed
@ChristianHuehn
ChristianHuehn deleted the feature/redesign-context-menu branch June 12, 2026 20:28
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