Skip to content

Comments

feat(mpp-idea): Redesign Code Review UI with DiffViewerPanel, Plan and Fix sections#17

Merged
phodal merged 4 commits intomasterfrom
feature/idea-code-review-ui-redesign
Nov 30, 2025
Merged

feat(mpp-idea): Redesign Code Review UI with DiffViewerPanel, Plan and Fix sections#17
phodal merged 4 commits intomasterfrom
feature/idea-code-review-ui-redesign

Conversation

@phodal
Copy link
Member

@phodal phodal commented Nov 30, 2025

Summary

Redesign the IdeaCodeReviewContent.kt to add missing Plan and Fix UI sections, and rewrite DiffViewerPanel to match DiffCenterView.kt from mpp-ui.

Changes

New Components

  • IdeaResizableSplitPane.kt - Horizontal resizable split pane with Jewel theming
  • IdeaVerticalResizableSplitPane.kt - Vertical resizable split pane with Jewel theming
  • SimpleJewelMarkdown.kt - Custom markdown renderer using JetBrains' intellij-markdown parser (fixes NoSuchMethodError crash)

New Icons

Added 8 new icons to IdeaComposeIcons.kt:

  • List, AccountTree, Edit, DriveFileRenameOutline, FolderOpen, ChevronRight, BugReport, Info

DiffViewerPanel Redesign

  • Commit info card with single/multiple commit views and issue indicators
  • File view mode toggle (List/Tree)
  • Issue loading/error states with loading spinner, retry button, configure token button
  • Compact file list view with expandable diff items showing:
    • Change type icons (add/delete/rename/modify)
    • File path with directory
    • Line count badges (+added/-deleted)
    • Expandable diff hunks
  • Tree view with directory grouping
  • Diff hunk/line rendering with proper line numbers and syntax highlighting

Bug Fixes

  • Fixed markdown rendering crash by replacing mikepenz library with custom SimpleJewelMarkdown
  • Added openFileViewer method to IdeaCodeReviewViewModel for opening files in IDE

Testing

  • Build passes: cd mpp-idea && ../gradlew compileKotlin

Summary by CodeRabbit

  • New Features

    • Redesigned code review UI with resizable horizontal & vertical split panes, file LIST/TREE views, enhanced diff viewer, inline issue cards, and ability to open files from the panel
    • Added several new UI icons for improved navigation
  • Refactor

    • Replaced the previous markdown renderer with a simpler IntelliJ-compatible implementation and removed legacy themed markdown helpers; markdown styling and link handling streamlined

✏️ Tip: You can customize this high-level summary in your review settings.

…tree views, and issue display

- Add IdeaResizableSplitPane and IdeaVerticalResizableSplitPane components
- Redesign DiffViewerPanel to match DiffCenterView from mpp-ui:
  - Add commit info card with issue display
  - Add file view mode toggle (list/tree)
  - Add expandable file list with diff hunks
  - Add tree view with directory grouping
  - Add issue loading/error states with refresh
- Add new icons: List, AccountTree, Edit, DriveFileRenameOutline, FolderOpen, ChevronRight, BugReport, Info
- Replace mikepenz markdown library with SimpleJewelMarkdown to fix NoSuchMethodError
- Add openFileViewer method to IdeaCodeReviewViewModel
@coderabbitai
Copy link

coderabbitai bot commented Nov 30, 2025

Walkthrough

Replaced the multiplatform markdown renderer with a local SimpleJewelMarkdown (intellij-markdown), removed JewelMarkdown and its theme helpers, added eight ImageVector icons, introduced horizontal and vertical resizable split-pane composables, and refactored the code-review UI/viewmodel (added openFileViewer).

Changes

Cohort / File(s) Summary
Build / Dependency
mpp-idea/build.gradle.kts
Removed com.mikepenz:multiplatform-markdown-renderer dependency; note: using local SimpleJewelMarkdown (intellij-markdown) instead to avoid Compose runtime mismatch.
Markdown renderer swap
mpp-idea/src/main/kotlin/.../renderer/IdeaMarkdownRenderer.kt, mpp-idea/src/main/kotlin/.../renderer/sketch/IdeaSketchRenderer.kt
Replaced JewelMarkdown usages with SimpleJewelMarkdown; removed color/typography parameters and updated imports.
Removed markdown wrappers
mpp-idea/src/main/kotlin/.../renderer/markdown/JewelMarkdown.kt, .../JewelMarkdownColors.kt, .../JewelMarkdownTypography.kt
Deleted JewelMarkdown composable and its Jewel-themed color/typography helper files (APIs removed).
New lightweight markdown renderer
mpp-idea/src/main/kotlin/.../renderer/markdown/SimpleJewelMarkdown.kt
Added SimpleJewelMarkdown(content, modifier, onLinkClick?) — IntelliJ-markdown-based GFM renderer with AST traversal and Jewel styling for common node types.
Icons added
mpp-idea/src/main/kotlin/.../toolwindow/IdeaComposeIcons.kt
Added eight lazy ImageVector properties: List, AccountTree, Edit, DriveFileRenameOutline, FolderOpen, ChevronRight, BugReport, Info.
Resizable split panes
mpp-idea/src/main/kotlin/.../toolwindow/components/IdeaResizableSplitPane.kt, .../IdeaVerticalResizableSplitPane.kt
New horizontal and vertical resizable split-pane composables with draggable divider, animated hover/drag feedback, ratio constraints, and custom Layout measurement/placement.
Code review UI refactor
mpp-idea/src/main/kotlin/.../toolwindow/codereview/IdeaCodeReviewContent.kt
Refactored to a resizable three-column layout using the new split panes; introduced private composables for commit list, diff viewer (list/tree), commit info, and an AI analysis panel.
ViewModel addition
mpp-idea/src/main/kotlin/.../toolwindow/codereview/IdeaCodeReviewViewModel.kt
Added public openFileViewer(path: String) to resolve project-relative paths, locate VirtualFile, and open it in the IDE editor.

Sequence Diagram(s)

mermaid
sequenceDiagram
autonumber
participant UI as Code Review UI
participant VM as IdeaCodeReviewViewModel
participant FS as LocalFileSystem
participant Editor as FileEditorManager
UI->>VM: openFileViewer(path)
VM->>VM: resolve basePath + path; check file existence
VM->>FS: findFileByIoFile(file)
FS-->>VM: VirtualFile? (found / null)
alt found
VM->>Editor: openFile(virtualFile) (async)
Editor-->>UI: file opened in editor
else not found
VM-->>UI: log/warn (file not found)

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Points requiring extra attention:

  • IdeaCodeReviewContent.kt — large UI restructure, many new private composables and state flows.
  • IdeaResizableSplitPane.kt & IdeaVerticalResizableSplitPane.kt — custom Layout math, drag handling and edge constraints.
  • SimpleJewelMarkdown.kt — correctness of AST traversal, table/code-fence rendering, and link handling.
  • Ensure all former JewelMarkdown usages and theme helpers are removed or adapted.

Possibly related PRs

Poem

🐰 I nibble nodes and hop through text so bright,
SimpleJewel paints headings left and right,
Dividers drag as panes expand and shrink,
Icons wink and files open in a blink,
A rabbit hums — the editor’s in sync.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 72.73% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title directly and specifically describes the main changes: redesigning Code Review UI with new DiffViewerPanel, Plan and Fix sections.
✨ 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 feature/idea-code-review-ui-redesign

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

Copy link

@augmentcode augmentcode bot left a comment

Choose a reason for hiding this comment

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

Review completed. 2 suggestions posted.

Comment augment review to trigger a new review at any time.

// Inline issue indicator
IdeaIssueIndicator(
commit = selectedCommit,
commitIndex = 0,
Copy link

Choose a reason for hiding this comment

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

In the single‑commit view, commitIndex is hardcoded to 0 when calling IdeaIssueIndicator, so refresh can target the wrong commit if the selected commit isn’t the first; consider passing the actual selected commit’s index.

🤖 Was this useful? React with 👍 or 👎

val basePath = project.basePath ?: return
val file = java.io.File(basePath, path)
if (file.exists()) {
val virtualFile = com.intellij.openapi.vfs.LocalFileSystem.getInstance().findFileByIoFile(file)
Copy link

Choose a reason for hiding this comment

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

Using LocalFileSystem.findFileByIoFile without ensuring the VFS is current can return null even for existing files, which may prevent opening a file that exists on disk (e.g., if it hasn’t been indexed/loaded yet).

🤖 Was this useful? React with 👍 or 👎

Copy link

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

Caution

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

⚠️ Outside diff range comments (1)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/IdeaMarkdownRenderer.kt (1)

18-27: Update outdated documentation.

Line 21 references the old multiplatform-markdown-renderer library, which has been replaced with SimpleJewelMarkdown backed by IntelliJ's markdown parser.

Apply this diff to update the documentation:

 /**
  * IntelliJ IDEA-specific Markdown renderer with Mermaid diagram support.
  * Uses Jewel components for native IntelliJ look and feel.
- * Uses multiplatform-markdown-renderer for proper markdown parsing.
+ * Uses SimpleJewelMarkdown backed by IntelliJ's markdown parser.
  *
  * @param content The markdown content to render
  * @param isComplete Whether the content is complete (not streaming)
  * @param parentDisposable Parent disposable for JCEF resource cleanup
  * @param modifier Compose modifier
  */
🧹 Nitpick comments (15)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/IdeaComposeIcons.kt (1)

928-980: New List / AccountTree icons are consistent and idiomatic

The List and AccountTree vectors follow the existing pattern (24×24 viewport, black fill, lazy initialization) and fit cleanly into the existing icon set; nothing blocking here. One tiny detail: in AccountTree, the verticalLineToRelative(8f) from y=17f briefly moves the path down to y=25f, which is just outside the 24px viewport and will be clipped. It’s harmless, but if you care about pixel‑perfect bounds you could adjust that segment (e.g., to land at y=24f or switch to an absolute verticalLineTo(23f) depending on the intended design).

Also applies to: 982-1020

mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/markdown/SimpleJewelMarkdown.kt (2)

44-201: Add a true raw‑text fallback for unhandled node types

The else branch comment says “render children or show raw text”, but currently only child traversal happens; leaf token nodes with no children will render nothing. You can make the behavior match the comment and improve robustness for unhandled types by adding a raw‑text fallback for leaf nodes:

-        else -> {
-            // For other node types, try to render children or show raw text
-            if (node.children.isNotEmpty()) {
-                node.children.forEach { child ->
-                    RenderNode(node = child, content = content)
-                }
-            }
-        }
+        else -> {
+            // For other node types, try to render children or show raw text
+            if (node.children.isNotEmpty()) {
+                node.children.forEach { child ->
+                    RenderNode(node = child, content = content)
+                }
+            } else {
+                val text = node.getTextInNode(content).toString().trim()
+                if (text.isNotEmpty()) {
+                    Text(
+                        text = text,
+                        style = JewelTheme.defaultTextStyle.copy(fontSize = 13.sp),
+                        modifier = Modifier.padding(vertical = 2.dp)
+                    )
+                }
+            }
+        }

This keeps existing behavior for structured nodes while ensuring stray or less common nodes still show something sensible.


214-219: Consider how to handle very short or malformed code fences

Currently extractCodeFenceContent returns an empty string when there are ≤ 2 lines, which effectively drops single‑line or malformed fences. That may be fine for your use case, but if you expect partial fences from streaming or tools, you might want to preserve whatever content is present instead of returning an empty string.

mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/sketch/IdeaSketchRenderer.kt (1)

18-24: Update KDoc to reference SimpleJewelMarkdown instead of JewelMarkdown

The doc still says “Markdown/Text -> JewelMarkdown”, but the implementation now uses SimpleJewelMarkdown. To keep the documentation accurate, update this line accordingly, for example:

- * - Markdown/Text -> JewelMarkdown
+ * - Markdown/Text -> SimpleJewelMarkdown
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/components/IdeaVerticalResizableSplitPane.kt (3)

83-83: Consider using a vertical resize cursor instead of Crosshair.

PointerIcon.Crosshair is typically used for precision selection, not for resize operations. For a vertical split pane, users would expect a north-south resize cursor to indicate the divider can be dragged vertically.

-                    .pointerHoverIcon(PointerIcon.Crosshair)
+                    .pointerHoverIcon(PointerIcon.Hand)

Note: Compose Multiplatform may have limited cursor options. If PointerIcon.Hand doesn't convey the resize intent well, consider creating a custom cursor or leaving as-is if platform constraints apply.


90-101: Scaled divider may overflow its container.

When dividerScale animates to 1.2f, the inner Spacer's height becomes dividerHeight * 1.2, which exceeds the parent Box's fixed height of dividerHeight.dp. This could cause visual clipping. If the scale effect is intentional for visual feedback, consider using Modifier.graphicsLayer { scaleY = dividerScale } instead to scale without affecting layout bounds.


123-128: State update during measurement phase.

Setting containerHeight inside the Layout's measure lambda is a side effect during composition, which could potentially trigger recomposition. While this pattern is common and works in practice, using Modifier.onGloballyPositioned { containerHeight = it.size.height } on the root modifier would be more idiomatic.

mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/components/IdeaResizableSplitPane.kt (2)

83-83: Consider using a horizontal resize cursor.

Similar to the vertical variant, PointerIcon.Crosshair is not the conventional cursor for horizontal resize operations. Users typically expect an east-west resize cursor for horizontal dividers.


36-140: Consider extracting shared logic between horizontal and vertical split panes.

Both IdeaResizableSplitPane and IdeaVerticalResizableSplitPane share nearly identical logic (~90% overlap). Consider creating a shared internal implementation parameterized by orientation to reduce duplication and ensure consistent behavior when making future changes.

This could be a follow-up refactor if the current implementation proves stable.

mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/codereview/IdeaCodeReviewContent.kt (6)

90-90: TODO: Token configuration not implemented.

The onConfigureToken callback currently has an empty implementation with a TODO comment. Consider creating an issue to track this.

Would you like me to open an issue to track the token configuration implementation?


169-173: Selection highlight may have insufficient contrast.

Using panelBackground.copy(alpha = 0.8f) for selected items versus panelBackground for unselected may not provide enough visual distinction. Consider using a different color (e.g., JewelTheme.globalColors.outlines.focused.copy(alpha = 0.1f)) or a more contrasting approach.


544-544: Fragile error detection via string matching.

Using errorMessage.contains("Authentication", ignoreCase = true) to detect auth errors is fragile and may break if error messages change or are localized. Consider using a structured error type or error code instead.

// Example: Use a sealed class or enum for error types
sealed class IssueLoadError {
    data class AuthenticationError(val message: String) : IssueLoadError()
    data class NetworkError(val message: String) : IssueLoadError()
    data class UnknownError(val message: String) : IssueLoadError()
}

1718-1725: Non-collapsible card using collapsible wrapper.

This section uses IdeaCollapsibleCard with isExpanded = true and an empty onExpandChange, making it effectively non-collapsible. While functional, this is slightly misleading. Consider adding a collapsible: Boolean = true parameter to IdeaCollapsibleCard or using a simpler card wrapper for non-collapsible sections.


1850-1856: Consider using icons for collapse indicator.

The collapsible card uses text (+/-) for the expand/collapse indicator, while other components use proper icons (ChevronRight, ExpandMore). Consider using consistent iconography for a more polished appearance.

 Text(
-    text = if (isExpanded) "-" else "+",
+    // Replace with:
+)
+Icon(
+    imageVector = if (isExpanded)
+        cc.unitmesh.devins.idea.toolwindow.IdeaComposeIcons.ExpandMore
+    else
+        cc.unitmesh.devins.idea.toolwindow.IdeaComposeIcons.ChevronRight,
+    contentDescription = if (isExpanded) "Collapse" else "Expand",
+    tint = JewelTheme.globalColors.text.normal,
+    modifier = Modifier.size(16.dp)
+)

1-40: Consider splitting this large file.

At 1900+ lines, this file contains multiple logical groupings that could be extracted into separate files for better maintainability:

  • IdeaCommitListComponents.kt - CommitListPanel, CommitItem
  • IdeaDiffViewerComponents.kt - DiffViewerPanel, IdeaCompactFileListView, IdeaFileTreeView, etc.
  • IdeaAIAnalysisComponents.kt - IdeaAIAnalysisPanel and related sections
  • IdeaUIComponents.kt - Reusable utilities like IdeaCollapsibleCard, IdeaBadge

This would improve code navigation and make the codebase more modular.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b9f92bd and 8f04e39.

📒 Files selected for processing (12)
  • mpp-idea/build.gradle.kts (1 hunks)
  • mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/IdeaMarkdownRenderer.kt (3 hunks)
  • mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/markdown/JewelMarkdown.kt (0 hunks)
  • mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/markdown/JewelMarkdownColors.kt (0 hunks)
  • mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/markdown/JewelMarkdownTypography.kt (0 hunks)
  • mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/markdown/SimpleJewelMarkdown.kt (1 hunks)
  • mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/sketch/IdeaSketchRenderer.kt (2 hunks)
  • mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/IdeaComposeIcons.kt (1 hunks)
  • mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/codereview/IdeaCodeReviewContent.kt (4 hunks)
  • mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/codereview/IdeaCodeReviewViewModel.kt (1 hunks)
  • mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/components/IdeaResizableSplitPane.kt (1 hunks)
  • mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/components/IdeaVerticalResizableSplitPane.kt (1 hunks)
💤 Files with no reviewable changes (3)
  • mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/markdown/JewelMarkdownTypography.kt
  • mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/markdown/JewelMarkdownColors.kt
  • mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/markdown/JewelMarkdown.kt
🧰 Additional context used
📓 Path-based instructions (1)
**/*.kt

📄 CodeRabbit inference engine (AGENTS.md)

Use expect/actual for platform-specific code in KMP projects (e.g., file I/O on JVM/JS/Wasm)

Files:

  • mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/components/IdeaVerticalResizableSplitPane.kt
  • mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/markdown/SimpleJewelMarkdown.kt
  • mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/components/IdeaResizableSplitPane.kt
  • mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/IdeaMarkdownRenderer.kt
  • mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/codereview/IdeaCodeReviewViewModel.kt
  • mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/IdeaComposeIcons.kt
  • mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/sketch/IdeaSketchRenderer.kt
  • mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/codereview/IdeaCodeReviewContent.kt
🧠 Learnings (4)
📚 Learning: 2025-11-30T02:30:49.805Z
Learnt from: CR
Repo: phodal/auto-dev-sketch PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-30T02:30:49.805Z
Learning: Applies to **/src/{androidMain,desktopMain}/**/*.kt : For Compose (Desktop/Android), use `AutoDevColors` from `cc.unitmesh.devins.ui.compose.theme` or `MaterialTheme.colorScheme`

Applied to files:

  • mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/IdeaMarkdownRenderer.kt
  • mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/sketch/IdeaSketchRenderer.kt
  • mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/codereview/IdeaCodeReviewContent.kt
📚 Learning: 2025-11-30T02:30:49.805Z
Learnt from: CR
Repo: phodal/auto-dev-sketch PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-30T02:30:49.805Z
Learning: Applies to {mpp-ui/src/jsMain/typescript/**/*.{ts,tsx},**/src/{androidMain,desktopMain}/**/*.kt} : DO NOT hardcode colors (e.g., `Color(0xFF...)` or `#hex`). Always use design tokens for consistency across platforms

Applied to files:

  • mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/IdeaMarkdownRenderer.kt
📚 Learning: 2025-11-30T02:30:49.805Z
Learnt from: CR
Repo: phodal/auto-dev-sketch PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-30T02:30:49.805Z
Learning: For IntelliJ IDEA Plugin build, use `cd mpp-idea && ../gradlew compileKotlin` instead of `./gradlew :mpp-idea:compileKotlin` from root

Applied to files:

  • mpp-idea/build.gradle.kts
📚 Learning: 2025-11-30T02:30:49.805Z
Learnt from: CR
Repo: phodal/auto-dev-sketch PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-30T02:30:49.805Z
Learning: Use `cd mpp-idea && ../gradlew test --tests "cc.unitmesh.devins.idea.renderer.JewelRendererTest"` for running standalone JUnit 5 tests that do not require IntelliJ Platform

Applied to files:

  • mpp-idea/build.gradle.kts
🧬 Code graph analysis (3)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/IdeaMarkdownRenderer.kt (1)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/markdown/SimpleJewelMarkdown.kt (1)
  • SimpleJewelMarkdown (30-42)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/sketch/IdeaSketchRenderer.kt (1)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/markdown/SimpleJewelMarkdown.kt (1)
  • SimpleJewelMarkdown (30-42)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/codereview/IdeaCodeReviewContent.kt (1)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/components/IdeaResizableSplitPane.kt (1)
  • IdeaResizableSplitPane (36-140)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Build (223)
  • GitHub Check: Build (241)
🔇 Additional comments (16)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/IdeaMarkdownRenderer.kt (2)

48-52: LGTM!

The migration to SimpleJewelMarkdown is correct. The simplified API (content + modifier only) aligns with the new implementation and removes the need for explicit color/typography configuration.


83-86: LGTM!

The migration to SimpleJewelMarkdown for inline markdown blocks within the Mermaid-aware renderer is consistent with the main renderer changes and correctly uses the new simplified API.

mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/IdeaComposeIcons.kt (3)

1022-1089: Edit / DriveFileRenameOutline icons: shapes and naming look good

Edit and DriveFileRenameOutline match their Material counterparts closely and keep the same naming convention as the rest of the file. Builder parameters and fill settings are consistent with the existing icons; no issues from a correctness or style perspective.


1091-1150: FolderOpen / ChevronRight: aligned with existing Folder and navigation icons

FolderOpen nicely complements the existing Folder icon, and ChevronRight matches the style and proportions of ExpandLess / ExpandMore. Both reuse the same 24×24 vector baseline and SolidColor fill, so they should integrate cleanly with the redesigned diff tree/list toggles.


1152-1256: BugReport / Info icons integrate cleanly with existing status glyphs

BugReport and Info reuse the same circular/status idiom as Error and CheckCircle above (same viewport, circle‑plus‑glyph composition, black fill), which will give consistent visual language for issue/diagnostic states. Paths look well‑formed and bounded; nothing stands out as problematic.

mpp-idea/build.gradle.kts (1)

108-109: Comment clarification about markdown renderer looks good

The note accurately reflects the migration to SimpleJewelMarkdown with the IntelliJ markdown parser and documents the Compose version‑mismatch rationale clearly.

mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/markdown/SimpleJewelMarkdown.kt (2)

30-42: Markdown parsing and Compose state usage are sound

Creating the GFM flavour, parser, and markdown tree with remember keyed by content is appropriate; it avoids unnecessary reparsing while ensuring updates when the input changes.


206-209: Header text extraction is straightforward and correct

Trimming leading # characters and then whitespace matches typical ATX header formatting and should work well with the upstream parser.

mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/sketch/IdeaSketchRenderer.kt (1)

9-10: Switch to SimpleJewelMarkdown in sketch renderer looks correct

The new import and usage of SimpleJewelMarkdown(fence.text, Modifier.fillMaxWidth()) align with its API and cleanly swap out the previous markdown renderer without affecting the surrounding control flow.

Also applies to: 48-51

mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/components/IdeaVerticalResizableSplitPane.kt (1)

36-45: LGTM - Well-structured vertical split pane component.

The component provides a clean API with sensible defaults. The implementation properly handles drag gestures, state management, and visual feedback. The consistency with IdeaResizableSplitPane is good for maintainability.

mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/components/IdeaResizableSplitPane.kt (1)

102-117: Good practice: keying pointerInput on containerWidth.

Using containerWidth as the key for pointerInput ensures the gesture detector is recreated when the container dimension changes, preventing stale closure issues.

mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/codereview/IdeaCodeReviewContent.kt (5)

50-106: Well-structured three-column layout.

The nested IdeaResizableSplitPane approach provides a clean implementation of the three-column resizable layout. The split ratios (18%, 55%) provide sensible defaults.


229-351: LGTM - DiffViewerPanel implementation.

The panel properly handles loading, empty, and populated states. The view mode toggle between list and tree views is well-implemented, and the component delegates rendering to appropriate child composables.


1015-1023: Potential display issue with prefix removal.

line.content.removePrefix(prefix) assumes the line content may start with the prefix. If line.content is already stripped of the prefix (from the diff parser), this is fine. However, if the content includes a different character at the start, it will display correctly. Just ensure consistency with how DiffLine.content is populated upstream.


1295-1417: Well-organized AI Analysis panel.

The panel effectively organizes multiple analysis stages (lint, AI analysis, plan, user input, fixes) into collapsible sections. The conditional rendering based on AnalysisStage is clear and maintainable.


1506-1557: LGTM - Lint analysis card.

The collapsible lint analysis card with error/warning counts as badges and per-file issue display is well-implemented. The limit of 5 issues per file with a "more" indicator is a good UX choice.

Comment on lines +1468 to +1470
if (stage != AnalysisStage.COMPLETED && stage != AnalysisStage.ERROR) {
CircularProgressIndicator()
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add size modifier to CircularProgressIndicator.

The CircularProgressIndicator() inside the status badge box has no size constraint and may render too large. Add a size modifier for consistent appearance within the badge.

 if (stage != AnalysisStage.COMPLETED && stage != AnalysisStage.ERROR) {
-    CircularProgressIndicator()
+    CircularProgressIndicator(modifier = Modifier.size(12.dp))
 }
📝 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
if (stage != AnalysisStage.COMPLETED && stage != AnalysisStage.ERROR) {
CircularProgressIndicator()
}
if (stage != AnalysisStage.COMPLETED && stage != AnalysisStage.ERROR) {
CircularProgressIndicator(modifier = Modifier.size(12.dp))
}
🤖 Prompt for AI Agents
In
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/codereview/IdeaCodeReviewContent.kt
around lines 1468-1470, the CircularProgressIndicator in the status badge lacks
a size constraint and can render too large; update the composable invocation to
apply a size modifier (e.g., Modifier.size(16.dp or 20.dp)) so it fits the
badge, and keep any existing alignment/padding modifiers consistent with the
surrounding layout.

horizontalArrangement = Arrangement.spacedBy(4.dp),
verticalAlignment = Alignment.CenterVertically
) {
CircularProgressIndicator()
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add size modifier to CircularProgressIndicator.

Same issue as in IdeaAnalysisHeader - the spinner should have a size modifier.

-CircularProgressIndicator()
+CircularProgressIndicator(modifier = Modifier.size(12.dp))
📝 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
CircularProgressIndicator()
CircularProgressIndicator(modifier = Modifier.size(12.dp))
🤖 Prompt for AI Agents
In
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/codereview/IdeaCodeReviewContent.kt
around line 1780, the CircularProgressIndicator is missing a size modifier; add
a Modifier.size(...) to the spinner (use the same dp value used in
IdeaAnalysisHeader so both spinners match), ensuring you import and use
Modifier.size from androidx.compose.ui and the dp unit from
androidx.compose.ui.unit.

- Add support for inline formatting (bold, italic, strikethrough, code)
- Add clickable links with URL annotations
- Add GFM table rendering with header/body rows
- Add horizontal rule support
- Add nested list support with proper indentation
- Add GFM checkbox/task list support
- Add code fence with language header display
- Add block quote with styled left border
- Add image alt text display
- Fix Composable context issues by passing colors as parameters
Copy link

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

♻️ Duplicate comments (2)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/codereview/IdeaCodeReviewContent.kt (2)

1428-1487: Constrain spinner size in AI header status badge

CircularProgressIndicator() in the small status badge can visually dominate the label. As previously noted, adding a size modifier keeps it proportional to the badge:

-                        if (stage != AnalysisStage.COMPLETED && stage != AnalysisStage.ERROR) {
-                            CircularProgressIndicator()
-                        }
+                        if (stage != AnalysisStage.COMPLETED && stage != AnalysisStage.ERROR) {
+                            CircularProgressIndicator(modifier = Modifier.size(12.dp))
+                        }

This matches the compact badge styling without affecting behavior.


1768-1792: Constrain spinner size in Fix Generation badge

The CircularProgressIndicator() inside the “Fix Generation” card’s badge row is also unbounded and can appear oversized next to IdeaBadge. Recommend mirroring the compact sizing used elsewhere:

-            if (isGenerating) {
-                Row(
+            if (isGenerating) {
+                Row(
                     horizontalArrangement = Arrangement.spacedBy(4.dp),
                     verticalAlignment = Alignment.CenterVertically
                 ) {
-                    CircularProgressIndicator()
+                    CircularProgressIndicator(modifier = Modifier.size(12.dp))
                     IdeaBadge(text = "Generating...", color = AutoDevColors.Indigo.c400)
                 }

The larger, centered spinner in the body (Box(...){ CircularProgressIndicator() }) can rightly stay full‑size.

🧹 Nitpick comments (1)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/codereview/IdeaCodeReviewViewModel.kt (1)

52-75: openFileViewer: EDT usage and VFS refresh look solid; consider guarding disposed project

The method correctly logs failure cases, resolves paths relative to basePath, refreshes VFS, and runs openFile on the EDT via invokeLater. One small robustness improvement would be to bail out if the project is disposed by the time the runnable executes:

val localFileSystem = LocalFileSystem.getInstance()
ApplicationManager.getApplication().invokeLater {
    if (project.isDisposed) {
        logger.warn("Project disposed before opening file: ${file.path}")
        return@invokeLater
    }
    val virtualFile = localFileSystem.refreshAndFindFileByIoFile(file)
    if (virtualFile != null) {
        FileEditorManager.getInstance(project).openFile(virtualFile, true)
    } else {
        logger.warn("VirtualFile not found for file: ${file.path}")
    }
}

This avoids trying to open editors on a disposed project in edge cases.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8f04e39 and ce9f550.

📒 Files selected for processing (3)
  • mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/markdown/SimpleJewelMarkdown.kt (1 hunks)
  • mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/codereview/IdeaCodeReviewContent.kt (4 hunks)
  • mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/codereview/IdeaCodeReviewViewModel.kt (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/markdown/SimpleJewelMarkdown.kt
🧰 Additional context used
📓 Path-based instructions (1)
**/*.kt

📄 CodeRabbit inference engine (AGENTS.md)

Use expect/actual for platform-specific code in KMP projects (e.g., file I/O on JVM/JS/Wasm)

Files:

  • mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/codereview/IdeaCodeReviewViewModel.kt
  • mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/codereview/IdeaCodeReviewContent.kt
🧠 Learnings (1)
📚 Learning: 2025-11-30T02:30:49.805Z
Learnt from: CR
Repo: phodal/auto-dev-sketch PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-30T02:30:49.805Z
Learning: Applies to **/src/{androidMain,desktopMain}/**/*.kt : For Compose (Desktop/Android), use `AutoDevColors` from `cc.unitmesh.devins.ui.compose.theme` or `MaterialTheme.colorScheme`

Applied to files:

  • mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/codereview/IdeaCodeReviewContent.kt
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Build (223)
  • GitHub Check: Build (241)
🔇 Additional comments (3)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/codereview/IdeaCodeReviewContent.kt (3)

57-106: Three‑pane layout and diff viewer wiring look consistent

The new nested IdeaResizableSplitPane layout cleanly separates commits, diff, and AI analysis, and DiffViewerPanel is correctly wired with selectedCommits, selectedCommitIndices, and callbacks (onViewFile, onRefreshIssue, onConfigureToken). Using the view model’s state flow via collectAsState() keeps the UI reactive without obvious recomposition pitfalls. No functional issues spotted here.

Also applies to: 214-354


356-751: Diff/file views and tree structure are well‑factored and performant enough for typical sizes

The commit info + issue indicator, compact file list, and tree view are nicely decomposed:

  • IdeaIssueIndicator covers loading/info/error states with clear affordances (refresh, token config) and uses small, focused composables (IdeaInlineIssueChip, IdeaIssueInfoCard).
  • IdeaCompactFileListView and IdeaFileTreeView avoid repeated indexOf scans and use either list indices or file.path for expansion, giving O(1) state checks per row.
  • IdeaDiffHunkView / IdeaDiffLineView correctly derive prefixes, line numbers, and colors from DiffLineType, and horizontal scrolling on content is a pragmatic choice for long lines.

Overall, the diff rendering and file navigation structure look sound, with no clear correctness or performance issues for expected diff sizes.

Also applies to: 753-959, 962-1032, 1038-1160, 1120-1142, 1147-1197, 1202-1295


1301-1423: AI analysis, plan, user input, and fixes flow is coherent and stage‑driven

IdeaAIAnalysisPanel and its subcomponents (IdeaAnalysisHeader, lint/analysis/plan/user input/fix sections, IdeaCollapsibleCard, IdeaBadge) form a clear stage machine around AnalysisStage and aiProgress. Conditions for rendering each section match the intended flow (lint → analysis → plan → optional user feedback → fixes), and the shared collapsible card primitive keeps the UI consistent and maintainable. Aside from the spinner sizing nits already noted, this structure looks ready for incremental enhancements (e.g., richer plan selection) without major changes.

Also applies to: 1512-1915

- Parse GFM table structure correctly using HEADER, ROW, and CELL types
- Calculate adaptive column weights based on content length
- Clean cell text by removing markdown formatting (|, `, **, *)
- Add proper vertical alignment for table rows
- Reference implementation from mpp-ui/MarkdownTableRenderer.kt
Copy link

@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

🧹 Nitpick comments (1)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/markdown/SimpleJewelMarkdown.kt (1)

625-645: Document magic numbers for table column constraints.

The values 0.15f (min) and 0.65f (max) for column width constraints lack explanation. Adding comments would improve maintainability.

         // Convert to weights with min/max constraints
         val floatLengths = lengths.map { it.coerceAtLeast(1).toFloat() }
         val total = floatLengths.sum()
+        // Constrain each column between 15% and 65% to prevent extremely narrow/wide columns
         val constrained = floatLengths.map { (it / total).coerceIn(0.15f, 0.65f) }
         val constrainedTotal = constrained.sum()
+        // Normalize weights to sum to 1.0
         constrained.map { it / constrainedTotal }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ce9f550 and f87cffc.

📒 Files selected for processing (1)
  • mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/markdown/SimpleJewelMarkdown.kt (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.kt

📄 CodeRabbit inference engine (AGENTS.md)

Use expect/actual for platform-specific code in KMP projects (e.g., file I/O on JVM/JS/Wasm)

Files:

  • mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/markdown/SimpleJewelMarkdown.kt
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Build (223)
  • GitHub Check: Build (241)
🔇 Additional comments (5)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/markdown/SimpleJewelMarkdown.kt (5)

216-307: LGTM!

The inline formatting builder comprehensively handles markdown inline elements with proper styling. The approach of using withStyle and pushStringAnnotation for links is correct.


349-442: LGTM!

Code block rendering is well-implemented with proper theming, language display, and horizontal scrolling support. The visual design with rounded corners and borders aligns well with the Jewel theme.


481-588: LGTM!

List rendering with nested support and GFM checkbox handling is well-implemented. The depth-based bullet variation and indentation provide good visual hierarchy.


755-810: LGTM!

Helper functions properly extract text from header and code fence nodes with appropriate fallbacks and bounds checking.


33-34: [No changes required]

This is a JVM-only IntelliJ plugin module (using kotlin("jvm") plugin), not a multiplatform module. The Desktop API usage is appropriate for this context and does not violate KMP guidelines.

Comment on lines +184 to +203
@Suppress("DEPRECATION")
ClickableText(
text = annotatedString,
style = JewelTheme.defaultTextStyle.copy(fontSize = 13.sp),
modifier = Modifier.padding(vertical = 2.dp),
onClick = { offset ->
annotatedString.getStringAnnotations("URL", offset, offset)
.firstOrNull()?.let { annotation ->
if (onLinkClick != null) {
onLinkClick(annotation.item)
} else {
try {
Desktop.getDesktop().browse(URI(annotation.item))
} catch (e: Exception) {
// Ignore
}
}
}
}
)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Address deprecated API and improve error handling.

Two concerns:

  1. ClickableText is deprecated in Compose. Consider migrating to Text with Modifier.clickable or ClickableText alternatives when available.
  2. The exception at lines 197-199 is silently ignored, making it difficult to diagnose link-opening failures.

Apply this diff to at least log the exception:

                         } else {
                             try {
                                 Desktop.getDesktop().browse(URI(annotation.item))
                             } catch (e: Exception) {
-                                // Ignore
+                                // Log or report link opening failure
+                                println("Failed to open link: ${annotation.item}, error: ${e.message}")
                             }
                         }
📝 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
@Suppress("DEPRECATION")
ClickableText(
text = annotatedString,
style = JewelTheme.defaultTextStyle.copy(fontSize = 13.sp),
modifier = Modifier.padding(vertical = 2.dp),
onClick = { offset ->
annotatedString.getStringAnnotations("URL", offset, offset)
.firstOrNull()?.let { annotation ->
if (onLinkClick != null) {
onLinkClick(annotation.item)
} else {
try {
Desktop.getDesktop().browse(URI(annotation.item))
} catch (e: Exception) {
// Ignore
}
}
}
}
)
@Suppress("DEPRECATION")
ClickableText(
text = annotatedString,
style = JewelTheme.defaultTextStyle.copy(fontSize = 13.sp),
modifier = Modifier.padding(vertical = 2.dp),
onClick = { offset ->
annotatedString.getStringAnnotations("URL", offset, offset)
.firstOrNull()?.let { annotation ->
if (onLinkClick != null) {
onLinkClick(annotation.item)
} else {
try {
Desktop.getDesktop().browse(URI(annotation.item))
} catch (e: Exception) {
// Log or report link opening failure
println("Failed to open link: ${annotation.item}, error: ${e.message}")
}
}
}
}
)

- Extract text from TEXT tokens within cells instead of raw substring
- Add collectTextFromNode to recursively collect text from AST nodes
- Handle WHITE_SPACE tokens as spaces
- Fixes table rendering showing one character per line
Copy link

@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 (1)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/markdown/SimpleJewelMarkdown.kt (1)

640-642: Consider documenting magic numbers for table column constraints.

The column weight constraints 0.15f and 0.65f control minimum and maximum column widths but lack explanation. Consider extracting these as named constants with comments describing the rationale (e.g., preventing columns from being too narrow or too wide).

+    // Column width constraints: minimum 15%, maximum 65% to prevent extreme sizes
+    private const val MIN_COLUMN_WEIGHT = 0.15f
+    private const val MAX_COLUMN_WEIGHT = 0.65f
+
     // Calculate adaptive column weights based on content length
     val columnWeights = remember(node, content) {
         val lengths = IntArray(columnsCount) { 0 }
         // ...
         val floatLengths = lengths.map { it.coerceAtLeast(1).toFloat() }
         val total = floatLengths.sum()
-        val constrained = floatLengths.map { (it / total).coerceIn(0.15f, 0.65f) }
+        val constrained = floatLengths.map { (it / total).coerceIn(MIN_COLUMN_WEIGHT, MAX_COLUMN_WEIGHT) }
         val constrainedTotal = constrained.sum()
         constrained.map { it / constrainedTotal }
     }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f87cffc and 35de079.

📒 Files selected for processing (1)
  • mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/markdown/SimpleJewelMarkdown.kt (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.kt

📄 CodeRabbit inference engine (AGENTS.md)

Use expect/actual for platform-specific code in KMP projects (e.g., file I/O on JVM/JS/Wasm)

Files:

  • mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/markdown/SimpleJewelMarkdown.kt
🔇 Additional comments (2)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/markdown/SimpleJewelMarkdown.kt (2)

265-267: Verify intentional spacing around inline code.

Adding spaces around inline code spans (" $codeText ") ensures visual separation but may introduce unwanted whitespace in some contexts. Confirm this is the desired behavior.


33-34: > Likely an incorrect or invalid review comment.

Comment on lines +195 to +196
try {
Desktop.getDesktop().browse(URI(annotation.item))
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Validate URLs before opening to prevent potential security issues.

Opening arbitrary URLs from markdown content without validation could pose security risks, especially if the markdown source is untrusted. Consider validating the URL scheme to allow only safe protocols like http and https.

Apply this diff to add URL validation:

                         } else {
                             try {
-                                Desktop.getDesktop().browse(URI(annotation.item))
+                                val uri = URI(annotation.item)
+                                if (uri.scheme in listOf("http", "https")) {
+                                    Desktop.getDesktop().browse(uri)
+                                }
                             } catch (e: Exception) {
                                 // Ignore
                             }
                         }
🤖 Prompt for AI Agents
In
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/markdown/SimpleJewelMarkdown.kt
around lines 195-196, validate the annotation.item URI before calling
Desktop.getDesktop().browse: parse annotation.item into a java.net.URI, check
its scheme (lowercased) is either "http" or "https", and only call
Desktop.browse when valid; otherwise skip and log or ignore. Also wrap the
parsing and browse call in a try/catch to handle URISyntaxException and
IOException and avoid throwing unchecked exceptions for malformed or unsupported
URLs.

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