Skip to content

fix: enable renaming of servers and remote devices in project tree#581

Merged
thiagoralves merged 1 commit into
developmentfrom
fix/server-remote-device-rename
Feb 3, 2026
Merged

fix: enable renaming of servers and remote devices in project tree#581
thiagoralves merged 1 commit into
developmentfrom
fix/server-remote-device-rename

Conversation

@thiagoralves
Copy link
Copy Markdown
Contributor

@thiagoralves thiagoralves commented Feb 2, 2026

Summary

  • Fixes the error "Only POU or datatype files can be renamed" when attempting to rename servers or remote devices from the project tree context menu
  • Fixes the error "Error saving file. File with name [name] does not exist" that appeared after renaming or deleting servers/remote devices

Changes

  • Add rename methods to serverActions and remoteDeviceActions in the shared slice
  • Update handleRenameFile in project-tree component to handle servers and remote devices
  • Fix save error by marking workspace as unsaved instead of calling saveFile() (servers/remote devices are stored in project.json, not as separate files in the file slice)

Test plan

  • Create a server in a project
  • Right-click on the server in the project tree and select "Rename"
  • Verify the rename works without error toasts
  • Create a remote device in a project
  • Right-click on the remote device in the project tree and select "Rename"
  • Verify the rename works without error toasts
  • Delete a server and verify no error toast appears
  • Delete a remote device and verify no error toast appears
  • Save the project and verify changes are persisted

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features
    • Users can now rename servers and remote devices in the project tree
    • Rename validation prevents duplicate names and requires valid naming
    • Changes reflect across tabs and editor views

The rename functionality was missing for servers and remote devices.
When attempting to rename these items via the context menu, users
received the error "Only POU or datatype files can be renamed".

Changes:
- Add rename methods to serverActions and remoteDeviceActions in shared slice
- Update handleRenameFile in project-tree to handle servers and remote devices
- Update error messages to reflect the new supported item types
- Fix saveFile error for servers/remote devices by marking workspace as
  unsaved instead (they are saved with project.json, not as separate files)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 2, 2026

Walkthrough

The changes extend rename functionality to servers and remote devices in the project tree. The component layer integrates rename actions into the tree UI, while the store layer implements validation logic, state updates, and error handling for rename workflows, alongside refactoring delete operations to mark workspaces as unsaved.

Changes

Cohort / File(s) Summary
Project Tree Component
src/renderer/components/_molecules/project-tree/index.tsx
Extended rename support by adding renameServer and renameRemoteDevice to destructured action hooks. Updated validation logic and handleRenameFile to invoke server/remote device rename functions with rollback on failure.
Shared Store Slice
src/renderer/store/slices/shared/index.ts
Implemented rename methods for servers and remote devices with validation (collision detection, non-empty names, existence checks). Refactored delete operations to mark workspace as unsaved instead of calling saveFile. Propagates renames across UI state and tab/editor names.

Sequence Diagram

sequenceDiagram
    participant User
    participant ProjectTree as ProjectTree<br/>(Component)
    participant Store as Store<br/>(Shared Slice)
    participant State as State<br/>(Projects)

    User->>ProjectTree: Initiates rename<br/>(contextmenu action)
    ProjectTree->>ProjectTree: Validate leaf type<br/>(server/remoteDevice)
    ProjectTree->>Store: Call renameServer/<br/>renameRemoteDevice
    Store->>Store: Validate non-empty name
    Store->>Store: Check for name collisions
    Store->>Store: Verify item exists
    alt Validation Success
        Store->>State: Update item name
        Store->>State: Update tab/editor names
        Store->>State: Update selected leaf<br/>if current item
        Store->>State: Mark workspace<br/>as 'unsaved'
        Store->>ProjectTree: Return success
        ProjectTree->>User: Confirm rename
    else Validation Failure
        Store->>Store: Show error toast
        Store->>ProjectTree: Return error
        ProjectTree->>User: Show error
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 A hop through the code, servers now bend
When asked for new names—no more to pretend!
Validation ensures no collisions confound,
While remote devices rename all around,
State marks itself unsaved, safe and sound! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Description check ⚠️ Warning The description covers the core changes and includes a detailed test plan, but lacks required template sections like issue references and acceptance criteria. Add issue reference (#issue_number), Jira task link if applicable, and complete the DOD checklist with test results and acceptance criteria verification.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: enabling rename functionality for servers and remote devices in the project tree.
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 docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/server-remote-device-rename

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: 1

🤖 Fix all issues with AI agents
In `@src/renderer/store/slices/shared/index.ts`:
- Around line 933-997: The rename handler currently ignores the return value of
getState().projectActions.updateServerName(serverName, newServerName); capture
its ProjectResponse (e.g., const resp =
await/getState()?.projectActions.updateServerName(...)) and check resp.ok before
proceeding; if resp.ok is false, show a failure toast and return
Promise.resolve({ success: false, error: { title: 'Error renaming server',
description: resp.message || 'Rename failed' } }); only call
getState().tabsActions.updateTabName, getState().editorActions.updateEditorName,
workspaceActions.setSelectedProjectTreeLeaf and
workspaceActions.setEditingState('unsaved') when resp.ok is true so the UI/state
stays consistent.
🧹 Nitpick comments (1)
src/renderer/components/_molecules/project-tree/index.tsx (1)

461-485: Consider conditionally hiding the Duplicate option for servers and remote devices.

The popover menu shows the Duplicate option for all leaf types, including servers and remote devices. However, clicking Duplicate on these items triggers an error toast since handleDuplicateFile only supports POUs and datatypes. This could be confusing for users.

💡 Suggested approach to conditionally filter options
 const popoverOptions = useMemo(() => {
-    return [
+    const options = [
       {
         name: 'Rename',
         onClick: () => {
           setIsEditing(true)
         },
         icon: <PencilIcon className='h-4 w-4 stroke-brand dark:stroke-brand-light' />,
       },
-      {
+    ]
+    if (isAPou || isDatatype) {
+      options.push({
         name: 'Duplicate',
         onClick: () => {
           void handleDuplicateFile()
         },
         icon: <DuplicateIcon className='h-4 w-4 stroke-brand dark:stroke-brand-light' />,
-      },
-      {
+      })
+    }
+    options.push({
         name: 'Delete',
         onClick: () => {
           handleDeleteFile()
         },
         icon: <CloseIcon className='h-4 w-4 stroke-brand dark:stroke-brand-light' />,
-      },
-    ]
-  }, [handleDeleteFile, handleDuplicateFile, setIsEditing])
+    })
+    return options
+  }, [handleDeleteFile, handleDuplicateFile, setIsEditing, isAPou, isDatatype])

Comment on lines +933 to 997
rename: (serverName, newServerName) => {
if (serverName === newServerName) {
return Promise.resolve({ success: false })
}

if (!(newServerName ?? '').trim()) {
toast({ title: 'Error', description: 'New name must be non-empty.', variant: 'fail' })
return Promise.resolve({
success: false,
error: {
title: 'Error renaming server',
description: 'New name must be non-empty.',
},
})
}

const servers = getState().project.data.servers
const isServerNameAlreadyUsed = servers?.some((s) => s.name === newServerName)
if (isServerNameAlreadyUsed) {
toast({
title: 'Error renaming server',
description: `Server with name ${newServerName} already exists.`,
variant: 'fail',
})
return Promise.resolve({
success: false,
error: {
title: 'Error renaming server',
description: `Server with name ${newServerName} already exists.`,
},
})
}

const server = servers?.find((s) => s.name === serverName)
if (!server) {
toast({
title: 'Error',
description: `Server with name ${serverName} not found.`,
variant: 'fail',
})
return Promise.resolve({
success: false,
error: {
title: 'Error renaming server',
description: `Server with name ${serverName} not found.`,
},
})
}

getState().projectActions.updateServerName(serverName, newServerName)
getState().tabsActions.updateTabName(serverName, newServerName)
getState().editorActions.updateEditorName(serverName, newServerName)

const selectedProjectTreeLeaf = getState().workspace.selectedProjectTreeLeaf
if (selectedProjectTreeLeaf.label === serverName) {
getState().workspaceActions.setSelectedProjectTreeLeaf({
label: newServerName,
type: selectedProjectTreeLeaf.type,
})
}

// Mark workspace as unsaved - servers are saved with project.json
getState().workspaceActions.setEditingState('unsaved')
return Promise.resolve({ success: true })
},
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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, let's find where projectActions is defined
echo "=== Searching for updateServerName definition ===" 
rg -n 'updateServerName' --type=ts src/renderer/store/

echo -e "\n=== Searching for projectActions definition ===" 
rg -n 'projectActions' --type=ts src/renderer/store/ | head -20

Repository: Autonomy-Logic/openplc-editor

Length of output: 2702


🏁 Script executed:

#!/bin/bash
# Let's examine the actual implementation of updateServerName
echo "=== Implementation of updateServerName in slice.ts ===" 
sed -n '1252,1270p' src/renderer/store/slices/project/slice.ts

echo -e "\n=== Type definition from types.ts ===" 
sed -n '325p' src/renderer/store/slices/project/types.ts

echo -e "\n=== Context: checking what projectResponseSchema is ===" 
rg -n 'projectResponseSchema' src/renderer/store/slices/project/types.ts | head -5

Repository: Autonomy-Logic/openplc-editor

Length of output: 1555


🏁 Script executed:

#!/bin/bash
# Check the complete projectResponseSchema definition
echo "=== projectResponseSchema definition ===" 
sed -n '144,155p' src/renderer/store/slices/project/types.ts

# Check if rename function handles the response from updateServerName
echo -e "\n=== Checking if updateServerName response is handled ===" 
sed -n '975,997p' src/renderer/store/slices/shared/index.ts

# Check the return type structure
echo -e "\n=== Check other similar rename implementations ===" 
rg -A 25 'pouActions\.rename.*=.*\(' src/renderer/store/slices/shared/index.ts | head -40

Repository: Autonomy-Logic/openplc-editor

Length of output: 1450


🏁 Script executed:

#!/bin/bash
# Let's look for pouActions.rename to see how it handles updatePouName response
echo "=== pouActions.rename implementation ===" 
sed -n '475,510p' src/renderer/store/slices/shared/index.ts

echo -e "\n=== datatypeActions.rename implementation ===" 
sed -n '800,835p' src/renderer/store/slices/shared/index.ts

Repository: Autonomy-Logic/openplc-editor

Length of output: 2663


🏁 Script executed:

#!/bin/bash
# Let's check the complete updateServerName implementation to see all error cases
echo "=== Full updateServerName implementation ===" 
sed -n '1252,1280p' src/renderer/store/slices/project/slice.ts

echo -e "\n=== Are response errors checked in rename function? ===" 
sed -n '980,997p' src/renderer/store/slices/shared/index.ts

Repository: Autonomy-Logic/openplc-editor

Length of output: 2074


Check the return value of updateServerName and handle failures properly.

The rename function calls getState().projectActions.updateServerName(serverName, newServerName) but ignores its return value. The method returns a ProjectResponse with ok: boolean and can fail with messages like 'Server not found' or 'Name already exists'. When updateServerName fails, the function continues anyway, updating tabs, editor, and workspace state, then returns { success: true }. This creates inconsistent state and misrepresents failure as success to the caller.

Store the result and check response.ok before proceeding with subsequent state updates, similar to how pouActions.rename verifies the renamed POU exists in state after the update.

🤖 Prompt for AI Agents
In `@src/renderer/store/slices/shared/index.ts` around lines 933 - 997, The rename
handler currently ignores the return value of
getState().projectActions.updateServerName(serverName, newServerName); capture
its ProjectResponse (e.g., const resp =
await/getState()?.projectActions.updateServerName(...)) and check resp.ok before
proceeding; if resp.ok is false, show a failure toast and return
Promise.resolve({ success: false, error: { title: 'Error renaming server',
description: resp.message || 'Rename failed' } }); only call
getState().tabsActions.updateTabName, getState().editorActions.updateEditorName,
workspaceActions.setSelectedProjectTreeLeaf and
workspaceActions.setEditingState('unsaved') when resp.ok is true so the UI/state
stays consistent.

@thiagoralves thiagoralves requested a review from Copilot February 2, 2026 22:29
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR enables renaming of servers and remote devices in the project tree, addressing errors that previously occurred when attempting to rename or delete these entities.

Changes:

  • Added rename methods to serverActions and remoteDeviceActions with validation for duplicate names and empty inputs
  • Updated project tree component to support renaming servers and remote devices
  • Fixed save errors by marking workspace as unsaved instead of calling saveFile() for servers/remote devices (they're stored in project.json)

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
src/renderer/store/slices/shared/index.ts Added rename methods for servers and remote devices with validation logic, and updated delete methods to mark workspace as unsaved
src/renderer/components/_molecules/project-tree/index.tsx Updated rename handler to support servers and remote devices, and improved error messages

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@thiagoralves thiagoralves merged commit 3f1e505 into development Feb 3, 2026
3 of 11 checks passed
@thiagoralves thiagoralves deleted the fix/server-remote-device-rename branch February 3, 2026 01:01
@coderabbitai coderabbitai Bot mentioned this pull request Apr 28, 2026
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