feat(mpp-idea): add IdeaDevInBlockRenderer for devin block support#20
feat(mpp-idea): add IdeaDevInBlockRenderer for devin block support#20
Conversation
Add missing devin block renderer to IdeaSketchRenderer to achieve feature parity with mpp-ui SketchRenderer. Changes: - Create IdeaDevInBlockRenderer.kt with Jewel theming - Parses devin blocks using ToolCallParser - Renders tool calls in expandable card format - Shows tool name, icon, and truncated parameters - Supports expand/collapse for full JSON parameter view - Falls back to IdeaCodeBlockRenderer for incomplete content - Update IdeaSketchRenderer.kt to route 'devin' blocks to new renderer - Update doc comment to reflect new DevIn support This ensures IdeaSketchRenderer supports all block types that SketchRenderer in mpp-ui supports: - markdown, md, empty -> SimpleJewelMarkdown - diff, patch -> IdeaDiffRenderer - thinking -> IdeaThinkingBlockRenderer - walkthrough -> IdeaWalkthroughBlockRenderer - mermaid, mmd -> MermaidDiagramView - devin -> IdeaDevInBlockRenderer (NEW) - else -> IdeaCodeBlockRenderer
WalkthroughAdds a new Kotlin Composable Changes
Sequence DiagramsequenceDiagram
actor User
participant IdeaSketchRenderer
participant IdeaDevInBlockRenderer
participant ToolCallParser
participant IdeaDevInToolItem
participant UI
User->>IdeaSketchRenderer: Requests render (code fence language="devin")
IdeaSketchRenderer->>IdeaDevInBlockRenderer: Instantiate with content & isComplete
alt block is complete
IdeaDevInBlockRenderer->>ToolCallParser: Parse tool calls from content
ToolCallParser-->>IdeaDevInBlockRenderer: Parsed tool calls
alt tool calls found
loop each tool call
IdeaDevInBlockRenderer->>IdeaDevInBlockRenderer: Resolve paths, format details, truncate
IdeaDevInBlockRenderer->>IdeaDevInToolItem: Create item (name, icon, details, params)
IdeaDevInToolItem->>UI: Render header (name, icon, inline details)
opt params exist
IdeaDevInToolItem->>UI: Render expandable JSON params on expand
end
end
else no tool calls
IdeaDevInBlockRenderer->>UI: Render as devin code block (fallback)
end
else block is incomplete
IdeaDevInBlockRenderer->>UI: Render as devin code block (fallback)
end
UI-->>User: Display rendered output
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
| if (workspaceRoot == null) return relativePath | ||
|
|
||
| // If already an absolute path, return as-is | ||
| if (relativePath.startsWith("/") || relativePath.matches(Regex("^[A-Za-z]:.*"))) { |
There was a problem hiding this comment.
On Windows, absolute paths can start with a backslash (e.g., "\\server..." UNC or "\path..."), so treating only "/" or drive-letter prefixes as absolute may misclassify paths and incorrectly prepend the workspace root. Consider handling leading backslashes as absolute as well.
🤖 Was this useful? React with 👍 or 👎
| appendLine("{") | ||
| params.entries.forEachIndexed { index, (key, value) -> | ||
| append(" \"$key\": ") | ||
| append("\"${value.replace("\"", "\\\"")}\"") |
There was a problem hiding this comment.
The manual JSON fallback only escapes double quotes; consider also escaping backslashes and control characters (e.g., newline, tab) to avoid producing invalid JSON in the expanded parameters view.
🤖 Was this useful? React with 👍 or 👎
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (3)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/sketch/IdeaDevInBlockRenderer.kt (3)
98-196: Consider either surfacingfilePath/toolTypeor dropping them fromIdeaDevInToolItemRight now
filePathandtoolTypeare passed in but not used insideIdeaDevInToolItem, while you already pay the cost of resolving the path and mapping the tool type. Either:
- Render one of these (e.g., show a truncated
filePathsubtitle or usetoolTypeto vary the icon), or- Remove the parameters and their computation until you actually need them.
That will keep the composable’s API and upstream logic focused on what’s currently displayed.
201-213: MakeresolveAbsolutePathOS‑aware instead of manually joining with/The current implementation mixes separators (e.g.,
C:\root/relative) and reimplements path joining logic. Since this is IDEA‑specific/JVM code, you can lean onjava.nio.file.Pathsfor more robust behavior and cleaner code.Example diff for this function:
-private fun resolveAbsolutePath(relativePath: String?, workspaceRoot: String?): String? { - if (relativePath == null) return null - if (workspaceRoot == null) return relativePath - - // If already an absolute path, return as-is - if (relativePath.startsWith("/") || relativePath.matches(Regex("^[A-Za-z]:.*"))) { - return relativePath - } - - // Combine workspace root with relative path - val separator = if (workspaceRoot.endsWith("/") || workspaceRoot.endsWith("\\")) "" else "/" - return "$workspaceRoot$separator$relativePath" -} +private fun resolveAbsolutePath(relativePath: String?, workspaceRoot: String?): String? { + if (relativePath.isNullOrBlank()) return null + if (workspaceRoot.isNullOrBlank()) return relativePath + + // If already an absolute path, return as-is + if (relativePath.startsWith("/") || relativePath.matches(Regex("^[A-Za-z]:.*"))) { + return relativePath + } + + // Combine workspace root with relative path in an OS-aware way + return java.nio.file.Paths.get(workspaceRoot, relativePath).toString() +}(You’d also need
import java.nio.file.Pathsat the top of the file.)
238-262: HoistJsonconfiguration to a reusable instance and keep JSON formatting simple
formatParamsAsJsoncurrently allocates a newJson { prettyPrint = true }instance on every call. You can avoid repeated allocations and keep things clearer by reusing a single configured instance:// Near the top of the file private val PrettyJson = Json { prettyPrint = true }and then:
private fun formatParamsAsJson(params: Map<String, String>): String { return try { PrettyJson.encodeToString( kotlinx.serialization.serializer(), params ) } catch (e: Exception) { // existing manual fallback } }This keeps behavior the same while being a bit more efficient and explicit about the JSON configuration. Please double‑check against the
kotlinx-serialization-jsondocs for your version to ensure thisserializer()usage matches the expected API surface.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/sketch/IdeaDevInBlockRenderer.kt(1 hunks)mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/sketch/IdeaSketchRenderer.kt(2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.kt
📄 CodeRabbit inference engine (AGENTS.md)
Use
expect/actualfor 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/sketch/IdeaDevInBlockRenderer.ktmpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/sketch/IdeaSketchRenderer.kt
🧬 Code graph analysis (2)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/sketch/IdeaDevInBlockRenderer.kt (1)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/sketch/IdeaCodeBlockRenderer.kt (1)
IdeaCodeBlockRenderer(18-52)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/sketch/IdeaSketchRenderer.kt (1)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/sketch/IdeaDevInBlockRenderer.kt (1)
IdeaDevInBlockRenderer(32-91)
⏰ 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 (241)
- GitHub Check: Build (223)
🔇 Additional comments (4)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/sketch/IdeaSketchRenderer.kt (2)
14-26: Doc update correctly reflects DevIn supportThe KDoc list of handled block types now includes DevIn, matching the new
"devin"branch below. No further changes needed here.
102-111: DevIn branch integration looks consistent with existing renderersRouting
"devin"fences toIdeaDevInBlockRendererwithblockIsCompleteand full‑width modifier mirrors the patterns used for other structured block types (thinking, walkthrough, mermaid). The streaming case is handled viablockIsComplete, relying on the renderer to fall back to a code block when incomplete.mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/sketch/IdeaDevInBlockRenderer.kt (2)
32-91: DevIn block renderer behavior and Compose usage look solidThe separation between complete and streaming states, using
remember { ToolCallParser() }plusremember(devinContent)for parsing, and falling back toIdeaCodeBlockRendererwhen there are no tool calls or the block is incomplete, is clean and should be efficient. The logic aligns with the PR description (interactive view on completion, code‑block view while streaming).
218-233: Details formatting and truncation logic are straightforward
formatToolCallDetails+truncateValuegive a readable, bounded summary string for the header row, with the full data still available via the expanded JSON view. This is a reasonable balance between information density and layout stability.
Address review feedback from Augment Code and CodeRabbit: 1. Remove unused parameters from IdeaDevInToolItem: - Removed filePath and toolType parameters that were passed but not used - Removed associated computation (resolveAbsolutePath, ToolType.fromName) - Removed unused imports (ToolType, WorkspaceManager, Paths) 2. Hoist Json configuration to reusable instance: - Added PrettyJson val at module level to avoid repeated allocations - Updated formatParamsAsJson to use the hoisted instance 3. Fix JSON escaping in fallback formatter: - Added escapeJsonString helper function - Properly escape backslashes, quotes, newlines, carriage returns, and tabs - This prevents invalid JSON in the expanded parameters view
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/sketch/IdeaDevInBlockRenderer.kt (1)
136-145: Consider using thetruncateValuehelper for consistency.Line 138 hard-codes
.take(60), whiletruncateValueat line 199 uses a default of 100 characters. This inconsistency could be confusing for future maintainers.Apply this diff to use the existing helper:
// Details (truncated parameters) if (details.isNotEmpty() && !expanded) { Text( - text = details.take(60) + if (details.length > 60) "..." else "", + text = truncateValue(details, maxLength = 60), style = JewelTheme.defaultTextStyle.copy( fontSize = 11.sp, color = JewelTheme.globalColors.text.info.copy(alpha = 0.7f) ), modifier = Modifier.weight(1f), maxLines = 1 )
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/sketch/IdeaDevInBlockRenderer.kt(1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.kt
📄 CodeRabbit inference engine (AGENTS.md)
Use
expect/actualfor 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/sketch/IdeaDevInBlockRenderer.kt
🧬 Code graph analysis (1)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/sketch/IdeaDevInBlockRenderer.kt (2)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/sketch/IdeaCodeBlockRenderer.kt (1)
IdeaCodeBlockRenderer(18-52)mpp-core/src/commonMain/kotlin/cc/unitmesh/devins/test/AstFormatter.kt (1)
appendLine(206-209)
🔇 Additional comments (4)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/sketch/IdeaDevInBlockRenderer.kt (4)
1-26: LGTM!The imports are appropriate, and the reusable
PrettyJsoninstance with pretty-print configuration is a good pattern for consistent JSON formatting throughout the file.
190-205: LGTM!The helper functions
formatToolCallDetailsandtruncateValueare clear and correctly implemented.
237-244: LGTM!The
escapeJsonStringfunction properly handles backslashes, quotes, and control characters for valid JSON string escaping.
44-44: Remove this suggestion—theremembercall is necessary.
ToolCallParserinstantiates aDevinBlockParser()internally as a field, which has initialization costs. While the parser itself is stateless, theremembercall appropriately prevents recreating these dependencies on every recomposition. The test suite pattern of reusing a single parser instance confirms this is the intended usage.Likely an incorrect or invalid review comment.
| private fun formatParamsAsJson(params: Map<String, String>): String { | ||
| return try { | ||
| PrettyJson.encodeToString( | ||
| kotlinx.serialization.serializer(), | ||
| params | ||
| ) | ||
| } catch (e: Exception) { | ||
| // Fallback to manual formatting with proper JSON escaping | ||
| buildString { | ||
| appendLine("{") | ||
| params.entries.forEachIndexed { index, (key, value) -> | ||
| append(" \"${escapeJsonString(key)}\": ") | ||
| append("\"${escapeJsonString(value)}\"") | ||
| if (index < params.size - 1) { | ||
| appendLine(",") | ||
| } else { | ||
| appendLine() | ||
| } | ||
| } | ||
| append("}") | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Fix incorrect serializer usage.
Lines 212-215 call kotlinx.serialization.serializer() without a type parameter, which will fail at runtime. The encodeToString function with an explicit serializer parameter expects the serializer to match the value type.
Apply this diff to fix the serialization:
private fun formatParamsAsJson(params: Map<String, String>): String {
return try {
- PrettyJson.encodeToString(
- kotlinx.serialization.serializer(),
- params
- )
+ // Use the inline reified version which infers the serializer
+ PrettyJson.encodeToString(params)
} catch (e: Exception) {
// Fallback to manual formatting with proper JSON escaping
buildString {Alternatively, if an explicit serializer is needed:
+import kotlinx.serialization.builtins.MapSerializer
+import kotlinx.serialization.builtins.serializer
+
private fun formatParamsAsJson(params: Map<String, String>): String {
return try {
PrettyJson.encodeToString(
- kotlinx.serialization.serializer(),
+ MapSerializer(String.serializer(), String.serializer()),
params
)📝 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.
| private fun formatParamsAsJson(params: Map<String, String>): String { | |
| return try { | |
| PrettyJson.encodeToString( | |
| kotlinx.serialization.serializer(), | |
| params | |
| ) | |
| } catch (e: Exception) { | |
| // Fallback to manual formatting with proper JSON escaping | |
| buildString { | |
| appendLine("{") | |
| params.entries.forEachIndexed { index, (key, value) -> | |
| append(" \"${escapeJsonString(key)}\": ") | |
| append("\"${escapeJsonString(value)}\"") | |
| if (index < params.size - 1) { | |
| appendLine(",") | |
| } else { | |
| appendLine() | |
| } | |
| } | |
| append("}") | |
| } | |
| } | |
| } | |
| private fun formatParamsAsJson(params: Map<String, String>): String { | |
| return try { | |
| // Use the inline reified version which infers the serializer | |
| PrettyJson.encodeToString(params) | |
| } catch (e: Exception) { | |
| // Fallback to manual formatting with proper JSON escaping | |
| buildString { | |
| appendLine("{") | |
| params.entries.forEachIndexed { index, (key, value) -> | |
| append(" \"${escapeJsonString(key)}\": ") | |
| append("\"${escapeJsonString(value)}\"") | |
| if (index < params.size - 1) { | |
| appendLine(",") | |
| } else { | |
| appendLine() | |
| } | |
| } | |
| append("}") | |
| } | |
| } | |
| } |
🤖 Prompt for AI Agents
In
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/sketch/IdeaDevInBlockRenderer.kt
around lines 210 to 232, the call to kotlinx.serialization.serializer() is
untyped and will fail at runtime; replace it with an explicit Map<String,
String> serializer and pass that to PrettyJson.encodeToString (for example use
MapSerializer(String.serializer(), String.serializer()) or the equivalent typed
serializer for Map<String, String>), keeping the same params argument and
imports for MapSerializer and String.serializer(); keep the existing catch
fallback unchanged.
Summary
Add missing devin block renderer to
IdeaSketchRendererto achieve feature parity with mpp-uiSketchRenderer.Changes
New File:
IdeaDevInBlockRenderer.ktToolCallParserfrom mpp-coreIdeaCodeBlockRendererfor incomplete content or parsing failuresModified:
IdeaSketchRenderer.kt"devin"case in thewhenblock to route devin content toIdeaDevInBlockRendererFeature Parity
IdeaSketchRenderernow has complete feature parity withSketchRendererfrom mpp-ui.Pull Request opened by Augment Code with guidance from the PR author
Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.