Skip to content

group mutual texts with or without id#95

Open
mir-dvlp wants to merge 1 commit intotolgee:mainfrom
mir-dvlp:mutual-texts-group
Open

group mutual texts with or without id#95
mir-dvlp wants to merge 1 commit intotolgee:mainfrom
mir-dvlp:mutual-texts-group

Conversation

@mir-dvlp
Copy link
Copy Markdown
Contributor

@mir-dvlp mir-dvlp commented Mar 24, 2026

Issue Source: #92

Summary by CodeRabbit

Release Notes

  • New Features
    • Node list items now display duplicate count badges, providing clear visibility into how many identical entries exist for each node
    • Ability to perform simultaneous operations on multiple grouped or similar nodes within a single workflow action
    • Enhanced node grouping and organization logic throughout connection-related workflows for improved efficiency and user experience

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 24, 2026

Walkthrough

This pull request introduces multi-node support to handle duplicate nodes as grouped selections. The changes include adding a duplicatesCount badge to NodeRow, refactoring the Connect view to accept and update multiple nodes simultaneously, implementing a grouping mechanism in the Index view to organize selected nodes by key characteristics, and extending ListItem to mutate all grouped nodes in parallel.

Changes

Cohort / File(s) Summary
Route Definition
src/ui/views/routes.ts
Updated "connect" route parameter from single node: NodeInfo to nodes: NodeInfo[], affecting all dependent route handling.
Display & Badges
src/ui/components/NodeList/NodeRow.tsx
Added optional duplicatesCount prop to render an additional badge displaying duplicate count as ${duplicatesCount}x when value exceeds 1.
Multi-node Connection Handler
src/ui/views/Connect/Connect.tsx
Changed from single node prop to nodes array; added early guard for empty arrays; extended all mutation operations (handleConnect, handleRemoveConnection) to update all provided nodes via .map().
Duplicate Grouping Logic
src/ui/views/Index/Index.tsx
Introduced groupedSelection via useMemo that groups nodes by trimmed characters and connection properties; updated NodeList to receive grouped items with individual node, grouped nodes, and duplicate count.
Batch Node Mutations
src/ui/views/Index/ListItem.tsx
Extended props to accept groupedNodes and duplicatesCount; introduced effectiveNodes memoization to dynamically switch mutation targets; updated key/namespace mutations to apply across all effective nodes when grouped; refactored connect-route navigation to pass grouped nodes.

Sequence Diagram

sequenceDiagram
    participant User
    participant Index
    participant GroupingLogic as Grouping<br/>(useMemo)
    participant ListItem
    participant ConnectView
    participant MutationAPI

    User->>Index: Select nodes with duplicates
    Index->>GroupingLogic: groupedSelection (group by chars + key/ns)
    GroupingLogic-->>Index: {node, groupedNodes, duplicatesCount}
    Index->>ListItem: items with groupedNodes
    
    alt User modifies key/namespace
        ListItem->>MutationAPI: Mutate key/ns for all effectiveNodes
        MutationAPI-->>ListItem: Update complete
    else User clicks connect
        ListItem->>ConnectView: setRoute("connect", {nodes: effectiveNodes})
        ConnectView->>MutationAPI: Update all nodes (translation, key, ns, connected)
        MutationAPI-->>ConnectView: Batch update complete
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

  • Adjustments to devmode #82: Simultaneously modifies NodeRow.tsx to add CSS class to namespace element, overlapping with this PR's addition of duplicatesCount badge rendering to the same component.

Suggested reviewers

  • ZuzanaOdstrcilova
  • Anty0
  • JanCizmar

Poem

🐰 Duplicates once scattered far and wide,
Now grouped together, side by side!
With badges bright and counts so true,
Many nodes bend to one review.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'group mutual texts with or without id' accurately describes the main change: implementing grouping functionality for duplicate/connected text nodes, with or without an associated key identifier.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

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

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/ui/views/Index/Index.tsx`:
- Around line 97-103: The grouping uses node.characters.trim() which merges
distinct-whitespace text nodes; change groupedSelection's grouping to use the
raw node.characters (no trim) so groupKey is built from node.characters (and
existing connected branch `connected:${node.key}::${node.ns ??
""}::${node.characters}`) to ensure grouping matches the rest of the pipeline
that expects exact characters; update the useMemo grouping logic that references
selection and node.characters accordingly.

In `@src/ui/views/Index/ListItem.tsx`:
- Around line 132-144: handleNsChange currently mutates the objects in
effectiveNodes in place; instead, avoid in-place mutation by creating a new
array of node objects and passing that to setNodesDataMutation.mutate (use
effectiveNodes.map to return {...current, key: keyName, ns: value}) and call
setNamespace(value) as before; do not assign to current.key/current.ns, and if
you need to suppress any follow-up effect that reacts to effectiveNodes updates,
track the user-intended namespace change in local component state or a ref
(e.g., a pendingNs ref) so downstream grouping/effect logic ignores transient
UI-only changes until the mutation succeeds.
- Around line 36-42: The current useMemo that creates effectiveNodes from
groupedNodes and node causes silent bulk-overwrites of all members in a group
when debounced edits occur; change the logic in ListItem so that groupedNodes
are only expanded into per-row edits when the user explicitly edits this row
(e.g., an edit flag/state set in your onChange/onBlur handlers) or only
bulk-sync when every member in groupedNodes already has identical key and ns
values to node before applying the fan-out; update the effectiveNodes
computation and any code paths that currently write back to grouped members (the
groupedNodes, node and effectiveNodes logic and the edit handlers used in this
component) to gate the fan-out behind that explicit-edit flag or the equality
check.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 036d0780-09a9-4c4f-bd7f-fac4f51a7343

📥 Commits

Reviewing files that changed from the base of the PR and between cad63ff and 061368c.

📒 Files selected for processing (5)
  • src/ui/components/NodeList/NodeRow.tsx
  • src/ui/views/Connect/Connect.tsx
  • src/ui/views/Index/Index.tsx
  • src/ui/views/Index/ListItem.tsx
  • src/ui/views/routes.ts

Comment on lines +97 to +103
const groupedSelection = useMemo(() => {
const groups = new Map<string, NodeInfo[]>();
selection.forEach((node) => {
const textKey = node.characters.trim();
const groupKey = node.connected
? `connected:${node.key}::${node.ns ?? ""}::${textKey}`
: `unconnected:${textKey}`;
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.

⚠️ Potential issue | 🟠 Major

Don't group whitespace-distinct texts together.

Using trim() here makes "Hello" and "Hello " share one row, but the rest of the pipeline still treats characters as exact text. That lets bulk edits/connects fan out across nodes that can still conflict on push. Group on the raw characters value, or normalize whitespace consistently everywhere.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/ui/views/Index/Index.tsx` around lines 97 - 103, The grouping uses
node.characters.trim() which merges distinct-whitespace text nodes; change
groupedSelection's grouping to use the raw node.characters (no trim) so groupKey
is built from node.characters (and existing connected branch
`connected:${node.key}::${node.ns ?? ""}::${node.characters}`) to ensure
grouping matches the rest of the pipeline that expects exact characters; update
the useMemo grouping logic that references selection and node.characters
accordingly.

Comment on lines +36 to +42
const effectiveNodes = useMemo(
() =>
groupedNodes && groupedNodes.length > 0
? groupedNodes
: [node],
[groupedNodes, node]
);
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.

⚠️ Potential issue | 🔴 Critical

Avoid bulk-rewriting grouped nodes on initial render.

With the new grouping in src/ui/views/Index/Index.tsx, unconnected duplicates can share a row even when their draft key/ns values already differ. This effect will then overwrite every node in the group to match the first node's keyName after the debounce, without any user edit in this row. Please gate the fan-out behind an explicit edit, or only bulk-sync groups whose current key/ns already agree.

Also applies to: 75-92

🧰 Tools
🪛 GitHub Check: Static check 🪲

[failure] 37-37:
Replace ⏎······groupedNodes·&&·groupedNodes.length·>·0⏎········?·groupedNodes⏎········:·[node] with ·(groupedNodes·&&·groupedNodes.length·>·0·?·groupedNodes·:·[node])

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/ui/views/Index/ListItem.tsx` around lines 36 - 42, The current useMemo
that creates effectiveNodes from groupedNodes and node causes silent
bulk-overwrites of all members in a group when debounced edits occur; change the
logic in ListItem so that groupedNodes are only expanded into per-row edits when
the user explicitly edits this row (e.g., an edit flag/state set in your
onChange/onBlur handlers) or only bulk-sync when every member in groupedNodes
already has identical key and ns values to node before applying the fan-out;
update the effectiveNodes computation and any code paths that currently write
back to grouped members (the groupedNodes, node and effectiveNodes logic and the
edit handlers used in this component) to gate the fan-out behind that
explicit-edit flag or the equality check.

Comment on lines +132 to 144
const handleNsChange = () => (value: string) => {
setNamespace(value);
setNodesDataMutation.mutate({
nodes: [{ ...node, key: keyName, ns: value }],
nodes: effectiveNodes.map((current) => ({
...current,
key: keyName,
ns: value,
})),
});
effectiveNodes.forEach((current) => {
current.key = keyName;
current.ns = value;
});
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.

⚠️ Potential issue | 🟠 Major

Don't mutate effectiveNodes in place.

These objects come from props/selection state. Rewriting current.key and current.ns locally makes the UI look updated before setNodesDataMutation succeeds, and it can leak stale values into later grouping/effect logic. Keep the array immutable and suppress the follow-up effect with local state/ref instead.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/ui/views/Index/ListItem.tsx` around lines 132 - 144, handleNsChange
currently mutates the objects in effectiveNodes in place; instead, avoid
in-place mutation by creating a new array of node objects and passing that to
setNodesDataMutation.mutate (use effectiveNodes.map to return {...current, key:
keyName, ns: value}) and call setNamespace(value) as before; do not assign to
current.key/current.ns, and if you need to suppress any follow-up effect that
reacts to effectiveNodes updates, track the user-intended namespace change in
local component state or a ref (e.g., a pendingNs ref) so downstream
grouping/effect logic ignores transient UI-only changes until the mutation
succeeds.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant