Skip to content

ADFA-4419: Remote peer editor decoration API - #1459

Merged
Daniel-ADFA merged 14 commits into
stagefrom
feat/ADFA-4419-remote-peer-editor-decoration
Aug 12, 2026
Merged

ADFA-4419: Remote peer editor decoration API#1459
Daniel-ADFA merged 14 commits into
stagefrom
feat/ADFA-4419-remote-peer-editor-decoration

Conversation

@Daniel-ADFA

@Daniel-ADFA Daniel-ADFA commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

Summary

Host-side support for the pair-programming plugin (ADFA-4419): lets the external .cgp pair plugin render remote collaborators in the editor and drive shared editing entirely through plugin-api. The pair plugin itself ships separately; this PR adds only the host contract + bridge it talks to.

Daniel-ADFA and others added 8 commits May 28, 2026 23:42
Add IdeEditorService.addRemotePeerMarker/removeRemotePeerMarker/clearRemotePeerMarkers as default-implemented (backward-compatible) methods so a plugin can draw a remote collaborator's caret/badge inside the editor.

Backed by a new EditorDecorationManager + RemotePeerMarkerWindow (an EditorPopupWindow overlay that tracks scroll via FEATURE_SCROLL_AS_CONTENT) in the app module; EditorProviderImpl resolves the live editor via EditorHandlerActivity.getEditorForFile and marshals onto the main thread, clearing markers on dispose. IdeEditorServiceImpl exposes read-gated overrides and the parallel EditorProvider contract methods.

The new interface methods are default-implemented so this is an additive, non-breaking change for the generated plugin-api lib and existing implementers. Consumed by the Pair pair-programming plugin.
…orations

Rename EditorDecorationManager -> PeerPresenceOverlayManager and extract a
focused PeerPresenceProvider interface out of the broad EditorProvider, so the
pair-programming peer-cursor overlay (floating named badges) reads as a distinct
concern from the generic EditorDecorationProvider (additive color spans) added
in #1448 (ADFA-4436).

Host-internal only: no plugin-api contract changed and the merged rainbow-
brackets plugin is unaffected. Verified with :app:compileV8DebugKotlin.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Claude Code Review

This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.

Tip: disable this comment in your organization's Code Review settings.

@Daniel-ADFA Daniel-ADFA changed the title ADFA-4419: Remote peer editor decoration (pair programming) ADFA-4419: Remote peer editor decoration API Jun 28, 2026
@Daniel-ADFA

Daniel-ADFA commented Jun 28, 2026

Copy link
Copy Markdown
Contributor Author

@claude review

@Daniel-ADFA
Daniel-ADFA requested a review from a team June 28, 2026 23:52
@coderabbitai

coderabbitai Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough
  • Add host-side support for remote peer cursor and presence rendering through plugin-api.
  • Add IdeEditorService APIs to show, hide, and clear peer cursors.
  • Add PeerCursorOverlayManager and PeerCursorWindow for labeled, colored, scroll-aware cursor overlays.
  • Add IdeProjectService.openProject(File) with permission, path, activity, and failure handling.
  • Improve plugin loading diagnostics and remove failed plugin artifacts.
  • Pass activityProvider to project service instances.

Risks and best-practice concerns

  • The change has a high review scope across editor, plugin, and project-management code.
  • Remote cursor rendering depends on editor state and UI-thread disposal.
  • openProject can recreate the foreground activity and change project state.
  • Plugin loading now deletes copied artifacts after load failure.
  • The external .cgp pair-programming plugin is not included, so end-to-end behavior requires a compatible plugin.

Walkthrough

The PR adds peer-cursor overlays, exposes an asynchronous openProject API, strengthens project path validation, and records plugin load failures for install verification.

Changes

Peer Cursor Overlay Feature

Layer / File(s) Summary
Plugin API contracts
plugin-api/src/main/kotlin/.../IdeServices.kt, plugin-api/api/plugin-api.api
Adds peer-cursor methods to IdeEditorService and updates the API declarations. Existing service declarations are reformatted without semantic changes.
Editor service delegation
plugin-manager/src/main/kotlin/.../IdeEditorServiceImpl.kt, plugin-manager/src/main/kotlin/.../PluginManager.kt
Adds peer-presence contracts, permission checks, delegation, and no-provider fallbacks.
Overlay manager and window
app/src/main/java/.../PeerCursorOverlayManager.kt
Adds per-file, per-peer cursor windows with editor anchoring, scrolling support, position clamping, directional indicators, and contrast-aware labels.
Editor provider wiring
app/src/main/java/.../EditorProviderImpl.kt
Resolves active editors, delegates cursor operations on the main thread, and clears overlays during disposal.

openProject Plugin API Implementation

Layer / File(s) Summary
Project service API contract
plugin-api/src/main/kotlin/.../IdeServices.kt, plugin-api/api/plugin-api.api
Adds openProject(projectDir: File): Boolean to IdeProjectService and its API metadata.
Project opening implementation
plugin-manager/src/main/kotlin/.../IdeProjectServiceImpl.kt
Validates permissions, canonical paths, directories, and activity state. It persists project state and recreates the activity on the UI thread.
Plugin context wiring
plugin-manager/src/main/kotlin/.../PluginManager.kt
Passes the activity provider to both IdeProjectServiceImpl construction paths.

Plugin Load Error Tracking

Layer / File(s) Summary
Load failure recording
plugin-manager/src/main/kotlin/.../PluginManager.kt
Records per-plugin load errors, preserves coroutine cancellation, and exposes getLoadError.
Install-time verification
app/src/main/java/.../PluginRepositoryImpl.kt
Verifies plugin availability after loading, deletes unavailable copied artifacts, and throws the recorded load error.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Plugin as IdeEditorServiceImpl
  participant Provider as EditorProviderImpl
  participant Overlay as PeerCursorOverlayManager
  participant Window as PeerCursorWindow
  Plugin->>Provider: showPeerCursor(file, line, column, peerId, peerName, peerColor)
  Provider->>Overlay: addMarker(...)
  Overlay->>Window: update(...)
  Window->>Window: show cursor badge
Loading
sequenceDiagram
  participant Caller as Plugin
  participant Service as IdeProjectServiceImpl
  participant Activity as ActivityProvider
  participant State as ProjectPreferences
  Caller->>Service: openProject(projectDir)
  Service->>Service: validate permission and canonical path
  Service->>Activity: obtain foreground activity
  Service->>State: persist project state
  Service->>Activity: recreate activity
Loading

Possibly related PRs

Suggested reviewers: dara-abijo-adfa

Poem

I’m a rabbit with a cursor bright,
I hop through projects day and night.
Peer badges bloom in colors new,
Failed plugins leave a clearer clue.
Open paths, then fields renew—
A tidy burrow built for you.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.70% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the main change: a remote peer editor decoration API for host-side pair-programming support.
Description check ✅ Passed The description accurately relates the changes to host-side support for a pair-programming plugin and remote collaborator rendering.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/ADFA-4419-remote-peer-editor-decoration

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

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🤖 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
`@app/src/main/java/com/itsaky/androidide/activities/editor/PeerPresenceOverlayManager.kt`:
- Around line 122-125: The clamping logic in PeerPresenceOverlayManager’s
position calculation does not handle the exact-fit case because when maxX is 0
it falls back to rawX, allowing the overlay to render off-screen; update the x
computation so the clamp applies whenever the editor width is known, including
when label.measuredWidth equals boundEditor.width, and keep the existing
rawX.coerceIn(0, maxX) behavior for all non-negative maxX values.

In
`@app/src/main/java/com/itsaky/androidide/repositories/PluginRepositoryImpl.kt`:
- Around line 147-154: The failure path in installPluginFromFile() leaves a
broken upgraded plugin on disk after the old version has already been removed,
so adjust the replace flow to avoid half-installed upgrades. Use the existing
load check around manager.loadPlugins()/manager.getPlugin(pluginId) to either
delay uninstalling the current plugin until the new package is proven loadable,
or restore the previous package on failure. In the failure branch that throws
IllegalStateException, also delete the copied finalFile so future
PluginRepositoryImpl loads do not keep retrying the bad artifact.

In
`@plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/core/PluginManager.kt`:
- Around line 279-284: The load path in PluginManager.loadPlugins is swallowing
coroutine cancellation by catching all Exception around loadPlugin, so a
cancelled job is treated like a plugin failure instead of stopping execution.
Narrow the catch in the loadPlugin/result block to let CancellationException
pass through unchanged, and only wrap genuine plugin load errors in
Result.failure before calling recordLoadFailure.

In
`@plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/services/IdeEditorServiceImpl.kt`:
- Around line 283-292: `hidePeerCursor` and `clearPeerCursors` in
`IdeEditorServiceImpl` are unnecessarily gated by `ensureFileAccessible(file)`,
which blocks overlay cleanup when the file/tab is no longer open. Remove the
accessibility check from these cleanup-only paths while keeping `requireRead()`
and the downstream `editorProvider.hidePeerCursor(...)` /
`editorProvider.clearPeerCursors(...)` calls so peer overlays can still be
dismissed by file key.

In
`@plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/services/IdeProjectServiceImpl.kt`:
- Around line 97-105: Use the validated canonical project path in
IdeProjectServiceImpl.openProject and apply the existing PathValidator before
switching projects. The current flow checks containment with
isUnderProjectsDir(projectDir) but later persists the original path, so update
the openProject logic to reuse the validated canonical target and run it through
PathValidator like getProjectByPath does before calling the project
switch/selection code.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e0348b09-b91a-4370-9cb1-9c72e245fff6

📥 Commits

Reviewing files that changed from the base of the PR and between c14733b and ea2293a.

📒 Files selected for processing (7)
  • app/src/main/java/com/itsaky/androidide/activities/editor/PeerPresenceOverlayManager.kt
  • app/src/main/java/com/itsaky/androidide/app/EditorProviderImpl.kt
  • app/src/main/java/com/itsaky/androidide/repositories/PluginRepositoryImpl.kt
  • plugin-api/src/main/kotlin/com/itsaky/androidide/plugins/services/IdeServices.kt
  • plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/core/PluginManager.kt
  • plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/services/IdeEditorServiceImpl.kt
  • plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/services/IdeProjectServiceImpl.kt

Comment thread app/src/main/java/com/itsaky/androidide/repositories/PluginRepositoryImpl.kt Outdated
- PeerPresenceOverlayManager: clamp peer badge on exact-fit width (maxX >= 0)
- PluginManager.loadPlugins: rethrow CancellationException instead of recording
  cancellation as a plugin load failure
- IdeEditorServiceImpl: don't gate hidePeerCursor/clearPeerCursors on file
  accessibility, so overlay cleanup still works after a tab closes
- IdeProjectServiceImpl.openProject: use the validated canonical path and run it
  through PathValidator before switching projects
- PluginRepositoryImpl: delete the broken artifact when an upgraded plugin fails
  to load, so loadPlugins() doesn't keep retrying it
- Drop PairTrace / [HOST] dev-trace Log.d (kept warn/error diagnostics)
Daniel-ADFA and others added 2 commits July 12, 2026 23:01
…e-peer-editor-decoration

# Conflicts:
#	plugin-api/src/main/kotlin/com/itsaky/androidide/plugins/services/IdeServices.kt
#	plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/core/PluginManager.kt
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 2

🧹 Nitpick comments (1)
plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/services/IdeProjectServiceImpl.kt (1)

5-5: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Replace android.util.Log with SLF4J logging.

IdeProjectServiceImpl still imports android.util.Log, and openProject() emits unstructured string logs with Log.w()/Log.e(). Use the module’s standard LoggerFactory logger with structured placeholders for these messages.

🤖 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
`@plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/services/IdeProjectServiceImpl.kt`
at line 5, Replace the android.util.Log usage in IdeProjectServiceImpl,
including the Log.w()/Log.e() calls in openProject(), with the module-standard
LoggerFactory SLF4J logger. Preserve the existing log levels and messages while
converting interpolated values to structured placeholders and passing arguments
separately.

Source: Coding guidelines

🤖 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
`@plugin-api/src/main/kotlin/com/itsaky/androidide/plugins/services/IdeServices.kt`:
- Around line 35-51: The changed files
plugin-api/src/main/kotlin/com/itsaky/androidide/plugins/services/IdeServices.kt
lines 35-51 and
plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/services/IdeProjectServiceImpl.kt
lines 95-125 require formatting updates: run Spotless or otherwise reformat both
complete files using tabs and LF line endings, and replace any newly introduced
em dashes or en dashes in IdeProjectServiceImpl with ASCII punctuation.

In
`@plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/services/IdeProjectServiceImpl.kt`:
- Around line 119-130: Update MainActivity.openProject so it no longer writes
ProjectManagerImpl.projectPath or GeneralPreferences.lastOpenedProject before
activity.recreate(). Move those assignments into the editor activity’s
onNewIntent/setup path that performs the actual reload, ensuring project state
changes only when recreation is successfully handled.

---

Nitpick comments:
In
`@plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/services/IdeProjectServiceImpl.kt`:
- Line 5: Replace the android.util.Log usage in IdeProjectServiceImpl, including
the Log.w()/Log.e() calls in openProject(), with the module-standard
LoggerFactory SLF4J logger. Preserve the existing log levels and messages while
converting interpolated values to structured placeholders and passing arguments
separately.
🪄 Autofix

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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e4e5cb3d-f162-4373-9267-4f582a290457

📥 Commits

Reviewing files that changed from the base of the PR and between a80a8fa and 3b9d039.

📒 Files selected for processing (8)
  • app/src/main/java/com/itsaky/androidide/activities/editor/PeerPresenceOverlayManager.kt
  • app/src/main/java/com/itsaky/androidide/app/EditorProviderImpl.kt
  • app/src/main/java/com/itsaky/androidide/repositories/PluginRepositoryImpl.kt
  • plugin-api/api/plugin-api.api
  • plugin-api/src/main/kotlin/com/itsaky/androidide/plugins/services/IdeServices.kt
  • plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/core/PluginManager.kt
  • plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/services/IdeEditorServiceImpl.kt
  • plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/services/IdeProjectServiceImpl.kt
🚧 Files skipped from review as they are similar to previous changes (5)
  • app/src/main/java/com/itsaky/androidide/repositories/PluginRepositoryImpl.kt
  • plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/services/IdeEditorServiceImpl.kt
  • app/src/main/java/com/itsaky/androidide/app/EditorProviderImpl.kt
  • app/src/main/java/com/itsaky/androidide/activities/editor/PeerPresenceOverlayManager.kt
  • plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/core/PluginManager.kt

Comment thread plugin-api/src/main/kotlin/com/itsaky/androidide/plugins/services/IdeServices.kt Outdated
spotlessApply reformatting across the seven files this branch touched,
plus the three lints ktlint cannot auto-fix: expand the wildcard import
in PluginManager, move the orphaned KDoc onto delegatingEditorProvider,
and rename INSTANCE/Loader to instance/loader per property-naming.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 4

🤖 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
`@plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/core/PluginManager.kt`:
- Around line 1255-1269: In PluginManager.kt, wrap activityProvider in a
delegating provider analogous to delegatingEditorProvider so
IdeProjectServiceImpl receives a provider that reflects later
setActivityProvider() updates. Apply this wrapper at both project-service
construction sites: lines 1255-1269 and 1505-1519; update each
IdeProjectServiceImpl call without changing unrelated providers or validation
logic.

In
`@plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/services/IdeProjectServiceImpl.kt`:
- Around line 101-102: Update the warning in IdeProjectServiceImpl’s
project-opening validation to use only a stable failure code or exception class,
removing both requested and configured project paths; also update the
catch/logging block around lines 133-135 to omit e.message and log only
non-sensitive exception metadata.
- Around line 176-178: Update the allowed-path check in the project
authorization method to compare canonical Path values, or require either an
exact root match or a separator-delimited descendant match. Ensure sibling paths
such as similarly prefixed directories are rejected while valid descendants
remain authorized, preserving the behavior used by getProjectByPath.
- Around line 52-57: Update IdeProjectServiceImpl query methods at
plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/services/IdeProjectServiceImpl.kt:52-57,
65-70, and 83-88 to catch only the expected recoverable provider exception, log
the failure with the fallback, and retain the existing fallback values. At
plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/services/IdeProjectServiceImpl.kt:133-135,
replace android.util.Log usage in openProject with the established SLF4J logger.
🪄 Autofix

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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 2de320a2-51cf-4d59-955f-11b66c5f62b5

📥 Commits

Reviewing files that changed from the base of the PR and between 3b9d039 and 13864e2.

📒 Files selected for processing (7)
  • app/src/main/java/com/itsaky/androidide/activities/editor/PeerPresenceOverlayManager.kt
  • app/src/main/java/com/itsaky/androidide/app/EditorProviderImpl.kt
  • app/src/main/java/com/itsaky/androidide/repositories/PluginRepositoryImpl.kt
  • plugin-api/src/main/kotlin/com/itsaky/androidide/plugins/services/IdeServices.kt
  • plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/core/PluginManager.kt
  • plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/services/IdeEditorServiceImpl.kt
  • plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/services/IdeProjectServiceImpl.kt
🚧 Files skipped from review as they are similar to previous changes (5)
  • app/src/main/java/com/itsaky/androidide/app/EditorProviderImpl.kt
  • app/src/main/java/com/itsaky/androidide/repositories/PluginRepositoryImpl.kt
  • plugin-api/src/main/kotlin/com/itsaky/androidide/plugins/services/IdeServices.kt
  • plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/services/IdeEditorServiceImpl.kt
  • app/src/main/java/com/itsaky/androidide/activities/editor/PeerPresenceOverlayManager.kt

@coderabbitai coderabbitai Bot left a comment

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.

Caution

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

⚠️ Outside diff range comments (2)
app/src/main/java/com/itsaky/androidide/activities/editor/PeerCursorOverlayManager.kt (2)

50-56: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Remove the empty per-file map after the last peer is removed.

Line [54] removes only the peer entry. When it is the last entry, markers retains an empty map and the file path until clear or clearAll runs. Remove the outer entry when byPeer.isEmpty().

Proposed fix
 fun removeMarker(
 	file: File,
 	peerId: String,
 ): Boolean {
-	val removed = markers[file.absolutePath]?.remove(peerId) ?: return false
+	val path = file.absolutePath
+	val byPeer = markers[path] ?: return false
+	val removed = byPeer.remove(peerId) ?: return false
 	removed.dismiss()
+	if (byPeer.isEmpty()) {
+		markers.remove(path)
+	}
 	return true
 }
🤖 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
`@app/src/main/java/com/itsaky/androidide/activities/editor/PeerCursorOverlayManager.kt`
around lines 50 - 56, Update removeMarker in PeerCursorOverlayManager to remove
the file’s outer markers entry after removing a peer when the per-file map is
empty. Preserve the current false return for missing files or peer IDs and the
dismiss/true behavior for successful removals.

69-130: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Unregister each window before dismissing it.

EditorPopupWindow.dismiss() does not unregister its scroll receiver. Call unregister() on every cleanup path before dismiss(); otherwise replaced windows can remain registered with the old editor and retain stale callbacks.

🤖 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
`@app/src/main/java/com/itsaky/androidide/activities/editor/PeerCursorOverlayManager.kt`
around lines 69 - 130, Update every cleanup path that removes a PeerCursorWindow
to call its unregister() method before dismiss(). Ensure replaced and otherwise
discarded windows are unregistered before dismissal so no stale scroll receiver
or callbacks remain attached to the old editor.

Source: Coding guidelines

🧹 Nitpick comments (2)
app/src/main/java/com/itsaky/androidide/activities/editor/PeerCursorOverlayManager.kt (2)

20-32: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add KDoc for the public overlay API.

PeerCursorOverlayManager, addMarker, removeMarker, clear, and clearAll do not document their threading requirements, return values, or dismissal side effects. Add concise KDoc before merge.

As per coding guidelines: public classes, functions, and non-obvious logic must have KDoc or Javadoc documenting contracts, threading, nullability, and side effects.

Also applies to: 50-64

🤖 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
`@app/src/main/java/com/itsaky/androidide/activities/editor/PeerCursorOverlayManager.kt`
around lines 20 - 32, Add concise KDoc for the public class
PeerCursorOverlayManager and its methods addMarker, removeMarker, clear, and
clearAll, documenting their threading requirements, return values, nullability,
and marker dismissal side effects. Preserve the existing behavior and cover the
related API methods shown in the diff.

Source: Coding guidelines


132-139: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add contrast tests and correct the threshold

contrastingTextColor uses non-linear RGB values and ignores alpha. For example, #999999 selects white at only 2.85:1 contrast, and translucent red over white drops to 1.56:1. Add light, dark, boundary, and alpha tests. Select the foreground from the composited background so it meets the intended contrast target.

🤖 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
`@app/src/main/java/com/itsaky/androidide/activities/editor/PeerCursorOverlayManager.kt`
around lines 132 - 139, Update contrastingTextColor to linearize RGB channels,
composite translucent backgrounds over the actual base background, and choose
the foreground using a contrast calculation that meets the intended target
rather than the fixed 0.6 luminance threshold. Add tests covering light, dark,
threshold-boundary, and alpha-composited backgrounds, including the cited gray
and translucent-red cases.

Source: Coding guidelines

🤖 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.

Outside diff comments:
In
`@app/src/main/java/com/itsaky/androidide/activities/editor/PeerCursorOverlayManager.kt`:
- Around line 50-56: Update removeMarker in PeerCursorOverlayManager to remove
the file’s outer markers entry after removing a peer when the per-file map is
empty. Preserve the current false return for missing files or peer IDs and the
dismiss/true behavior for successful removals.
- Around line 69-130: Update every cleanup path that removes a PeerCursorWindow
to call its unregister() method before dismiss(). Ensure replaced and otherwise
discarded windows are unregistered before dismissal so no stale scroll receiver
or callbacks remain attached to the old editor.

---

Nitpick comments:
In
`@app/src/main/java/com/itsaky/androidide/activities/editor/PeerCursorOverlayManager.kt`:
- Around line 20-32: Add concise KDoc for the public class
PeerCursorOverlayManager and its methods addMarker, removeMarker, clear, and
clearAll, documenting their threading requirements, return values, nullability,
and marker dismissal side effects. Preserve the existing behavior and cover the
related API methods shown in the diff.
- Around line 132-139: Update contrastingTextColor to linearize RGB channels,
composite translucent backgrounds over the actual base background, and choose
the foreground using a contrast calculation that meets the intended target
rather than the fixed 0.6 luminance threshold. Add tests covering light, dark,
threshold-boundary, and alpha-composited backgrounds, including the cited gray
and translucent-red cases.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 5058d050-67b1-4fca-b564-4c7101fafde3

📥 Commits

Reviewing files that changed from the base of the PR and between 13864e2 and 1d38188.

📒 Files selected for processing (7)
  • app/src/main/java/com/itsaky/androidide/activities/editor/PeerCursorOverlayManager.kt
  • app/src/main/java/com/itsaky/androidide/app/EditorProviderImpl.kt
  • plugin-api/api/plugin-api.api
  • plugin-api/src/main/kotlin/com/itsaky/androidide/plugins/services/IdeServices.kt
  • plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/core/PluginManager.kt
  • plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/services/IdeEditorServiceImpl.kt
  • plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/services/IdeProjectServiceImpl.kt
🚧 Files skipped from review as they are similar to previous changes (6)
  • plugin-api/api/plugin-api.api
  • plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/core/PluginManager.kt
  • app/src/main/java/com/itsaky/androidide/app/EditorProviderImpl.kt
  • plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/services/IdeEditorServiceImpl.kt
  • plugin-api/src/main/kotlin/com/itsaky/androidide/plugins/services/IdeServices.kt
  • plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/services/IdeProjectServiceImpl.kt

@Daniel-ADFA
Daniel-ADFA merged commit 2d7444a into stage Aug 12, 2026
4 checks passed
@Daniel-ADFA
Daniel-ADFA deleted the feat/ADFA-4419-remote-peer-editor-decoration branch August 12, 2026 17:55
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.

4 participants