Skip to content

feat: unified simulator Start/Stop with auto-debug#625

Merged
thiagoralves merged 3 commits into
developmentfrom
feat/unified-simulator-flow
Feb 25, 2026
Merged

feat: unified simulator Start/Stop with auto-debug#625
thiagoralves merged 3 commits into
developmentfrom
feat/unified-simulator-flow

Conversation

@thiagoralves
Copy link
Copy Markdown
Contributor

@thiagoralves thiagoralves commented Feb 25, 2026

Summary

  • Unified simulator flow: When "Simulator" is selected as target, clicking Start now triggers build → simulate → debug in one click. Clicking Stop disconnects the debugger then stops the simulator. Build and Debug buttons are disabled with helpful tooltips ("Use Start to build and run" / "Use Start to debug").
  • Skips redundant compilation: The debugger no longer calls compileForDebugger or performs MD5 verification for the simulator target, since the firmware was just built and loaded in the same action.
  • Fixes sidebar tooltips: Radix Tooltip positioning refs were lost through non-ref-forwarding button wrappers, making all sidebar tooltips invisible. Wrapping trigger children in a native DOM element gives Radix an anchor to position against.
  • Fixes debugger disconnect race condition: A straggling poll could fire after disconnect cleared the connection type, producing a spurious "Debugger connection lost: No connection type stored" error. An early return now silences polls that arrive after intentional disconnect.

Test plan

  • Select "Simulator" as target — verify Build (download) and Debug buttons are disabled and show updated tooltips on hover
  • Click Start — verify project builds, simulator starts, and debugger connects automatically (console shows the full pipeline)
  • Click Stop — verify debugger disconnects and simulator stops cleanly, no spurious error messages in console
  • Click Start again after Stop — verify full build→simulate→debug cycle runs again
  • Switch to a non-simulator target — verify Build and Debug buttons re-enable with original tooltips
  • Hover over all sidebar buttons (Search, Toolbox, Compile, Start, Debug, Exit) — verify tooltips appear correctly
  • Connect debugger to real hardware, then disconnect — verify no "connection lost: No connection type stored" message appears

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Debugger can automatically attach after a successful simulator build.
  • Improvements

    • Buttons, icons and tooltips clarified for simulator vs runtime modes.
    • Simulator start/stop and debug flows more reliably manage auto-connect and connection state.
    • Centralized debugger handling improves responsiveness when opening the debugger.
  • Bug Fixes

    • Tooltip trigger layout fixed to use full width.
    • Attempts to use the debugger now return an explicit "Debugger not connected" status.

… click

When "Simulator" is selected as the target, the Build and Debug buttons
are now disabled. Clicking Start triggers the full pipeline: save project,
compile, load firmware into the simulator, and auto-connect the debugger
— skipping the redundant compileForDebugger and MD5 verification steps.
Clicking Stop disconnects the debugger first, then stops the simulator.

Also fixes two pre-existing bugs:
- Sidebar activity bar tooltips were invisible because Radix Tooltip's
  positioning ref was lost through non-ref-forwarding button wrappers.
  Wrapping children in a native DOM element gives Radix an anchor.
- Debugger disconnect race condition: a straggling poll could fire after
  disconnect cleared the connection type, producing a spurious
  "connection lost" error. An early return now silences this.

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

coderabbitai Bot commented Feb 25, 2026

Walkthrough

Adds centralized debugger-session utilities, integrates them into workspace activity bar to support simulator auto-attach and lifecycle handling, adds a null-guard in the main IPC debugger variables handler, and makes a small tooltip DOM wrapper change.

Changes

Cohort / File(s) Summary
IPC Debugger Handler
src/main/modules/ipc/main.ts
Added an early null-guard in handleDebuggerGetVariablesList to return { success: false, error: 'Debugger not connected' } when debuggerConnectionType is null.
Renderer — Activity Bar / Simulator Debugging
src/renderer/components/_organisms/workspace-activity-bar/default.tsx
Replaced inline debug-tree/index/FB mapping logic with new debugger-session helpers; added pendingSimulatorDebugRef, connectDebuggerAfterBuild, async/await refactors for build/firmware flows, simulator-specific start/stop and cleanup that auto-connects/disconnects debugger, and updated UI/tooltip states for simulator mode.
Renderer — Tooltip Layout
src/renderer/components/_molecules/workspace-activity-bar/tooltip-button.tsx
Wrapped TooltipTrigger child in a full-width <div className="w-full"> to stabilize trigger layout.
New shared utilities
src/renderer/utils/debugger-session.ts
Added centralized debugger utilities: disconnectDebugger, buildVariableIndexMap, buildDebugVariableTreeMap, buildFbInstanceMap, connectAndActivateDebugger, plus related types and results.

Sequence Diagram

sequenceDiagram
    participant User
    participant Renderer
    participant BuildProcess
    participant DebugFile
    participant DebuggerService
    participant IPCMain

    User->>Renderer: Click Start (simulate + auto-debug)
    Renderer->>BuildProcess: Trigger build/verify (await)
    BuildProcess-->>Renderer: Build success (debug file produced)
    Renderer->>DebugFile: Parse debug file, build index & trees
    DebugFile-->>Renderer: indexMap, treeMap, fbDebugInstancesMap
    Renderer->>DebuggerService: connectAndActivateDebugger(params)
    DebuggerService->>IPCMain: Establish IPC connection to simulator debugger
    IPCMain-->>DebuggerService: Connection established
    DebuggerService-->>Renderer: Activated (indices, selection, UI shown)
    User->>Renderer: Click Stop
    Renderer->>DebuggerService: disconnectDebugger()
    DebuggerService->>IPCMain: Clear connection / state
    IPCMain-->>DebuggerService: Disconnected
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

feature, enhancement

Suggested reviewers

  • vmleroy

Poem

🐰 I hopped through trees of indexes bright,
I stitched the map by moonlit byte.
When firmware wakes and simulators hum,
I nudge the debugger—let's have some fun!
Then tidy up and hop back home.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description includes a clear summary of changes and a detailed test plan, but lacks several required DOD checklist items (code completeness, self-review, test coverage metrics, integration/E2E test status) specified in the template. Complete the DOD checklist with status checks for code review, test coverage percentage, integration tests, and end-to-end test results to meet repository standards.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: unified simulator Start/Stop with auto-debug' accurately describes the main objective of the changeset: unified simulator flow with automatic debugging on Start and proper cleanup on Stop.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/unified-simulator-flow

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

🧹 Nitpick comments (2)
src/renderer/components/_organisms/workspace-activity-bar/default.tsx (2)

657-716: Dead code: simulator block in handleDebuggerClick is now unreachable.

The early return at Line 659 (if (isCurrentBoardSimulator) return) makes the if (isSimulatorTarget(currentBoardInfo)) block at Lines 697–716 (and the connectionType = 'simulator' assignment) permanently unreachable. This should be removed.

♻️ Proposed cleanup
  const handleDebuggerClick = async () => {
    // Simulator target uses the unified Start/Stop flow instead
    if (isCurrentBoardSimulator) return

    const { workspace, project, deviceDefinitions, workspaceActions, consoleActions, deviceActions } =
      useOpenPLCStore.getState()

    // ... (disconnect path unchanged) ...

    let targetIpAddress: string | undefined
    let connectionType: 'tcp' | 'rtu' | 'websocket' | 'simulator' = 'tcp'
    // ...

-     if (isSimulatorTarget(currentBoardInfo)) {
-       // Check if simulator has firmware loaded
-       const running = await (window.bridge.simulatorIsRunning as () => Promise<boolean>)()
-       if (!running) {
-         const response = await showDebuggerMessage(
-           'warning',
-           'Simulator Empty',
-           'No firmware is running on the simulator. Would you like to build and upload the project first?',
-           ['Build & Upload', 'Cancel'],
-         )
-         if (response === 0) {
-           setIsDebuggerProcessing(false)
-           void verifyAndCompile()
-           return
-         } else {
-           setIsDebuggerProcessing(false)
-           return
-         }
-       }
-       connectionType = 'simulator'
-     } else if (isRuntimeTarget) {
+     if (isRuntimeTarget) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderer/components/_organisms/workspace-activity-bar/default.tsx` around
lines 657 - 716, The simulator-specific branch in handleDebuggerClick is
unreachable because of the early guard if (isCurrentBoardSimulator) return;
remove the dead code: delete the isSimulatorTarget(currentBoardInfo) conditional
block (the simulatorIsRunning check, showDebuggerMessage flow, and any returns
inside it) and remove any assignment of connectionType = 'simulator' so the
function no longer contains unused simulator logic; keep the initial
isCurrentBoardSimulator guard in place and ensure no other references (e.g.,
connectionType, simulator-specific variables) remain unused after removal.

467-655: Substantial code duplication between connectDebuggerAfterBuild and handleMd5Verification.

The index-map building (Lines 490–522), debug-tree construction (Lines 524–575), and FB-instance-map building (Lines 577–636) in connectDebuggerAfterBuild are near-identical copies of the same logic in handleMd5Verification (Lines 1176–1341). A bug fix or enhancement in one will likely not be applied to the other.

Consider extracting a shared helper, e.g.:

const buildDebugContext = (
  project: typeof useOpenPLCStore.getState()['project'],
  parsed: ReturnType<typeof parseDebugFile>,
): { indexMap: Map<string, number>; treeMap: Map<string, DebugTreeNode>; fbInstancesMap: Map<string, FbInstanceInfo[]> } => {
  // ... shared implementation ...
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderer/components/_organisms/workspace-activity-bar/default.tsx` around
lines 467 - 655, The connectDebuggerAfterBuild function duplicates the
index-map, debug-tree and FB-instance-map logic already present in
handleMd5Verification; extract that shared logic into a helper (suggested name
buildDebugContext) that accepts the current project and parsed debug file
(parseDebugFile result) and returns { indexMap, treeMap, fbInstancesMap } (Maps
of debug indexes, DebugTreeNode map and FbInstanceInfo[] map). Move the
traversal/lookup logic that uses buildDebugTree, findGlobalVariableIndex,
findVariableIndexWithFallback, StandardFunctionBlocks and the
project.data.pous/instances iteration into buildDebugContext, have it also
perform the complex count and silent-tree build catches, and then replace the
duplicated blocks in connectDebuggerAfterBuild and handleMd5Verification with
calls to buildDebugContext and subsequent calls to
workspaceActions.setDebugVariableTree, workspaceActions.setFbDebugInstances,
workspaceActions.setDebugVariableIndexes and
workspaceActions.setFbSelectedInstance as before.
🤖 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/renderer/components/_organisms/workspace-activity-bar/default.tsx`:
- Around line 344-352: handleSimulatorControl sets
pendingSimulatorDebugRef.current = true before calling verifyAndCompile(), but
handleRequest can return early on validation failures without resetting that
flag; update the early-exit paths in handleRequest (the CPP/Python validation
failure branches referenced around where it returns before firmware handling) to
explicitly set pendingSimulatorDebugRef.current = false before returning so the
flag is cleared on all non-firmware/early-abort paths, leaving the existing
.catch() that clears it for firmware errors and preserving
connectDebuggerAfterBuild behavior when the build succeeds.

---

Nitpick comments:
In `@src/renderer/components/_organisms/workspace-activity-bar/default.tsx`:
- Around line 657-716: The simulator-specific branch in handleDebuggerClick is
unreachable because of the early guard if (isCurrentBoardSimulator) return;
remove the dead code: delete the isSimulatorTarget(currentBoardInfo) conditional
block (the simulatorIsRunning check, showDebuggerMessage flow, and any returns
inside it) and remove any assignment of connectionType = 'simulator' so the
function no longer contains unused simulator logic; keep the initial
isCurrentBoardSimulator guard in place and ensure no other references (e.g.,
connectionType, simulator-specific variables) remain unused after removal.
- Around line 467-655: The connectDebuggerAfterBuild function duplicates the
index-map, debug-tree and FB-instance-map logic already present in
handleMd5Verification; extract that shared logic into a helper (suggested name
buildDebugContext) that accepts the current project and parsed debug file
(parseDebugFile result) and returns { indexMap, treeMap, fbInstancesMap } (Maps
of debug indexes, DebugTreeNode map and FbInstanceInfo[] map). Move the
traversal/lookup logic that uses buildDebugTree, findGlobalVariableIndex,
findVariableIndexWithFallback, StandardFunctionBlocks and the
project.data.pous/instances iteration into buildDebugContext, have it also
perform the complex count and silent-tree build catches, and then replace the
duplicated blocks in connectDebuggerAfterBuild and handleMd5Verification with
calls to buildDebugContext and subsequent calls to
workspaceActions.setDebugVariableTree, workspaceActions.setFbDebugInstances,
workspaceActions.setDebugVariableIndexes and
workspaceActions.setFbSelectedInstance as before.

ℹ️ Review info

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between a554dfe and fe2870e.

📒 Files selected for processing (3)
  • src/main/modules/ipc/main.ts
  • src/renderer/components/_molecules/workspace-activity-bar/tooltip-button.tsx
  • src/renderer/components/_organisms/workspace-activity-bar/default.tsx

Comment on lines +344 to +352
.then(async (result) => {
if (result.success) {
setSimulatorRunning(true)
addLog({ id: crypto.randomUUID(), level: 'info', message: 'Simulator is running.' })

// Auto-connect debugger after build when triggered from the Start button
if (pendingSimulatorDebugRef.current) {
pendingSimulatorDebugRef.current = false
await connectDebuggerAfterBuild()
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 | 🟡 Minor

pendingSimulatorDebugRef.current not reset if handleRequest returns early before firmware.

handleSimulatorControl sets pendingSimulatorDebugRef.current = true before calling void verifyAndCompile(). If compilation aborts early inside handleRequest (e.g., CPP/Python validation failure at Lines 248–251 or 938–941), neither of those paths resets the flag. The firmware-path .catch() at Line 364 correctly resets it, but pre-firmware failure paths do not.

While this is benign today (Compile is disabled for simulator targets and a subsequent Start press unconditionally sets the flag back to true), explicitly resetting it in the early-exit paths of handleRequest would make the lifecycle easier to reason about.

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

In `@src/renderer/components/_organisms/workspace-activity-bar/default.tsx` around
lines 344 - 352, handleSimulatorControl sets pendingSimulatorDebugRef.current =
true before calling verifyAndCompile(), but handleRequest can return early on
validation failures without resetting that flag; update the early-exit paths in
handleRequest (the CPP/Python validation failure branches referenced around
where it returns before firmware handling) to explicitly set
pendingSimulatorDebugRef.current = false before returning so the flag is cleared
on all non-firmware/early-abort paths, leaving the existing .catch() that clears
it for firmware errors and preserving connectDebuggerAfterBuild behavior when
the build succeeds.

Extract 5 helper functions (disconnectDebugger, buildVariableIndexMap,
buildDebugVariableTreeMap, buildFbInstanceMap, connectAndActivateDebugger)
into a new debugger-session.ts module. Both the simulator flow and the
non-simulator MD5 verification flow now call the same helpers, removing
~300 lines of duplicated logic and 2 inline normalizeTypeString redefinitions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
src/renderer/components/_organisms/workspace-activity-bar/default.tsx (4)

286-287: ⚠️ Potential issue | 🟡 Minor

Remove debug console.log statements.

🐛 Proposed fix
-    console.log('processado:', processedProjectData)
-    console.log('original:', projectData)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderer/components/_organisms/workspace-activity-bar/default.tsx` around
lines 286 - 287, Remove the two debug console.log calls that print
processedProjectData and projectData in the Workspace Activity Bar component
(remove the lines referencing console.log('processado:', processedProjectData)
and console.log('original:', projectData)); if you need to preserve diagnostic
information, replace them with a proper logger call or conditional debug-only
logging tied to an existing logging utility, otherwise just delete the
statements from default.tsx.

20-34: ⚠️ Potential issue | 🟠 Major

Type declarations are splitting the import block — move them after all imports.

The CppPouData and ProjectDataWithCpp type declarations at lines 20–28 are sandwiched between two import groups (lines 1–18 and lines 29–34). This violates the simple-import-sort rule and will fail the ESLint check.

♻️ Proposed fix
 import { wrapUnsupportedComments } from '@root/utils/PLC/wrap-unsupported-comments'
 import { parsePlcStatus } from '@root/utils/plc-status'
 import { addPythonLocalVariables } from '@root/utils/python/addPythonLocalVariables'
 import { generateSTCode } from '@root/utils/python/generateSTCode'
 import { injectPythonCode } from '@root/utils/python/injectPythonCode'
 import { useEffect, useRef, useState } from 'react'
 
+type CppPouData = {
+  name: string
+  code: string
+  variables: unknown[]
+}
+
+type ProjectDataWithCpp = PLCProjectData & {
+  originalCppPous?: CppPouData[]
+}
+
 import { ... } from '../../_molecules/...'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderer/components/_organisms/workspace-activity-bar/default.tsx` around
lines 20 - 34, The type declarations CppPouData and ProjectDataWithCpp are
placed between import groups and break the simple-import-sort rule; move the two
type blocks (CppPouData and ProjectDataWithCpp) so they appear after all import
statements (i.e., below the last import such as injectPythonCode) to keep
imports contiguous and satisfy ESLint; ensure the moved types remain in the same
module scope and update any local references to CppPouData / ProjectDataWithCpp
if necessary.

185-185: ⚠️ Potential issue | 🟡 Minor

Remove debug console.log — line 185 is a leftover artifact.

🐛 Proposed fix
-      console.log(processedProjectData)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderer/components/_organisms/workspace-activity-bar/default.tsx` at
line 185, Remove the leftover debug statement console.log(processedProjectData)
from the component (the stray call to console.log with processedProjectData); if
you need persistent telemetry or debug info, replace it with the app logging
utility (e.g., processLogger.debug or the established logger) or conditionally
log behind a DEBUG flag, otherwise just delete the console.log line.

558-613: 🛠️ Refactor suggestion | 🟠 Major

Dead simulator code path in handleDebuggerClick should be removed.

The early return at line 560 (if (isCurrentBoardSimulator) return) makes the entire simulator-specific branch at lines 594–613 unreachable. This is stale logic left over from before the unified Start/Stop flow.

♻️ Proposed fix — remove unreachable simulator branch
 const handleDebuggerClick = async () => {
   // Simulator target uses the unified Start/Stop flow instead
   if (isCurrentBoardSimulator) return

   const { workspace, project, deviceDefinitions, workspaceActions, consoleActions, deviceActions } =
     useOpenPLCStore.getState()

   if (workspace.isDebuggerVisible) {
     await disconnectDebugger(workspaceActions)
     return
   }

   ...

-      if (isSimulatorTarget(currentBoardInfo)) {
-        // Check if simulator has firmware loaded
-        const running = await (window.bridge.simulatorIsRunning as () => Promise<boolean>)()
-        if (!running) {
-          const response = await showDebuggerMessage(
-            'warning',
-            'Simulator Empty',
-            'No firmware is running on the simulator. Would you like to build and upload the project first?',
-            ['Build & Upload', 'Cancel'],
-          )
-          if (response === 0) {
-            // Trigger full build, then restart debugger flow
-            setIsDebuggerProcessing(false)
-            void verifyAndCompile()
-            return
-          } else {
-            setIsDebuggerProcessing(false)
-            return
-          }
-        }
-        connectionType = 'simulator'
-      } else if (isRuntimeTarget) {
+      if (isRuntimeTarget) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderer/components/_organisms/workspace-activity-bar/default.tsx` around
lines 558 - 613, The early return via isCurrentBoardSimulator makes the whole
simulator-specific branch in handleDebuggerClick unreachable; remove the entire
if (isSimulatorTarget(currentBoardInfo)) { ... } block (including calls to
window.bridge.simulatorIsRunning, showDebuggerMessage and the verifyAndCompile
restart path) inside handleDebuggerClick, and then tidy up any resulting unused
variables or imports (e.g., showDebuggerMessage, verifyAndCompile, or
simulatorIsRunning references) to avoid dead code or linter errors.
♻️ Duplicate comments (1)
src/renderer/components/_organisms/workspace-activity-bar/default.tsx (1)

248-251: pendingSimulatorDebugRef.current not reset on CPP validation failure early-return.

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

In `@src/renderer/components/_organisms/workspace-activity-bar/default.tsx` around
lines 248 - 251, When validationFailed triggers the early return in the block
that currently calls setIsCompiling(false), also clear the pending simulator
debug reference by resetting pendingSimulatorDebugRef.current (e.g., set to
null/undefined) before returning so leftover pending debug state is not
retained; update the validationFailed branch that contains setIsCompiling to
explicitly clear pendingSimulatorDebugRef.current (and any related
timeout/cleanup if applicable) prior to the return.
🤖 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/renderer/components/_organisms/workspace-activity-bar/default.tsx`:
- Around line 1295-1302: The DebuggerButton's disabled styling uses a hardcoded
class string and omits the sibling/child hover suppression in
disabledButtonClass; update the className logic on DebuggerButton (inside
TooltipSidebarWrapperButton) to use the same disabledButtonClass when
(isDebuggerProcessing || isCurrentBoardSimulator) is true (or add the missing
[&>*:first-child]:hover:bg-transparent to the existing class string) so the
inner element's hover state is disabled consistently like DownloadButton.

In `@src/renderer/utils/debugger-session.ts`:
- Around line 276-278: The exported function getWorkspaceActions is unused;
remove its export and definition to eliminate dead code (delete the
getWorkspaceActions function that returns
useOpenPLCStore.getState().workspaceActions), or if it was intended for external
use add explicit usage/imports where needed or document it; locate the symbol
getWorkspaceActions and the call to useOpenPLCStore.getState().workspaceActions
to remove or wire it up accordingly.

---

Outside diff comments:
In `@src/renderer/components/_organisms/workspace-activity-bar/default.tsx`:
- Around line 286-287: Remove the two debug console.log calls that print
processedProjectData and projectData in the Workspace Activity Bar component
(remove the lines referencing console.log('processado:', processedProjectData)
and console.log('original:', projectData)); if you need to preserve diagnostic
information, replace them with a proper logger call or conditional debug-only
logging tied to an existing logging utility, otherwise just delete the
statements from default.tsx.
- Around line 20-34: The type declarations CppPouData and ProjectDataWithCpp are
placed between import groups and break the simple-import-sort rule; move the two
type blocks (CppPouData and ProjectDataWithCpp) so they appear after all import
statements (i.e., below the last import such as injectPythonCode) to keep
imports contiguous and satisfy ESLint; ensure the moved types remain in the same
module scope and update any local references to CppPouData / ProjectDataWithCpp
if necessary.
- Line 185: Remove the leftover debug statement
console.log(processedProjectData) from the component (the stray call to
console.log with processedProjectData); if you need persistent telemetry or
debug info, replace it with the app logging utility (e.g., processLogger.debug
or the established logger) or conditionally log behind a DEBUG flag, otherwise
just delete the console.log line.
- Around line 558-613: The early return via isCurrentBoardSimulator makes the
whole simulator-specific branch in handleDebuggerClick unreachable; remove the
entire if (isSimulatorTarget(currentBoardInfo)) { ... } block (including calls
to window.bridge.simulatorIsRunning, showDebuggerMessage and the
verifyAndCompile restart path) inside handleDebuggerClick, and then tidy up any
resulting unused variables or imports (e.g., showDebuggerMessage,
verifyAndCompile, or simulatorIsRunning references) to avoid dead code or linter
errors.

---

Duplicate comments:
In `@src/renderer/components/_organisms/workspace-activity-bar/default.tsx`:
- Around line 248-251: When validationFailed triggers the early return in the
block that currently calls setIsCompiling(false), also clear the pending
simulator debug reference by resetting pendingSimulatorDebugRef.current (e.g.,
set to null/undefined) before returning so leftover pending debug state is not
retained; update the validationFailed branch that contains setIsCompiling to
explicitly clear pendingSimulatorDebugRef.current (and any related
timeout/cleanup if applicable) prior to the return.

ℹ️ Review info

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between fe2870e and c820a50.

📒 Files selected for processing (2)
  • src/renderer/components/_organisms/workspace-activity-bar/default.tsx
  • src/renderer/utils/debugger-session.ts

Comment on lines +1295 to 1302
<TooltipSidebarWrapperButton tooltipContent={isCurrentBoardSimulator ? 'Use Start to debug' : 'Debugger'}>
<DebuggerButton
onClick={() => void handleDebuggerClick()}
disabled={isDebuggerProcessing}
disabled={isDebuggerProcessing || isCurrentBoardSimulator}
isActive={isDebuggerVisible}
className={cn(isDebuggerProcessing && 'cursor-not-allowed opacity-50')}
className={cn((isDebuggerProcessing || isCurrentBoardSimulator) && 'cursor-not-allowed opacity-50')}
/>
</TooltipSidebarWrapperButton>
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 | 🟡 Minor

DebuggerButton disabled styling is inconsistent with disabledButtonClass.

Line 1300 uses 'cursor-not-allowed opacity-50' directly, missing the [&>*:first-child]:hover:bg-transparent class present in disabledButtonClass (line 95). The hover state will remain active on the inner element, inconsistent with DownloadButton at line 1261.

🐛 Proposed fix
-          className={cn((isDebuggerProcessing || isCurrentBoardSimulator) && 'cursor-not-allowed opacity-50')}
+          className={cn((isDebuggerProcessing || isCurrentBoardSimulator) && disabledButtonClass)}
📝 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
<TooltipSidebarWrapperButton tooltipContent={isCurrentBoardSimulator ? 'Use Start to debug' : 'Debugger'}>
<DebuggerButton
onClick={() => void handleDebuggerClick()}
disabled={isDebuggerProcessing}
disabled={isDebuggerProcessing || isCurrentBoardSimulator}
isActive={isDebuggerVisible}
className={cn(isDebuggerProcessing && 'cursor-not-allowed opacity-50')}
className={cn((isDebuggerProcessing || isCurrentBoardSimulator) && 'cursor-not-allowed opacity-50')}
/>
</TooltipSidebarWrapperButton>
<TooltipSidebarWrapperButton tooltipContent={isCurrentBoardSimulator ? 'Use Start to debug' : 'Debugger'}>
<DebuggerButton
onClick={() => void handleDebuggerClick()}
disabled={isDebuggerProcessing || isCurrentBoardSimulator}
isActive={isDebuggerVisible}
className={cn((isDebuggerProcessing || isCurrentBoardSimulator) && disabledButtonClass)}
/>
</TooltipSidebarWrapperButton>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderer/components/_organisms/workspace-activity-bar/default.tsx` around
lines 1295 - 1302, The DebuggerButton's disabled styling uses a hardcoded class
string and omits the sibling/child hover suppression in disabledButtonClass;
update the className logic on DebuggerButton (inside
TooltipSidebarWrapperButton) to use the same disabledButtonClass when
(isDebuggerProcessing || isCurrentBoardSimulator) is true (or add the missing
[&>*:first-child]:hover:bg-transparent to the existing class string) so the
inner element's hover state is disabled consistently like DownloadButton.

Comment thread src/renderer/utils/debugger-session.ts Outdated
- Remove redundant `projectPous` param from `buildFbInstanceMap` (always
  identical to `pous`)
- Catch rejected `verifyAndCompile()` promise so `pendingSimulatorDebugRef`
  doesn't stick true on IPC failure
- Remove unused `getWorkspaceActions` function and `useOpenPLCStore` import
- Remove unnecessary `WorkspaceActions` re-export

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@thiagoralves thiagoralves merged commit 8f775a1 into development Feb 25, 2026
8 of 9 checks passed
@thiagoralves thiagoralves deleted the feat/unified-simulator-flow branch February 25, 2026 21:20
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

♻️ Duplicate comments (2)
src/renderer/components/_organisms/workspace-activity-bar/default.tsx (2)

1302-1302: ⚠️ Potential issue | 🟡 Minor

Use the shared disabled style constant for DebuggerButton too.

This still bypasses disabledButtonClass, so the inner hover suppression is inconsistent with the other disabled buttons.

🔧 Proposed fix
-          className={cn((isDebuggerProcessing || isCurrentBoardSimulator) && 'cursor-not-allowed opacity-50')}
+          className={cn((isDebuggerProcessing || isCurrentBoardSimulator) && disabledButtonClass)}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderer/components/_organisms/workspace-activity-bar/default.tsx` at
line 1302, The DebuggerButton is using an inline className expression which
bypasses the shared disabled styles; update the DebuggerButton render to apply
the shared disabledButtonClass constant instead of the inline
"(isDebuggerProcessing || isCurrentBoardSimulator) && 'cursor-not-allowed
opacity-50'" expression so the button uses the same disabledButtonClass
(including hover suppression) as other disabled buttons—locate the
DebuggerButton JSX in default.tsx and replace the inline className logic with a
conditional that adds disabledButtonClass when (isDebuggerProcessing ||
isCurrentBoardSimulator).

450-453: ⚠️ Potential issue | 🟡 Minor

pendingSimulatorDebugRef can stay true when Start aborts before firmware load.

Line 450 sets the pending flag, but early-abort paths (e.g., validation/save exits) do not reliably clear it. Please clear it when compile does not actually start, not only in promise rejection handlers.

🔧 Proposed fix (explicit “compile started” contract)
-  const handleRequest = () => {
+  const handleRequest = (): boolean => {
@@
       if (validationFailed) {
         setIsCompiling(false)
-        return
+        return false
       }
@@
     window.bridge.runCompileProgram(
@@
     )
+    return true
   }

-  const verifyAndCompile = async () => {
+  const verifyAndCompile = async (): Promise<boolean> => {
     if (editingState === 'unsaved') {
       const res = await saveProject({ data: projectData, meta: projectMeta }, deviceDefinitions)
       if (!res.success) {
-        return
+        return false
       }

-      handleRequest()
+      return handleRequest()
     } else {
-      handleRequest()
+      return handleRequest()
     }
   }
@@
-        pendingSimulatorDebugRef.current = true
-        verifyAndCompile().catch(() => {
-          pendingSimulatorDebugRef.current = false
-        })
+        pendingSimulatorDebugRef.current = true
+        const started = await verifyAndCompile().catch(() => false)
+        if (!started) {
+          pendingSimulatorDebugRef.current = false
+        }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderer/components/_organisms/workspace-activity-bar/default.tsx` around
lines 450 - 453, pendingSimulatorDebugRef.current is set true before calling
verifyAndCompile(), but verifyAndCompile may early-abort (validation/save exits)
and never actually start compilation, leaving the flag stuck; change the
contract so verifyAndCompile() communicates whether compilation actually started
(e.g., return Promise<boolean> or provide an onStart callback) and only set
pendingSimulatorDebugRef.current = true when verifyAndCompile indicates
compilation began (or clear it immediately if the returned value/flag is false);
update the callsite around pendingSimulatorDebugRef and the verifyAndCompile
implementation to use that boolean/onStart signal and keep the existing .catch()
clearing as a fallback.
🤖 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/renderer/utils/debugger-session.ts`:
- Around line 26-32: Make disconnectDebugger perform cleanup regardless of
whether window.bridge.debuggerDisconnect() rejects: wrap the await
window.bridge.debuggerDisconnect() in a try/catch (or use try { await ... }
catch (e) { /* log via console or process logger but do not rethrow */ }) and
move the workspaceActions mutations (setDebuggerVisible, setDebuggerTargetIp,
setDebugForcedVariables, clearFbDebugContext) into a finally block (or after the
catch) so state is reset even if the bridge call fails; reference the
disconnectDebugger function and workspaceActions methods and ensure errors from
window.bridge.debuggerDisconnect are swallowed/logged but not propagated.

---

Duplicate comments:
In `@src/renderer/components/_organisms/workspace-activity-bar/default.tsx`:
- Line 1302: The DebuggerButton is using an inline className expression which
bypasses the shared disabled styles; update the DebuggerButton render to apply
the shared disabledButtonClass constant instead of the inline
"(isDebuggerProcessing || isCurrentBoardSimulator) && 'cursor-not-allowed
opacity-50'" expression so the button uses the same disabledButtonClass
(including hover suppression) as other disabled buttons—locate the
DebuggerButton JSX in default.tsx and replace the inline className logic with a
conditional that adds disabledButtonClass when (isDebuggerProcessing ||
isCurrentBoardSimulator).
- Around line 450-453: pendingSimulatorDebugRef.current is set true before
calling verifyAndCompile(), but verifyAndCompile may early-abort
(validation/save exits) and never actually start compilation, leaving the flag
stuck; change the contract so verifyAndCompile() communicates whether
compilation actually started (e.g., return Promise<boolean> or provide an
onStart callback) and only set pendingSimulatorDebugRef.current = true when
verifyAndCompile indicates compilation began (or clear it immediately if the
returned value/flag is false); update the callsite around
pendingSimulatorDebugRef and the verifyAndCompile implementation to use that
boolean/onStart signal and keep the existing .catch() clearing as a fallback.

ℹ️ Review info

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between c820a50 and f61bda0.

📒 Files selected for processing (2)
  • src/renderer/components/_organisms/workspace-activity-bar/default.tsx
  • src/renderer/utils/debugger-session.ts

Comment on lines +26 to +32
export async function disconnectDebugger(workspaceActions: WorkspaceActions): Promise<void> {
await window.bridge.debuggerDisconnect()
workspaceActions.setDebuggerVisible(false)
workspaceActions.setDebuggerTargetIp(null)
workspaceActions.setDebugForcedVariables(new Map())
workspaceActions.clearFbDebugContext()
}
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

Make disconnect cleanup best-effort even when bridge disconnect fails.

If window.bridge.debuggerDisconnect() rejects, cleanup state mutations are skipped. That can leave debugger UI/state stale and cause unhandled promise rejections in fire-and-forget call sites.

🔧 Proposed fix
 export async function disconnectDebugger(workspaceActions: WorkspaceActions): Promise<void> {
-  await window.bridge.debuggerDisconnect()
-  workspaceActions.setDebuggerVisible(false)
-  workspaceActions.setDebuggerTargetIp(null)
-  workspaceActions.setDebugForcedVariables(new Map())
-  workspaceActions.clearFbDebugContext()
+  try {
+    await window.bridge.debuggerDisconnect()
+  } catch {
+    // best-effort disconnect: still clear local debugger state
+  } finally {
+    workspaceActions.setDebuggerVisible(false)
+    workspaceActions.setDebuggerTargetIp(null)
+    workspaceActions.setDebugForcedVariables(new Map())
+    workspaceActions.clearFbDebugContext()
+  }
 }
📝 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
export async function disconnectDebugger(workspaceActions: WorkspaceActions): Promise<void> {
await window.bridge.debuggerDisconnect()
workspaceActions.setDebuggerVisible(false)
workspaceActions.setDebuggerTargetIp(null)
workspaceActions.setDebugForcedVariables(new Map())
workspaceActions.clearFbDebugContext()
}
export async function disconnectDebugger(workspaceActions: WorkspaceActions): Promise<void> {
try {
await window.bridge.debuggerDisconnect()
} catch {
// best-effort disconnect: still clear local debugger state
} finally {
workspaceActions.setDebuggerVisible(false)
workspaceActions.setDebuggerTargetIp(null)
workspaceActions.setDebugForcedVariables(new Map())
workspaceActions.clearFbDebugContext()
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderer/utils/debugger-session.ts` around lines 26 - 32, Make
disconnectDebugger perform cleanup regardless of whether
window.bridge.debuggerDisconnect() rejects: wrap the await
window.bridge.debuggerDisconnect() in a try/catch (or use try { await ... }
catch (e) { /* log via console or process logger but do not rethrow */ }) and
move the workspaceActions mutations (setDebuggerVisible, setDebuggerTargetIp,
setDebugForcedVariables, clearFbDebugContext) into a finally block (or after the
catch) so state is reset even if the bridge call fails; reference the
disconnectDebugger function and workspaceActions methods and ensure errors from
window.bridge.debuggerDisconnect are swallowed/logged but not propagated.

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