Skip to content

feat: Add P0 core components and GenUI components to NanoDSL#517

Merged
phodal merged 24 commits intomasterfrom
feat/nanodsl-p0-components
Dec 17, 2025
Merged

feat: Add P0 core components and GenUI components to NanoDSL#517
phodal merged 24 commits intomasterfrom
feat/nanodsl-p0-components

Conversation

@phodal
Copy link
Owner

@phodal phodal commented Dec 16, 2025

  • Add P0 form input components: DatePicker, Radio, RadioGroup, Switch, NumberInput
  • Add P0 feedback components: Modal, Alert, Progress, Spinner
  • Add GenUI components: GenCanvas, SplitView, SmartTextField, Slider, DateRangePicker, DataChart, DataTable
  • Update Parser to support new component syntax
  • Update HtmlRenderer, ComposeNanoRenderer, and React NanoRenderer
  • Update Prompt Templates with new component examples
  • Add component file structure for better maintainability
  • Extend Button component with disabled_if property

All components are fully integrated into:

  • NanoSpecV1 (schema definitions)
  • NanoNode AST (AST nodes)
  • NanoIRConverter (IR conversion)
  • IndentParser (parsing)
  • All renderers (Html, Compose, React)

Compiles successfully on JVM and JS platforms.

Summary by CodeRabbit

  • New Features

    • Many UI components added (date/date-range pickers, radio/radio-group, switches, number/slider inputs, smart text field, modal, alert, progress, spinner, split view, canvas, data chart/table) plus a component registry and converters.
  • Behavior Changes

    • Renderer refactored to modular layout/content/input/feedback/data/control-flow components with runtime-driven state, richer bindings, events, expressions, loops/conditionals, and conditional button disabling.
  • Platform

    • Cross-platform image decoding and caching implemented for Android, iOS, JVM, JS and WASM.
  • Docs

    • Expanded DSL prompts and component documentation/examples.
  • Tests

    • Added unit tests for data components, rendering utils, runtime, and interpolation.
  • Style

    • Webview CSS for DataTable/DataChart and HStack wrap.

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

- Add P0 form input components: DatePicker, Radio, RadioGroup, Switch, NumberInput
- Add P0 feedback components: Modal, Alert, Progress, Spinner
- Add GenUI components: GenCanvas, SplitView, SmartTextField, Slider, DateRangePicker, DataChart, DataTable
- Update Parser to support new component syntax
- Update HtmlRenderer, ComposeNanoRenderer, and React NanoRenderer
- Update Prompt Templates with new component examples
- Add component file structure for better maintainability
- Extend Button component with disabled_if property

All components are fully integrated into:
- NanoSpecV1 (schema definitions)
- NanoNode AST (AST nodes)
- NanoIRConverter (IR conversion)
- IndentParser (parsing)
- All renderers (Html, Compose, React)

Compiles successfully on JVM and JS platforms.
Copilot AI review requested due to automatic review settings December 16, 2025 23:43
@coderabbitai
Copy link

coderabbitai bot commented Dec 16, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Refactors the Nano renderer into modular per-component renderers, adds a runtime-driven NanoStateRuntime for state and actions, extends AST/parser/IR with many UI primitives (inputs, feedback, data, layout), adds platform-specific image decoding and image cache implementations, removes the old JVM Compose renderer, and adds tests, specs, and build updates.

Changes

Cohort / File(s) Summary
Platform image decoding
mpp-ui/src/androidMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.android.kt, mpp-ui/src/iosMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.ios.kt, mpp-ui/src/wasmJsMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.wasmJs.kt, mpp-ui/src/jsMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.js.kt
Add platform internal actual fun decodeImageBytesToBitmap(bytes: ByteArray): ImageBitmap implementations for Android/iOS/WASM/JS.
Image cache (multiplatform + platforms)
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoImageCache.kt, .../NanoImageCache.jvm.kt, .../NanoImageCache.android.kt, .../NanoImageCache.ios.kt, .../NanoImageCache.js.kt, .../NanoImageCache.wasmJs.kt
New NanoImageCache with getOrPut, key hashing, expect/actual platform read/write implementations (JVM/Android/iOS/WASM/JS).
Core renderer refactor & runtime
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.kt, mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoStateRuntime.kt
Replace monolithic renderer with runtime-driven lifecycle: runtime snapshot, runtime.apply(action), renderNode callback delegating to modular components.
Removed old JVM renderer
mpp-ui/src/jvmMain/kotlin/cc/unitmesh/devins/ui/nano/ComposeNanoRenderer.kt
Remove previous ComposeNanoRenderer file and its monolithic dispatch.
Modular Compose components & utils
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoLayoutComponents.kt, .../NanoContentComponents.kt, .../NanoInputComponents.kt, .../NanoFeedbackComponents.kt, .../NanoControlFlowComponents.kt, .../NanoDataComponents.kt, .../NanoRenderUtils.kt
Add modular layout, content, input, feedback, control-flow, data components and rendering utilities (text interpolation, binding resolution, image handling, spacing helpers).
Web renderer + styles
mpp-vscode/webview/src/components/nano/NanoRenderer.tsx, mpp-vscode/webview/src/components/nano/NanoRenderer.css
Extend TSX renderer with many Render* implementations (DatePicker, Radio, Slider, Modal, DataChart, DataTable, etc.) and add HStack wrap, DataTable/DataChart CSS.
AST expansions
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ast/NanoNode.kt
Add many NanoNode variants (SplitView, GenCanvas, SmartTextField, Slider, DateRangePicker, DataChart, DataTable, DatePicker, Radio/Group, Switch, NumberInput, Modal, Alert, Progress, Spinner) and add flex/wrap/disabled_if/label/onChange props.
Parser enhancements
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/parser/IndentParser.kt
Support multiline inline component args, on_* event blocks, bracketed literals, actions map, and new props/events wiring.
IR conversion extensions
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverter.kt
Add converters for new NanoNode variants, propagate props/bindings/actions (flex, wrap, disabled_if, onChange) into NanoIR.
HTML renderer additions
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.kt
Add renderers for new components (DatePicker, Radio/Group, Switch, NumberInput, Modal, Alert, Progress, Spinner, SplitView, Slider, DataChart, DataTable) and flex/wrap style support.
Component framework & registry
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/ComponentDefinition.kt, .../ComponentRegistry.kt, .../ComponentConverterUtils.kt
Introduce ComponentDefinition, ComponentRegistry, and ComponentConverterUtils for binding/action/body conversion and component specs.
Concrete component defs
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/input/ButtonComponent.kt, .../input/DatePickerComponent.kt, .../feedback/ModalComponent.kt
Add Button, DatePicker, Modal component implementations (AST creation + AST→IR conversion).
Spec & prompts updates
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/spec/v1/NanoSpecV1.kt, xiuper-ui/src/commonMain/resources/prompts/detailed.txt, mpp-core/.../NanoDSLAgent.kt, xiuper-ui/src/{ios,js,wasmJs}/kotlin/.../ResourceLoader.*.kt
Expand NanoSpecV1 with new components/props (including Button.disabled_if) and update embedded prompts/resources.
Tests added/updated
mpp-ui/src/jvmTest/.../NanoDataComponentsTest.kt, mpp-ui/src/jvmTest/.../NanoRenderUtilsTest.kt, mpp-ui/src/commonTest/.../NanoStateRuntimeTest.kt, xiuper-ui/src/commonTest/.../NanoIRConverterCommonTest.kt, xiuper-ui/src/jvmTest/.../HtmlRendererTest.kt, mpp-ui/src/jvmTest/.../NanoTextInterpolationTest.kt
Add tests for DataTable/DataChart parsing, NanoRenderUtils, NanoStateRuntime, IR wrap/flex, Button.disabled_if and Slider, and text interpolation.
Agent / LLM streaming API
mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgent.kt, mpp-core/src/commonMain/kotlin/cc/unitmesh/llm/PromptStreamingService.kt, mpp-core/src/commonMain/kotlin/cc/unitmesh/llm/KoogPromptStreamingServiceAdapter.kt
Switch NanoDSLAgent to use PromptStreamingService, add PromptStreamingService interface and Koog adapter for backward compatibility.
CLI / harness / build tasks
mpp-ui/src/jvmMain/kotlin/.../cli/NanoDslTextRenderCli.kt, mpp-core/src/jvmMain/kotlin/.../NanoDslScenarioHarness.kt, mpp-ui/build.gradle.kts, mpp-core/build.gradle.kts
New JVM CLI harnesses and JavaExec tasks, add mpp-ui dependency on :xiuper-ui, and new run tasks.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Host as Host UI (Composable)
    participant Renderer as StatefulNanoRenderer
    participant Runtime as NanoStateRuntime
    participant Layout as NanoLayoutComponents
    participant Input as NanoInputComponents
    participant Content as NanoContentComponents

    Host->>Renderer: Render(ir, modifier)
    Renderer->>Runtime: snapshot()
    Renderer->>Layout: renderNode(rootIR, snapshot, onAction)
    Layout->>Renderer: renderNode(childIR, snapshot, onAction)
    Renderer->>Content: RenderText / RenderImage for content nodes
    Renderer->>Input: RenderButton / RenderInput for input nodes
    Input->>Renderer: onAction(NanoActionIR) (user interaction)
    Renderer->>Runtime: apply(action)
    Runtime-->>Renderer: updated snapshot (state changed)
    Renderer->>Host: recomposition with updated state
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • Focus areas:
    • Cross-module consistency: parser → AST → IR → renderer mappings and new props (flex/wrap/disabled_if).
    • NanoStateRuntime: mutation operators (SET/APPEND/REMOVE/ADD), coercion, and sequence handling.
    • Platform expect/actual implementations for image decoding and image cache persistence.
    • ComponentRegistry/ComponentConverterUtils payload shapes and nullability.
    • Removal of ComposeNanoRenderer: ensure no stale references remain.

Possibly related issues

Possibly related PRs

Poem

🐇 I hopped through code with tiny feet,

Pickers, charts, modals — a cozy feat.
Buttons hush when disabled_if calls,
Tables scroll and split-view halls.
A carrot cheer — the UI grows neat!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 17.44% 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 title accurately summarizes the main changes: addition of P0 core form/feedback components and GenUI components to the NanoDSL framework. It is specific and clearly reflects the primary scope of the changeset.
✨ 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 feat/nanodsl-p0-components

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

❤️ Share

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

Copy link

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

🧹 Nitpick comments (11)
mpp-ui/src/androidMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.android.kt (1)

10-13: Add error handling for invalid image data.

The Image.makeFromEncoded(bytes) function can throw exceptions or return null when provided with invalid or corrupted image data. Consider adding error handling to gracefully handle these cases:

 internal actual fun decodeImageBytesToBitmap(bytes: ByteArray): ImageBitmap {
-    val skiaImage = Image.makeFromEncoded(bytes)
-    return skiaImage.toComposeImageBitmap()
+    val skiaImage = Image.makeFromEncoded(bytes)
+        ?: throw IllegalArgumentException("Failed to decode image bytes")
+    return skiaImage.toComposeImageBitmap()
 }
mpp-ui/src/iosMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.ios.kt (1)

10-13: Add error handling for invalid image data.

Similar to the Android implementation, Image.makeFromEncoded(bytes) can fail with invalid image data. Add error handling for robustness:

 internal actual fun decodeImageBytesToBitmap(bytes: ByteArray): ImageBitmap {
-    val skiaImage = Image.makeFromEncoded(bytes)
-    return skiaImage.toComposeImageBitmap()
+    val skiaImage = Image.makeFromEncoded(bytes)
+        ?: throw IllegalArgumentException("Failed to decode image bytes")
+    return skiaImage.toComposeImageBitmap()
 }
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/ComponentConverterUtils.kt (1)

16-99: Code duplication with NanoIRConverter.

convertBinding and convertAction are duplicated between ComponentConverterUtils and NanoIRConverter (lines 322-390). While the comment states this is for avoiding duplication in component files, having two identical implementations creates maintenance risk.

Consider having NanoIRConverter delegate to ComponentConverterUtils for these methods:

// In NanoIRConverter.kt
-    private fun convertBinding(binding: Binding): NanoBindingIR {
-        return when (binding) {
-            // ... duplicated logic
-        }
-    }
+    private fun convertBinding(binding: Binding): NanoBindingIR {
+        return ComponentConverterUtils.convertBinding(binding)
+    }

-    private fun convertAction(action: NanoAction): NanoActionIR {
-        return when (action) {
-            // ... duplicated logic
-        }
-    }
+    private fun convertAction(action: NanoAction): NanoActionIR {
+        return ComponentConverterUtils.convertAction(action)
+    }
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/input/DatePickerComponent.kt (1)

48-65: Duplicate conversion logic with NanoIRConverter.

The convertToIR implementation duplicates the logic in NanoIRConverter.convertDatePicker (lines 496-512). This creates maintenance burden when the conversion logic needs to change.

Consider delegating to NanoIRConverter:

     override fun convertToIR(node: NanoNode): NanoIR {
         require(node is NanoNode.DatePicker)
-        
-        val props = mutableMapOf<String, JsonElement>()
-        node.format?.let { props["format"] = JsonPrimitive(it) }
-        node.minDate?.let { props["minDate"] = JsonPrimitive(it) }
-        node.maxDate?.let { props["maxDate"] = JsonPrimitive(it) }
-        node.placeholder?.let { props["placeholder"] = JsonPrimitive(it) }
-
-        val bindings = node.value?.let {
-            mapOf("value" to convertBinding(it))
-        }
-
-        val actions = node.onChange?.let {
-            mapOf("onChange" to convertAction(it))
-        }
-
-        return NanoIR(type = "DatePicker", props = props, bindings = bindings, actions = actions)
+        return NanoIRConverter.convert(node)
     }
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/feedback/ModalComponent.kt (2)

63-64: Inconsistent helper usage for action conversion.

Line 64 calls ComponentConverterUtils.convertAction(it) directly, while line 60 uses the private convertBinding helper. For consistency with DatePickerComponent (which has both convertBinding and convertAction helpers), consider adding a private convertAction helper:

+    private fun convertAction(action: NanoAction): NanoActionIR {
+        return ComponentConverterUtils.convertAction(action)
+    }
+
     private fun convertBinding(binding: Binding): NanoBindingIR {
         return ComponentConverterUtils.convertBinding(binding)
     }

Then update line 64:

         val actions = node.onClose?.let {
-            mapOf("onClose" to ComponentConverterUtils.convertAction(it))
+            mapOf("onClose" to convertAction(it))
         }

51-73: Duplicate conversion logic with NanoIRConverter.

Similar to DatePickerComponent, this duplicates NanoIRConverter.convertModal (lines 581-602). Consider delegating:

     override fun convertToIR(node: NanoNode): NanoIR {
         require(node is NanoNode.Modal)
-        
-        val props = mutableMapOf<String, JsonElement>()
-        // ... duplicated logic
-        return NanoIR(
-            type = "Modal",
-            // ...
-        )
+        return NanoIRConverter.convert(node)
     }
mpp-ui/src/jvmMain/kotlin/cc/unitmesh/devins/ui/nano/ComposeNanoRenderer.kt (3)

399-411: Unused variable option.

The option property is extracted on line 402 but never used in the composable. Either remove it or use it (e.g., as the RadioButton value for accessibility/semantics).

     @Composable
     fun RenderRadio(ir: NanoIR, modifier: Modifier = Modifier) {
         val label = ir.props["label"]?.jsonPrimitive?.content ?: ""
-        val option = ir.props["option"]?.jsonPrimitive?.content ?: ""
         Row(
             modifier = modifier,
             verticalAlignment = Alignment.CenterVertically
         ) {
             RadioButton(selected = false, onClick = {})
             Spacer(modifier = Modifier.width(8.dp))
             Text(text = label)
         }
     }

607-616: RenderDateRangePicker passes identical IR to both child pickers.

Both date pickers receive the same ir object, meaning they will display the same placeholder and bind to the same state. Consider passing distinct props for start/end dates or rendering placeholder labels.

     @Composable
     fun RenderDateRangePicker(ir: NanoIR, modifier: Modifier = Modifier) {
+        val startPlaceholder = ir.props["startPlaceholder"]?.jsonPrimitive?.content ?: "Start date"
+        val endPlaceholder = ir.props["endPlaceholder"]?.jsonPrimitive?.content ?: "End date"
         Row(
             modifier = modifier,
             horizontalArrangement = Arrangement.spacedBy(8.dp)
         ) {
-            RenderDatePicker(ir, Modifier.weight(1f))
-            RenderDatePicker(ir, Modifier.weight(1f))
+            // Render start date picker
+            Box(
+                modifier = Modifier
+                    .weight(1f)
+                    .border(1.dp, MaterialTheme.colorScheme.outline, RoundedCornerShape(4.dp))
+                    .padding(horizontal = 12.dp, vertical = 16.dp)
+            ) {
+                Text(text = startPlaceholder, color = MaterialTheme.colorScheme.onSurfaceVariant)
+            }
+            // Render end date picker
+            Box(
+                modifier = Modifier
+                    .weight(1f)
+                    .border(1.dp, MaterialTheme.colorScheme.outline, RoundedCornerShape(4.dp))
+                    .padding(horizontal = 12.dp, vertical = 16.dp)
+            ) {
+                Text(text = endPlaceholder, color = MaterialTheme.colorScheme.onSurfaceVariant)
+            }
         }
     }

514-518: Use lambda-based progress overload for Material 3 LinearProgressIndicator.

The progress: Float signature is deprecated in Material 3, use the overload that takes progress as a lambda.

-            LinearProgressIndicator(
-                progress = progress,
-                modifier = Modifier.fillMaxWidth()
-            )
+            LinearProgressIndicator(
+                progress = { progress },
+                modifier = Modifier.fillMaxWidth()
+            )
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.kt (2)

299-305: Non-standard format attribute on HTML date input.

The format attribute is not a standard HTML attribute for <input type="date">. Browser date inputs use ISO format (YYYY-MM-DD) and locale-based display. Consider using a data-format attribute for client-side formatting libraries.

     fun renderDatePicker(ir: NanoIR): String {
         val format = ir.props["format"]?.jsonPrimitive?.content ?: "YYYY-MM-DD"
         val placeholder = ir.props["placeholder"]?.jsonPrimitive?.content ?: ""
         val bindingAttr = renderBindingAttribute(ir)
         val actionAttr = renderActionAttribute(ir)
-        return "<input type=\"date\" class=\"nano-datepicker\" placeholder=\"$placeholder\" format=\"$format\"$bindingAttr$actionAttr>\n"
+        return "<input type=\"date\" class=\"nano-datepicker\" placeholder=\"$placeholder\" data-format=\"$format\"$bindingAttr$actionAttr>\n"
     }

443-452: Consider safer child access pattern.

While the null check on line 448 is correct, using ir.children!![1] after it is safe but relies on the preceding check. A more idiomatic approach uses getOrNull.

     fun renderSplitView(ir: NanoIR): String {
         val ratio = ir.props["ratio"]?.jsonPrimitive?.content?.toFloatOrNull() ?: 0.5f
         val leftWidth = (ratio * 100).toInt()
         val rightWidth = 100 - leftWidth
         return buildString {
             append("<div class=\"nano-splitview\">\n")
             append("  <div class=\"nano-splitview-left\" style=\"width: ${leftWidth}%\">\n")
-            if (ir.children?.isNotEmpty() == true) append("    ${renderNode(ir.children[0]).replace("\n", "\n    ")}")
+            ir.children?.getOrNull(0)?.let { append("    ${renderNode(it).replace("\n", "\n    ")}") }
             append("  </div>\n")
             append("  <div class=\"nano-splitview-right\" style=\"width: ${rightWidth}%\">\n")
-            if (ir.children?.size ?: 0 > 1) append("    ${renderNode(ir.children!![1]).replace("\n", "\n    ")}")
+            ir.children?.getOrNull(1)?.let { append("    ${renderNode(it).replace("\n", "\n    ")}") }
             append("  </div>\n")
             append("</div>\n")
         }
     }
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b4088e3 and 6cbc5c4.

📒 Files selected for processing (18)
  • mpp-ui/src/androidMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.android.kt (1 hunks)
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.kt (1 hunks)
  • mpp-ui/src/iosMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.ios.kt (1 hunks)
  • mpp-ui/src/jvmMain/kotlin/cc/unitmesh/devins/ui/nano/ComposeNanoRenderer.kt (3 hunks)
  • mpp-ui/src/wasmJsMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.wasmJs.kt (1 hunks)
  • mpp-vscode/webview/src/components/nano/NanoRenderer.tsx (2 hunks)
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ast/NanoNode.kt (2 hunks)
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/ComponentConverterUtils.kt (1 hunks)
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/ComponentDefinition.kt (1 hunks)
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/ComponentRegistry.kt (1 hunks)
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/feedback/ModalComponent.kt (1 hunks)
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/input/ButtonComponent.kt (1 hunks)
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/input/DatePickerComponent.kt (1 hunks)
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverter.kt (3 hunks)
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/parser/IndentParser.kt (2 hunks)
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.kt (2 hunks)
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/spec/v1/NanoSpecV1.kt (4 hunks)
  • xiuper-ui/src/commonMain/resources/prompts/detailed.txt (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.kt

📄 CodeRabbit inference engine (AGENTS.md)

**/*.kt: Use expect/actual pattern for platform-specific code (for example: Platform implementations)
Use concrete classes (not interfaces) and Promise (not Flow) for @JsExport annotations
Avoid emoji and UTF-8 characters in WASM code
When modifying CodingAgentRenderer, update ALL implementations: DefaultCodingAgentRenderer, ComposeRenderer, JewelRenderer, ServerSideRenderer, and JsRendererAdapter in Kotlin
When modifying CodingAgentRenderer, update JVM CLI implementations: CodingCliRenderer and ConsoleRenderer
Use AutoDevColors or MaterialTheme.colorScheme for Compose styling

Files:

  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/ComponentDefinition.kt
  • mpp-ui/src/iosMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.ios.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/ComponentRegistry.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverter.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/feedback/ModalComponent.kt
  • mpp-ui/src/wasmJsMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.wasmJs.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/input/ButtonComponent.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/parser/IndentParser.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/ComponentConverterUtils.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ast/NanoNode.kt
  • mpp-ui/src/androidMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.android.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/input/DatePickerComponent.kt
  • mpp-ui/src/jvmMain/kotlin/cc/unitmesh/devins/ui/nano/ComposeNanoRenderer.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/spec/v1/NanoSpecV1.kt
**/*.{kt,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Never use hardcoded colors - always use design tokens from the design system

Files:

  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/ComponentDefinition.kt
  • mpp-ui/src/iosMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.ios.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/ComponentRegistry.kt
  • mpp-vscode/webview/src/components/nano/NanoRenderer.tsx
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverter.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/feedback/ModalComponent.kt
  • mpp-ui/src/wasmJsMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.wasmJs.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/input/ButtonComponent.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/parser/IndentParser.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/ComponentConverterUtils.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ast/NanoNode.kt
  • mpp-ui/src/androidMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.android.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/input/DatePickerComponent.kt
  • mpp-ui/src/jvmMain/kotlin/cc/unitmesh/devins/ui/nano/ComposeNanoRenderer.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/spec/v1/NanoSpecV1.kt
🧠 Learnings (5)
📚 Learning: 2025-12-07T08:40:10.859Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Applies to mpp-vscode/src/**/*.ts : When modifying `CodingAgentRenderer`, update VSCode implementations: `mpp-vscode/src/bridge/mpp-core.ts` and `mpp-vscode/src/providers/chat-view.ts`

Applied to files:

  • mpp-vscode/webview/src/components/nano/NanoRenderer.tsx
📚 Learning: 2025-12-07T08:40:10.859Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Applies to **/*.ts : When modifying `CodingAgentRenderer`, update ALL TypeScript implementations: `BaseRenderer.ts`, `CliRenderer.ts`, `ServerRenderer.ts`, and `TuiRenderer.ts`

Applied to files:

  • mpp-vscode/webview/src/components/nano/NanoRenderer.tsx
📚 Learning: 2025-12-07T08:40:10.859Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Applies to **/*.kt : When modifying `CodingAgentRenderer`, update ALL implementations: `DefaultCodingAgentRenderer`, `ComposeRenderer`, `JewelRenderer`, `ServerSideRenderer`, and `JsRendererAdapter` in Kotlin

Applied to files:

  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverter.kt
  • mpp-ui/src/androidMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.android.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.kt
  • mpp-ui/src/jvmMain/kotlin/cc/unitmesh/devins/ui/nano/ComposeNanoRenderer.kt
📚 Learning: 2025-12-07T08:40:10.859Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Applies to **/*.kt : When modifying `CodingAgentRenderer`, update JVM CLI implementations: `CodingCliRenderer` and `ConsoleRenderer`

Applied to files:

  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.kt
  • mpp-ui/src/jvmMain/kotlin/cc/unitmesh/devins/ui/nano/ComposeNanoRenderer.kt
📚 Learning: 2025-12-07T08:40:10.859Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Applies to **/*.kt : Use `AutoDevColors` or `MaterialTheme.colorScheme` for Compose styling

Applied to files:

  • mpp-ui/src/jvmMain/kotlin/cc/unitmesh/devins/ui/nano/ComposeNanoRenderer.kt
🧬 Code graph analysis (4)
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverter.kt (3)
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/ComponentConverterUtils.kt (2)
  • convertBinding (18-33)
  • convertAction (35-86)
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/feedback/ModalComponent.kt (1)
  • convertBinding (76-78)
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/input/DatePickerComponent.kt (2)
  • convertBinding (68-70)
  • convertAction (72-74)
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.kt (1)
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/NanoRenderer.kt (1)
  • renderNode (38-38)
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/input/DatePickerComponent.kt (2)
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/feedback/ModalComponent.kt (1)
  • convertBinding (76-78)
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverter.kt (2)
  • convertBinding (322-337)
  • convertAction (339-390)
mpp-ui/src/jvmMain/kotlin/cc/unitmesh/devins/ui/nano/ComposeNanoRenderer.kt (1)
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.kt (1)
  • RenderNode (128-153)
⏰ 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). (3)
  • GitHub Check: Agent
  • GitHub Check: Test mpp-core (KMP)
  • GitHub Check: Build
🔇 Additional comments (19)
xiuper-ui/src/commonMain/resources/prompts/detailed.txt (1)

41-41: LGTM! Button component correctly updated with the new disabled_if property.

mpp-vscode/webview/src/components/nano/NanoRenderer.tsx (1)

69-87: LGTM! Clean dispatcher extension.

The new component cases are well-organized and follow the established pattern consistently.

mpp-ui/src/wasmJsMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.wasmJs.kt (1)

9-11: LGTM!

The WASM/JS implementation using decodeToImageBitmap() is clean and appropriate for the platform.

xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/ComponentDefinition.kt (1)

11-31: Well-designed component abstraction.

The ComponentDefinition interface provides a clean contract for component implementations, with clear documentation and appropriate separation of schema validation, AST creation, and IR conversion concerns.

xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/parser/IndentParser.kt (2)

849-849: Button component properly extended with disabled_if support.

The addition of the disabledIf parameter to the Button component is correctly integrated, checking both args and props for the disabled_if property.


907-1062: Comprehensive component parsing implementation.

The new component branches (P0 form inputs, feedback components, and GenUI components) are consistently implemented with:

  • Proper parameter extraction from both args and props
  • Appropriate binding support for stateful values
  • Safe type conversions using nullable methods
  • Action wiring for interactive components

The implementation follows the established patterns and maintains consistency across all new components.

xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/input/ButtonComponent.kt (1)

18-62: Well-implemented component definition with disabled_if support.

The ButtonComponent implementation correctly:

  • Defines the component spec with the new disabled_if property
  • Extracts all properties in createASTNode including the conditional disable expression
  • Serializes properties to IR format in convertToIR
  • Uses ComponentConverterUtils for action conversion

The implementation follows the ComponentDefinition contract and integrates cleanly with the broader component system.

mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.kt (1)

428-428: Dispatchers.Default is appropriate for this suspending operation. The function uses Ktor's .get() which is a suspending function, not blocking I/O. Dispatchers.IO is only needed for blocking APIs; when using suspending functions, any dispatcher can be used, and network libraries providing suspending functions don't require Dispatchers.IO. Since this code is in commonMain and must work across multiple platforms, Dispatchers.IO is available only on JVM and native targets, making Dispatchers.Default the correct choice for multiplatform compatibility.

Likely an incorrect or invalid review comment.

xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/ComponentRegistry.kt (1)

17-25: The component registry structure is appropriate for a phased rollout approach. The current implementation registers three core components (Button, DatePicker, Modal) with TODO comments indicating areas for future expansion. However, no additional components mentioned in your review appear to exist in the codebase. Verify which components are actually part of this PR before requesting registration changes, as ComponentRegistry.getComponent() is not currently called elsewhere in the codebase and poses no immediate runtime risk.

Likely an incorrect or invalid review comment.

xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ast/NanoNode.kt (2)

143-143: LGTM!

The disabledIf property addition to Button enables conditional disabling based on expressions. The nullable String type is appropriate for this use case.


313-508: LGTM!

The new component definitions follow consistent patterns:

  • GenUI Foundation (GenCanvas, SplitView) with layout capabilities
  • Structured Input (SmartTextField, Slider, DateRangePicker) with bindings and validation
  • Data Artifacts (DataChart, DataTable) with data references
  • P0 Form Input and Feedback components with appropriate props, bindings, and actions

The sealed class hierarchy enables exhaustive pattern matching in the converter.

xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverter.kt (2)

38-58: LGTM!

The dispatch cases are correctly organized by tier/category and cover all new NanoNode variants. This maintains exhaustive pattern matching for the sealed class.


391-640: LGTM!

The new converter functions are well-structured and consistent:

  • Props are built conditionally using ?.let
  • Bindings and actions are converted where applicable
  • Children are recursively converted using convert()
  • The IR type names match the component names
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/input/DatePickerComponent.kt (1)

68-74: LGTM!

The private helper methods correctly delegate to ComponentConverterUtils, following the DRY principle.

xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/spec/v1/NanoSpecV1.kt (3)

353-356: LGTM! Component category sets are comprehensive.

All 16 new components are properly categorized:

  • layoutComponents: includes SplitView
  • containerComponents: includes GenCanvas, Modal
  • contentComponents: includes DataChart, DataTable, Alert, Progress, Spinner
  • inputComponents: includes all form input components

The organization is consistent and aligns with the component specs defined above.


106-109: Button disabled_if enhancement looks good.

The new disabled_if prop of type EXPRESSION enables conditional disabling, which is a useful pattern for form validation scenarios.


323-350: DataChart and DataTable specs are well-designed.

Both components properly define required props (data, and columns for DataTable) and optional configuration props. The use of PropType.EXPRESSION for data sources enables flexible state binding.

xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.kt (1)

32-71: LGTM! Dispatch table is complete and well-organized.

All 16 new component types are properly routed to their respective render functions, with clear section comments for P0 Form Input, Feedback, and GenUI components.

mpp-ui/src/jvmMain/kotlin/cc/unitmesh/devins/ui/nano/ComposeNanoRenderer.kt (1)

72-90: Ensure StatefulNanoRenderer handles all new component types added to ComposeNanoRenderer.

The new components (DatePicker, RadioGroup, NumberInput, Modal, Alert, Progress, Spinner, GenCanvas, SplitView, SmartTextField, Slider, DateRangePicker, DataChart, DataTable) are added to ComposeNanoRenderer.RenderNode but may not be handled in StatefulNanoRenderer.kt. Verify that StatefulNanoRenderer's RenderNode contains matching case branches for each new component type, or these will fall through to RenderUnknown at runtime.

Comment on lines 485 to 506
@Composable
fun RenderAlert(ir: NanoIR, modifier: Modifier = Modifier) {
val type = ir.props["type"]?.jsonPrimitive?.content ?: "info"
val message = ir.props["message"]?.jsonPrimitive?.content ?: ""
val backgroundColor = when (type) {
"success" -> Color(0xFF4CAF50)
"error" -> Color(0xFFF44336)
"warning" -> Color(0xFFFF9800)
else -> MaterialTheme.colorScheme.primaryContainer
}
Card(
modifier = modifier.fillMaxWidth(),
colors = CardDefaults.cardColors(containerColor = backgroundColor)
) {
Row(
modifier = Modifier.padding(16.dp),
verticalAlignment = Alignment.CenterVertically
) {
Text(text = message, modifier = Modifier.weight(1f))
}
}
}
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

Hardcoded colors violate coding guidelines.

RenderAlert uses hardcoded Color(0xFF...) values instead of design tokens. Per coding guidelines, use AutoDevColors (as done in RenderBadge on lines 241-248) or MaterialTheme.colorScheme.

Apply this diff to use design tokens:

     @Composable
     fun RenderAlert(ir: NanoIR, modifier: Modifier = Modifier) {
         val type = ir.props["type"]?.jsonPrimitive?.content ?: "info"
         val message = ir.props["message"]?.jsonPrimitive?.content ?: ""
         val backgroundColor = when (type) {
-            "success" -> Color(0xFF4CAF50)
-            "error" -> Color(0xFFF44336)
-            "warning" -> Color(0xFFFF9800)
+            "success" -> AutoDevColors.Signal.success
+            "error" -> AutoDevColors.Signal.error
+            "warning" -> AutoDevColors.Signal.warn
             else -> MaterialTheme.colorScheme.primaryContainer
         }

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +402 to +418
const RenderModal: React.FC<{ ir: NanoIR; context: NanoRenderContext }> = ({ ir, context }) => {
const title = ir.props.title;
const size = ir.props.size || 'md';
return (
<div className={`nano-modal size-${size}`}>
<div className="nano-modal-backdrop"></div>
<div className="nano-modal-content">
{title && <div className="nano-modal-header"><h3>{title}</h3><button className="nano-modal-close">×</button></div>}
<div className="nano-modal-body">
{ir.children?.map((child, i) => (
<RenderNode key={i} ir={child} context={context} />
))}
</div>
</div>
</div>
);
};
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

Non-functional Modal close button.

The close button in the Modal component has no onClick handler, making it non-functional. Consider adding a handler to dismiss the modal or remove the button if this is purely presentational.

 const RenderModal: React.FC<{ ir: NanoIR; context: NanoRenderContext }> = ({ ir, context }) => {
   const title = ir.props.title;
   const size = ir.props.size || 'md';
+  
+  const handleClose = () => {
+    // In preview mode, you might dispatch a close action or just hide
+    if (context.dispatch) {
+      context.dispatch({ type: 'CloseModal', modalId: ir.props.id });
+    }
+  };
+  
   return (
     <div className={`nano-modal size-${size}`}>
       <div className="nano-modal-backdrop"></div>
       <div className="nano-modal-content">
-        {title && <div className="nano-modal-header"><h3>{title}</h3><button className="nano-modal-close">×</button></div>}
+        {title && <div className="nano-modal-header"><h3>{title}</h3><button className="nano-modal-close" onClick={handleClose}>×</button></div>}
         <div className="nano-modal-body">
           {ir.children?.map((child, i) => (
             <RenderNode key={i} ir={child} context={context} />
           ))}
         </div>
       </div>
     </div>
   );
 };

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In mpp-vscode/webview/src/components/nano/NanoRenderer.tsx around lines 402 to
418, the modal's close button is rendered without any onClick handler so it does
nothing; add an onClick handler that calls the modal dismiss API on the provided
context (e.g., context.closeModal or context.dismiss with the modal id) or
toggles the modal-visible state passed via props, and ensure the handler is
bound (use an inline arrow or memoized callback) and that any required prop
(close function or id) is added to NanoRenderContext/NanoIR types; if the modal
is purely presentational instead, remove the button and associated markup
instead of adding a handler.

Comment on lines 420 to 433
const RenderAlert: React.FC<{ ir: NanoIR; context: NanoRenderContext }> = ({ ir, context }) => {
const type = ir.props.type || 'info';
const message = ir.props.message;
const closable = ir.props.closable || false;
return (
<div className={`nano-alert type-${type}`}>
{message && <span className="nano-alert-message">{message}</span>}
{ir.children?.map((child, i) => (
<RenderNode key={i} ir={child} context={context} />
))}
{closable && <button className="nano-alert-close">×</button>}
</div>
);
};
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

Non-functional Alert close button.

When closable is true, the Alert renders a close button without an onClick handler. Consider adding functionality or documenting that this is presentational only.

 const RenderAlert: React.FC<{ ir: NanoIR; context: NanoRenderContext }> = ({ ir, context }) => {
   const type = ir.props.type || 'info';
   const message = ir.props.message;
   const closable = ir.props.closable || false;
+  
+  const handleClose = () => {
+    // Dismiss alert logic
+    if (context.dispatch) {
+      context.dispatch({ type: 'DismissAlert', alertId: ir.props.id });
+    }
+  };
+  
   return (
     <div className={`nano-alert type-${type}`}>
       {message && <span className="nano-alert-message">{message}</span>}
       {ir.children?.map((child, i) => (
         <RenderNode key={i} ir={child} context={context} />
       ))}
-      {closable && <button className="nano-alert-close">×</button>}
+      {closable && <button className="nano-alert-close" onClick={handleClose}>×</button>}
     </div>
   );
 };

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In mpp-vscode/webview/src/components/nano/NanoRenderer.tsx around lines 420-433,
the Alert renders a close button when closable is true but has no onClick
handler so it does nothing; add a real close behavior by either wiring the
button to a provided onClose/onCloseNode prop in ir.props or, if none exists,
manage internal local state (useState) to hide/remove the alert when clicked and
add keyboard support (onKeyDown for Enter/Escape) and an accessible aria-label;
ensure the handler stops event propagation as needed and update the JSX to
conditionally render the button only when the handler exists or the component
manages state.

@augmentcode
Copy link

augmentcode bot commented Dec 16, 2025

🤖 Augment PR Summary

Summary: This PR expands NanoDSL with P0 form/feedback components plus GenUI foundation components, and introduces a component-definition file structure to improve maintainability.

Changes:

  • Added new AST node types for P0 inputs/feedback and GenUI components; extended Button with disabled_if.
  • Updated NanoSpecV1 schema sets and prompt templates to include the new component surface area.
  • Extended IndentParser to parse the new component syntax/arguments.
  • Extended NanoIRConverter to emit IR for all newly added nodes.
  • Updated renderers across HTML, Compose (JVM), and VSCode React webview to render the new IR types.
  • Added multiplatform decodeImageBytesToBitmap actual implementations for Android/iOS/WASM.

Technical Notes: Adds ComponentRegistry/ComponentDefinition and shared conversion utilities to help migrate components into per-component files and reduce duplication.

🤖 Was this summary useful? React with 👍 or 👎

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. 4 suggestions posted. Overall the component additions and cross-layer wiring look cohesive; comments focus on a few edge cases and action semantics.

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

type = "showToast",
payload = mapOf("message" to JsonPrimitive(action.message))
)
is NanoAction.Sequence -> NanoActionIR(
Copy link

Choose a reason for hiding this comment

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

For NanoAction.Sequence, convertAction currently drops the nested actions list, so sequence actions can’t be reconstructed/executed downstream. Consider encoding the actions array into the payload similarly to other action types.

🤖 Was this useful? React with 👍 or 👎

NanoNode.DateRangePicker(
name = nameArg ?: props["name"],
bind = bindArg?.let { Binding.parse(it) },
onChange = onClick
Copy link

Choose a reason for hiding this comment

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

The parser only collects on_click actions and assigns them to fields like onChange, so DSL props like on_change/on_row_click (present in NanoSpecV1) won’t take effect. This can make schema-valid NanoDSL silently lose the intended action behavior (also applies to other new components mapping onChange = onClick).

🤖 Was this useful? React with 👍 or 👎

val max = ir.props["max"]?.jsonPrimitive?.content?.toFloatOrNull() ?: 100f
val showText = ir.props["showText"]?.jsonPrimitive?.content?.toBoolean() ?: true
val status = ir.props["status"]?.jsonPrimitive?.content ?: "normal"
val percentage = ((value / max) * 100).toInt().coerceIn(0, 100)
Copy link

Choose a reason for hiding this comment

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

If max is 0 (or parses to 0), value / max can produce Infinity/NaN, leading to an invalid percentage and broken progress rendering. Consider guarding max <= 0 before computing percentage (this pattern also appears in the other renderers’ progress calculations).

🤖 Was this useful? React with 👍 or 👎


@Composable
fun RenderSplitView(ir: NanoIR, modifier: Modifier = Modifier) {
val ratio = ir.props["ratio"]?.jsonPrimitive?.content?.toFloatOrNull() ?: 0.5f
Copy link

Choose a reason for hiding this comment

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

If ratio is <= 0 or >= 1 (or otherwise out of range), Modifier.weight(ratio) / Modifier.weight(1f - ratio) can become 0/negative and may crash layout. Consider clamping/validating ratio to a safe positive range before using it.

🤖 Was this useful? React with 👍 or 👎

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds a comprehensive set of new components to the NanoDSL framework, organized into P0 core components and GenUI components (Tiers 1-3). The implementation extends the Button component with conditional disabling, adds form input components (DatePicker, Radio, RadioGroup, Switch, NumberInput), feedback components (Modal, Alert, Progress, Spinner), and GenUI components (GenCanvas, SplitView, SmartTextField, Slider, DateRangePicker, DataChart, DataTable). All components are integrated across the parsing, AST, IR conversion, and rendering layers for HTML, Compose, and React platforms.

Key changes include:

  • Addition of 17 new component types with full schema definitions and validation rules
  • Extension of all three renderers to support the new component types
  • Introduction of a new component file structure with ComponentDefinition interface and ComponentRegistry
  • Platform-specific implementations for image decoding across WASM, iOS, and Android
  • Updates to prompt templates documenting some of the new components

Reviewed changes

Copilot reviewed 18 out of 18 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
xiuper-ui/src/commonMain/resources/prompts/detailed.txt Updates documentation table with new component properties and P0 form inputs
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/spec/v1/NanoSpecV1.kt Adds component specifications for all new components with validation rules and enum constraints
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.kt Implements HTML rendering for all new components
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/parser/IndentParser.kt Adds parsing logic for new component syntax and property mapping
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverter.kt Implements IR conversion for new components with binding and action handling
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/input/DatePickerComponent.kt New component definition file demonstrating the component file architecture
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/input/ButtonComponent.kt Refactored Button component using new ComponentDefinition pattern
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/feedback/ModalComponent.kt New Modal component with the ComponentDefinition structure
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/ComponentRegistry.kt Central registry for component definitions (partial implementation)
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/ComponentDefinition.kt Interface defining the component definition contract
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/ComponentConverterUtils.kt Shared utilities for component conversion to avoid duplication
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ast/NanoNode.kt Adds AST node data classes for all new components
mpp-vscode/webview/src/components/nano/NanoRenderer.tsx Implements React rendering for all new components
mpp-ui/src/wasmJsMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.wasmJs.kt WASM platform-specific image decoding implementation
mpp-ui/src/jvmMain/kotlin/cc/unitmesh/devins/ui/nano/ComposeNanoRenderer.kt Implements Compose rendering for all new components
mpp-ui/src/iosMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.ios.kt iOS platform-specific image decoding implementation
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.kt Changes Dispatchers.IO to Dispatchers.Default for image loading
mpp-ui/src/androidMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.android.kt Android platform-specific image decoding implementation

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

Comment on lines +21 to +24
// TODO: Add more components as they are refactored
// Feedback components
ModalComponent.name to ModalComponent,
// TODO: Add Alert, Progress, Spinner, etc.
Copy link

Copilot AI Dec 16, 2025

Choose a reason for hiding this comment

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

The ComponentRegistry only registers a subset of the components (Button, DatePicker, Modal) despite many more components being added in this PR. This creates an inconsistency where the registry exists but is incomplete. Either all components should be registered, or the registry pattern should be fully removed if it's not being used. The TODO comments suggest this is work in progress, but the incomplete state may cause confusion about which component system to use.

Suggested change
// TODO: Add more components as they are refactored
// Feedback components
ModalComponent.name to ModalComponent,
// TODO: Add Alert, Progress, Spinner, etc.
TextFieldComponent.name to TextFieldComponent,
CheckboxComponent.name to CheckboxComponent,
RadioButtonComponent.name to RadioButtonComponent,
SwitchComponent.name to SwitchComponent,
SliderComponent.name to SliderComponent,
SelectComponent.name to SelectComponent,
// Feedback components
ModalComponent.name to ModalComponent,
AlertComponent.name to AlertComponent,
ProgressComponent.name to ProgressComponent,
SpinnerComponent.name to SpinnerComponent,

Copilot uses AI. Check for mistakes.
optionalProps = listOf(
PropSpec("checked", PropType.BINDING, description = "Switch state binding"),
PropSpec("label", PropType.STRING, description = "Switch label"),
PropSpec("size", PropType.ENUM, "md", allowedValues = SPACING_VALUES)
Copy link

Copilot AI Dec 16, 2025

Choose a reason for hiding this comment

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

The Switch component's size property uses SPACING_VALUES for validation, but SPACING_VALUES are defined for spacing purposes (typically "xs", "sm", "md", "lg", "xl" for margins and padding). For a Switch component size, these values might be appropriate, but consider if a dedicated SIZE_VALUES constant would be more semantically clear, or if the size property should use different allowed values specific to switch sizing.

Copilot uses AI. Check for mistakes.
PropSpec("intent", PropType.ENUM, "default", allowedValues = INTENT_VALUES),
PropSpec("icon", PropType.STRING)
PropSpec("icon", PropType.STRING),
PropSpec("disabled_if", PropType.EXPRESSION, description = "Conditional disable expression")
Copy link

Copilot AI Dec 16, 2025

Choose a reason for hiding this comment

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

The disabled_if property is added to the Button component specification and AST node, but it is not being rendered in any of the renderers (HtmlRenderer, ComposeNanoRenderer, React NanoRenderer). This means the functionality will not work when buttons are rendered. The property should be used to set the disabled attribute or state in all three renderers based on the expression evaluation.

Copilot uses AI. Check for mistakes.
"Progress" -> {
NanoNode.Progress(
value = args["value"]?.toFloatOrNull() ?: props["value"]?.toFloatOrNull(),
max = args["max"]?.toFloatOrNull() ?: props["max"]?.toFloatOrNull() ?: 100f,
Copy link

Copilot AI Dec 16, 2025

Choose a reason for hiding this comment

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

The Progress component's default value for max is specified as "100" in the spec, but in the IndentParser it defaults to 100f when the property is not provided. This creates an inconsistency where the default is only applied during parsing but not in the spec's default value. Consider whether this default should also be reflected in the NanoNode.Progress data class or if the spec and parser should be consistent in their default handling.

Suggested change
max = args["max"]?.toFloatOrNull() ?: props["max"]?.toFloatOrNull() ?: 100f,
max = args["max"]?.toFloatOrNull() ?: props["max"]?.toFloatOrNull(),

Copilot uses AI. Check for mistakes.
Comment on lines 593 to 600
Column(modifier = modifier) {
if (label != null) {
Text(text = label, style = MaterialTheme.typography.labelMedium)
Spacer(modifier = Modifier.height(4.dp))
}
MaterialSlider(
value = min,
onValueChange = {},
Copy link

Copilot AI Dec 16, 2025

Choose a reason for hiding this comment

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

The MaterialSlider component is initialized with value = min, which means the slider will always start at the minimum value instead of reflecting the actual bound value. This should use the actual value from the binding or state, not the minimum value.

Suggested change
Column(modifier = modifier) {
if (label != null) {
Text(text = label, style = MaterialTheme.typography.labelMedium)
Spacer(modifier = Modifier.height(4.dp))
}
MaterialSlider(
value = min,
onValueChange = {},
val defaultValue = ir.props["value"]?.jsonPrimitive?.content?.toFloatOrNull() ?: min
var sliderValue by androidx.compose.runtime.remember { androidx.compose.runtime.mutableStateOf(defaultValue.coerceIn(min, max)) }
Column(modifier = modifier) {
if (label != null) {
Text(text = label, style = MaterialTheme.typography.labelMedium)
Spacer(modifier = Modifier.height(4.dp))
}
MaterialSlider(
value = sliderValue,
onValueChange = { sliderValue = it },

Copilot uses AI. Check for mistakes.
Comment on lines +1043 to +1052
"DataChart" -> {
val nameArg = extractFirstArg(argsStr)
NanoNode.DataChart(
name = nameArg ?: props["name"],
type = args["type"] ?: props["type"],
data = args["data"] ?: props["data"],
xAxis = args["x_axis"] ?: props["x_axis"],
yAxis = args["y_axis"] ?: props["y_axis"],
color = args["color"] ?: props["color"]
)
Copy link

Copilot AI Dec 16, 2025

Choose a reason for hiding this comment

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

DataChart and DataTable specify data as a required property in the ComponentSpec, but the parser allows it to be null without validation. This inconsistency means the component could be created with missing required data, leading to potential runtime issues when the renderers try to access this property. Consider either making the property required in the AST node or removing the required constraint from the spec.

Copilot uses AI. Check for mistakes.
Update the logic to count only VStack/Card children and apply equal
space distribution when there are 2 or more such components, rather
than the previous approach that only handled the first child when
justify="between" and there were exactly 2 children.
- Update DEFAULT_PROMPT in NanoDSLAgent.kt with all new components
- Update STANDARD_PROMPT in ResourceLoader for all platforms (JS, WasmJS, iOS)
- Add comprehensive component list including:
  * Form inputs: DatePicker, RadioGroup, Switch, NumberInput, SmartTextField, Slider, DateRangePicker
  * Feedback: Modal, Alert, Progress, Spinner
  * Layout: GenCanvas, SplitView
  * Data: DataChart, DataTable
- Include usage examples and property descriptions
- Ensure LLM can generate code using new components

This fixes the issue where NanoDSLAgent was not generating code with newly added components.
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: 3

Caution

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

⚠️ Outside diff range comments (5)
xiuper-ui/src/wasmJsMain/kotlin/cc/unitmesh/xuiper/prompt/ResourceLoader.wasmJs.kt (2)

106-121: Update MINIMAL_PROMPT to include new components.

The MINIMAL_PROMPT still only lists the original components and doesn't include any of the new P0 or GenUI components added in this PR (DatePicker, Radio, RadioGroup, Switch, NumberInput, Modal, Alert, Progress, Spinner, GenCanvas, SplitView, SmartTextField, Slider, DateRangePicker, DataChart, DataTable). This creates inconsistency between the prompt templates.

Consider updating line 108 to include at least the most essential new components:

-Components: VStack, HStack, Card, Text, Button, Input, Image, Badge, Icon, Divider
+Components: VStack, HStack, Card, Text, Button, Input, Select, Checkbox, DatePicker, Switch, Image, Badge, Icon, Divider, Modal, Alert, Progress, Spinner

123-256: Update VERBOSE_PROMPT to include comprehensive documentation for new components.

The VERBOSE_PROMPT is missing all 17 new components that were added to STANDARD_PROMPT. Since VERBOSE_PROMPT is intended to provide the most comprehensive specification, it should include detailed documentation for all components, especially the new P0 and GenUI components (Form inputs, Feedback components, Data components, GenCanvas, SplitView, etc.).

The Component Reference section (lines 135-180) should be expanded to include:

  • Form Input Components: DatePicker, Radio, RadioGroup, Switch, NumberInput, SmartTextField, Slider, DateRangePicker, Select, Checkbox, TextArea
  • Action Components: Form, updated Button with disabled_if
  • Feedback Components: Modal, Alert, Progress, Spinner
  • Data Components: DataChart, DataTable
  • Layout Components: GenCanvas, SplitView
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.kt (1)

434-443: Use Dispatchers.IO instead of Dispatchers.Default for the HTTP request.

Dispatchers.Default is optimized for CPU-bound work with a limited thread pool (equal to CPU core count). Network I/O operations like client.get() should use Dispatchers.IO, which provides a much larger thread pool (64+ threads) designed for blocking I/O. Using Dispatchers.Default for network calls risks starving the thread pool if multiple concurrent image loads occur. This applies to all platforms in this multiplatform code, as Dispatchers.IO is now supported across Kotlin/JVM, Kotlin/Native, and other platforms.

mpp-vscode/webview/src/components/nano/NanoRenderer.tsx (1)

118-136: Check for non-numeric flex values.

Line 136 uses Number(flex) || 1, which defaults to 1 for non-numeric values. If flex contains invalid data (e.g., a non-numeric string), it will fall back to 1, which may mask configuration errors.

Consider adding validation or a warning when flex/weight values are invalid.

-        const flexValue = shouldApplyFlex ? (Number(flex) || 1) : undefined;
+        const parsedFlex = shouldApplyFlex && flex ? Number(flex) : undefined;
+        const flexValue = parsedFlex && !isNaN(parsedFlex) ? parsedFlex : (shouldApplyFlex ? 1 : undefined);
xiuper-ui/src/iosMain/kotlin/cc/unitmesh/xuiper/prompt/ResourceLoader.ios.kt (1)

11-11: Fix incorrect comment referencing WasmJs.

The comment mentions "For WasmJs" but this is the iOS implementation file (iosMain/kotlin). This appears to be a copy-paste error.

Apply this diff:

-        // For WasmJs, we embed the prompt templates directly
+        // For iOS, we embed the prompt templates directly
♻️ Duplicate comments (4)
mpp-vscode/webview/src/components/nano/NanoRenderer.tsx (4)

390-402: Previous issue: Non-functional increment/decrement buttons.

As noted in the previous review, the NumberInput buttons have no onClick handlers. This issue remains unaddressed.


408-424: Previous issue: Non-functional Modal close button.

As noted in the previous review, the Modal close button has no onClick handler. This issue remains unaddressed.


426-439: Previous issue: Non-functional Alert close button.

As noted in the previous review, the Alert close button has no onClick handler when closable is true. This issue remains unaddressed.


531-550: Previous issue: Complex objects in data attributes and incomplete implementation.

As noted in the previous review, DataChart and DataTable pass complex objects to HTML data-* attributes, which will stringify to "[object Object]". Additionally, DataTable contains placeholder comments. These issues remain unaddressed.

🧹 Nitpick comments (3)
xiuper-ui/src/jsMain/kotlin/cc/unitmesh/xuiper/prompt/ResourceLoader.js.kt (2)

41-53: Inconsistent binding syntax between standard and GenUI components.

Standard form components use value=binding or checked=binding (e.g., DatePicker, Switch), while GenUI components on lines 51-53 use bind=binding (e.g., SmartTextField, Slider, DateRangePicker). This inconsistency may confuse AI models or users generating DSL code.

Consider unifying the binding syntax, or if the distinction is intentional, add a brief note explaining when to use bind= vs value=/checked=.


123-134: VERBOSE_PROMPT is missing new component documentation.

The prompt claims to be a "Complete NanoDSL Specification" but doesn't include any of the 16 new components added in this PR (DatePicker, Radio, RadioGroup, Switch, NumberInput, Modal, Alert, Progress, Spinner, GenCanvas, SplitView, SmartTextField, Slider, DateRangePicker, DataChart, DataTable). Users selecting the verbose prompt won't have guidance for these components.

Consider updating VERBOSE_PROMPT to include the new components, or revising the "Complete" claim if the intent is to keep it focused on core components only.

Would you like me to help draft the additional component documentation sections to add to VERBOSE_PROMPT?

mpp-vscode/webview/src/components/nano/NanoRenderer.tsx (1)

408-424: Consider adding accessibility attributes to Modal and Alert.

Modal should include role="dialog", aria-modal="true", and aria-labelledby for the title. Alert should include role="alert" for screen reader announcements. Close buttons should have aria-label="Close".

 const RenderModal: React.FC<{ ir: NanoIR; context: NanoRenderContext }> = ({ ir, context }) => {
   const title = ir.props.title;
   const size = ir.props.size || 'md';
+  const titleId = `modal-title-${ir.props.id || 'default'}`;
   return (
-    <div className={`nano-modal size-${size}`}>
+    <div className={`nano-modal size-${size}`} role="dialog" aria-modal="true" aria-labelledby={titleId}>
       <div className="nano-modal-backdrop"></div>
       <div className="nano-modal-content">
-        {title && <div className="nano-modal-header"><h3>{title}</h3><button className="nano-modal-close">×</button></div>}
+        {title && <div className="nano-modal-header"><h3 id={titleId}>{title}</h3><button className="nano-modal-close" aria-label="Close">×</button></div>}
         <div className="nano-modal-body">
           {ir.children?.map((child, i) => (
             <RenderNode key={i} ir={child} context={context} />
           ))}
         </div>
       </div>
     </div>
   );
 };
 
 const RenderAlert: React.FC<{ ir: NanoIR; context: NanoRenderContext }> = ({ ir, context }) => {
   const type = ir.props.type || 'info';
   const message = ir.props.message;
   const closable = ir.props.closable || false;
   return (
-    <div className={`nano-alert type-${type}`}>
+    <div className={`nano-alert type-${type}`} role="alert">
       {message && <span className="nano-alert-message">{message}</span>}
       {ir.children?.map((child, i) => (
         <RenderNode key={i} ir={child} context={context} />
       ))}
-      {closable && <button className="nano-alert-close">×</button>}
+      {closable && <button className="nano-alert-close" aria-label="Close">×</button>}
     </div>
   );
 };

Also applies to: 426-439

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6cbc5c4 and 5b00c8b.

📒 Files selected for processing (6)
  • mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgent.kt (2 hunks)
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.kt (3 hunks)
  • mpp-vscode/webview/src/components/nano/NanoRenderer.tsx (4 hunks)
  • xiuper-ui/src/iosMain/kotlin/cc/unitmesh/xuiper/prompt/ResourceLoader.ios.kt (1 hunks)
  • xiuper-ui/src/jsMain/kotlin/cc/unitmesh/xuiper/prompt/ResourceLoader.js.kt (1 hunks)
  • xiuper-ui/src/wasmJsMain/kotlin/cc/unitmesh/xuiper/prompt/ResourceLoader.wasmJs.kt (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{kt,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Never use hardcoded colors - always use design tokens from the design system

Files:

  • mpp-vscode/webview/src/components/nano/NanoRenderer.tsx
  • xiuper-ui/src/iosMain/kotlin/cc/unitmesh/xuiper/prompt/ResourceLoader.ios.kt
  • mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgent.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.kt
  • xiuper-ui/src/wasmJsMain/kotlin/cc/unitmesh/xuiper/prompt/ResourceLoader.wasmJs.kt
  • xiuper-ui/src/jsMain/kotlin/cc/unitmesh/xuiper/prompt/ResourceLoader.js.kt
**/*.kt

📄 CodeRabbit inference engine (AGENTS.md)

**/*.kt: Use expect/actual pattern for platform-specific code (for example: Platform implementations)
Use concrete classes (not interfaces) and Promise (not Flow) for @JsExport annotations
Avoid emoji and UTF-8 characters in WASM code
When modifying CodingAgentRenderer, update ALL implementations: DefaultCodingAgentRenderer, ComposeRenderer, JewelRenderer, ServerSideRenderer, and JsRendererAdapter in Kotlin
When modifying CodingAgentRenderer, update JVM CLI implementations: CodingCliRenderer and ConsoleRenderer
Use AutoDevColors or MaterialTheme.colorScheme for Compose styling

Files:

  • xiuper-ui/src/iosMain/kotlin/cc/unitmesh/xuiper/prompt/ResourceLoader.ios.kt
  • mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgent.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.kt
  • xiuper-ui/src/wasmJsMain/kotlin/cc/unitmesh/xuiper/prompt/ResourceLoader.wasmJs.kt
  • xiuper-ui/src/jsMain/kotlin/cc/unitmesh/xuiper/prompt/ResourceLoader.js.kt
🧠 Learnings (4)
📚 Learning: 2025-12-07T08:40:10.859Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Applies to mpp-vscode/src/**/*.ts : When modifying `CodingAgentRenderer`, update VSCode implementations: `mpp-vscode/src/bridge/mpp-core.ts` and `mpp-vscode/src/providers/chat-view.ts`

Applied to files:

  • mpp-vscode/webview/src/components/nano/NanoRenderer.tsx
📚 Learning: 2025-12-07T08:40:10.859Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Applies to **/*.ts : When modifying `CodingAgentRenderer`, update ALL TypeScript implementations: `BaseRenderer.ts`, `CliRenderer.ts`, `ServerRenderer.ts`, and `TuiRenderer.ts`

Applied to files:

  • mpp-vscode/webview/src/components/nano/NanoRenderer.tsx
📚 Learning: 2025-12-07T08:40:10.859Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Applies to **/*.kt : When modifying `CodingAgentRenderer`, update ALL implementations: `DefaultCodingAgentRenderer`, `ComposeRenderer`, `JewelRenderer`, `ServerSideRenderer`, and `JsRendererAdapter` in Kotlin

Applied to files:

  • mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgent.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.kt
📚 Learning: 2025-12-07T08:40:10.859Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Applies to **/*.kt : When modifying `CodingAgentRenderer`, update JVM CLI implementations: `CodingCliRenderer` and `ConsoleRenderer`

Applied to files:

  • mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgent.kt
🧬 Code graph analysis (1)
mpp-vscode/webview/src/components/nano/NanoRenderer.tsx (2)
mpp-ui/src/jvmMain/kotlin/cc/unitmesh/devins/ui/nano/ComposeNanoRenderer.kt (17)
  • RenderDatePicker (383-397)
  • RenderRadio (399-411)
  • RenderRadioGroup (413-420)
  • RenderSwitch (422-435)
  • RenderNumberInput (437-458)
  • RenderModal (464-483)
  • RenderAlert (485-506)
  • RenderProgress (508-527)
  • RenderSpinner (529-542)
  • RenderGenCanvas (548-555)
  • RenderSplitView (557-568)
  • RenderSmartTextField (570-586)
  • RenderSlider (588-605)
  • RenderDateRangePicker (607-616)
  • RenderDataChart (618-631)
  • RenderDataTable (633-645)
  • RenderNode (52-98)
mpp-vscode/webview/src/types/nano.ts (2)
  • NanoIR (56-80)
  • NanoRenderContext (85-94)
⏰ 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: Test mpp-core (KMP)
  • GitHub Check: Build
🔇 Additional comments (15)
xiuper-ui/src/jsMain/kotlin/cc/unitmesh/xuiper/prompt/ResourceLoader.js.kt (1)

26-67: LGTM on the STANDARD_PROMPT component documentation structure.

The new component sections are well-organized with clear categorization (Layout, Display, Form Input, Action, Feedback, Data). All components from the PR objectives are documented with appropriate props and examples.

xiuper-ui/src/wasmJsMain/kotlin/cc/unitmesh/xuiper/prompt/ResourceLoader.wasmJs.kt (2)

1-10: AI summary is inconsistent with the actual code.

The AI summary describes "decodeImageBytesToBitmap" implementation for WASM/JS, but this file actually contains a ResourceLoader implementation with embedded prompt templates. This mismatch suggests the summary may be referring to a different file.


26-68: LGTM! New component documentation is comprehensive.

The STANDARD_PROMPT template has been properly updated with all the new P0 and GenUI components mentioned in the PR objectives. The documentation includes clear syntax examples and prop descriptions for each component category.

mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.kt (3)

203-207: LGTM on the auto-distribution logic.

The logic correctly counts VStack/Card siblings to determine space distribution. Using a threshold of 2 or more children makes sense for equal-weight distribution.


225-229: LGTM on the conditional weight distribution.

The implementation correctly applies equal weights only to VStack/Card children when auto-distribution is enabled, preserving explicit flex/weight properties from the first condition.


780-780: Good use of expect/actual pattern for platform-specific image decoding.

This follows the coding guideline for platform-specific code in Kotlin Multiplatform. Based on the AI summary, the actual implementations exist for Android, iOS, and WASM.

mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgent.kt (7)

514-526: LGTM on the expanded Layout and Display component documentation.

The new components (GenCanvas, SplitView, Divider) are documented with clear parameter syntax. The component list aligns with the PR objectives.


528-541: LGTM on Form Input components documentation.

Comprehensive documentation for the new P0 form input components (DatePicker, Radio, RadioGroup, Switch, NumberInput) and GenUI components (SmartTextField, Slider, DateRangePicker).


543-555: LGTM on Action, Feedback, and Data components documentation.

The new feedback components (Modal, Alert, Progress, Spinner) and data components (DataChart, DataTable) are well-documented with appropriate parameters.


557-569: State management example uses the new component style correctly.

The example demonstrates proper 4-space indentation and shows state mutation via on_click.


571-580: Bindings documentation is clear and consistent.

The distinction between one-way (<<) and two-way (:=) bindings is well-explained with practical examples.


582-603: HTTP request documentation is comprehensive.

The enhanced Fetch action with headers, on_success, and on_error callbacks is well-documented. HTTP methods and content types are also listed.


605-622: LGTM on Conditional Rendering and Size Tokens sections.

The new sections provide useful reference for conditional rendering syntax and available tokens. The output rules correctly specify 4-space indentation and nanodsl code fence wrapping.

mpp-vscode/webview/src/components/nano/NanoRenderer.tsx (1)

69-87: LGTM! Well-organized component dispatch.

The new component cases are cleanly organized with category comments and follow the existing pattern consistently.

xiuper-ui/src/iosMain/kotlin/cc/unitmesh/xuiper/prompt/ResourceLoader.ios.kt (1)

26-39: LGTM! Well-organized layout and display sections.

The restructuring into "Layout Components" and "Display Components" improves clarity, and the new GenCanvas and SplitView components are properly documented with their key properties.

Comment on lines 353 to 356
const RenderDatePicker: React.FC<{ ir: NanoIR; context: NanoRenderContext }> = ({ ir, context }) => {
const placeholder = ir.props.placeholder || 'Select date';
return <input type="date" className="nano-datepicker" placeholder={placeholder} />;
};
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

Placeholder may not display on date inputs.

Most browsers ignore the placeholder attribute on input[type="date"]. Consider using a label or helper text instead.

 const RenderDatePicker: React.FC<{ ir: NanoIR; context: NanoRenderContext }> = ({ ir, context }) => {
   const placeholder = ir.props.placeholder || 'Select date';
-  return <input type="date" className="nano-datepicker" placeholder={placeholder} />;
+  const label = ir.props.label || placeholder;
+  return (
+    <div className="nano-datepicker-wrapper">
+      {label && <label className="nano-datepicker-label">{label}</label>}
+      <input type="date" className="nano-datepicker" />
+    </div>
+  );
 };
🤖 Prompt for AI Agents
In mpp-vscode/webview/src/components/nano/NanoRenderer.tsx around lines 353 to
356, the code sets a placeholder on an input[type="date"] which most browsers
ignore; change the rendering to surface the intended helper text as either a
visible label or helper text adjacent to the date input and keep accessibility
attributes: create a <label> tied to the input's id when ir.props.placeholder
exists (or render an adjacent <span className="hint"> with the helper text),
ensure the input has an id and appropriate aria-describedby or aria-label, and
remove reliance on the placeholder for conveying the instruction.

Comment on lines 497 to 507
const RenderSmartTextField: React.FC<{ ir: NanoIR; context: NanoRenderContext }> = ({ ir, context }) => {
const label = ir.props.label;
const placeholder = ir.props.placeholder || '';
const validation = ir.props.validation;
return (
<div className="nano-smarttextfield">
{label && <label className="nano-smarttextfield-label">{label}</label>}
<input type="text" className="nano-smarttextfield-input" placeholder={placeholder} pattern={validation} />
</div>
);
};
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

Validate regex pattern before use.

Line 504 passes ir.props.validation directly to the HTML pattern attribute. If validation contains an invalid regex or is not a string, it could cause unexpected behavior.

 const RenderSmartTextField: React.FC<{ ir: NanoIR; context: NanoRenderContext }> = ({ ir, context }) => {
   const label = ir.props.label;
   const placeholder = ir.props.placeholder || '';
   const validation = ir.props.validation;
+  const pattern = typeof validation === 'string' ? validation : undefined;
   return (
     <div className="nano-smarttextfield">
       {label && <label className="nano-smarttextfield-label">{label}</label>}
-      <input type="text" className="nano-smarttextfield-input" placeholder={placeholder} pattern={validation} />
+      <input type="text" className="nano-smarttextfield-input" placeholder={placeholder} pattern={pattern} />
     </div>
   );
 };
🤖 Prompt for AI Agents
In mpp-vscode/webview/src/components/nano/NanoRenderer.tsx around lines 497 to
507, the code assigns ir.props.validation directly to the input's pattern
attribute which can be non-string or an invalid regular expression; validate
that validation is a non-empty string and construct a RegExp inside a try/catch
to confirm it’s valid before passing it to the pattern prop, and only set the
pattern attribute when validation passes (otherwise omit it and optionally log
or warn about the invalid pattern).

Comment on lines 41 to 67
### Form Input Components
- `Input(value=binding, placeholder="...", type="text|email|password")` - Text input
- `TextArea(value=binding, placeholder="...", rows=4)` - Multi-line text input
- `Select(value=binding, options=[...], placeholder="...")` - Dropdown select
- `Checkbox(checked=binding, label="...")` - Checkbox input
- `DatePicker(value=binding, format="YYYY-MM-DD", placeholder="...")` - Date picker
- `Radio(option="value", label="...", name="group")` - Single radio button
- `RadioGroup(value=binding, options=[...], name="group")` - Radio button group
- `Switch(checked=binding, label="...", size="sm|md")` - Toggle switch
- `NumberInput(value=binding, min=0, max=100, step=1, placeholder="...")` - Number input with +/- buttons
- `SmartTextField(label="...", bind=binding, validation="...", placeholder="...")` - Text input with validation
- `Slider(label="...", bind=binding, min=0, max=100, step=1)` - Range slider
- `DateRangePicker(bind=binding)` - Date range picker

### Action Components
- `Button("label", intent="primary|secondary|danger", icon="...", disabled_if="condition")` - Clickable button
- `Form(onSubmit=action)` - Form container

### Feedback Components
- `Modal(open=binding, title="...", size="sm|md|lg", closable=true)` - Modal dialog
- `Alert(type="info|success|error|warning", message="...", closable=true)` - Alert banner
- `Progress(value=binding, max=100, showText=true, status="normal|success|error")` - Progress bar
- `Spinner(size="sm|md|lg", text="...")` - Loading spinner

### Data Components
- `DataChart(type="line|bar|pie", data=binding)` - Data chart visualization
- `DataTable(columns=[...], data=binding)` - Data table
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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Locate and read the ResourceLoader.ios.kt file
find . -type f -name "ResourceLoader.ios.kt" 2>/dev/null | head -5

Repository: phodal/auto-dev

Length of output: 137


🏁 Script executed:

#!/bin/bash
# Read the ResourceLoader.ios.kt file and show lines around 41-67
cat -n ./xiuper-ui/src/iosMain/kotlin/cc/unitmesh/xuiper/prompt/ResourceLoader.ios.kt | sed -n '35,75p'

Repository: phodal/auto-dev

Length of output: 2524


🏁 Script executed:

#!/bin/bash
# Search for component class definitions and their property names
echo "=== Searching for component implementations ==="

# Look for SmartTextField, DatePicker, Slider implementations
rg -n "class SmartTextField|class DatePicker|class Slider|class RadioGroup|class NumberInput" --type=kotlin -A 5

echo -e "\n=== Searching for bind/value property patterns ==="
# Search for property definitions
rg -n "val (value|bind|checked):" --type=kotlin -B 2 -A 2 | head -50

Repository: phodal/auto-dev

Length of output: 7627


🏁 Script executed:

#!/bin/bash
# Search for DateRangePicker to verify its property name
echo "=== Searching for DateRangePicker ==="
rg -n "class DateRangePicker" --type=kotlin -A 5

# Also search for other feedback components
echo -e "\n=== Searching for Modal, Alert, Progress, Spinner ==="
rg -n "class Modal|class Alert|class Progress|class Spinner" --type=kotlin -A 5 | grep -A 5 "xiuper-ui"

Repository: phodal/auto-dev

Length of output: 3577


Fix component documentation to match actual implementations.

Verification against the actual component definitions in NanoNode.kt reveals two critical documentation errors:

  1. Progress component (line 62): Documentation shows value=binding, but the actual implementation uses value: Float? (numeric, not binding). Should be Progress(value=0-100, max=100, showText=true, status="normal|success|error").

  2. Alert component (line 61): Documentation doesn't reflect that Alert has no binding properties. The actual implementation only supports type, message, closable, icon, and onClose. Remove any binding examples for this component.

  3. Modal component (line 60): Confirm the open=binding property is correct; actual implementation uses open: Binding?.

The binding syntax inconsistency (some components using bind= vs value=) is actually intentional and matches the implementations—this is correct and doesn't require changes.

🤖 Prompt for AI Agents
In xiuper-ui/src/iosMain/kotlin/cc/unitmesh/xuiper/prompt/ResourceLoader.ios.kt
around lines 41 to 67, update the component docs to match NanoNode.kt: change
Progress to show numeric value usage (e.g., Progress(value=0-100, max=100,
showText=true, status="normal|success|error") instead of value=binding), remove
any binding examples for Alert and document its actual props (type, message,
closable, icon, onClose — no binding), and confirm/keep Modal documented as
open=binding since the implementation uses open: Binding?; do not change the
intentional bind= vs value= wording for other components.

- Remove GenCanvas from all layers (spec, AST, parser, IR, renderers)
- Remove GenCanvas from prompt templates across all platforms
- Simplify layout structure - SplitView can be used directly
- Remove redundant LAYOUT_VALUES constant

Rationale:
- GenCanvas name was confusing and non-intuitive
- Created redundancy: GenCanvas(layout="SplitView") + SplitView(ratio=0.4)
- Functionality can be achieved with existing components
- Simplifies the DSL and reduces component count

Usage before:
  GenCanvas(layout="SplitView"):
      SplitView(ratio=0.4):
          ...

Usage after:
  SplitView(ratio=0.4):
      ...

Addresses user feedback about 'Unknown GenCanvas' errors.
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

♻️ Duplicate comments (18)
xiuper-ui/src/iosMain/kotlin/cc/unitmesh/xuiper/prompt/ResourceLoader.ios.kt (2)

61-61: Alert documentation is incomplete.

The Alert component documentation is missing the icon and onClose properties that exist in the actual implementation (per previous review findings). The complete property set should be: type, message, closable, icon, onClose.

Apply this diff to complete the Alert documentation:

-- `Alert(type="info|success|error|warning", message="...", closable=true)` - Alert banner
+- `Alert(type="info|success|error|warning", message="...", closable=true, icon="...", onClose=action)` - Alert banner

62-62: Progress component documentation still incorrect.

The Progress component uses value: Float? (numeric, not binding) in the actual implementation. The documentation should show numeric value usage instead of binding syntax.

Apply this diff to fix the Progress documentation:

-- `Progress(value=binding, max=100, showText=true, status="normal|success|error")` - Progress bar
+- `Progress(value=50, max=100, showText=true, status="normal|success|error")` - Progress bar (value is numeric 0-100)
mpp-ui/src/jvmMain/kotlin/cc/unitmesh/devins/ui/nano/ComposeNanoRenderer.kt (3)

484-505: Hardcoded colors violate coding guidelines.

Lines 489-491 use hardcoded Color(0xFF...) values instead of design tokens. Per coding guidelines, use AutoDevColors.Signal (as demonstrated in RenderBadge on lines 241-248) or MaterialTheme.colorScheme.

Apply this diff to use design tokens:

     @Composable
     fun RenderAlert(ir: NanoIR, modifier: Modifier = Modifier) {
         val type = ir.props["type"]?.jsonPrimitive?.content ?: "info"
         val message = ir.props["message"]?.jsonPrimitive?.content ?: ""
         val backgroundColor = when (type) {
-            "success" -> Color(0xFF4CAF50)
-            "error" -> Color(0xFFF44336)
-            "warning" -> Color(0xFFFF9800)
+            "success" -> AutoDevColors.Signal.success
+            "error" -> AutoDevColors.Signal.error
+            "warning" -> AutoDevColors.Signal.warn
             else -> MaterialTheme.colorScheme.primaryContainer
         }

As per coding guidelines: "Never use hardcoded colors - always use design tokens from the design system."


547-558: Validate ratio to prevent layout crashes.

If ratio is ≤ 0 or ≥ 1, Modifier.weight(ratio) or Modifier.weight(1f - ratio) can become 0 or negative, potentially causing layout crashes. Consider clamping ratio to a safe positive range (e.g., 0.1f..0.9f).

Apply this diff to add validation:

     @Composable
     fun RenderSplitView(ir: NanoIR, modifier: Modifier = Modifier) {
-        val ratio = ir.props["ratio"]?.jsonPrimitive?.content?.toFloatOrNull() ?: 0.5f
+        val ratio = (ir.props["ratio"]?.jsonPrimitive?.content?.toFloatOrNull() ?: 0.5f).coerceIn(0.1f, 0.9f)
         Row(modifier = modifier.fillMaxWidth()) {
             Box(modifier = Modifier.weight(ratio)) {
                 ir.children?.firstOrNull()?.let { RenderNode(it) }
             }
             Box(modifier = Modifier.weight(1f - ratio)) {
                 ir.children?.getOrNull(1)?.let { RenderNode(it) }
             }
         }
     }

578-595: Slider initialization uses min instead of actual value.

Line 589 initializes the MaterialSlider with value = min, meaning the slider always starts at the minimum value instead of reflecting the actual bound value from props. This should use the actual value if provided.

Apply this diff to use the actual value:

     @Composable
     fun RenderSlider(ir: NanoIR, modifier: Modifier = Modifier) {
         val label = ir.props["label"]?.jsonPrimitive?.content
         val min = ir.props["min"]?.jsonPrimitive?.content?.toFloatOrNull() ?: 0f
         val max = ir.props["max"]?.jsonPrimitive?.content?.toFloatOrNull() ?: 100f
+        val defaultValue = ir.props["value"]?.jsonPrimitive?.content?.toFloatOrNull() ?: min
         Column(modifier = modifier) {
             if (label != null) {
                 Text(text = label, style = MaterialTheme.typography.labelMedium)
                 Spacer(modifier = Modifier.height(4.dp))
             }
             MaterialSlider(
-                value = min,
+                value = defaultValue.coerceIn(min, max),
                 onValueChange = {},
                 valueRange = min..max,
                 modifier = Modifier.fillMaxWidth()
             )
         }
     }
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/parser/IndentParser.kt (3)

916-916: onClick mapped to onChange limits DSL expressiveness.

The parser collects only on_click actions but assigns them to onChange fields. DSL props like on_change won't be recognized, silently losing the intended action behavior. This applies to DatePicker, Switch, NumberInput, Slider, and DateRangePicker.


980-987: Progress max default handling inconsistency.

The parser defaults max to 100f when not provided, but this default is only applied during parsing and not reflected in NanoNode.Progress. Consider whether the default should be in the data class or if the spec's default value should be null to match.


1035-1054: data property marked required in spec but nullable in parser.

Both DataChart and DataTable specify data as a required property in the ComponentSpec, but the parser allows it to be null without validation. This could lead to runtime issues when renderers access this property.

xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.kt (2)

399-411: Guard against division by zero in progress percentage.

If max is 0 (or parses to 0), value / max produces Infinity/NaN, resulting in an invalid percentage and broken rendering.

     fun renderProgress(ir: NanoIR): String {
         val value = ir.props["value"]?.jsonPrimitive?.content?.toFloatOrNull() ?: 0f
         val max = ir.props["max"]?.jsonPrimitive?.content?.toFloatOrNull() ?: 100f
         val showText = ir.props["showText"]?.jsonPrimitive?.content?.toBoolean() ?: true
         val status = ir.props["status"]?.jsonPrimitive?.content ?: "normal"
-        val percentage = ((value / max) * 100).toInt().coerceIn(0, 100)
+        val percentage = if (max > 0f) ((value / max) * 100).toInt().coerceIn(0, 100) else 0

476-492: Null values rendered as literal "null" in data attributes.

When data or columns props are missing, the nullable ?.content returns null, which is interpolated as the string "null" in the HTML output (e.g., data-data="null"). This may cause client-side parsing issues.

     fun renderDataChart(ir: NanoIR): String {
         val type = ir.props["type"]?.jsonPrimitive?.content ?: "line"
         val data = ir.props["data"]?.jsonPrimitive?.content
-        return "<div class=\"nano-datachart type-$type\" data-data=\"$data\"><!-- Chart will be rendered by client-side library --></div>\n"
+        val dataAttr = if (data != null) " data-data=\"$data\"" else ""
+        return "<div class=\"nano-datachart type-$type\"$dataAttr><!-- Chart will be rendered by client-side library --></div>\n"
     }
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/spec/v1/NanoSpecV1.kt (2)

184-194: Reconsider the size property validation values for Switch.

The Switch component's size property uses SPACING_VALUES for validation, but these values are defined for spacing purposes (margins, padding). While the values may work, consider whether a dedicated size constant (e.g., SIZE_VALUES) would be more semantically appropriate for component sizing.


106-107: Ensure disabled_if is implemented in all renderers.

The disabled_if property specification is added here, but past reviews indicate it's not being rendered in HtmlRenderer, ComposeNanoRenderer, or React NanoRenderer. The property should be evaluated and used to set the disabled state in all three renderers.

Run the following script to verify if disabled_if has been implemented in the renderers:

#!/bin/bash
# Description: Check if disabled_if property is handled in all three renderers

echo "=== Checking HtmlRenderer ==="
rg -n "disabled_if" --type=kt -g "*HtmlRenderer*"

echo -e "\n=== Checking ComposeNanoRenderer ==="
rg -n "disabled_if" --type=kt -g "*ComposeNanoRenderer*"

echo -e "\n=== Checking React NanoRenderer ==="
rg -n "disabled_if" --type=kt -g "*React*Renderer*" -g "*.kt"

echo -e "\n=== Checking TypeScript/TSX renderers ==="
rg -n "disabled_if" --type=ts --type=tsx
mpp-vscode/webview/src/components/nano/NanoRenderer.tsx (6)

352-355: Placeholder may not display on date inputs.

This issue was already noted in a previous review. Most browsers ignore the placeholder attribute on input[type="date"].


389-401: Non-functional increment/decrement buttons.

This issue was already noted in a previous review. The buttons have no onClick handlers making them non-functional.


407-423: Non-functional Modal close button.

This issue was already noted in a previous review. The close button has no onClick handler.


425-438: Non-functional Alert close button.

This issue was already noted in a previous review. When closable is true, the close button has no onClick handler.


485-495: Validate regex pattern before use.

This issue was already noted in a previous review. The validation prop is passed directly to the pattern attribute without type checking.


519-538: Data attributes won't work for complex objects.

This issue was already noted in a previous review. Both DataChart and DataTable pass potentially complex objects/arrays to HTML data-* attributes, which will be stringified to "[object Object]". Additionally, the DataTable has placeholder comments indicating incomplete implementation.

🧹 Nitpick comments (8)
mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgent.kt (3)

527-540: Inconsistent binding parameter naming and syntax.

The form input components have two inconsistencies:

  1. Parameter naming: Some components use value=binding (Input, DatePicker, NumberInput) while others use bind=binding (SmartTextField, Slider, DateRangePicker).

  2. Binding syntax: The component examples here show value=binding but the Bindings section (line 577) demonstrates Input(value := state.name) with the := operator.

Consider standardizing to help the LLM generate consistent code:

-### Form Input Components
-- `Input(value=binding, placeholder="...", type="text|email|password")` - Text input
+### Form Input Components
+- `Input(value := binding, placeholder="...", type="text|email|password")` - Text input
 ...
-- `SmartTextField(label="...", bind=binding, validation="...", placeholder="...")` - Text input with validation
-- `Slider(label="...", bind=binding, min=0, max=100, step=1)` - Range slider
-- `DateRangePicker(bind=binding)` - Date range picker
+- `SmartTextField(label="...", value := binding, validation="...", placeholder="...")` - Text input with validation
+- `Slider(label="...", value := binding, min=0, max=100, step=1)` - Range slider
+- `DateRangePicker(value := binding)` - Date range picker

551-554: Data binding syntax inconsistency.

The Data Components use data=binding but per the Bindings section (line 571-572), one-way binding should use << syntax. Since data visualization components are typically read-only, they should follow the one-way binding pattern:

 ### Data Components
-- `DataChart(type="line|bar|pie", data=binding)` - Data chart visualization
-- `DataTable(columns=[...], data=binding)` - Data table
+- `DataChart(type="line|bar|pie", data << binding)` - Data chart visualization
+- `DataTable(columns=[...], data << binding)` - Data table

556-579: Text binding syntax inconsistency between examples.

The State Management example (line 565) shows:

Text("Count: {count}")

But the Bindings example (line 576) shows:

Text(content << f"{state.count}")

Consider aligning these examples for consistency. Either update line 565 to use the explicit binding syntax, or clarify when each form is appropriate:

     Card:
         VStack:
-            Text("Count: {count}")
+            Text(content << f"Count: {state.count}")
             Button("Increment"):
                 on_click: state.count += 1
mpp-ui/src/jvmMain/kotlin/cc/unitmesh/devins/ui/nano/ComposeNanoRenderer.kt (1)

398-410: Remove unused property extraction.

Line 401 extracts the option property but never uses it. Consider removing this unused variable.

Apply this diff:

     @Composable
     fun RenderRadio(ir: NanoIR, modifier: Modifier = Modifier) {
         val label = ir.props["label"]?.jsonPrimitive?.content ?: ""
-        val option = ir.props["option"]?.jsonPrimitive?.content ?: ""
         Row(
             modifier = modifier,
             verticalAlignment = Alignment.CenterVertically
         ) {
             RadioButton(selected = false, onClick = {})
             Spacer(modifier = Modifier.width(8.dp))
             Text(text = label)
         }
     }
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverter.kt (1)

321-389: Consider consolidating convertBinding and convertAction with ComponentConverterUtils.

These helper methods duplicate logic in ComponentConverterUtils. Consider delegating to the utils class to avoid drift between implementations.

+import cc.unitmesh.xuiper.components.ComponentConverterUtils
+
 private fun convertBinding(binding: Binding): NanoBindingIR {
-    return when (binding) {
-        is Binding.Subscribe -> NanoBindingIR(
-            mode = "subscribe",
-            expression = binding.expression
-        )
-        // ... rest of implementation
-    }
+    return ComponentConverterUtils.convertBinding(binding)
 }
 
 private fun convertAction(action: NanoAction): NanoActionIR {
-    return when (action) {
-        // ... implementation
-    }
+    return ComponentConverterUtils.convertAction(action)
 }
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/spec/v1/NanoSpecV1.kt (1)

247-256: Consider dedicated size values for Spinner.

Similar to the Switch component, Spinner's size property uses SPACING_VALUES, which are semantically intended for spacing (margins, padding) rather than component sizing. While functional, a dedicated SIZE_VALUES constant would be more semantically clear.

mpp-vscode/webview/src/components/nano/NanoRenderer.tsx (1)

133-135: Consider handling explicit flex: 0 values.

Line 135 uses Number(flex) || 1, which means an explicit flex: 0 would be coerced to 1. While flex: 0 is rare, it's a valid CSS value that prevents a flex item from growing.

-        const flexValue = shouldApplyFlex ? (Number(flex) || 1) : undefined;
+        const flexValue = shouldApplyFlex 
+          ? (flex !== undefined && flex !== null ? Number(flex) : 1) 
+          : undefined;
xiuper-ui/src/wasmJsMain/kotlin/cc/unitmesh/xuiper/prompt/ResourceLoader.wasmJs.kt (1)

105-255: Consider updating MINIMAL_PROMPT and VERBOSE_PROMPT for consistency.

While STANDARD_PROMPT was expanded to document the new components, MINIMAL_PROMPT and VERBOSE_PROMPT were not updated. For consistency across all prompt variants, consider:

  1. Adding a brief mention of the new component categories to MINIMAL_PROMPT (while keeping it minimal)
  2. Updating VERBOSE_PROMPT's Component Reference section to include the new Form Input, Feedback, and Data components

This is optional if the intention is to keep these prompts stable and focused on core components only.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5b00c8b and bd1df01.

📒 Files selected for processing (11)
  • mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgent.kt (2 hunks)
  • mpp-ui/src/jvmMain/kotlin/cc/unitmesh/devins/ui/nano/ComposeNanoRenderer.kt (3 hunks)
  • mpp-vscode/webview/src/components/nano/NanoRenderer.tsx (4 hunks)
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ast/NanoNode.kt (2 hunks)
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverter.kt (3 hunks)
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/parser/IndentParser.kt (2 hunks)
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.kt (2 hunks)
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/spec/v1/NanoSpecV1.kt (4 hunks)
  • xiuper-ui/src/iosMain/kotlin/cc/unitmesh/xuiper/prompt/ResourceLoader.ios.kt (1 hunks)
  • xiuper-ui/src/jsMain/kotlin/cc/unitmesh/xuiper/prompt/ResourceLoader.js.kt (1 hunks)
  • xiuper-ui/src/wasmJsMain/kotlin/cc/unitmesh/xuiper/prompt/ResourceLoader.wasmJs.kt (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{kt,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Never use hardcoded colors - always use design tokens from the design system

Files:

  • mpp-vscode/webview/src/components/nano/NanoRenderer.tsx
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverter.kt
  • xiuper-ui/src/wasmJsMain/kotlin/cc/unitmesh/xuiper/prompt/ResourceLoader.wasmJs.kt
  • xiuper-ui/src/iosMain/kotlin/cc/unitmesh/xuiper/prompt/ResourceLoader.ios.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/parser/IndentParser.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ast/NanoNode.kt
  • mpp-ui/src/jvmMain/kotlin/cc/unitmesh/devins/ui/nano/ComposeNanoRenderer.kt
  • mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgent.kt
  • xiuper-ui/src/jsMain/kotlin/cc/unitmesh/xuiper/prompt/ResourceLoader.js.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/spec/v1/NanoSpecV1.kt
**/*.kt

📄 CodeRabbit inference engine (AGENTS.md)

**/*.kt: Use expect/actual pattern for platform-specific code (for example: Platform implementations)
Use concrete classes (not interfaces) and Promise (not Flow) for @JsExport annotations
Avoid emoji and UTF-8 characters in WASM code
When modifying CodingAgentRenderer, update ALL implementations: DefaultCodingAgentRenderer, ComposeRenderer, JewelRenderer, ServerSideRenderer, and JsRendererAdapter in Kotlin
When modifying CodingAgentRenderer, update JVM CLI implementations: CodingCliRenderer and ConsoleRenderer
Use AutoDevColors or MaterialTheme.colorScheme for Compose styling

Files:

  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverter.kt
  • xiuper-ui/src/wasmJsMain/kotlin/cc/unitmesh/xuiper/prompt/ResourceLoader.wasmJs.kt
  • xiuper-ui/src/iosMain/kotlin/cc/unitmesh/xuiper/prompt/ResourceLoader.ios.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/parser/IndentParser.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ast/NanoNode.kt
  • mpp-ui/src/jvmMain/kotlin/cc/unitmesh/devins/ui/nano/ComposeNanoRenderer.kt
  • mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgent.kt
  • xiuper-ui/src/jsMain/kotlin/cc/unitmesh/xuiper/prompt/ResourceLoader.js.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/spec/v1/NanoSpecV1.kt
🧠 Learnings (6)
📚 Learning: 2025-12-07T08:40:10.859Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Applies to mpp-vscode/src/**/*.ts : When modifying `CodingAgentRenderer`, update VSCode implementations: `mpp-vscode/src/bridge/mpp-core.ts` and `mpp-vscode/src/providers/chat-view.ts`

Applied to files:

  • mpp-vscode/webview/src/components/nano/NanoRenderer.tsx
📚 Learning: 2025-12-07T08:40:10.859Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Applies to **/*.ts : When modifying `CodingAgentRenderer`, update ALL TypeScript implementations: `BaseRenderer.ts`, `CliRenderer.ts`, `ServerRenderer.ts`, and `TuiRenderer.ts`

Applied to files:

  • mpp-vscode/webview/src/components/nano/NanoRenderer.tsx
📚 Learning: 2025-12-07T08:40:10.859Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Applies to **/*.kt : When modifying `CodingAgentRenderer`, update ALL implementations: `DefaultCodingAgentRenderer`, `ComposeRenderer`, `JewelRenderer`, `ServerSideRenderer`, and `JsRendererAdapter` in Kotlin

Applied to files:

  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverter.kt
  • mpp-ui/src/jvmMain/kotlin/cc/unitmesh/devins/ui/nano/ComposeNanoRenderer.kt
  • mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgent.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.kt
📚 Learning: 2025-12-07T08:40:10.859Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Applies to **/*.kt : When modifying `CodingAgentRenderer`, update JVM CLI implementations: `CodingCliRenderer` and `ConsoleRenderer`

Applied to files:

  • mpp-ui/src/jvmMain/kotlin/cc/unitmesh/devins/ui/nano/ComposeNanoRenderer.kt
  • mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgent.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.kt
📚 Learning: 2025-12-07T08:40:10.859Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Applies to **/*.{kt,ts,tsx} : Never use hardcoded colors - always use design tokens from the design system

Applied to files:

  • mpp-ui/src/jvmMain/kotlin/cc/unitmesh/devins/ui/nano/ComposeNanoRenderer.kt
📚 Learning: 2025-12-07T08:40:10.859Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Applies to **/*.kt : Use `AutoDevColors` or `MaterialTheme.colorScheme` for Compose styling

Applied to files:

  • mpp-ui/src/jvmMain/kotlin/cc/unitmesh/devins/ui/nano/ComposeNanoRenderer.kt
🧬 Code graph analysis (4)
mpp-vscode/webview/src/components/nano/NanoRenderer.tsx (2)
mpp-vscode/webview/src/types/nano.ts (2)
  • NanoIR (56-80)
  • NanoRenderContext (85-94)
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ast/NanoNode.kt (25)
  • label (139-145)
  • name (14-499)
  • name (19-24)
  • name (29-32)
  • name (45-49)
  • name (122-126)
  • name (208-247)
  • name (252-258)
  • name (331-337)
  • name (343-351)
  • name (357-361)
  • name (369-376)
  • name (382-387)
  • size (494-498)
  • type (470-477)
  • value (151-155)
  • value (288-297)
  • value (303-312)
  • value (395-402)
  • value (408-413)
  • value (419-424)
  • value (441-449)
  • value (483-488)
  • text (113-116)
  • ratio (320-323)
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverter.kt (3)
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/ComponentConverterUtils.kt (2)
  • convertBinding (18-33)
  • convertAction (35-86)
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/feedback/ModalComponent.kt (1)
  • convertBinding (76-78)
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/input/DatePickerComponent.kt (2)
  • convertBinding (68-70)
  • convertAction (72-74)
mpp-ui/src/jvmMain/kotlin/cc/unitmesh/devins/ui/nano/ComposeNanoRenderer.kt (1)
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.kt (1)
  • RenderNode (128-153)
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.kt (1)
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/NanoRenderer.kt (1)
  • renderNode (38-38)
🔇 Additional comments (34)
mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgent.kt (2)

514-526: LGTM!

The Layout and Display Components section is well-structured. The color values (green|red|blue) are appropriately documented as design tokens rather than hardcoded color values.


581-621: LGTM!

The Actions, HTTP Requests, Conditional Rendering, and Size Tokens sections are well-documented. The HTTP example comprehensively demonstrates the new headers, on_success, and on_error parameters. The Size Tokens section provides a good reference for consistent styling.

xiuper-ui/src/iosMain/kotlin/cc/unitmesh/xuiper/prompt/ResourceLoader.ios.kt (3)

26-32: LGTM! Layout components documentation is clear and comprehensive.

The reorganization into "Layout Components" and the additions (VStack align, Card padding/shadow, SplitView) improve the prompt clarity.


40-53: LGTM! Comprehensive form input components documentation.

The expanded form component set provides good coverage of common input types, and the binding syntax variations (value=, checked=, bind=) align with actual implementations.


54-57: LGTM! Action and Data components are well-documented.

The Button's new disabled_if property and the Data components (DataChart, DataTable) expand the DSL's capabilities appropriately.

Also applies to: 64-67

mpp-ui/src/jvmMain/kotlin/cc/unitmesh/devins/ui/nano/ComposeNanoRenderer.kt (2)

16-16: LGTM!

The import alias for MaterialSlider and the extended dispatch routing for new components are correctly implemented.

Also applies to: 72-89


597-635: LGTM!

The placeholder implementations for RenderDateRangePicker, RenderDataChart, and RenderDataTable are appropriate for this static renderer.

xiuper-ui/src/jsMain/kotlin/cc/unitmesh/xuiper/prompt/ResourceLoader.js.kt (1)

26-66: LGTM! Comprehensive component documentation.

The prompt template updates are well-organized with clear categorization (Layout, Display, Form Input, Action, Feedback, Data) and include all the new P0 and GenUI components with their respective props and usage examples.

xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/parser/IndentParser.kt (2)

849-849: LGTM! Button now supports conditional disabling.

The disabledIf property is correctly extracted from both args and props, aligning with the NanoSpecV1 and prompt documentation.


996-1034: LGTM! GenUI component parsing is well-structured.

The SplitView, SmartTextField, Slider, and DateRangePicker components correctly extract props and bindings with appropriate fallbacks (bindvalue).

xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverter.kt (3)

171-171: LGTM! Button IR now includes disabled_if.

The disabledIf property is correctly propagated to the IR as disabled_if.


38-57: LGTM! Comprehensive dispatch for new components.

The convert() dispatcher is well-organized with clear tier/category comments and exhaustively handles all new NanoNode variants.


391-627: LGTM! New component converters are well-implemented.

All converter methods follow a consistent pattern with proper handling of optional props, bindings, actions, and recursive child conversion where applicable.

xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ast/NanoNode.kt (3)

139-145: LGTM! Button extended with disabledIf.

The new property enables conditional button disabling with proper nullable default.


313-388: LGTM! GenUI component AST nodes are well-documented.

SplitView, SmartTextField, Slider, DateRangePicker, DataChart, and DataTable data classes include clear KDoc examples and appropriate property types.


389-498: LGTM! P0 component AST nodes are complete.

DatePicker, Radio, RadioGroup, Switch, NumberInput, Modal, Alert, Progress, and Spinner data classes cover all required properties with consistent nullable defaults and action/binding support.

xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.kt (4)

48-65: LGTM! Comprehensive render dispatch for new components.

The renderNode() method correctly routes all new P0 and GenUI component types to their dedicated render functions.


298-358: LGTM! P0 form input renderers are well-structured.

The renderers correctly generate accessible HTML with proper input types, labels, and wrapper elements. The Switch uses role="switch" for accessibility.


364-397: LGTM! Modal and Alert renderers are well-structured.

Both renderers correctly handle nested content, optional properties (title, closable), and include proper HTML structure for overlay/modal patterns.


428-474: LGTM! GenUI component renderers are well-implemented.

SplitView correctly calculates panel widths from ratio, SmartTextField supports validation patterns, and Slider/DateRangePicker render appropriate input types.

xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/spec/v1/NanoSpecV1.kt (9)

27-30: LGTM!

The new validation constant lists are well-defined with standard UI values appropriate for their respective component types.


149-161: LGTM!

The DatePicker component specification is well-structured with appropriate prop types and defaults. The format default "YYYY-MM-DD" follows ISO 8601 standard.


162-183: LGTM!

Both Radio and RadioGroup components are well-defined with appropriate properties. The distinction between single Radio and RadioGroup (with allowsChildren) correctly models the radio button pattern.


195-208: LGTM!

The NumberInput component specification correctly uses PropType.FLOAT for numeric constraints (min, max, step) and PropType.INT for precision, which aligns with typical number input semantics.


210-222: LGTM!

The Modal component specification is well-structured. Notably, it correctly uses a dedicated MODAL_SIZE_VALUES constant for size validation and appropriately sets both allowsChildren and allowsActions.


223-246: LGTM!

Both Alert and Progress components are well-defined with appropriate prop types and dedicated validation constants (ALERT_TYPE_VALUES, PROGRESS_STATUS_VALUES). The use of PropType.FLOAT for Progress value is correct for percentage-based progress tracking.


269-312: LGTM!

The GenUI components (SplitView, SmartTextField, Slider, DateRangePicker) are well-structured with appropriate prop types. Notably:

  • SplitView correctly uses PropType.FLOAT for the ratio property
  • SmartTextField and others appropriately use PropType.BINDING for state management
  • Slider uses PropType.FLOAT for numeric constraints

314-340: LGTM!

The data artifact components (DataChart and DataTable) are well-designed:

  • Both correctly use PropType.EXPRESSION for the required data property, allowing flexible data sources
  • DataChart uses a dedicated CHART_TYPE_VALUES constant for type validation
  • DataTable appropriately requires both data and column definitions

343-346: LGTM!

The category sets are comprehensive and correctly updated to include all new components. The categorization aligns with the component definitions:

  • Layout: VStack, HStack, SplitView
  • Container: Card, Form, Modal
  • Content: Text, Image, Badge, Divider, DataChart, DataTable, Alert, Progress, Spinner
  • Input: All form controls including new DatePicker, Radio, RadioGroup, Switch, NumberInput
mpp-vscode/webview/src/components/nano/NanoRenderer.tsx (3)

69-86: LGTM! Component routing is well-organized.

The new component cases are cleanly categorized (P0 Form Inputs, P0 Feedback, GenUI Tier 1-3) and follow the existing dispatch pattern consistently.


357-387: LGTM! Radio and Switch components are well-structured.

The Radio and RadioGroup components follow proper HTML patterns with label wrappers, and the Switch component correctly uses role="switch" for accessibility.


440-463: LGTM! Progress, Spinner, SplitView, and Slider implementations are sound.

These components use appropriate inline styles for layout (width percentages, flex) without hardcoding colors, adhering to the coding guidelines. The Progress percentage calculation is correct, and the SplitView ratio-based width distribution is well-implemented.

Also applies to: 469-483, 497-508

xiuper-ui/src/wasmJsMain/kotlin/cc/unitmesh/xuiper/prompt/ResourceLoader.wasmJs.kt (2)

9-18: LGTM!

The ResourceLoader implementation correctly handles WasmJs constraints by embedding prompt templates directly in code. The error handling for unknown resource paths is appropriate.


26-67: All documented component syntaxes and properties are correctly implemented and verified.

The expanded documentation in lines 26-67 accurately reflects actual component implementations in the codebase:

  • All P0 form input components (DatePicker, RadioGroup, Switch, NumberInput, SmartTextField, Slider, DateRangePicker) are defined with matching properties
  • All P0 feedback components (Modal, Alert, Progress, Spinner) have correct property definitions
  • All data components (DataChart, DataTable) match documentation
  • Button's disabled_if property is confirmed in ButtonComponent.kt as an EXPRESSION type
  • No emoji or problematic UTF-8 characters present (WASM guideline compliant)

The component categorization and syntax examples are accurate and consistent with the parser and renderer implementations.

Comment on lines 510 to 517
const RenderDateRangePicker: React.FC<{ ir: NanoIR; context: NanoRenderContext }> = ({ ir, context }) => {
return (
<div className="nano-daterangepicker">
<input type="date" className="nano-daterangepicker-start" />
<input type="date" className="nano-daterangepicker-end" />
</div>
);
};
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 labels to distinguish start and end dates.

The DateRangePicker renders two date inputs without any labels or distinguishing text. Users won't be able to tell which input is for the start date and which is for the end date, impacting both accessibility and usability.

 const RenderDateRangePicker: React.FC<{ ir: NanoIR; context: NanoRenderContext }> = ({ ir, context }) => {
   return (
     <div className="nano-daterangepicker">
-      <input type="date" className="nano-daterangepicker-start" />
-      <input type="date" className="nano-daterangepicker-end" />
+      <label className="nano-daterangepicker-label">
+        <span>Start:</span>
+        <input type="date" className="nano-daterangepicker-start" aria-label="Start date" />
+      </label>
+      <label className="nano-daterangepicker-label">
+        <span>End:</span>
+        <input type="date" className="nano-daterangepicker-end" aria-label="End date" />
+      </label>
     </div>
   );
 };
📝 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
const RenderDateRangePicker: React.FC<{ ir: NanoIR; context: NanoRenderContext }> = ({ ir, context }) => {
return (
<div className="nano-daterangepicker">
<input type="date" className="nano-daterangepicker-start" />
<input type="date" className="nano-daterangepicker-end" />
</div>
);
};
const RenderDateRangePicker: React.FC<{ ir: NanoIR; context: NanoRenderContext }> = ({ ir, context }) => {
return (
<div className="nano-daterangepicker">
<label className="nano-daterangepicker-label">
<span>Start:</span>
<input type="date" className="nano-daterangepicker-start" aria-label="Start date" />
</label>
<label className="nano-daterangepicker-label">
<span>End:</span>
<input type="date" className="nano-daterangepicker-end" aria-label="End date" />
</label>
</div>
);
};
🤖 Prompt for AI Agents
In mpp-vscode/webview/src/components/nano/NanoRenderer.tsx around lines 510 to
517 the two date inputs are rendered without any labels or accessible
identifiers; update the JSX to add clear, accessible labels for the start and
end inputs (either visible <label htmlFor="...">Start date</label> / <label
htmlFor="...">End date</label> with matching unique ids, or add descriptive
aria-label/aria-labelledby attributes), and consider adding placeholder text
and/or a visually-hidden label variant for compact UI; ensure ids are unique per
component instance and that labels/aria attributes correctly associate with the
inputs to improve usability and accessibility.

Remove ComposeNanoRenderer and update related Nano components to streamline the rendering architecture across platforms.
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

Caution

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

⚠️ Outside diff range comments (1)
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.kt (1)

229-262: Review the interaction between explicit weights and auto-distribution.

When shouldAutoDistribute is true and some children have explicit weight while others don't, both the explicit weight and the auto-assigned weight(1f) will be applied, which may produce unexpected space distribution.

For example, if you have two VStack children where one specifies weight=2f and the other has no weight, the second VStack will get weight(1f) from auto-distribution, resulting in a 2:1 ratio instead of potentially intended behavior.

Consider whether auto-distribution should be disabled when any child has an explicit weight, or adjust the logic to account for mixed scenarios.

// Consider checking if any children have explicit weights
val hasExplicitWeights = ir.children?.any { child ->
    child.props["flex"]?.jsonPrimitive?.content?.toFloatOrNull() != null ||
    child.props["weight"]?.jsonPrimitive?.content?.toFloatOrNull() != null
} ?: false

val shouldAutoDistribute = vstackOrCardChildren >= 2 && !hasExplicitWeights
♻️ Duplicate comments (4)
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/parser/IndentParser.kt (3)

908-957: Parser only captures on_click actions, breaking on_change DSL syntax.

As noted in previous reviews, the parser only collects on_click actions (line 282, 296-307) and assigns them to fields like onChange for DatePicker, Switch, and NumberInput. This means DSL properties like on_change (which are valid according to NanoSpecV1) will be silently ignored, breaking the intended action behavior.

Users writing on_change: state.value = newValue will see their actions not execute because the parser never collects them.

Consider extending the parser to collect component-specific actions:

// In parseComponentCall, add:
val onChange = ir.children?.find { /* parse on_change block */ }

// Then in createNode:
"DatePicker" -> {
    // ... existing code ...
    onChange = onChange ?: onClick  // Prefer on_change, fallback to on_click
}

959-969: Modal and Alert suffer from the same action mapping issue.

Modal's onClose and Alert's onClose are assigned from onClick, meaning DSL syntax like on_close: ... will be ignored by the parser. This is consistent with the broader issue across all components.


1013-1054: GenUI components have multiple consistency issues.

  1. Action mapping: Slider, DateRangePicker, and DataTable assign their action fields from onClick, which means DSL properties like on_change or on_row_click (from NanoSpecV1) won't work.

  2. Required properties: DataChart and DataTable allow data to be null (line 1040, 1051), but the ComponentSpec marks data as required. This creates a mismatch between the spec and parser behavior, potentially causing runtime issues in renderers that assume data is present.

Consider either:

  • Making data required in the parser with validation
  • Or relaxing the required constraint in the spec
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.kt (1)

483-499: Null values still rendered as literal "null" in data attributes.

As flagged in previous reviews, when data or columns props are missing, ?.content returns null, which gets interpolated as the string "null" in HTML attributes (e.g., data-data="null"). This can cause client-side parsing issues when JavaScript tries to parse these attributes.

Apply the suggested fix from previous reviews:

 fun renderDataChart(ir: NanoIR): String {
     val type = ir.props["type"]?.jsonPrimitive?.content ?: "line"
     val data = ir.props["data"]?.jsonPrimitive?.content
-    return "<div class=\"nano-datachart type-$type\" data-data=\"$data\"><!-- Chart will be rendered by client-side library --></div>\n"
+    val dataAttr = if (data != null) " data-data=\"$data\"" else ""
+    return "<div class=\"nano-datachart type-$type\"$dataAttr><!-- Chart will be rendered by client-side library --></div>\n"
 }

 fun renderDataTable(ir: NanoIR): String {
     val columns = ir.props["columns"]?.jsonPrimitive?.content
     val data = ir.props["data"]?.jsonPrimitive?.content
     val actionAttr = renderActionAttribute(ir)
+    val columnsAttr = if (columns != null) " data-columns=\"$columns\"" else ""
+    val dataAttr = if (data != null) " data-data=\"$data\"" else ""
     return buildString {
-        append("<table class=\"nano-datatable\" data-columns=\"$columns\" data-data=\"$data\"$actionAttr>\n")
+        append("<table class=\"nano-datatable\"$columnsAttr$dataAttr$actionAttr>\n")
         append("  <thead><tr><!-- Columns will be populated dynamically --></tr></thead>\n")
         append("  <tbody><!-- Rows will be populated dynamically --></tbody>\n")
         append("</table>\n")
     }
 }
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bd1df01 and 73e4e33.

📒 Files selected for processing (7)
  • mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgent.kt (2 hunks)
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.kt (5 hunks)
  • mpp-ui/src/jvmMain/kotlin/cc/unitmesh/devins/ui/nano/ComposeNanoRenderer.kt (0 hunks)
  • mpp-vscode/webview/src/components/nano/NanoRenderer.tsx (5 hunks)
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ast/NanoNode.kt (2 hunks)
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/parser/IndentParser.kt (2 hunks)
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.kt (2 hunks)
💤 Files with no reviewable changes (1)
  • mpp-ui/src/jvmMain/kotlin/cc/unitmesh/devins/ui/nano/ComposeNanoRenderer.kt
🚧 Files skipped from review as they are similar to previous changes (1)
  • mpp-vscode/webview/src/components/nano/NanoRenderer.tsx
🧰 Additional context used
📓 Path-based instructions (2)
**/*.kt

📄 CodeRabbit inference engine (AGENTS.md)

**/*.kt: Use expect/actual pattern for platform-specific code (for example: Platform implementations)
Use concrete classes (not interfaces) and Promise (not Flow) for @JsExport annotations
Avoid emoji and UTF-8 characters in WASM code
When modifying CodingAgentRenderer, update ALL implementations: DefaultCodingAgentRenderer, ComposeRenderer, JewelRenderer, ServerSideRenderer, and JsRendererAdapter in Kotlin
When modifying CodingAgentRenderer, update JVM CLI implementations: CodingCliRenderer and ConsoleRenderer
Use AutoDevColors or MaterialTheme.colorScheme for Compose styling

Files:

  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.kt
  • mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgent.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ast/NanoNode.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/parser/IndentParser.kt
**/*.{kt,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Never use hardcoded colors - always use design tokens from the design system

Files:

  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.kt
  • mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgent.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ast/NanoNode.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/parser/IndentParser.kt
🧠 Learnings (2)
📚 Learning: 2025-12-07T08:40:10.859Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Applies to **/*.kt : When modifying `CodingAgentRenderer`, update ALL implementations: `DefaultCodingAgentRenderer`, `ComposeRenderer`, `JewelRenderer`, `ServerSideRenderer`, and `JsRendererAdapter` in Kotlin

Applied to files:

  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.kt
  • mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgent.kt
📚 Learning: 2025-12-07T08:40:10.859Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Applies to **/*.kt : When modifying `CodingAgentRenderer`, update JVM CLI implementations: `CodingCliRenderer` and `ConsoleRenderer`

Applied to files:

  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.kt
  • mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgent.kt
🧬 Code graph analysis (1)
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.kt (2)
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/state/NanoStateManager.kt (1)
  • onAction (82-84)
mpp-vscode/webview/src/types/nano.ts (1)
  • NanoActionIR (28-31)
⏰ 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: Test mpp-core (KMP)
  • GitHub Check: Build
🔇 Additional comments (13)
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.kt (5)

136-178: LGTM! Well-organized component routing.

The dispatcher is comprehensive and properly routes all new components to their respective render functions. The grouping comments improve readability and maintainability.


460-469: Verify the dispatcher change from Dispatchers.IO to Dispatchers.Default.

The function performs network I/O (downloading image bytes via HTTP) followed by decoding. Typically, Dispatchers.IO is recommended for I/O-bound operations.

Please confirm whether switching to Dispatchers.Default is intentional and whether it aligns with the expected workload characteristics (CPU-bound vs I/O-bound).


770-1019: LGTM! Consistent and well-structured component implementations.

All P0 form input and feedback components follow consistent patterns:

  • Proper state binding resolution via the bindings map
  • Action handling through onAction callback
  • Appropriate use of Material3 components
  • Consistent error handling and fallback values

The implementations are clean and maintainable.


1025-1171: LGTM! GenUI components implemented with placeholders for data visualization.

The GenUI components are properly implemented. Note that DataChart and DataTable are placeholder implementations showing informational text rather than actual charts/tables, which is acceptable for this initial implementation phase.

Consider tracking the full implementation of these data visualization components in a follow-up issue.


1177-1237: LGTM! Clean control flow and helper implementations.

The conditional rendering, for-loop, and helper functions (resolveBindingValue, evaluateCondition) are well-implemented with appropriate logic for:

  • Binding resolution from state paths
  • Truthiness evaluation for different data types
  • Proper scoping for loop variables
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/parser/IndentParser.kt (2)

849-849: LGTM! Button disabledIf property correctly implemented.

The new disabledIf property is properly sourced from both args and props, following the same pattern as other optional Button properties.


980-987: Progress component default values are handled downstream.

The parser doesn't apply default values for max (spec default is "100"), passing null instead. Default handling is deferred to the renderer implementations, which is acceptable as long as all renderers consistently handle the defaults.

Verify that StatefulNanoRenderer, HtmlRenderer, and other renderers properly default max to 100 when not provided.

xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ast/NanoNode.kt (2)

143-143: LGTM! Button disabledIf property properly defined.

The new optional disabledIf property is correctly typed as String? to support condition expressions and follows the existing pattern for optional properties in the Button data class.


314-501: LGTM! Well-structured AST node definitions for all new components.

All new data classes (GenUI Tier 1-3 and P0 components) are properly integrated into the sealed class hierarchy with:

  • Appropriate property types and nullability
  • Consistent default values (null for optional properties, emptyList() for children)
  • Proper support for bindings and actions where applicable
  • Clear documentation comments

The Progress component correctly uses String? for value/max to support both literal values and state binding expressions.

mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgent.kt (1)

514-622: LGTM! Comprehensive and accurate documentation updates.

The NanoDSL syntax documentation has been thoroughly updated to reflect all new components:

  • P0 form input components (DatePicker, Radio, RadioGroup, Switch, NumberInput)
  • P0 feedback components (Modal, Alert, Progress, Spinner)
  • GenUI components (SplitView, SmartTextField, Slider, DateRangePicker, DataChart, DataTable)
  • Updated Button with disabled_if property

The documentation is well-structured with clear examples and aligns with the implementations across the codebase. The image generation guidance using scene paths (e.g., "/singapore-marina-bay.jpg") matches the approach used in extractImagePrompt.

xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.kt (3)

48-65: LGTM! Complete component routing with clear organization.

All new components are properly routed to their respective render methods with clear grouping comments for P0 Core Form Input, P0 Feedback, and GenUI Tier 1-3 components.


298-358: LGTM! Clean HTML rendering for P0 form input components.

All form input component renderers properly:

  • Extract props with appropriate defaults
  • Render binding and action attributes via helper methods
  • Generate semantic HTML with appropriate ARIA attributes (e.g., role="switch" for Switch)
  • Follow consistent patterns with the existing codebase

399-418: LGTM! Progress rendering with proper division-by-zero guard.

The renderProgress method correctly guards against division by zero on line 408 with if (max > 0f), addressing the concern raised in previous reviews. The method also properly handles both literal values and binding expressions through the isBinding check.

Add label support to Checkbox component with clickable label to toggle state.
Replace text input with Material3 DatePicker dialog for better date selection UX.
Include date formatting helper to convert milliseconds to YYYY-MM-DD format.
Add dynamic text interpolation with state variables and built-in date/time values. Support simple arithmetic expressions in text content. Improve input component layout with width constraints for better HStack behavior.
Add new UI components for the nano interface including control flow, layout, feedback, content, and input components. Also update the StatefulNanoRenderer to work with these new components.
Extract data visualization components from NanoFeedbackComponents into a dedicated NanoDataComponents module. This includes enhanced functionality for chart rendering with proper YAML/JSON support and improved table rendering with formatting options. Updated StatefulNanoRenderer to use the new module and deprecated the old methods in NanoFeedbackComponents.
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: 6

♻️ Duplicate comments (2)
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/parser/IndentParser.kt (2)

1036-1055: Required data property not validated.

As noted in a previous review, DataChart and DataTable specify data as required in the ComponentSpec, but the parser allows it to be null. This inconsistency could lead to runtime issues when renderers try to access the data.


908-958: Action handler mismatch persists for new components.

As noted in a previous review, the parser only collects on_click actions but assigns them to fields like onChange. This means DSL props like on_change for DatePicker, Switch, NumberInput, Slider, DateRangePicker, and DataTable won't be parsed and will be silently ignored.

Consider adding parsing support for on_change or document that on_click should be used for change handlers.

🧹 Nitpick comments (9)
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoRenderUtils.kt (1)

94-109: Hardcoded year default will become stale.

The default year 2024 should use the current year from Clock.System.now() to avoid stale defaults.

     fun getDefaultForVariable(varName: String): Int {
+        val currentYear = Clock.System.now().toLocalDateTime(TimeZone.currentSystemDefault()).year
         return when {
             varName.contains("day", ignoreCase = true) -> 1
             varName.contains("month", ignoreCase = true) -> 1
-            varName.contains("year", ignoreCase = true) -> 2024
+            varName.contains("year", ignoreCase = true) -> currentYear
             // ... rest unchanged
mpp-ui/src/jvmTest/kotlin/cc/unitmesh/devins/ui/nano/NanoDataComponentsTest.kt (1)

14-77: Tests verify parsing but don't validate component types.

The tests confirm that parsing succeeds and basic structure is present, but don't verify that the actual DataTable or DataChart nodes are created. Consider adding assertions to check the types of child nodes.

// Example enhancement for deeper validation:
val vstack = result.children.first() as? NanoNode.VStack
assertNotNull(vstack)
val card = vstack.children.filterIsInstance<NanoNode.Card>().first()
val dataTable = card.children.filterIsInstance<NanoNode.DataTable>().firstOrNull()
assertNotNull(dataTable, "Expected DataTable component in Card")
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoControlFlowComponents.kt (1)

39-62: Consider exposing loop index in item state.

The forEachIndexed captures index but doesn't add it to itemState. Exposing it would enable index-based rendering patterns (e.g., {state.index} for row numbers).

             items.forEachIndexed { index, item ->
                 val itemState = state.toMutableMap().apply {
                     if (variable != null) {
                         this[variable] = item ?: ""
                     }
+                    this["_index"] = index
+                    this["_isFirst"] = index == 0
+                    this["_isLast"] = index == items.lastIndex
                 }
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoContentComponents.kt (1)

186-195: Consider reusing HttpClient for efficiency.

Creating and closing an HttpClient for each image load is inefficient. For multiple images, this creates significant overhead.

Consider using a shared singleton client or passing a client instance:

// At module level or in a DI context
private val sharedHttpClient by lazy { HttpClientFactory.create() }

private suspend fun loadImageFromUrl(url: String): ImageBitmap = withContext(Dispatchers.Default) {
    val response: HttpResponse = sharedHttpClient.get(url)
    val bytes = response.readBytes()
    decodeImageBytesToBitmap(bytes)
}

Note: If using a shared client, ensure proper lifecycle management at app shutdown.

mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoDataComponents.kt (2)

406-422: Remove deprecated method or migrate usages.

The parseColumns method is marked as deprecated but remains in the codebase. Consider removing it entirely if all usages have been migrated to parseColumnDefs.

If no usages remain, apply this diff to remove the deprecated method:

-    /**
-     * Parse columns from string format (legacy method).
-     * Supports: "col1,col2,col3" or JSON array format
-     */
-    @Deprecated("Use parseColumnDefs instead")
-    private fun parseColumns(columnsStr: String?): List<String> {
-        if (columnsStr.isNullOrBlank()) return emptyList()
-
-        return try {
-            // Try JSON array format first
-            if (columnsStr.trim().startsWith("[")) {
-                val parsed = YamlUtils.load(columnsStr) as? List<*>
-                parsed?.mapNotNull { it?.toString() } ?: emptyList()
-            } else {
-                // Simple comma-separated format
-                columnsStr.split(",").map { it.trim() }.filter { it.isNotEmpty() }
-            }
-        } catch (e: Exception) {
-            columnsStr.split(",").map { it.trim() }.filter { it.isNotEmpty() }
-        }
-    }

64-101: Consider simplifying chart code generation logic.

The buildChartCode function has nested conditionals and multiple parsing attempts that could be refactored for clarity. The logic mixes concerns of format detection, parsing, and code generation.

Consider extracting format detection into a separate helper function to improve readability and testability.

mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoInputComponents.kt (3)

119-127: Inefficient Boolean to String conversion.

Converting Boolean to String before wrapping in JsonPrimitive is unnecessary. JsonPrimitive can accept Boolean directly.

Apply this diff for cleaner code:

                     if (statePath != null) {
                         onAction(NanoActionIR(
                             type = "stateMutation",
                             payload = mapOf(
                                 "path" to JsonPrimitive(statePath),
                                 "operation" to JsonPrimitive("SET"),
-                                "value" to JsonPrimitive(newValue.toString())
+                                "value" to JsonPrimitive(newValue)
                             )
                         ))
                     }

Same issue exists at line 143.


406-415: NumberInput validation regex allows incomplete inputs.

The regex -?\\d*\\.?\\d* accepts incomplete numbers like "-", ".", or "-.". This could cause issues when parsing with toDoubleOrNull() on line 412.

Consider tightening the validation or handling edge cases:

-            onValueChange = { newValue ->
-                if (statePath != null && newValue.matches(Regex("-?\\d*\\.?\\d*"))) {
+            onValueChange = { newValue ->
+                // Allow empty or valid partial/complete numbers
+                val isValid = newValue.isEmpty() || 
+                             newValue.matches(Regex("-?\\d+(\\.\\d*)?")) ||
+                             newValue.matches(Regex("-?\\.\\d+")) ||
+                             newValue == "-"
+                if (statePath != null && isValid) {
                     onAction(NanoActionIR(

Or add a null check:

                             "path" to JsonPrimitive(statePath),
                             "operation" to JsonPrimitive("SET"),
-                            "value" to JsonPrimitive(newValue.toDoubleOrNull() ?: 0)
+                            "value" to JsonPrimitive(newValue.toDoubleOrNull() ?: return@onValueChange)
                         )
                     ))

255-272: DatePicker uses disabled state for read-only behavior.

The DatePicker TextField is marked as enabled = false to make it read-only, but this might prevent the clickable modifier from working on some platforms. Consider using readOnly = true without enabled = false.

Test if removing enabled = false works correctly:

             modifier = modifier
                 .fillMaxWidth()
                 .clickable { showDialog = true },
             singleLine = true,
             readOnly = true,
-            enabled = false,
             colors = OutlinedTextFieldDefaults.colors(
-                disabledTextColor = MaterialTheme.colorScheme.onSurface,
-                disabledBorderColor = MaterialTheme.colorScheme.outline,
-                disabledLeadingIconColor = MaterialTheme.colorScheme.onSurfaceVariant,
-                disabledPlaceholderColor = MaterialTheme.colorScheme.onSurfaceVariant
             )
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 73e4e33 and fcba653.

📒 Files selected for processing (12)
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoContentComponents.kt (1 hunks)
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoControlFlowComponents.kt (1 hunks)
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoDataComponents.kt (1 hunks)
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoFeedbackComponents.kt (1 hunks)
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoInputComponents.kt (1 hunks)
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoLayoutComponents.kt (1 hunks)
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoRenderUtils.kt (1 hunks)
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.kt (2 hunks)
  • mpp-ui/src/jvmTest/kotlin/cc/unitmesh/devins/ui/nano/NanoDataComponentsTest.kt (1 hunks)
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ast/NanoNode.kt (3 hunks)
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverter.kt (4 hunks)
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/parser/IndentParser.kt (3 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.kt

📄 CodeRabbit inference engine (AGENTS.md)

**/*.kt: Use expect/actual pattern for platform-specific code (for example: Platform implementations)
Use concrete classes (not interfaces) and Promise (not Flow) for @JsExport annotations
Avoid emoji and UTF-8 characters in WASM code
When modifying CodingAgentRenderer, update ALL implementations: DefaultCodingAgentRenderer, ComposeRenderer, JewelRenderer, ServerSideRenderer, and JsRendererAdapter in Kotlin
When modifying CodingAgentRenderer, update JVM CLI implementations: CodingCliRenderer and ConsoleRenderer
Use AutoDevColors or MaterialTheme.colorScheme for Compose styling

Files:

  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoDataComponents.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoContentComponents.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoControlFlowComponents.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoFeedbackComponents.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/parser/IndentParser.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoRenderUtils.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoInputComponents.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverter.kt
  • mpp-ui/src/jvmTest/kotlin/cc/unitmesh/devins/ui/nano/NanoDataComponentsTest.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoLayoutComponents.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ast/NanoNode.kt
**/*.{kt,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Never use hardcoded colors - always use design tokens from the design system

Files:

  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoDataComponents.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoContentComponents.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoControlFlowComponents.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoFeedbackComponents.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/parser/IndentParser.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoRenderUtils.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoInputComponents.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverter.kt
  • mpp-ui/src/jvmTest/kotlin/cc/unitmesh/devins/ui/nano/NanoDataComponentsTest.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoLayoutComponents.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ast/NanoNode.kt
🧠 Learnings (4)
📓 Common learnings
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Refer to `docs/design-system-compose.md` for Compose design system documentation
📚 Learning: 2025-12-07T08:40:10.859Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Applies to **/*.kt : When modifying `CodingAgentRenderer`, update ALL implementations: `DefaultCodingAgentRenderer`, `ComposeRenderer`, `JewelRenderer`, `ServerSideRenderer`, and `JsRendererAdapter` in Kotlin

Applied to files:

  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoContentComponents.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoFeedbackComponents.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.kt
📚 Learning: 2025-12-07T08:40:10.859Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Applies to **/*.kt : When modifying `CodingAgentRenderer`, update JVM CLI implementations: `CodingCliRenderer` and `ConsoleRenderer`

Applied to files:

  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.kt
📚 Learning: 2025-12-07T08:40:10.859Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Applies to **/*.kt : Use `AutoDevColors` or `MaterialTheme.colorScheme` for Compose styling

Applied to files:

  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.kt
🧬 Code graph analysis (4)
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoContentComponents.kt (1)
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.kt (1)
  • decodeImageBytesToBitmap (172-172)
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoInputComponents.kt (3)
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/state/NanoStateManager.kt (1)
  • onAction (82-84)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaInputListener.kt (1)
  • Button (10-13)
mpp-vscode/webview/src/types/nano.ts (1)
  • NanoActionIR (28-31)
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverter.kt (3)
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/ComponentConverterUtils.kt (2)
  • convertBinding (18-33)
  • convertAction (35-86)
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/feedback/ModalComponent.kt (1)
  • convertBinding (76-78)
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/input/DatePickerComponent.kt (2)
  • convertBinding (68-70)
  • convertAction (72-74)
mpp-ui/src/jvmTest/kotlin/cc/unitmesh/devins/ui/nano/NanoDataComponentsTest.kt (1)
mpp-core/src/commonMain/kotlin/cc/unitmesh/devins/test/DevInsTestCase.kt (3)
  • assertEquals (45-49)
  • assertNotNull (54-59)
  • assertTrue (29-33)
⏰ 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: Test mpp-core (KMP)
  • GitHub Check: Build
🔇 Additional comments (19)
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoRenderUtils.kt (1)

189-210: LGTM!

The spacing and padding conversion functions provide clear, consistent mappings from semantic tokens to dp values. The different defaults (8.dp for spacing, 16.dp for padding) are reasonable design choices.

xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverter.kt (3)

38-57: LGTM - Comprehensive component coverage.

The exhaustive when expression now covers all new component types with clear tier-based organization. The pattern is consistent and maintainable.


192-201: LGTM!

The Checkbox converter now properly handles the label property alongside the checked binding, aligning with the enhanced AST node.


393-631: Well-structured component converters.

All new component converters follow a consistent pattern: extracting props with safe null handling via ?.let, converting bindings and actions where applicable, and recursively processing children. The tier-based organization with section comments aids maintainability.

xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/parser/IndentParser.kt (1)

960-980: LGTM for property parsing.

The Modal and Alert component parsing correctly handles boolean properties with safe ?.toBoolean() calls and extracts all relevant props. The onClose = onClick mapping is part of the broader action handler pattern already noted.

mpp-ui/src/jvmTest/kotlin/cc/unitmesh/devins/ui/nano/NanoDataComponentsTest.kt (1)

125-139: LGTM - Good edge case coverage.

Testing inline string data for DataTable is a valuable edge case that verifies the parser handles non-state-bound data.

mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.kt (2)

115-164: Excellent modular refactoring.

The delegation to specialized component objects (NanoLayoutComponents, NanoContentComponents, NanoInputComponents, NanoFeedbackComponents, NanoDataComponents, NanoControlFlowComponents) with a renderNode callback for recursion is a clean, maintainable architecture. The tier-based organization with comments aids navigation.


168-172: Remove this comment - all required platform-specific implementations exist.

The expect function already has actual implementations for all platforms that use the Nano renderer: Android, iOS, WASM/JS (browser), and JVM. The jsMain source set is Node.js CLI-only and does not use this UI-specific function, so no implementation is required there.

Likely an incorrect or invalid review comment.

mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoFeedbackComponents.kt (2)

148-175: Good deprecation pattern with migration guidance.

The deprecated wrappers with @Deprecated annotations and ReplaceWith provide a clear migration path to NanoDataComponents.


64-129: LGTM - Proper use of design tokens.

RenderAlert and RenderProgress correctly use AutoDevColors.Signal and MaterialTheme.colorScheme for styling, following the coding guidelines.

mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoContentComponents.kt (2)

86-120: Image generation flow handles state changes correctly.

The use of remember(originalSrc) for state variables ensures proper reset when the source changes. The sequential LaunchedEffects with guard conditions prevent race conditions.


197-293: LGTM - Good design token usage and icon mapping.

RenderBadge uses AutoDevColors.Signal colors with Color.Black for yellow text (accessibility contrast). RenderIcon provides a comprehensive mapping from semantic icon names to Material Icons with proper size and color handling via design tokens.

mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoLayoutComponents.kt (3)

87-93: Potential unbounded height layout issue.

Using wrapContentHeight(unbounded = true) can cause layout issues in certain nested scenarios, especially with scrollable content. Ensure this behavior is tested with various content sizes.


69-72: Auto-distribution logic is well-designed.

The auto-distribution of space for VStack/Card children in HStack when multiple siblings are present is a thoughtful design choice that improves layout behavior.

Also applies to: 90-94


28-30: The extension functions toSpacing() and toPadding() are already defined in NanoRenderUtils.kt in the same package. Since they are String extension functions, they are automatically available in NanoLayoutComponents.kt without explicit imports. No action required.

mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoInputComponents.kt (1)

27-61: Input components follow consistent patterns.

The implementation of Button and Input components demonstrates good consistency in state management and action handling patterns. The use of NanoRenderUtils.interpolateText for button labels is particularly well-designed.

Also applies to: 63-97

xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ast/NanoNode.kt (3)

139-145: Good addition of disabledIf property to Button.

Adding conditional disabling via disabledIf is a valuable feature that improves the declarative nature of the DSL. This aligns well with the reactive binding pattern used elsewhere in the AST.


161-164: Checkbox label parameter improves usability.

Adding an optional label parameter to Checkbox makes it more consistent with other input components (Radio, Switch) and reduces the need for wrapping components just to add labels.


315-502: Comprehensive expansion of the component library.

The addition of 16+ new component types (GenUI foundation, structured inputs, data artifacts, P0 form inputs, and feedback components) significantly expands the DSL's capabilities. The structure follows consistent patterns:

  • Proper sealed class hierarchy
  • Optional parameters with sensible defaults
  • Bindings for reactive state
  • Action callbacks for events
  • Comprehensive KDoc comments

The organization into tiers (Tier 1: GenUI Foundation, Tier 2: Structured Input, Tier 3: Data Artifacts, P0: Core Form/Feedback) is well-thought-out and maintainable.

)
} else {
// Fallback for platforms without chart support
RenderChartFallback(chartType, resolvedData, modifier)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Type mismatch in RenderChartFallback call.

The RenderChartFallback function signature expects data: String?, but resolvedData (of type Any?) is passed. This will cause a compilation error.

Apply this diff to fix the type mismatch:

         } else {
             // Fallback for platforms without chart support
-            RenderChartFallback(chartType, resolvedData, modifier)
+            RenderChartFallback(chartType, resolvedData?.toString(), modifier)
         }
📝 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
RenderChartFallback(chartType, resolvedData, modifier)
} else {
// Fallback for platforms without chart support
RenderChartFallback(chartType, resolvedData?.toString(), modifier)
}
🤖 Prompt for AI Agents
In mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoDataComponents.kt
around line 57, the call RenderChartFallback(chartType, resolvedData, modifier)
passes resolvedData (Any?) but the function expects data: String?; change the
argument to a nullable String by converting or casting resolvedData — e.g. pass
resolvedData as? String ?: resolvedData?.toString() (which yields null if
resolvedData is null, or the original string or its stringified form) so the
call matches the expected String? parameter.

- Add YAML/JSON parsing for list/dict state defaults
- Implement field inference for charts and column inference for tables
- Enhance binding resolution to return actual types (List/Map) not just strings
- Add proper chart rendering with SVG bars and table rendering with data
- Support bracket literal extraction in argument parsing for complex structures
- Add multi-line component call parsing support in IndentParser
- Implement JS platform support for image decoding
- Enhance Modal with proper dialog implementation and LLM title generation
- Add DateRangePicker with proper state binding and date selection
- Improve Alert with closable option and LLM message generation
- Refactor input components to support both 'value' and 'bind' bindings
- Add structured default parsing for List and Map types
Add support for APPEND/REMOVE operations in state mutations for list values.
Implement onChange actions for input components including Checkbox, TextField,
and Select. Add support for "in list" expressions to bind checkbox state to
list membership. Improve state path resolution with validation and support
for uncontrolled components.
- Refactor NanoRenderUtils and related components across platforms
- Add test coverage for NanoRenderUtils
- Update TypeScript renderer and CSS styles
- Modify input, layout, and feedback components
- Update IndentParser for better handling
Replace fixed width with adaptive fillMaxWidth by default, enhance aspect ratio handling using aspectRatio() modifier, and add minimum height constraints for better responsiveness across different screen sizes.
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: 5

Caution

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

⚠️ Outside diff range comments (1)
xiuper-ui/src/commonMain/resources/prompts/detailed.txt (1)

70-78: Section numbering inconsistency.

Section 7 (Form Components) at line 70 is followed by Section 6 (State Management) at line 78. This creates confusion in the prompt template.

-### 7. Form Components
+### 7. Form Components
 ```nanodsl
 Form(onSubmit=request.login):
     Input(name="email", value := state.email)
     Input(name="password", type="password", value := state.password)
     Button("Submit", type="submit")

-### 6. State Management
+### 8. State Management


</blockquote></details>

</blockquote></details>
♻️ Duplicate comments (6)
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoDataComponents.kt (1)

361-361: Replace deprecated Divider with HorizontalDivider.

The Divider component is deprecated in Material3. Use HorizontalDivider() instead.

-                Divider()
+                HorizontalDivider()
mpp-vscode/webview/src/components/nano/NanoRenderer.tsx (4)

692-704: NumberInput increment/decrement buttons lack functionality.

The buttons have no onClick handlers, making them non-functional in the current implementation.


717-717: Modal close button lacks onClick handler.

The close button is rendered without functionality.


738-738: Alert close button lacks onClick handler.

When closable is true, the close button has no handler.


820-827: DateRangePicker inputs lack labels for accessibility.

The two date inputs have no labels to distinguish start from end date.

mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoRenderUtils.kt (1)

440-456: Inaccurate date calculation will produce wrong results.

The approximate date calculation using days / 365 and days % 30 will produce incorrect dates due to leap years and varying month lengths. kotlinx-datetime is already imported and used elsewhere in this file.

Apply this diff to use proper date conversion:

     fun formatDateFromMillis(millis: Long): String {
-        // Simple conversion: millis / 86400000 = days since epoch
-        val days = millis / 86400000L
-        // Approximate year (will be off by a few days due to leap years, but good enough for display)
-        val year = 1970 + (days / 365)
-        val remainingDays = days % 365
-        val month = (remainingDays / 30).coerceIn(0, 11) + 1
-        val day = (remainingDays % 30).coerceIn(0, 30) + 1
-
-        return buildString {
-            append(year.toString().padStart(4, '0'))
-            append('-')
-            append(month.toString().padStart(2, '0'))
-            append('-')
-            append(day.toString().padStart(2, '0'))
-        }
+        val instant = kotlinx.datetime.Instant.fromEpochMilliseconds(millis)
+        val localDate = instant.toLocalDateTime(kotlinx.datetime.TimeZone.UTC).date
+        return "${localDate.year}-${localDate.monthNumber.toString().padStart(2, '0')}-${localDate.dayOfMonth.toString().padStart(2, '0')}"
     }
🧹 Nitpick comments (3)
mpp-ui/src/jsMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.js.kt (1)

9-11: Consider handling decode exceptions for robustness.

decodeToImageBitmap() can throw if the byte array is malformed or not a valid image format. In a JS environment, this could surface as an uncaught exception. Consider wrapping in a try-catch or documenting that callers must handle exceptions.

xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/parser/IndentParser.kt (1)

1207-1207: Consider generalizing bracket literal extraction.

Currently only columns is extracted via extractBracketLiteral. Other components like RadioGroup with options=["a","b"] or DataChart with complex data might need similar handling.

Consider extracting bracket literals for all keys that commonly use array/object syntax:

-        // Capture complex literals that include commas/braces so regex parsing doesn't drop them.
-        extractBracketLiteral("columns")?.let { result["columns"] = it }
+        // Capture complex literals that include commas/braces so regex parsing doesn't drop them.
+        listOf("columns", "options", "data").forEach { key ->
+            extractBracketLiteral(key)?.let { result[key] = it }
+        }
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverter.kt (1)

328-343: Potential code duplication with ComponentConverterUtils.

The convertBinding method here (lines 328-343) appears identical to ComponentConverterUtils.convertBinding shown in the relevant code snippets. Consider consolidating to a single utility.

-    private fun convertBinding(binding: Binding): NanoBindingIR {
-        return when (binding) {
-            is Binding.Subscribe -> NanoBindingIR(
-                mode = "subscribe",
-                expression = binding.expression
-            )
-            is Binding.TwoWay -> NanoBindingIR(
-                mode = "twoWay",
-                expression = binding.path
-            )
-            is Binding.Static -> NanoBindingIR(
-                mode = "static",
-                expression = binding.value
-            )
-        }
-    }
+    private fun convertBinding(binding: Binding): NanoBindingIR =
+        ComponentConverterUtils.convertBinding(binding)
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fcba653 and cdca4a5.

📒 Files selected for processing (15)
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoContentComponents.kt (1 hunks)
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoDataComponents.kt (1 hunks)
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoFeedbackComponents.kt (1 hunks)
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoInputComponents.kt (1 hunks)
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoLayoutComponents.kt (1 hunks)
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoRenderUtils.kt (1 hunks)
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.kt (5 hunks)
  • mpp-ui/src/jsMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.js.kt (1 hunks)
  • mpp-ui/src/jvmTest/kotlin/cc/unitmesh/devins/ui/nano/NanoRenderUtilsTest.kt (1 hunks)
  • mpp-vscode/webview/src/components/nano/NanoRenderer.css (2 hunks)
  • mpp-vscode/webview/src/components/nano/NanoRenderer.tsx (6 hunks)
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ast/NanoNode.kt (3 hunks)
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverter.kt (4 hunks)
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/parser/IndentParser.kt (7 hunks)
  • xiuper-ui/src/commonMain/resources/prompts/detailed.txt (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoContentComponents.kt
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{kt,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Never use hardcoded colors - always use design tokens from the design system

Files:

  • mpp-vscode/webview/src/components/nano/NanoRenderer.tsx
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoDataComponents.kt
  • mpp-ui/src/jsMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.js.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoFeedbackComponents.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverter.kt
  • mpp-ui/src/jvmTest/kotlin/cc/unitmesh/devins/ui/nano/NanoRenderUtilsTest.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoInputComponents.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/parser/IndentParser.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoLayoutComponents.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoRenderUtils.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ast/NanoNode.kt
**/*.kt

📄 CodeRabbit inference engine (AGENTS.md)

**/*.kt: Use expect/actual pattern for platform-specific code (for example: Platform implementations)
Use concrete classes (not interfaces) and Promise (not Flow) for @JsExport annotations
Avoid emoji and UTF-8 characters in WASM code
When modifying CodingAgentRenderer, update ALL implementations: DefaultCodingAgentRenderer, ComposeRenderer, JewelRenderer, ServerSideRenderer, and JsRendererAdapter in Kotlin
When modifying CodingAgentRenderer, update JVM CLI implementations: CodingCliRenderer and ConsoleRenderer
Use AutoDevColors or MaterialTheme.colorScheme for Compose styling

Files:

  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoDataComponents.kt
  • mpp-ui/src/jsMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.js.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoFeedbackComponents.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverter.kt
  • mpp-ui/src/jvmTest/kotlin/cc/unitmesh/devins/ui/nano/NanoRenderUtilsTest.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoInputComponents.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/parser/IndentParser.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoLayoutComponents.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoRenderUtils.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ast/NanoNode.kt
🧠 Learnings (6)
📓 Common learnings
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Refer to `docs/design-system-compose.md` for Compose design system documentation
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Applies to **/*.kt : When modifying `CodingAgentRenderer`, update ALL implementations: `DefaultCodingAgentRenderer`, `ComposeRenderer`, `JewelRenderer`, `ServerSideRenderer`, and `JsRendererAdapter` in Kotlin
📚 Learning: 2025-12-07T08:40:10.859Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Applies to mpp-vscode/src/**/*.ts : When modifying `CodingAgentRenderer`, update VSCode implementations: `mpp-vscode/src/bridge/mpp-core.ts` and `mpp-vscode/src/providers/chat-view.ts`

Applied to files:

  • mpp-vscode/webview/src/components/nano/NanoRenderer.tsx
  • mpp-vscode/webview/src/components/nano/NanoRenderer.css
📚 Learning: 2025-12-07T08:40:10.859Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Applies to **/*.ts : When modifying `CodingAgentRenderer`, update ALL TypeScript implementations: `BaseRenderer.ts`, `CliRenderer.ts`, `ServerRenderer.ts`, and `TuiRenderer.ts`

Applied to files:

  • mpp-vscode/webview/src/components/nano/NanoRenderer.tsx
  • mpp-vscode/webview/src/components/nano/NanoRenderer.css
📚 Learning: 2025-12-07T08:40:10.859Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Applies to **/*.kt : When modifying `CodingAgentRenderer`, update ALL implementations: `DefaultCodingAgentRenderer`, `ComposeRenderer`, `JewelRenderer`, `ServerSideRenderer`, and `JsRendererAdapter` in Kotlin

Applied to files:

  • mpp-ui/src/jsMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.js.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoFeedbackComponents.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.kt
📚 Learning: 2025-12-07T08:40:10.859Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Applies to **/*.kt : When modifying `CodingAgentRenderer`, update JVM CLI implementations: `CodingCliRenderer` and `ConsoleRenderer`

Applied to files:

  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.kt
📚 Learning: 2025-12-07T08:40:10.859Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Applies to **/*.kt : Use `AutoDevColors` or `MaterialTheme.colorScheme` for Compose styling

Applied to files:

  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.kt
🧬 Code graph analysis (6)
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoFeedbackComponents.kt (2)
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/state/NanoStateManager.kt (1)
  • onAction (82-84)
mpp-vscode/webview/src/types/nano.ts (1)
  • NanoActionIR (28-31)
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverter.kt (1)
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/ComponentConverterUtils.kt (2)
  • convertBinding (18-33)
  • convertAction (35-86)
mpp-ui/src/jvmTest/kotlin/cc/unitmesh/devins/ui/nano/NanoRenderUtilsTest.kt (1)
mpp-core/src/commonMain/kotlin/cc/unitmesh/devins/test/DevInsTestCase.kt (2)
  • assertEquals (45-49)
  • assertNotNull (54-59)
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoInputComponents.kt (2)
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/state/NanoStateManager.kt (1)
  • onAction (82-84)
mpp-vscode/webview/src/types/nano.ts (1)
  • NanoActionIR (28-31)
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/parser/IndentParser.kt (1)
mpp-vscode/webview/src/components/nano/NanoRenderer.tsx (2)
  • isIdentStart (267-269)
  • isIdentPart (270-272)
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoRenderUtils.kt (1)
mpp-vscode/webview/src/components/nano/NanoRenderer.tsx (7)
  • parseTerm (182-193)
  • match (254-260)
  • parseFactor (195-212)
  • parseExpression (171-180)
  • peek (261-263)
  • parseNumber (214-231)
  • parseIdentifierValue (233-249)
⏰ 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
  • GitHub Check: Test mpp-core (KMP)
🔇 Additional comments (42)
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/parser/IndentParser.kt (4)

281-363: Well-structured multi-line argument parser.

The parseMultilineInlineComponentCall function correctly handles nested parentheses, string escaping, and multi-line content. The character-by-character state machine approach is robust for the DSL syntax.


1003-1053: Consistent implementation of P0 form input components.

The parsing logic for DatePicker, Radio, RadioGroup, Switch, and NumberInput correctly extracts bindings from value/checked args, falls back to props, and supports format/range constraints. The pattern is consistent with existing Input/Checkbox components.


1160-1204: Robust bracket literal extraction for complex argument values.

The extractBracketLiteral function correctly handles nested brackets, string escaping, and mixed delimiters. This enables parsing of complex inline literals like columns=["Name", "Age"].


1209-1301: Comprehensive subscribe binding parser.

The << binding parsing correctly handles complex expressions with nested parentheses, brackets, and string literals. The depth tracking ensures proper comma-delimited argument separation.

xiuper-ui/src/commonMain/resources/prompts/detailed.txt (2)

46-53: Good coverage of new interactive components.

The documentation now includes all P0 form input components (DatePicker, Radio, RadioGroup, Switch, NumberInput) and GenUI structured input components (SmartTextField, Slider, DateRangePicker) with their respective props.


55-68: Feedback and Data component sections address previous review feedback.

The addition of sections 5 (Feedback Components) and 6 (Data Components) addresses the previous review comments about missing Modal, Alert, Progress, Spinner, DataChart, and DataTable documentation.

xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ast/NanoNode.kt (3)

139-145: Button component extended with conditional disable support.

The addition of disabledIf: String? enables declarative conditional button disabling, aligning with the DSL's reactive binding philosophy.


161-165: Checkbox enhanced with label and onChange handler.

The expanded Checkbox signature supports common form patterns where a label is rendered alongside the checkbox and change events need to be handled.


488-493: Progress value/max as String enables flexible binding.

The KDoc clearly documents that value and max can be either literal numbers or state binding expressions. This design choice supports both value="50" and value="state.progress" patterns.

xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverter.kt (4)

38-57: Exhaustive when branch for new node types.

The dispatch in convert() is properly extended to handle all 15 new node types across Tier 1-3 and P0 categories. The sealed class ensures compile-time exhaustiveness checking.


400-409: SplitView converter correctly handles ratio and children.

The converter properly maps the optional ratio property and recursively converts children, following the established pattern for container components.


575-596: Modal converter handles open binding, actions, and nested children.

The Modal converter correctly maps the open binding for visibility control, onClose action, and recursively converts child content. This supports the common modal dialog pattern.


617-625: Progress converter preserves value/max as strings.

Consistent with the NanoNode.Progress design, the converter stores value and max as string primitives, allowing renderers to parse or resolve bindings as needed.

mpp-vscode/webview/src/components/nano/NanoRenderer.css (2)

34-41: LGTM!

The HStack wrap behavior and checkbox styling are well-implemented. Using flex: 0 0 100% ensures checkboxes render one-per-line in wrapped HStacks as intended.


219-278: LGTM!

DataTable and DataChart styles correctly integrate with VSCode theming variables (--vscode-*). The table styling with collapsed borders, themed backgrounds, and the chart legend wrapping behavior are appropriate.

mpp-ui/src/jvmTest/kotlin/cc/unitmesh/devins/ui/nano/NanoRenderUtilsTest.kt (1)

1-51: LGTM!

Good test coverage for template interpolation and numeric expression evaluation. The tests cover key scenarios: variable substitution, arithmetic expressions, string transformations, and handling of unknown keys.

mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoDataComponents.kt (2)

51-76: LGTM!

The chart rendering logic properly resolves data bindings, builds chart code in YAML format, and falls back gracefully when chart support is unavailable.


300-387: LGTM!

The DataTable implementation handles multiple input formats (lists, maps, JSON/YAML, CSV-like) with proper column inference and formatting support. The scrollable table with alternating row backgrounds provides good UX.

mpp-vscode/webview/src/components/nano/NanoRenderer.tsx (5)

47-57: LGTM!

The getByPath utility correctly handles the state. prefix and nested path resolution.


162-273: Solid arithmetic expression parser.

The NumericExprParser class implements a clean recursive descent parser supporting basic arithmetic operations. The division-by-zero protection (returning 0) is a sensible choice for UI rendering contexts.


829-874: Good implementation of DataChart with SVG rendering.

The chart now renders actual bar visualization using SVG with proper theming integration via CSS classes. The legend shows up to 8 items which is a reasonable limit.


876-939: Good implementation of DataTable with column inference.

The table now properly renders headers and rows with format support (currency, percent). Column inference from the first row is a sensible fallback when explicit columns aren't provided.


594-624: Good state default initialization for Component nodes.

The implementation correctly parses structured literals for list/dict/map/object types and merges defaults into the child context, enabling proper nested component rendering.

mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoFeedbackComponents.kt (3)

232-257: LGTM!

The Progress component correctly evaluates binding expressions for value/max and calculates the progress percentage. Uses MaterialTheme for consistent styling.


276-303: LGTM!

The deprecated wrappers for DataChart and DataTable are properly annotated with @Deprecated and include appropriate ReplaceWith guidance pointing to NanoDataComponents.


186-203: Good use of design tokens for Alert styling.

The Alert component correctly uses AutoDevColors.Signal.* for theming instead of hardcoded colors, following the coding guidelines.

mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.kt (5)

35-45: LGTM on structured default parsing.

The parseStructuredDefault helper cleanly handles both JSON and YAML literals via YamlUtils.load, with a sensible fallback to the raw string on parse failure.


71-80: Clean handling of list and map state types.

The initialization logic properly parses structured defaults for list, dict, map, and object types, with appropriate type guards to ensure the parsed result matches the expected type before assignment.


111-128: Well-implemented APPEND/REMOVE operations for list state.

The duplicate prevention in APPEND (if (currentValue.contains(trimmed))) and the use of filterNot for REMOVE are correct. The string trimming and quote removal handle common edge cases.


169-173: Good use of composable lambda for recursive rendering.

The renderNode lambda pattern cleanly enables cross-module composition while maintaining the same signature across all component renderers.


175-218: Comprehensive component type dispatch.

The when block covers all P0 core components, feedback components, GenUI components, and control flow elements. The delegation to separate modules (NanoLayoutComponents, NanoContentComponents, NanoInputComponents, NanoFeedbackComponents, NanoDataComponents, NanoControlFlowComponents) follows good separation of concerns.

mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoRenderUtils.kt (3)

18-30: Good use of kotlinx-datetime for built-in variables.

The getBuiltInVariables function correctly uses kotlinx.datetime for accurate date/time values, providing useful template variables like currentYear, today, and now.


245-363: Well-structured recursive-descent expression parser.

The NumericExprParser correctly implements operator precedence (additive → multiplicative → unary/factor) and handles parentheses, identifiers with dot notation, and numeric literals. The division-by-zero guard (line 279) is a safe choice.


485-506: Good design token mapping for spacing/padding.

The toSpacing() and toPadding() extension functions provide consistent mapping from semantic tokens (xs, sm, md, lg, xl, none) to concrete Dp values, aligning with the coding guidelines for using design tokens.

mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoInputComponents.kt (4)

30-33: Good use of lenient JSON parser for options.

The lenientJson configuration with isLenient = true and ignoreUnknownKeys = true is appropriate for parsing user-provided DSL options that may not be strictly formatted.


74-81: Solid validation of state paths.

The resolveStatePathFromBinding function correctly validates that paths are simple identifiers or dotted paths, preventing expression strings like "x" in state.items from being misinterpreted as writable state paths.


119-122: Proper use of MaterialTheme for button colors.

The button colors use MaterialTheme.colorScheme.error for danger intent, following the coding guideline to use MaterialTheme.colorScheme for Compose styling.


734-847: DateRangePicker is now fully implemented.

The previous review flagged this as a non-functional placeholder. The current implementation includes proper date range dialog, state parsing for List/Map/String formats, and state mutation logic. This addresses the prior concern.

mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoLayoutComponents.kt (4)

72-100: Smart layout adaptation for mixed content.

The logic to detect Image+VStack combinations and switch to FlowRow is a thoughtful solution to prevent text compression. The explicit wrap support and checkbox handling in FlowRow mode also shows good attention to real-world use cases.


108-128: Good weight distribution strategy.

The three-tier approach—explicit flex/weight, auto-distribution for multiple VStack/Card siblings, and default content sizing—provides flexibility while maintaining sensible defaults.


116-116: Consider if wrapContentHeight(unbounded = true) is intentional.

Using unbounded = true allows children to exceed the parent's maximum height constraints. This could cause layout overflow in constrained containers. Verify this is the intended behavior.

Also applies to: 121-121


183-214: Well-designed responsive SplitView.

The responsive breakpoint at 720dp with fallback to vertical stacking is a good UX pattern. Clamping the ratio to 0.1-0.9 prevents degenerate splits.

Comment on lines +371 to +389
data class DataChart(
val name: String? = null,
val type: String? = null,
val data: String? = null,
val xAxis: String? = null,
val yAxis: String? = null,
val color: String? = null
) : NanoNode()

/**
* DataTable - Interactive data table
* Example: `DataTable("Flights"): columns: ["Airline", "Time", "Price"] data: state.available_flights`
*/
data class DataTable(
val name: String? = null,
val columns: String? = null,
val data: String? = null,
val onRowClick: NanoAction? = null
) : NanoNode()
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

🧩 Analysis chain

🏁 Script executed:

find . -name "NanoNode.kt" -o -name "*Renderer*.kt" | head -20

Repository: phodal/auto-dev

Length of output: 1792


🏁 Script executed:

find . -name "NanoNode.kt" -type f

Repository: phodal/auto-dev

Length of output: 127


🏁 Script executed:

sed -n '365,395p' ./xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ast/NanoNode.kt

Repository: phodal/auto-dev

Length of output: 1096


🏁 Script executed:

rg -n "DataChart|DataTable" --type=kt -B 2 -A 15 | grep -E "(class|fun|DataChart|DataTable|data|state\.|binding)" | head -80

Repository: phodal/auto-dev

Length of output: 85


🏁 Script executed:

rg -n "DataChart|DataTable" -A 10 xiuper-ui/src | head -100

Repository: phodal/auto-dev

Length of output: 11278


🏁 Script executed:

rg -n "renderDataChart|renderDataTable" xiuper-ui/src -A 20 | head -150

Repository: phodal/auto-dev

Length of output: 6019


🏁 Script executed:

rg -n "class.*Renderer.*DataChart|class.*Renderer.*DataTable" xiuper-ui/src -A 5

Repository: phodal/auto-dev

Length of output: 41


🏁 Script executed:

find xiuper-ui/src -name "*Renderer*.kt" -type f | xargs rg -l "DataChart|DataTable"

Repository: phodal/auto-dev

Length of output: 132


🏁 Script executed:

find xiuper-ui/src -name "*Renderer*.kt" -type f | head -20

Repository: phodal/auto-dev

Length of output: 355


🏁 Script executed:

rg -n "renderDataChart|renderDataTable" xiuper-ui/src --type-list | head -5

Repository: phodal/auto-dev

Length of output: 448


🏁 Script executed:

cat xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/NanoRenderer.kt | head -100

Repository: phodal/auto-dev

Length of output: 3179


🏁 Script executed:

grep -n "renderDataChart\|renderDataTable" xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/NanoRenderer.kt -A 5

Repository: phodal/auto-dev

Length of output: 41


🏁 Script executed:

wc -l xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/NanoRenderer.kt

Repository: phodal/auto-dev

Length of output: 136


🏁 Script executed:

sed -n '100,269p' xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/NanoRenderer.kt

Repository: phodal/auto-dev

Length of output: 4446


🏁 Script executed:

grep -n "renderDataChart\|renderDataTable\|DataChart\|DataTable" xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/NanoRenderer.kt

Repository: phodal/auto-dev

Length of output: 41


🏁 Script executed:

wc -l xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.kt

Repository: phodal/auto-dev

Length of output: 136


Use design tokens for DataChart color property instead of arbitrary strings.

The data property design using String? is sound for supporting both literal and state binding expressions. However, the color property in DataChart violates the coding guideline requiring design tokens instead of hardcoded colors. Per guidelines for **/*.kt files: "Never use hardcoded colors - always use design tokens from the design system."

The color: String? should accept predefined design token values or use AutoDevColors/MaterialTheme.colorScheme for Compose contexts, rather than allowing arbitrary color strings.

🤖 Prompt for AI Agents
In xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ast/NanoNode.kt around
lines 371 to 389, the DataChart.color property currently typed as String? allows
arbitrary hardcoded colors which violates the design-token rule; change the
property to accept a design-token type (e.g., an existing ColorToken/DesignToken
enum or a sealed class that enumerates allowed tokens) or a wrapper like
AutoDevColorToken? (nullable) instead of String?, update the data class
signature accordingly, adjust any serialization/deserialization and usages to
map token names to actual Compose colors via
AutoDevColors/MaterialTheme.colorScheme at render time, and add a brief comment
linking to the design token source.

Parse "align" property from component props and apply appropriate
horizontalAlignment to Column components. Also add vertical spacing
to Card's Column for consistent layout.
@phodal
Copy link
Owner Author

phodal commented Dec 17, 2025

Pushed wrap/flex end-to-end support (Issue #518):\n- NanoSpecV1: HStack.wrap + flex props (VStack/HStack/Card/Form/SplitView)\n- IndentParser: parse wrap/flex from args/props\n- IRConverter: emit props.wrap/props.flex\n- HtmlRenderer: support wrap (flex-wrap) + flex style\n- Tests: NanoIRConverterCommonTest covers wrap/flex\n\nCommit: 75845c8

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 (10)
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/parser/IndentParser.kt (1)

1015-1163: Parser only recognizes on_click: syntax for all action handlers.

Multiple components (DatePicker, Switch, NumberInput, Slider, DateRangePicker, DataTable) assign the onClick action to their respective change handlers (onChange, onRowClick). This means the DSL only supports on_click: syntax, and properties like on_change: or on_row_click: mentioned in NanoSpecV1 won't take effect.

Consider either:

  • Documenting that all actions use on_click: syntax in the DSL
  • Extending the parser to recognize component-specific action keywords

Run the following script to check if action keywords other than on_click are referenced in specs or documentation:

#!/bin/bash
# Search for action property definitions in specs and docs
rg -n "on_change|on_row_click|on_close|on_submit" --type=kt -C3
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ast/NanoNode.kt (3)

147-153: Verify that Button.disabledIf is rendered in all target renderers.

The disabledIf property was added to the Button AST node, but a past comment indicated it's not being rendered in HtmlRenderer, ComposeNanoRenderer, or React NanoRenderer. This would make the property non-functional.

#!/bin/bash
# Verify disabled_if is handled in all renderers
rg -n "disabled_if|disabledIf" --type=kt --type=tsx -C5 | grep -E "(render|disabled)"

384-391: DataChart.color violates design token guideline.

The color: String? property allows arbitrary hardcoded color strings, which violates the coding guideline requiring design tokens instead of hardcoded colors.

As per coding guidelines: Use design token types or AutoDevColors/MaterialTheme.colorScheme references instead of String? for color properties.


512-516: Spinner.color also violates design token guideline.

Similar to DataChart, the color: String? property on Spinner allows arbitrary color strings instead of design tokens.

As per coding guidelines: Use design token types or AutoDevColors/MaterialTheme.colorScheme references instead of String? for color properties. This applies to all components with color properties (Badge, Icon, DataChart, Spinner).

Based on learnings: Use AutoDevColors or MaterialTheme.colorScheme for Compose styling.

xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/spec/v1/NanoSpecV1.kt (4)

111-111: Button disabled_if property may not be rendered.

The disabled_if property is specified but a past comment indicated it's not being rendered in any of the renderers (HtmlRenderer, ComposeNanoRenderer, React NanoRenderer).

Verify renderer implementations include disabled_if handling:

#!/bin/bash
# Check if disabled_if is used in rendering logic
fd -e kt -e tsx | xargs rg -l "disabled.*if|disabledIf" | xargs rg -C5 "render.*Button"

194-194: Switch size using SPACING_VALUES may be semantically unclear.

The Switch component's size property uses SPACING_VALUES (xs, sm, md, lg, xl) which are defined for spacing purposes. While these values may work for sizing, consider whether a dedicated SIZE_VALUES constant would be more semantically appropriate for component sizing.

This is a minor semantic clarity issue, not a functional problem.


240-249: Progress max default type inconsistency.

The spec defines max with default value "100" (string), but the parser at IndentParser.kt:1091 uses 100f (float). This creates a minor inconsistency between the spec and implementation.

Consider aligning the default value format between spec and parser for consistency.


320-346: DataChart and DataTable required data property not enforced by parser.

Both components specify data as a required property in the spec, but IndentParser.kt allows these properties to be null without validation. This inconsistency means components could be created with missing required data.

Consider either:

  • Making the properties nullable in the spec
  • Adding validation in the parser to enforce required properties
  • Handling missing data gracefully in renderers
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.kt (2)

417-436: Guard against division by zero in Progress rendering.

Line 426 computes (value / max) * 100 but doesn't guard against max being 0, which would produce Infinity or NaN. While line 421 defaults max to 100f, the actual value could be explicitly set to 0 in the DSL.

Apply this guard:

-        val percentage = if (max > 0f) ((value / max) * 100).toInt().coerceIn(0, 100) else 0
+        val percentage = if (max > 0f) ((value / max) * 100).toInt().coerceIn(0, 100) else 0

Actually, the current code already has the guard if (max > 0f). This past comment appears to be resolved. No action needed.


503-519: Null values rendered as literal "null" in data attributes.

Lines 506 and 514 interpolate nullable content values directly into HTML attributes. When the property is missing, this produces data-data="null" which can cause client-side parsing issues.

Apply this diff to omit attributes when values are null:

     fun renderDataChart(ir: NanoIR): String {
         val type = ir.props["type"]?.jsonPrimitive?.content ?: "line"
         val data = ir.props["data"]?.jsonPrimitive?.content
-        return "<div class=\"nano-datachart type-$type\" data-data=\"$data\"><!-- Chart will be rendered by client-side library --></div>\n"
+        val dataAttr = if (data != null) " data-data=\"$data\"" else ""
+        return "<div class=\"nano-datachart type-$type\"$dataAttr><!-- Chart will be rendered by client-side library --></div>\n"
     }

     fun renderDataTable(ir: NanoIR): String {
         val columns = ir.props["columns"]?.jsonPrimitive?.content
         val data = ir.props["data"]?.jsonPrimitive?.content
         val actionAttr = renderActionAttribute(ir)
+        val columnsAttr = if (columns != null) " data-columns=\"$columns\"" else ""
+        val dataAttr = if (data != null) " data-data=\"$data\"" else ""
         return buildString {
-            append("<table class=\"nano-datatable\" data-columns=\"$columns\" data-data=\"$data\"$actionAttr>\n")
+            append("<table class=\"nano-datatable\"$columnsAttr$dataAttr$actionAttr>\n")
             append("  <thead><tr><!-- Columns will be populated dynamically --></tr></thead>\n")
             append("  <tbody><!-- Rows will be populated dynamically --></tbody>\n")
             append("</table>\n")
         }
     }
🧹 Nitpick comments (2)
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/parser/IndentParser.kt (2)

912-919: Consider simplifying the wrap property parsing.

The current implementation works correctly but could be more concise:

-                wrap = (args["wrap"] ?: props["wrap"])?.let { raw ->
-                    when (raw.trim().lowercase()) {
-                        "wrap" -> true
-                        "true" -> true
-                        "false" -> false
-                        else -> null
-                    }
-                },
+                wrap = (args["wrap"] ?: props["wrap"])?.trim()?.lowercase()?.let {
+                    it == "wrap" || it == "true"
+                },

1222-1314: Consider refactoring subscribe binding parser for maintainability.

The subscribe binding parsing logic (93 lines) is dense and complex, handling string states, multiple bracket types, and expression boundaries. While functionally correct, extracting helper methods would improve maintainability:

  • scanToNextTopLevelComma() to handle expression boundary detection
  • parseKeyIdentifier() to extract the binding key
  • parseBindingExpression() to capture the expression value

This is a readability improvement, not a functional issue. The current implementation works correctly.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 262b8a6 and 75845c8.

📒 Files selected for processing (6)
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ast/NanoNode.kt (7 hunks)
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverter.kt (8 hunks)
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/parser/IndentParser.kt (8 hunks)
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.kt (8 hunks)
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/spec/v1/NanoSpecV1.kt (6 hunks)
  • xiuper-ui/src/commonTest/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverterCommonTest.kt (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.kt

📄 CodeRabbit inference engine (AGENTS.md)

**/*.kt: Use expect/actual pattern for platform-specific code (for example: Platform implementations)
Use concrete classes (not interfaces) and Promise (not Flow) for @JsExport annotations
Avoid emoji and UTF-8 characters in WASM code
When modifying CodingAgentRenderer, update ALL implementations: DefaultCodingAgentRenderer, ComposeRenderer, JewelRenderer, ServerSideRenderer, and JsRendererAdapter in Kotlin
When modifying CodingAgentRenderer, update JVM CLI implementations: CodingCliRenderer and ConsoleRenderer
Use AutoDevColors or MaterialTheme.colorScheme for Compose styling

Files:

  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverter.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/spec/v1/NanoSpecV1.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.kt
  • xiuper-ui/src/commonTest/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverterCommonTest.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/parser/IndentParser.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ast/NanoNode.kt
**/*.{kt,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Never use hardcoded colors - always use design tokens from the design system

Files:

  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverter.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/spec/v1/NanoSpecV1.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.kt
  • xiuper-ui/src/commonTest/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverterCommonTest.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/parser/IndentParser.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ast/NanoNode.kt
🧠 Learnings (6)
📓 Common learnings
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Refer to `docs/design-system-compose.md` for Compose design system documentation
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Applies to **/*.kt : When modifying `CodingAgentRenderer`, update ALL implementations: `DefaultCodingAgentRenderer`, `ComposeRenderer`, `JewelRenderer`, `ServerSideRenderer`, and `JsRendererAdapter` in Kotlin
📚 Learning: 2025-12-07T08:40:10.859Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Applies to **/*.kt : When modifying `CodingAgentRenderer`, update ALL implementations: `DefaultCodingAgentRenderer`, `ComposeRenderer`, `JewelRenderer`, `ServerSideRenderer`, and `JsRendererAdapter` in Kotlin

Applied to files:

  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ast/NanoNode.kt
📚 Learning: 2025-12-07T08:40:10.859Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Applies to **/*.kt : When modifying `CodingAgentRenderer`, update JVM CLI implementations: `CodingCliRenderer` and `ConsoleRenderer`

Applied to files:

  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ast/NanoNode.kt
📚 Learning: 2025-12-07T08:40:10.859Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Applies to **/*.{kt,ts,tsx} : Never use hardcoded colors - always use design tokens from the design system

Applied to files:

  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ast/NanoNode.kt
📚 Learning: 2025-12-07T08:40:10.859Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Applies to **/*.kt : Use `AutoDevColors` or `MaterialTheme.colorScheme` for Compose styling

Applied to files:

  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ast/NanoNode.kt
📚 Learning: 2025-12-07T08:40:10.859Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Applies to mpp-ui/src/jsMain/typescript/**/*.ts : Use `semanticInk`/`semanticChalk` design tokens from `mpp-ui/src/jsMain/typescript/design-system/` for CLI/TUI styling

Applied to files:

  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ast/NanoNode.kt
🧬 Code graph analysis (3)
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.kt (1)
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/NanoRenderer.kt (1)
  • renderNode (38-38)
xiuper-ui/src/commonTest/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverterCommonTest.kt (1)
mpp-core/src/commonMain/kotlin/cc/unitmesh/devins/test/DevInsTestCase.kt (1)
  • assertEquals (45-49)
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/parser/IndentParser.kt (1)
mpp-vscode/webview/src/components/nano/NanoRenderer.tsx (2)
  • isIdentStart (267-269)
  • isIdentPart (270-272)
⏰ 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: Test mpp-core (KMP)
  • GitHub Check: Build
🔇 Additional comments (10)
xiuper-ui/src/commonTest/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverterCommonTest.kt (1)

102-125: LGTM!

The test comprehensively validates the wrap and flex property parsing and IR conversion. The assertions correctly verify the HStack wrap property and the flex values on Card children.

xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/parser/IndentParser.kt (2)

258-363: LGTM!

The multi-line inline component parser correctly handles nested parentheses, string literals, and escape sequences across multiple lines. The implementation is necessarily complex but appears correct.


1173-1217: LGTM!

The extractBracketLiteral helper correctly handles nested brackets/braces and string literals with escaping. This is essential for parsing complex properties like column definitions in DataTable.

xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ast/NanoNode.kt (1)

57-92: LGTM!

The flex property additions to VStack, HStack, and Card are well-documented and appropriately typed as nullable Float. The KDoc comments clearly explain the purpose of these properties in flex layouts.

xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverter.kt (2)

38-57: LGTM!

The IR converter correctly dispatches all new component types and properly converts the flex and wrap properties to IR format. The nullable handling using let ensures only present values are included in the IR props.

Also applies to: 89-89, 103-104, 117-117, 175-175, 295-295


403-640: LGTM!

All new component converter methods follow consistent patterns and correctly transform NanoNode AST into NanoIR. The handling of props, bindings, actions, and children is appropriate for each component type.

xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/spec/v1/NanoSpecV1.kt (1)

27-30: LGTM!

The new validation constant lists are appropriate for their component types, and the flex/wrap property additions align with the AST changes. The specifications are well-structured.

Also applies to: 40-40, 52-53, 65-65

xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.kt (3)

18-24: LGTM!

The renderFlexStyleAttribute helper includes good defensive guards against invalid values and correctly applies the flex style with min-width: 0 to prevent text overflow in flex containers.


56-73: LGTM!

The extended dispatcher correctly routes all new component types, and the flex style integration is consistently applied across VStack, HStack, Card, and Form. The wrap property handling correctly supports both "wrap" and "true" values.

Also applies to: 88-137


580-735: LGTM!

The CSS additions correctly support the wrap behavior and provide appropriate styling for all new components. The .nano-hstack.wrap { flex-wrap: wrap; } rule properly enables wrapping.

- Updated Nano component implementations across platforms
- Enhanced Nano rendering utilities and converters
- Improved Nano IR conversion and parsing
- Updated VS Code Nano renderer integration
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)
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverter.kt (1)

398-401: Critical: Missing payload for Sequence actions.

The NanoAction.Sequence converter returns a NanoActionIR with only the type field, omitting the payload with the nested actions array. This is inconsistent with ComponentConverterUtils.convertAction (lines 83-88 in ComponentConverterUtils.kt) which correctly includes the payload. Without the payload, sequence actions cannot be reconstructed or executed downstream.

Apply this diff to include the actions payload:

-            is NanoAction.Sequence -> NanoActionIR(
-                type = "sequence"
-            )
+            is NanoAction.Sequence -> NanoActionIR(
+                type = "sequence",
+                payload = mapOf(
+                    "actions" to JsonArray(action.actions.map { actionIrToJson(convertAction(it)) })
+                )
+            )

You'll also need to add the actionIrToJson helper method similar to ComponentConverterUtils:

private fun actionIrToJson(action: NanoActionIR): JsonElement {
    val obj = mutableMapOf<String, JsonElement>("type" to JsonPrimitive(action.type))
    action.payload?.takeIf { it.isNotEmpty() }?.let { payload ->
        obj["payload"] = JsonObject(payload)
    }
    return JsonObject(obj)
}
♻️ Duplicate comments (14)
xiuper-ui/src/iosMain/kotlin/cc/unitmesh/xuiper/prompt/ResourceLoader.ios.kt (1)

61-62: Fix component documentation to match actual implementations.

Two documentation errors remain from the previous review:

  1. Progress component (line 62):

    • Change value=state.progress to value=50 (or document as value=0-100) since the implementation uses numeric Float?, not a binding
    • Change status="normal|success|exception|active" to status="normal|success|error"
  2. Alert component (line 61):

    • Add missing props: icon and onClose
    • Complete signature should be: Alert(type="info|success|error|warning", message="...", closable=true, icon="...", onClose=action)

Apply these corrections:

-        - `Alert(type="info|success|error|warning", message="...", closable=true)` - Alert banner
-        - `Progress(value=state.progress, max=100, showText=true, status="normal|success|exception|active")` - Progress bar
+        - `Alert(type="info|success|error|warning", message="...", closable=true, icon="...", onClose=action)` - Alert banner
+        - `Progress(value=50, max=100, showText=true, status="normal|success|error")` - Progress bar
mpp-vscode/webview/src/components/nano/NanoRenderer.tsx (6)

696-699: Placeholder attribute won't work on date inputs.

Most browsers ignore the placeholder attribute on input[type="date"] elements, so users won't see "Select date" as a hint.

This issue was previously flagged. Consider using a visible label or helper text instead of relying on placeholder.


758-770: Non-functional increment/decrement buttons.

The increment and decrement buttons in NumberInput have no onClick handlers, making them non-functional.

This issue was previously flagged and marked as addressed, but the current code still lacks handlers. If this is intentional (presentational only), consider adding a comment noting that these buttons are non-interactive in the preview renderer.


776-792: Non-functional Modal close button.

The close button in the Modal header has no onClick handler, making it non-functional.

This issue was previously flagged. Consider adding a handler that calls a dismiss function from the context, or document that this is presentational only in preview mode.


794-807: Non-functional Alert close button.

When closable is true, the Alert renders a close button without an onClick handler, making it non-functional.

This issue was previously flagged. Consider adding functionality or documenting that this is presentational only.


861-871: Validation pattern should be type-checked before use.

The validation property is passed directly to the HTML pattern attribute without checking if it's a valid string or regex pattern.

This issue was previously flagged. Consider validating that validation is a non-empty string before assigning it to pattern.


886-893: DateRangePicker lacks labels for accessibility.

The two date inputs have no labels or distinguishing text, making it unclear which is for the start date and which is for the end date.

This issue was previously flagged. Consider adding visible labels or aria-label attributes to distinguish start and end date inputs.

mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoInputComponents.kt (3)

414-435: Click handler addressed with Box wrapper.

The past review comment about click handlers on disabled OutlinedTextField has been addressed by wrapping the field in a Box with the clickable modifier. The disabled colors are also explicitly set to maintain visual appearance.


771-888: DateRangePicker now fully implemented.

The previous placeholder implementation has been replaced with a functional date range picker that:

  • Parses multiple input formats (List, Map, String with various separators)
  • Uses Material3 DateRangePickerState
  • Preserves the state format (List/Map/String) when encoding updates

This addresses the past review comment about the non-functional placeholder.


817-838: Same Box wrapper pattern applied consistently.

The DateRangePicker uses the same Box wrapper pattern as DatePicker to handle clicks on a disabled OutlinedTextField, maintaining consistency and addressing the platform-specific click propagation concern.

mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoRenderUtils.kt (1)

491-501: Date calculation now uses kotlinx.datetime correctly.

The previous inaccurate date calculation using approximate days/365 has been replaced with proper Instant.fromEpochMilliseconds and toLocalDateTime(TimeZone.UTC) conversion. This addresses the past review comment about incorrect date results.

xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.kt (2)

438-457: Progress rendering with proper guard against division issues.

The percentage calculation now includes if (max > 0f) guard and uses coerceIn(0, 100) to ensure valid output, addressing the past review concern about potential Infinity/NaN values.


557-576: Data attributes now properly handled.

The renderDataChart and renderDataTable methods now:

  1. Use conditional attribute building (data?.let { ... } ?: "")
  2. Apply escapeHtmlAttr to prevent XSS and attribute injection
  3. No longer render literal "null" strings

This addresses the past review comment about null values in data attributes.

xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ast/NanoNode.kt (1)

522-526: Use design tokens for Spinner color property instead of arbitrary strings.

The color property accepts String? which allows hardcoded color values, violating the coding guideline: "Never use hardcoded colors - always use design tokens from the design system."

Based on coding guidelines, colors should reference design tokens or use AutoDevColors/MaterialTheme.colorScheme for Compose contexts. Consider constraining this property to accept only design token identifiers rather than arbitrary color strings.

Apply a similar fix as suggested for DataChart.color: change the property to accept a design-token type (e.g., a ColorToken enum, sealed class, or wrapper) instead of String?, and map token names to actual colors at render time.

🧹 Nitpick comments (10)
xiuper-ui/src/commonMain/resources/prompts/detailed.txt (1)

65-69: Standardize example syntax across sections.

Examples use inconsistent syntax patterns:

  • Line 67: open := state.showHelp (binding with :=)
  • Line 82: columns=["name","role"] (named arguments)

Choose one pattern for consistency, or add a note explaining both are valid. This helps the LLM learn the DSL more reliably.

Suggest aligning on one pattern, e.g. named arguments with optional binding:

-Modal(open := state.showHelp, title="Help", size="md"):
+Modal(open=state.showHelp, title="Help", size="md"):

Also applies to: 80-84

mpp-vscode/webview/src/components/nano/NanoRenderer.tsx (1)

47-57: Consider guarding against prototype pollution.

The getByPath function traverses object properties using bracket notation without checking for dangerous keys like __proto__, constructor, or prototype. While this is a read-only operation and may not be immediately exploitable in this renderer context, hardening against prototype pollution is a recommended security practice.

Apply this diff to add a safeguard:

 function getByPath(obj: any, path: string): any {
   if (!path) return undefined;
   const normalized = path.startsWith('state.') ? path.slice('state.'.length) : path;
   const parts = normalized.split('.').filter(Boolean);
   let current: any = obj;
   for (const part of parts) {
     if (current == null) return undefined;
+    if (['__proto__', 'constructor', 'prototype'].includes(part)) return undefined;
     current = current[part];
   }
   return current;
 }
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/spec/v1/NanoSpecV1.kt (1)

342-348: Consider PropType.EXPRESSION for DataTable columns.

DataTable.data uses PropType.EXPRESSION (line 346) to support both state bindings and literals, but DataTable.columns uses PropType.STRING (line 347) even though its description mentions "array or state binding". For consistency and flexibility, consider using PropType.EXPRESSION for columns as well.

Apply this diff if expression support is intended:

-                PropSpec("columns", PropType.STRING, description = "Column definitions (array or state binding)"),
+                PropSpec("columns", PropType.EXPRESSION, description = "Column definitions (array or state binding)"),
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoContentComponents.kt (1)

254-278: Use design tokens instead of hardcoded colors.

Line 268 uses hardcoded Color.Black and Color.White for badge text colors. Per coding guidelines, always use design tokens like MaterialTheme.colorScheme.onPrimary, MaterialTheme.colorScheme.onSurface, or AutoDevColors for proper theming support.

Apply this diff to use design tokens:

-        val textColor = if (colorName == "yellow") Color.Black else Color.White
+        val textColor = when (colorName) {
+            "yellow" -> MaterialTheme.colorScheme.onSurface
+            else -> MaterialTheme.colorScheme.onPrimary
+        }

As per coding guidelines, which specify using AutoDevColors or MaterialTheme.colorScheme for Compose styling.

xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/parser/IndentParser.kt (2)

1113-1120: Consider default value consistency for Progress.max.

The Progress component's max property has no default value applied in the parser (line 1116), but the spec indicates a default of "100". Consider whether the default should be applied during parsing or if the spec and parser should be consistent in their default handling. This could lead to inconsistent behavior if downstream code expects a default value.

If defaults should be applied during parsing:

-                    max = args["max"] ?: props["max"],
+                    max = args["max"] ?: props["max"] ?: "100",

1176-1195: Consider validation for required properties.

DataChart and DataTable specify data as a required property in the ComponentSpec, but the parser allows it to be null without validation (lines 1181, 1192). This inconsistency means components could be created with missing required data, potentially leading to runtime issues when renderers access this property. Consider either making the property required in the AST node or removing the required constraint from the spec.

mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoFeedbackComponents.kt (1)

234-259: Consider adding indeterminate progress support.

The RenderProgress implementation handles determinate progress well, but consider supporting indeterminate progress (when value is not provided) which is common for loading states.

 @Composable
 fun RenderProgress(ir: NanoIR, state: Map<String, Any>, modifier: Modifier) {
     val valueStr = ir.props["value"]?.jsonPrimitive?.content
     val maxStr = ir.props["max"]?.jsonPrimitive?.content
     val showText = ir.props["showText"]?.jsonPrimitive?.booleanOrNull ?: true
+    val indeterminate = ir.props["indeterminate"]?.jsonPrimitive?.booleanOrNull ?: (valueStr == null)

-        // Resolve binding / expression values
-        val value = NanoRenderUtils.evaluateNumberOrNull(valueStr, state)?.toFloat() ?: 0f
-        val max = NanoRenderUtils.evaluateNumberOrNull(maxStr, state)?.toFloat() ?: 100f
+    // Resolve binding / expression values
+    val value = NanoRenderUtils.evaluateNumberOrNull(valueStr, state)?.toFloat() ?: 0f
+    val max = NanoRenderUtils.evaluateNumberOrNull(maxStr, state)?.toFloat() ?: 100f
     val progress = if (max > 0f) (value / max).coerceIn(0f, 1f) else 0f

     Column(modifier = modifier.fillMaxWidth()) {
-        LinearProgressIndicator(
-            progress = { progress },
-            modifier = Modifier.fillMaxWidth().height(8.dp),
-        )
+        if (indeterminate) {
+            LinearProgressIndicator(modifier = Modifier.fillMaxWidth().height(8.dp))
+        } else {
+            LinearProgressIndicator(
+                progress = { progress },
+                modifier = Modifier.fillMaxWidth().height(8.dp),
+            )
+        }
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.kt (2)

63-73: Consider error logging for parse failures.

The parseStructuredDefault function silently catches exceptions and returns the raw string. Consider logging parse failures for debugging purposes in development builds.

 private fun parseStructuredDefault(raw: String): Any {
     val trimmed = raw.trim()
     if (trimmed.isEmpty()) return ""

     return try {
         // JSON is valid YAML 1.2, so this handles both JSON/YAML list/map literals.
         YamlUtils.load(trimmed) ?: raw
     } catch (_: Exception) {
+        // Log for debugging: println("Failed to parse structured default: $trimmed")
         raw
     }
 }

120-137: Potential duplicate prevention issue with APPEND.

The APPEND operation checks currentValue.contains(trimmed) to prevent duplicates, but this uses reference/structural equality. If the list contains objects, this may not work as expected. For string lists this is fine, but consider documenting this limitation.

xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.kt (1)

720-741: Hardcoded colors in CSS - consider design tokens.

The generated CSS contains hardcoded color values (e.g., #6200EE, #03DAC6, #B00020). While this is acceptable for static HTML output, consider documenting that these should align with the design system tokens used in Compose (AutoDevColors).

As per coding guidelines, hardcoded colors should be avoided. For HTML rendering, consider generating CSS variables from the design system or adding a comment noting these match AutoDevColors.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 75845c8 and 9fcc4f9.

📒 Files selected for processing (19)
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoContentComponents.kt (1 hunks)
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoControlFlowComponents.kt (1 hunks)
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoDataComponents.kt (1 hunks)
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoFeedbackComponents.kt (1 hunks)
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoInputComponents.kt (1 hunks)
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoRenderUtils.kt (1 hunks)
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.kt (6 hunks)
  • mpp-ui/src/jvmTest/kotlin/cc/unitmesh/devins/ui/nano/NanoRenderUtilsTest.kt (1 hunks)
  • mpp-vscode/webview/src/components/nano/NanoRenderer.tsx (8 hunks)
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ast/NanoNode.kt (7 hunks)
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/ComponentConverterUtils.kt (1 hunks)
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverter.kt (8 hunks)
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/parser/IndentParser.kt (11 hunks)
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.kt (10 hunks)
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/spec/v1/NanoSpecV1.kt (6 hunks)
  • xiuper-ui/src/commonMain/resources/prompts/detailed.txt (4 hunks)
  • xiuper-ui/src/commonTest/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverterCommonTest.kt (1 hunks)
  • xiuper-ui/src/iosMain/kotlin/cc/unitmesh/xuiper/prompt/ResourceLoader.ios.kt (1 hunks)
  • xiuper-ui/src/jvmTest/kotlin/cc/unitmesh/xuiper/render/HtmlRendererTest.kt (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • mpp-ui/src/jvmTest/kotlin/cc/unitmesh/devins/ui/nano/NanoRenderUtilsTest.kt
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{kt,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Never use hardcoded colors - always use design tokens from the design system

Files:

  • mpp-vscode/webview/src/components/nano/NanoRenderer.tsx
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/ComponentConverterUtils.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverter.kt
  • xiuper-ui/src/jvmTest/kotlin/cc/unitmesh/xuiper/render/HtmlRendererTest.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoInputComponents.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoFeedbackComponents.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoContentComponents.kt
  • xiuper-ui/src/commonTest/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverterCommonTest.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/spec/v1/NanoSpecV1.kt
  • xiuper-ui/src/iosMain/kotlin/cc/unitmesh/xuiper/prompt/ResourceLoader.ios.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoRenderUtils.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoDataComponents.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoControlFlowComponents.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ast/NanoNode.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/parser/IndentParser.kt
**/*.kt

📄 CodeRabbit inference engine (AGENTS.md)

**/*.kt: Use expect/actual pattern for platform-specific code (for example: Platform implementations)
Use concrete classes (not interfaces) and Promise (not Flow) for @JsExport annotations
Avoid emoji and UTF-8 characters in WASM code
When modifying CodingAgentRenderer, update ALL implementations: DefaultCodingAgentRenderer, ComposeRenderer, JewelRenderer, ServerSideRenderer, and JsRendererAdapter in Kotlin
When modifying CodingAgentRenderer, update JVM CLI implementations: CodingCliRenderer and ConsoleRenderer
Use AutoDevColors or MaterialTheme.colorScheme for Compose styling

Files:

  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/ComponentConverterUtils.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverter.kt
  • xiuper-ui/src/jvmTest/kotlin/cc/unitmesh/xuiper/render/HtmlRendererTest.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoInputComponents.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoFeedbackComponents.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoContentComponents.kt
  • xiuper-ui/src/commonTest/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverterCommonTest.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/spec/v1/NanoSpecV1.kt
  • xiuper-ui/src/iosMain/kotlin/cc/unitmesh/xuiper/prompt/ResourceLoader.ios.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoRenderUtils.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoDataComponents.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoControlFlowComponents.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ast/NanoNode.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/parser/IndentParser.kt
🧠 Learnings (8)
📓 Common learnings
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Refer to `docs/design-system-compose.md` for Compose design system documentation
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Applies to **/*.kt : When modifying `CodingAgentRenderer`, update ALL implementations: `DefaultCodingAgentRenderer`, `ComposeRenderer`, `JewelRenderer`, `ServerSideRenderer`, and `JsRendererAdapter` in Kotlin
📚 Learning: 2025-12-07T08:40:10.859Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Applies to **/*.ts : When modifying `CodingAgentRenderer`, update ALL TypeScript implementations: `BaseRenderer.ts`, `CliRenderer.ts`, `ServerRenderer.ts`, and `TuiRenderer.ts`

Applied to files:

  • mpp-vscode/webview/src/components/nano/NanoRenderer.tsx
📚 Learning: 2025-12-07T08:40:10.859Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Applies to mpp-vscode/src/**/*.ts : When modifying `CodingAgentRenderer`, update VSCode implementations: `mpp-vscode/src/bridge/mpp-core.ts` and `mpp-vscode/src/providers/chat-view.ts`

Applied to files:

  • mpp-vscode/webview/src/components/nano/NanoRenderer.tsx
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoFeedbackComponents.kt
📚 Learning: 2025-12-07T08:40:10.859Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Applies to **/*.kt : When modifying `CodingAgentRenderer`, update ALL implementations: `DefaultCodingAgentRenderer`, `ComposeRenderer`, `JewelRenderer`, `ServerSideRenderer`, and `JsRendererAdapter` in Kotlin

Applied to files:

  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverter.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoFeedbackComponents.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoContentComponents.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ast/NanoNode.kt
📚 Learning: 2025-12-07T08:40:10.859Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Applies to **/*.kt : When modifying `CodingAgentRenderer`, update JVM CLI implementations: `CodingCliRenderer` and `ConsoleRenderer`

Applied to files:

  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ast/NanoNode.kt
📚 Learning: 2025-12-07T08:40:10.859Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Applies to **/*.kt : Use `AutoDevColors` or `MaterialTheme.colorScheme` for Compose styling

Applied to files:

  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoControlFlowComponents.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ast/NanoNode.kt
📚 Learning: 2025-12-07T08:40:10.859Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Applies to **/*.{kt,ts,tsx} : Never use hardcoded colors - always use design tokens from the design system

Applied to files:

  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoControlFlowComponents.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ast/NanoNode.kt
📚 Learning: 2025-12-07T08:40:10.859Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Applies to mpp-ui/src/jsMain/typescript/**/*.ts : Use `semanticInk`/`semanticChalk` design tokens from `mpp-ui/src/jsMain/typescript/design-system/` for CLI/TUI styling

Applied to files:

  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoControlFlowComponents.kt
  • xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ast/NanoNode.kt
🧬 Code graph analysis (9)
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/ComponentConverterUtils.kt (3)
mpp-vscode/webview/src/types/nano.ts (2)
  • NanoBindingIR (36-40)
  • NanoActionIR (28-31)
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverter.kt (2)
  • convertBodyField (281-291)
  • convertAction (351-402)
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/input/DatePickerComponent.kt (1)
  • convertAction (72-74)
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverter.kt (3)
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/ComponentConverterUtils.kt (2)
  • convertBinding (19-34)
  • convertAction (36-90)
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/feedback/ModalComponent.kt (1)
  • convertBinding (76-78)
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/input/DatePickerComponent.kt (2)
  • convertBinding (68-70)
  • convertAction (72-74)
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoInputComponents.kt (4)
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/state/NanoStateManager.kt (1)
  • onAction (82-84)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaInputListener.kt (1)
  • Button (10-13)
mpp-vscode/webview/src/types/nano.ts (1)
  • NanoActionIR (28-31)
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.kt (1)
  • renderNode (55-94)
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoFeedbackComponents.kt (3)
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/state/NanoStateManager.kt (1)
  • onAction (82-84)
mpp-vscode/webview/src/types/nano.ts (1)
  • NanoActionIR (28-31)
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.kt (1)
  • renderNode (55-94)
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoContentComponents.kt (2)
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.kt (1)
  • decodeImageBytesToBitmap (235-235)
mpp-ui/src/jsMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.js.kt (1)
  • decodeImageBytesToBitmap (9-11)
xiuper-ui/src/commonTest/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverterCommonTest.kt (1)
mpp-core/src/commonMain/kotlin/cc/unitmesh/devins/test/DevInsTestCase.kt (3)
  • assertEquals (45-49)
  • assertNotNull (54-59)
  • assertTrue (29-33)
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoRenderUtils.kt (1)
mpp-vscode/webview/src/components/nano/NanoRenderer.tsx (7)
  • parseTerm (243-254)
  • match (315-321)
  • parseFactor (256-273)
  • parseExpression (232-241)
  • peek (322-324)
  • parseNumber (275-292)
  • parseIdentifierValue (294-310)
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.kt (1)
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/NanoRenderer.kt (1)
  • renderNode (38-38)
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/parser/IndentParser.kt (1)
mpp-vscode/webview/src/components/nano/NanoRenderer.tsx (2)
  • isIdentStart (328-330)
  • isIdentPart (331-333)
⏰ 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: Test mpp-core (KMP)
  • GitHub Check: Build
🔇 Additional comments (62)
xiuper-ui/src/commonMain/resources/prompts/detailed.txt (1)

53-53: SmartTextField props are correctly documented.

Verification confirms that all four props documented on line 53 (label, bind, validation, placeholder) are correctly defined in the SmartTextField data class. The validation prop is a legitimate String property and is appropriately included as a direct prop. Documentation is accurate.

mpp-vscode/webview/src/components/nano/NanoRenderer.tsx (9)

701-711: LGTM!

The Radio component implementation is clean and accessible, with proper label wrapping and value/name attributes.


713-746: LGTM!

RadioGroup correctly handles both declarative children (Radio components) and data-driven options, with robust parsing via tryParseStructuredLiteral and proper normalization to {value, label} format.


748-756: LGTM!

Switch component correctly uses role="switch" for accessibility and provides proper label association.


809-828: LGTM!

Progress component correctly resolves dynamic value and max from state, intelligently detects binding vs. static values, and displays appropriate text format (either "value / max" for bindings or "percentage%" for static values).


830-839: LGTM!

Spinner component is straightforward with proper size variants and optional descriptive text.


845-859: LGTM!

SplitView correctly calculates left and right panel widths based on the ratio property and renders children in appropriately sized containers.


873-884: LGTM!

Slider component properly implements a range input with configurable min, max, and step values, with an optional label.


895-940: LGTM! Excellent data visualization implementation.

DataChart now renders an actual SVG bar chart with proper data binding, field inference, and a legend. The implementation correctly:

  • Resolves and parses data from state
  • Infers x/y fields with sensible defaults
  • Handles empty data gracefully
  • Uses CSS classes for styling (avoiding hardcoded colors)
  • Provides accessible SVG with role="img" and aria-label

942-1005: LGTM! Robust data table implementation.

DataTable now fully renders tables with proper column and data handling:

  • Resolves columns and data from state with multiple fallback strategies
  • Normalizes column definitions to a consistent format
  • Infers columns from first data row when not specified
  • Applies cell formatting (currency, percent) via formatCell
  • Handles string-based column specs like "a,b,c"
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/spec/v1/NanoSpecV1.kt (7)

27-31: LGTM!

The new constant definitions are well-chosen and semantically appropriate for their respective component properties.


40-41: LGTM!

The flex, wrap, align, justify, and shadow properties are well-integrated and follow flexbox conventions. The consistent use of flex across components enables proper nesting behavior.

Also applies to: 52-54, 65-66


111-112: LGTM!

The icon and disabled_if properties are correctly specified. PropType.EXPRESSION is appropriate for conditional disable logic.


153-213: LGTM!

The P0 form input components are well-specified:

  • DatePicker supports proper date constraints and formatting
  • Radio/RadioGroup provide flexible composition options
  • Switch correctly uses COMPONENT_SIZE_VALUES (addressing the previous concern)
  • NumberInput appropriately uses FLOAT for numeric bounds and INT for precision

214-261: LGTM!

The feedback components are properly configured:

  • Modal includes essential properties for dialog management
  • Alert supports standard notification types with flexible content
  • Progress provides comprehensive progress indication options
  • Spinner follows consistent sizing patterns

Category assignments are appropriate.


274-293: LGTM!

SplitView and GenCanvas are properly configured for layout purposes. The 0.5 default ratio for SplitView provides a sensible 50/50 split.

Note: These components use "bind" for their binding property (distinct from "value" and "checked" used elsewhere), which appears intentional for GenUI/smart components.


355-358: LGTM!

All new components are correctly categorized in their respective component sets. The categorization aligns with the component specifications and makes semantic sense.

xiuper-ui/src/jvmTest/kotlin/cc/unitmesh/xuiper/render/HtmlRendererTest.kt (2)

89-102: LGTM!

The test validates the new disabled_if attribute rendering for Button components and properly asserts the presence of data-disabled-if in the HTML output.


328-394: LGTM!

The Slider tests provide comprehensive coverage for both labeled and unlabeled scenarios, validating container structure, bindings, value display, and input attributes (min/max/step).

xiuper-ui/src/commonTest/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverterCommonTest.kt (2)

102-125: LGTM!

The test validates IR conversion for the new wrap and flex layout properties, ensuring HStack and Card components correctly serialize these props to the intermediate representation.


127-147: LGTM!

The test validates IR conversion for SplitView and GenCanvas components, ensuring correct structure with ratio, flex, and binding properties.

xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/ComponentConverterUtils.kt (3)

19-34: LGTM!

The convertBinding method correctly handles all binding variants (Subscribe, TwoWay, Static) and produces properly structured NanoBindingIR objects.


36-90: LGTM! Past review concern addressed.

The convertAction method now correctly encodes the nested actions array in the payload for NanoAction.Sequence (lines 83-88), addressing the previous review comment about dropped sequence actions.


92-110: LGTM!

The private helper methods actionIrToJson and convertBodyField are correctly implemented and provide proper JSON serialization support for actions and body fields.

xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverter.kt (4)

86-118: LGTM!

The flex layout enhancements correctly add flex properties to VStack, HStack, and Card, plus wrap support for HStack, enabling responsive flex-based layouts.


170-210: LGTM!

The Button and Checkbox enhancements add important functionality: disabled_if for conditional button states and proper props/actions wiring for Checkbox onChange events.


404-481: LGTM!

The Tier 1-2 component converters (SplitView, GenCanvas, SmartTextField, Slider, DateRangePicker) follow a consistent pattern and properly handle props, bindings, and actions.


483-656: LGTM!

The Tier 3 and P0 component converters (DataChart, DataTable, DatePicker, Radio, RadioGroup, Switch, NumberInput, Modal, Alert, Progress, Spinner) are properly implemented with consistent patterns for props, bindings, actions, and children handling.

mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoDataComponents.kt (3)

51-76: LGTM!

The RenderDataChart function correctly converts resolved data to a String (line 61) before passing it to RenderChartFallback, avoiding the type mismatch mentioned in past comments. The implementation properly handles chart rendering with fallback support.


300-387: LGTM!

The RenderDataTable implementation correctly uses HorizontalDivider() (line 361) instead of the deprecated Divider component, and provides robust table rendering with column inference, data parsing, and scrollable display.


389-583: LGTM!

The helper methods provide comprehensive data parsing support for multiple formats (JSON, CSV-like, maps, lists) with proper deprecated annotations and type-safe column/row extraction.

mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoControlFlowComponents.kt (1)

1-77: LGTM!

The control flow components (RenderConditional, RenderForLoop, RenderUnknown) are well-implemented and correctly use design tokens (AutoDevColors.Signal.error) instead of hardcoded colors, addressing previous review guidance.

mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoContentComponents.kt (3)

39-69: LGTM!

The RenderText composable properly handles content binding resolution, state interpolation, and style mapping using MaterialTheme.typography, following design system guidelines.


71-253: LGTM!

The RenderImage composable provides comprehensive image handling with generation, loading, error states, and adaptive sizing. It correctly uses MaterialTheme.colorScheme for colors and implements proper async/await patterns.


280-357: LGTM!

The RenderIcon composable provides comprehensive icon name mapping and correctly uses design tokens (MaterialTheme.colorScheme, AutoDevColors.Signal) for icon colors. The RenderDivider is appropriately simple.

xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/parser/IndentParser.kt (5)

42-365: LGTM!

The multi-line component call parsing (lines 260-365) correctly handles nested parentheses and string literals, enabling complex multi-line component declarations like DataTable(...) to span multiple lines while maintaining proper structure.


377-481: LGTM!

The enhanced event handling (lines 391-406) now generically captures all on_* events (click, change, row_click, close, submit) using the new ON_EVENT_REGEX patterns, addressing previous limitations where only on_click was captured.


911-923: LGTM!

The enhanced createNode signature now accepts an actions map, enabling proper event routing to component-specific handlers (onClick, onChange, onRowClick, onClose, onSubmitAction) with sensible fallbacks, addressing previous event mapping limitations.


932-946: LGTM!

The HStack wrap property parsing (lines 936-943) correctly handles multiple string formats ("wrap", "true", "false") and returns the appropriate Boolean value for flex-wrap support.


1200-1365: LGTM!

The enhanced parseArgs method (with extractBracketLiteral and improved << binding parsing) correctly handles complex nested structures including parentheses, brackets, braces, and string literals, enabling sophisticated binding expressions.

mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoFeedbackComponents.kt (4)

1-30: LGTM! Well-organized feedback components module.

The file structure is clean with proper documentation and clear separation of concerns. The use of AutoDevColors for alert styling complies with the coding guidelines.


32-94: LGTM! Modal rendering with proper race condition handling.

The llmService is correctly captured into a local val service variable before the null check and usage, addressing the previous race condition concern. The LLM fallback for generating modal content is a nice feature for GenUI scenarios.


150-232: LGTM! Alert component with design system compliance.

The component correctly uses AutoDevColors.Signal.* tokens for styling different alert types, complying with the coding guidelines. The LLM fallback pattern mirrors the Modal implementation appropriately.


278-305: LGTM! Clean deprecation pattern for migrated components.

The deprecated wrappers with @Deprecated annotations and ReplaceWith clauses provide a clear migration path to NanoDataComponents.

mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.kt (2)

35-61: LGTM! Clean state initialization from IR.

The initStateFromIR function properly handles various types including primitives, lists, and maps. The use of parseStructuredDefault for complex types is a good approach.


178-227: LGTM! Clean modular dispatch pattern.

The renderNode lambda for recursive rendering is well-designed, enabling clean delegation to external component modules. The when dispatch covers all component types consistently.

mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoInputComponents.kt (4)

1-36: LGTM! Well-structured input components module.

The file is well-organized with clear documentation. The lenientJson configuration is appropriate for handling varied option formats.


76-83: Good validation of state paths.

The regex validation [A-Za-z_]\w*(\.[A-Za-z_]\w*)* correctly prevents expressions like "x" in state.items from being treated as writable state paths.


100-138: LGTM! Button with disabled_if support.

The RenderButton correctly evaluates the disabled_if condition and applies the disabled state consistently to both the click handler guard and the enabled prop.


688-769: LGTM! Well-implemented Slider component.

The RenderSlider implementation properly handles:

  • Step calculation with coerceAtLeast(0)
  • Display value formatting based on state type
  • State mutations with proper encoding

The UI layout with label and value display is clean.

mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoRenderUtils.kt (4)

1-31: LGTM! Clean utility module setup.

The imports are appropriate, and the getBuiltInVariables function provides useful date/time variables for text interpolation.


45-82: LGTM! Robust expression evaluation.

The interpolateText and evaluateExpression functions properly handle:

  • Both {expr} and ${expr} syntax
  • Fast path for plain variables
  • Numeric literals
  • Arithmetic expressions
  • String method chains

Returning empty string for unknown expressions is a safe fallback.


246-364: LGTM! Well-implemented recursive descent parser.

The NumericExprParser correctly implements:

  • Standard operator precedence (+ - lower than * /)
  • Parentheses for grouping
  • Division by near-zero protection (returns 0.0)
  • State variable resolution with state. prefix handling

The implementation mirrors the TypeScript version in NanoRenderer.tsx appropriately.


530-551: LGTM! Consistent spacing/padding utilities.

The extension functions provide a clean mapping from string tokens to Dp values, with sensible defaults.

xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.kt (3)

18-39: LGTM! Useful helper functions.

The renderFlexStyleAttribute properly handles flex styling with min-width:0 to prevent overflow. The escapeHtmlAttr function correctly escapes all necessary HTML special characters.


197-218: LGTM! Button with disabled_if support.

The disabled_if attribute is properly escaped and rendered as a data attribute for client-side evaluation.


792-870: LGTM! Comprehensive slider CSS styling.

The slider CSS includes proper cross-browser support with both -webkit- and -moz- prefixes, hover states, and transitions. The styling is consistent with the rest of the component library.

xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ast/NanoNode.kt (5)

60-61: LGTM: Flex and wrap properties are consistent and well-documented.

The addition of flex properties across layout components (VStack, HStack, Card, Form, SplitView, GenCanvas) and the wrap property on HStack follow a consistent pattern with clear documentation. These properties enable flexbox-like layout behavior which aligns with the PR objectives.

Also applies to: 73-76, 89-90, 290-292, 335-336, 346-347


147-153: LGTM: Button disabledIf property is a sensible addition.

The disabledIf property enables conditional disabling based on expressions, which is consistent with the DSL's binding patterns. Since all parameters after label have default values, this is backward compatible when using named parameters (idiomatic in Kotlin).


169-173: LGTM: Checkbox extension improves usability.

Adding label and onChange properties makes Checkbox more self-contained and aligns with typical checkbox UI patterns. All properties remain optional, ensuring backward compatibility.


407-412: LGTM: DataTable design supports flexible data binding.

The DataTable component appropriately uses String? for columns and data properties to support both literal values and state binding expressions, consistent with the DSL's binding philosophy. The onRowClick handler enables row interaction.


511-516: LGTM: Progress component design supports flexible value binding.

The explicit documentation (lines 508-509) clarifying that value and max accept both literal numbers and state binding expressions is helpful. Using String? for these properties is appropriate given the DSL's dual-mode binding semantics.

Comment on lines +267 to +268
PropSpec("onSubmit", PropType.STRING),
PropSpec("flex", PropType.FLOAT, description = "Flex factor when used inside HStack/SplitView")
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

🧩 Analysis chain

🏁 Script executed:

# First, let's locate and examine the NanoSpecV1.kt file
find . -name "NanoSpecV1.kt" -type f

Repository: phodal/auto-dev

Length of output: 133


🏁 Script executed:

# Check the file content around lines 267-268 and surrounding lines
if [ -f "xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/spec/v1/NanoSpecV1.kt" ]; then
  sed -n '260,280p' "xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/spec/v1/NanoSpecV1.kt"
fi

Repository: phodal/auto-dev

Length of output: 1050


🏁 Script executed:

# Search for all PropSpec definitions with action-related names and their types
rg -n 'PropSpec.*\(.*on\w+|PropSpec.*\(.*click|PropSpec.*\(.*submit' "xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/spec/v1/NanoSpecV1.kt" -A1

Repository: phodal/auto-dev

Length of output: 1449


🏁 Script executed:

# Check lines 324 and 348 for DateRangePicker.on_change and DataTable.on_row_click
if [ -f "xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/spec/v1/NanoSpecV1.kt" ]; then
  echo "=== Line 324 context ==="; 
  sed -n '320,330p' "xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/spec/v1/NanoSpecV1.kt";
  echo -e "\n=== Line 348 context ===";
  sed -n '344,354p' "xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/spec/v1/NanoSpecV1.kt"
fi

Repository: phodal/auto-dev

Length of output: 1199


🏁 Script executed:

# Search for all PropType.STRING and PropType.EXPRESSION to understand the pattern
rg -n 'PropType\.(STRING|EXPRESSION)' "xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/spec/v1/NanoSpecV1.kt" | head -30

Repository: phodal/auto-dev

Length of output: 2428


Standardize action handler property types across components.

Form.onSubmit uses PropType.STRING while DateRangePicker.on_change (line 324) and DataTable.on_row_click (line 348) use PropType.EXPRESSION. This inconsistency may confuse DSL users about the expected format for action handlers.

Change Form.onSubmit to PropType.EXPRESSION for consistency with other action handlers.

🤖 Prompt for AI Agents
In xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/spec/v1/NanoSpecV1.kt
around lines 267-268, the Form.onSubmit property is declared as PropType.STRING
while other action handlers use PropType.EXPRESSION; change the PropSpec for
"onSubmit" to use PropType.EXPRESSION (i.e., replace PropType.STRING with
PropType.EXPRESSION) so action handler types are consistent across components.

Comment on lines 27 to 29
| VStack | spacing, align | Vertical stack |
| HStack | spacing, align, justify | Horizontal stack |
| Card | padding, shadow, radius | Container card |
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

🧩 Analysis chain

🏁 Script executed:

find . -type f -name "*.kt" -o -name "*.java" -o -name "*.tsx" -o -name "*.ts" -o -name "*.jsx" -o -name "*.js" | head -20

Repository: phodal/auto-dev

Length of output: 1754


🏁 Script executed:

git ls-files | grep -E "(VStack|HStack|Card)" | head -20

Repository: phodal/auto-dev

Length of output: 505


🏁 Script executed:

rg -l "class (VStack|HStack|Card)" --type-list

Repository: phodal/auto-dev

Length of output: 448


🏁 Script executed:

rg "wrap|flex" xiuper-ui/src/commonMain --type-list

Repository: phodal/auto-dev

Length of output: 448


🏁 Script executed:

find . -type f -name "detailed.txt" 2>/dev/null

Repository: phodal/auto-dev

Length of output: 168


🏁 Script executed:

find . -type d -name "xiuper-ui" 2>/dev/null

Repository: phodal/auto-dev

Length of output: 70


🏁 Script executed:

rg "VStack|HStack|Card" --type kotlin -A 5 | head -40

Repository: phodal/auto-dev

Length of output: 4244


🏁 Script executed:

rg "wrap.*flex|flex.*wrap" . --type kotlin --type txt | head -20

Repository: phodal/auto-dev

Length of output: 181


🏁 Script executed:

cat -n ./xiuper-ui/src/commonMain/resources/prompts/detailed.txt

Repository: phodal/auto-dev

Length of output: 7528


🏁 Script executed:

rg "wrap|flex" ./xiuper-ui/src/commonMain/kotlin --type kotlin -B 2 -A 2

Repository: phodal/auto-dev

Length of output: 29627


🏁 Script executed:

find ./xiuper-ui -type f -name "*Component*" -o -name "*Stack*" | head -20

Repository: phodal/auto-dev

Length of output: 594


🏁 Script executed:

rg "class.*VStack|class.*HStack|class.*Card|object.*VStack|object.*HStack|object.*Card" ./xiuper-ui --type kotlin -A 10

Repository: phodal/auto-dev

Length of output: 3490


Update Layout Components table to include flex support; wrap applies only to HStack.

The documentation is incomplete. Based on the codebase implementation:

 ### 2. Layout Components
 | Component | Props | Description |
 |-----------|-------|-------------|
-| VStack | spacing, align | Vertical stack |
-| HStack | spacing, align, justify | Horizontal stack |
-| Card | padding, shadow, radius | Container card |
+| VStack | spacing, align, flex | Vertical stack |
+| HStack | spacing, align, justify, wrap, flex | Horizontal stack |
+| Card | padding, shadow, radius, flex | Container card |

Note: wrap only applies to HStack (allows children to wrap on narrow widths). flex applies to all three components when used as children in flex containers like HStack or SplitView.

📝 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
| VStack | spacing, align | Vertical stack |
| HStack | spacing, align, justify | Horizontal stack |
| Card | padding, shadow, radius | Container card |
### 2. Layout Components
| Component | Props | Description |
|-----------|-------|-------------|
| VStack | spacing, align, flex | Vertical stack |
| HStack | spacing, align, justify, wrap, flex | Horizontal stack |
| Card | padding, shadow, radius, flex | Container card |
🤖 Prompt for AI Agents
In xiuper-ui/src/commonMain/resources/prompts/detailed.txt around lines 27 to
29, the Layout Components table is missing the "wrap" and "flex" attribute
details; update the table so that HStack's row lists "spacing, align, justify,
wrap" (wrap applies only to HStack) and add "flex" to the VStack, HStack, and
Card rows (documenting that flex applies when these components are children in
flex containers such as HStack or SplitView); ensure the table text clarifies
that wrap is HStack-only and flex is applicable to all three when used inside
flex containers.

Comment on lines +30 to +31
| SplitView | ratio | Left/right split layout |
| GenCanvas | bind | AI-generated canvas/preview area |
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

Consolidate duplicated SplitView documentation.

SplitView is documented twice with conflicting prop signatures:

  • Section 2 (line 30): ratio only
  • Section 6 (line 74): ratio, flex

The GenUI version (line 74) is more complete and aligns with the PR changes. Remove the Layout Components version and consolidate into a single entry with full prop signature.

 ### 2. Layout Components
 | Component | Props | Description |
 |-----------|-------|-------------|
 | VStack | spacing, align | Vertical stack |
 | HStack | spacing, align, justify | Horizontal stack |
 | Card | padding, shadow, radius | Container card |
-| SplitView | ratio | Left/right split layout |
 | GenCanvas | bind | AI-generated canvas/preview area |

Then update line 74 to note it's both a layout and GenUI component, or move the full definition to the GenUI section as the canonical reference.

Also applies to: 74-75

🤖 Prompt for AI Agents
In xiuper-ui/src/commonMain/resources/prompts/detailed.txt around lines 30-31
and 74-75, there are duplicate SplitView entries with conflicting prop
signatures; remove the Layout Components entry at lines 30-31 (the one listing
only "ratio"), keep the more complete GenUI entry at ~74, and update that entry
to state it is both a layout and GenUI component (or move the full definition
into the GenUI section as the canonical reference) so there is a single,
authoritative SplitView doc with props "ratio, flex".

Comment on lines +86 to 92
### 7. Form Components
```nanodsl
Form(onSubmit=request.login):
Input(name="email", value := state.email)
Input(name="password", type="password", value := state.password)
Button("Submit", type="submit")
```
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Update Form example to demonstrate new Button props.

The Form section still uses the old Button syntax. Update it to demonstrate disabled_if or other new props introduced in this PR, so the LLM learns to use them.

 ### 7. Form Components
 ```nanodsl
 Form(onSubmit=request.login):
     Input(name="email", value := state.email)
     Input(name="password", type="password", value := state.password)
-    Button("Submit", type="submit")
+    Button("Submit", type="submit", disabled_if=state.loading)

This also demonstrates the new `disabled_if` prop to the LLM.

<details>
<summary>🤖 Prompt for AI Agents</summary>

In xiuper-ui/src/commonMain/resources/prompts/detailed.txt around lines 86 to
92, the Form example still uses the old Button syntax; update the Button call to
demonstrate the new props introduced in this PR by replacing the existing
Button("Submit", type="submit") line with a Button that includes the new
disabled_if prop (e.g., disabled_if=state.loading) so the example shows how to
disable the submit button while loading; keep the surrounding nanodsl block and
indentation unchanged.


</details>

<!-- fingerprinting:phantom:triton:mongoose -->

<!-- This is an auto-generated comment by CodeRabbit -->

Introduce a dedicated NanoStateRuntime class to handle state initialization,
mutations, and snapshots. This refactors state logic from StatefulNanoRenderer
into a separate, testable component with comprehensive test coverage.
Add state parameter to RenderBadge function to enable dynamic text rendering based on component state. Introduce resolveStringProp utility function to handle property resolution from IR with state binding support. Also add built-in len() function for text interpolation to calculate length of various data types.
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 (9)
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoContentComponents.kt (3)

18-18: Unused import.

Layout is imported but not used in this file.

-import androidx.compose.ui.layout.Layout

244-252: HTTP client created and closed per image download.

Creating a new HttpClient for each image download is inefficient. Consider reusing a shared client or injecting one.

-    private suspend fun downloadImageBytes(url: String): ByteArray = withContext(Dispatchers.Default) {
-        val client = cc.unitmesh.agent.tool.impl.http.HttpClientFactory.create()
-        try {
-            val response: io.ktor.client.statement.HttpResponse = client.get(url)
-            response.readBytes()
-        } finally {
-            client.close()
-        }
-    }
+    private suspend fun downloadImageBytes(url: String): ByteArray = withContext(Dispatchers.Default) {
+        // Consider using a shared HttpClient instance for better performance
+        cc.unitmesh.agent.tool.impl.http.HttpClientFactory.create().use { client ->
+            client.get(url).readBytes()
+        }
+    }

The use extension is more idiomatic for resource cleanup in Kotlin.


148-156: Image service initialization is hardcoded to "glm" provider.

The image generation service is hardcoded to use the "glm" provider. This reduces flexibility if other providers become available.

Consider making the provider configurable or using a more generic lookup mechanism.

#!/bin/bash
# Check if there are other image generation providers available
rg -n "ImageGenerationService.create" --type=kt -C2
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoRenderUtils.kt (2)

583-604: toSpacing() and toPadding() are nearly identical.

These two extension functions differ only in their default return values (8.dp vs 16.dp). Consider consolidating into a single helper with a default parameter.

+private fun String.toDp(default: Dp): Dp = when (this) {
+    "xs" -> 4.dp
+    "sm" -> 8.dp
+    "md" -> 16.dp
+    "lg" -> 24.dp
+    "xl" -> 32.dp
+    "none" -> 0.dp
+    else -> default
+}
+
 fun String.toSpacing(): Dp = when (this) {
-    "xs" -> 4.dp
-    "sm" -> 8.dp
-    "md" -> 16.dp
-    "lg" -> 24.dp
-    "xl" -> 32.dp
-    "none" -> 0.dp
-    else -> 8.dp
+    else -> toDp(8.dp)
 }

 fun String.toPadding(): Dp = when (this) {
-    "xs" -> 4.dp
-    "sm" -> 8.dp
-    "md" -> 16.dp
-    "lg" -> 24.dp
-    "xl" -> 32.dp
-    "none" -> 0.dp
-    else -> 16.dp
+    else -> toDp(16.dp)
 }

37-49: Consider caching getBuiltInVariables() result to reduce repeated Clock and map allocations.

The function is called during component rendering in multiple places (button labels, checkboxes, content, badges). While each call happens once per component render rather than in tight loops, caching the result with a reasonable TTL (e.g., per render cycle) or having callers cache it could reduce unnecessary Clock.System.now() calls and map allocations when components re-render frequently.

mpp-ui/src/commonTest/kotlin/cc/unitmesh/devins/ui/nano/NanoStateRuntimeTest.kt (1)

79-132: LGTM!

Good test for sequence action processing with multiple nested state mutations. Validates that actions are applied in order.

Consider adding edge case tests in a follow-up for:

  • Invalid mutation paths
  • ADD/SUBTRACT operations
  • APPEND/REMOVE on lists

Would you like me to generate additional test cases covering edge cases and other mutation operations?

mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoStateRuntime.kt (1)

74-78: Variable shadowing: raw is redeclared in inner scope.

The variable raw on line 75 shadows the outer raw from line 63. This can cause confusion.

             "list" -> {
-                val raw = defaultValue?.jsonPrimitive?.content ?: "[]"
-                val parsed = parseStructuredDefault(raw)
+                val listRaw = defaultValue?.jsonPrimitive?.content ?: "[]"
+                val parsed = parseStructuredDefault(listRaw)
                 if (parsed is List<*>) parsed else emptyList<Any>()
             }

Same issue applies to lines 79-83.

mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.kt (1)

46-50: Subscribing to each declared key individually may be inefficient.

For components with many state keys, calling collectAsState() in a loop creates multiple flow subscriptions. Consider exposing a single combined flow or snapshot observable from NanoStateRuntime.

// Alternative: single snapshot flow that emits on any state change
val snapshot by runtime.snapshotFlow().collectAsState(initial = runtime.snapshot())

This is a minor concern if state key counts are typically small.

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

150-150: LGTM! Consider removing redundant platform-specific declarations.

The dependency is correctly added to commonMain, making :xiuper-ui available across all platforms. Since xiuper-ui is a multiplatform library supporting jvm, js, wasmJs, and iOS targets, the Kotlin Multiplatform plugin automatically propagates commonMain dependencies to corresponding platform sourceSets.

The explicit declarations at lines 200 (jvmMain), 337 (iosMain), 358 (jsMain), and 396 (wasmJsMain) are redundant and can be safely removed. The androidMain declaration at line 273 should remain because xiuper-ui has no Android target, and the exclusion rules for kotlin-logging need to be applied at the consumer level.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9fcc4f9 and 7542fb9.

📒 Files selected for processing (6)
  • mpp-ui/build.gradle.kts (1 hunks)
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoContentComponents.kt (1 hunks)
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoRenderUtils.kt (1 hunks)
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoStateRuntime.kt (1 hunks)
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.kt (3 hunks)
  • mpp-ui/src/commonTest/kotlin/cc/unitmesh/devins/ui/nano/NanoStateRuntimeTest.kt (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.kt

📄 CodeRabbit inference engine (AGENTS.md)

**/*.kt: Use expect/actual pattern for platform-specific code (for example: Platform implementations)
Use concrete classes (not interfaces) and Promise (not Flow) for @JsExport annotations
Avoid emoji and UTF-8 characters in WASM code
When modifying CodingAgentRenderer, update ALL implementations: DefaultCodingAgentRenderer, ComposeRenderer, JewelRenderer, ServerSideRenderer, and JsRendererAdapter in Kotlin
When modifying CodingAgentRenderer, update JVM CLI implementations: CodingCliRenderer and ConsoleRenderer
Use AutoDevColors or MaterialTheme.colorScheme for Compose styling

Files:

  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoStateRuntime.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoContentComponents.kt
  • mpp-ui/src/commonTest/kotlin/cc/unitmesh/devins/ui/nano/NanoStateRuntimeTest.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoRenderUtils.kt
**/*.{kt,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Never use hardcoded colors - always use design tokens from the design system

Files:

  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoStateRuntime.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoContentComponents.kt
  • mpp-ui/src/commonTest/kotlin/cc/unitmesh/devins/ui/nano/NanoStateRuntimeTest.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoRenderUtils.kt
🧠 Learnings (4)
📓 Common learnings
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Applies to **/*.ts : When modifying `CodingAgentRenderer`, update ALL TypeScript implementations: `BaseRenderer.ts`, `CliRenderer.ts`, `ServerRenderer.ts`, and `TuiRenderer.ts`
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Applies to **/*.kt : When modifying `CodingAgentRenderer`, update ALL implementations: `DefaultCodingAgentRenderer`, `ComposeRenderer`, `JewelRenderer`, `ServerSideRenderer`, and `JsRendererAdapter` in Kotlin
📚 Learning: 2025-12-07T08:40:10.859Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Applies to **/*.kt : When modifying `CodingAgentRenderer`, update ALL implementations: `DefaultCodingAgentRenderer`, `ComposeRenderer`, `JewelRenderer`, `ServerSideRenderer`, and `JsRendererAdapter` in Kotlin

Applied to files:

  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoContentComponents.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.kt
📚 Learning: 2025-12-07T08:40:10.859Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Applies to **/*.kt : When modifying `CodingAgentRenderer`, update JVM CLI implementations: `CodingCliRenderer` and `ConsoleRenderer`

Applied to files:

  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.kt
📚 Learning: 2025-12-07T08:40:10.859Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Applies to **/*.kt : Use `AutoDevColors` or `MaterialTheme.colorScheme` for Compose styling

Applied to files:

  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.kt
🧬 Code graph analysis (4)
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoStateRuntime.kt (1)
mpp-vscode/webview/src/types/nano.ts (1)
  • NanoActionIR (28-31)
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoContentComponents.kt (2)
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.kt (1)
  • decodeImageBytesToBitmap (122-122)
mpp-ui/src/jsMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.js.kt (1)
  • decodeImageBytesToBitmap (9-11)
mpp-ui/src/commonTest/kotlin/cc/unitmesh/devins/ui/nano/NanoStateRuntimeTest.kt (2)
mpp-core/src/commonMain/kotlin/cc/unitmesh/devins/test/DevInsTestCase.kt (2)
  • assertEquals (45-49)
  • assertTrue (29-33)
mpp-vscode/webview/src/types/nano.ts (1)
  • NanoActionIR (28-31)
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoRenderUtils.kt (2)
mpp-vscode/webview/src/components/nano/NanoRenderer.tsx (7)
  • parseTerm (243-254)
  • match (315-321)
  • parseFactor (256-273)
  • parseExpression (232-241)
  • peek (322-324)
  • parseNumber (275-292)
  • parseIdentifierValue (294-310)
mpp-core/src/commonMain/kotlin/cc/unitmesh/devins/compiler/template/TemplateCompiler.kt (1)
  • evaluateCondition (151-168)
⏰ 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: Test mpp-core (KMP)
  • GitHub Check: Build
🔇 Additional comments (16)
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoContentComponents.kt (2)

39-69: LGTM!

RenderText correctly resolves bindings, interpolates state expressions, and maps style props to Material typography. Good use of MaterialTheme.typography for text styles.


281-352: LGTM!

RenderIcon provides comprehensive icon name mapping and uses AutoDevColors.Signal and MaterialTheme.colorScheme for colors per coding guidelines. The fallback to Icons.Default.Info is reasonable.

mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoRenderUtils.kt (3)

544-554: LGTM!

The formatDateFromMillis implementation correctly uses kotlinx.datetime for proper date conversion from epoch milliseconds. This addresses the previous review feedback.


287-405: Well-structured recursive descent parser.

NumericExprParser is a clean implementation following standard expression parsing patterns with proper precedence handling. The structure mirrors the TypeScript implementation in NanoRenderer.tsx appropriately for cross-platform consistency.


464-537: LGTM!

evaluateCondition provides comprehensive condition evaluation with support for negation, comparison operators, and truthy evaluation. The loose equality handling is appropriate for a UI DSL.

mpp-ui/src/commonTest/kotlin/cc/unitmesh/devins/ui/nano/NanoStateRuntimeTest.kt (2)

12-35: LGTM!

Good test coverage for initial state availability and condition evaluation integration. The test correctly validates that state is accessible immediately after runtime construction.


37-77: LGTM!

Good coverage of stateMutation with SET operation for both integer and boolean types. Tests both state. prefixed and bare path formats.

mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoStateRuntime.kt (5)

126-136: APPEND operation silently deduplicates entries.

The APPEND operation checks if (currentValue.contains(trimmed)) and skips appending duplicates. This behavior might be intentional for set-like semantics, but it could be surprising if users expect list semantics.

Consider either:

  1. Documenting this behavior in the class KDoc
  2. Adding a separate ADD_UNIQUE operation for dedup behavior
  3. Always appending regardless of duplicates

Is the deduplication behavior intentional for APPEND?


17-27: LGTM!

Excellent class documentation covering design goals: deterministic, cross-platform, testable, and compatible path handling. The KDoc clearly explains the runtime's purpose.


37-45: LGTM!

snapshot() correctly filters to declared keys and omits null values, maintaining the Map<String, Any> contract for renderer consumption.


152-189: LGTM!

coerceSetValue provides robust type coercion with proper precedence: current runtime type → declared type → best-effort parsing. The fallback chain is well-designed.


217-227: LGTM!

Using YamlUtils.load for parsing structured defaults is appropriate since "JSON is valid YAML 1.2". The exception handling with fallback to raw string is sensible.

mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.kt (4)

65-69: LGTM!

The renderNode composable lambda pattern elegantly enables recursive rendering while maintaining proper state and action handler threading through the component tree.


71-114: LGTM!

Comprehensive component dispatch covering all P0 components (DatePicker, Radio, RadioGroup, Switch, NumberInput, Modal, Alert, Progress, Spinner) and GenUI components (SplitView, SmartTextField, Slider, DateRangePicker, DataChart, DataTable). The modular organization into separate component files improves maintainability.


18-26: LGTM!

Good documentation update listing the component organization across separate files. This helps developers navigate the modular structure.


118-122: LGTM!

The expect fun decodeImageBytesToBitmap follows the expect/actual pattern per coding guidelines for platform-specific code. The relevant code snippets confirm actual implementations exist in JS (and presumably other platforms).

else -> MaterialTheme.colorScheme.primaryContainer
}

val textColor = if (colorName == "yellow") Color.Black else Color.White
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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash

# Find the AutoDevColors definition
echo "=== Searching for AutoDevColors definition ==="
find . -name "*.kt" -type f | xargs grep -l "object AutoDevColors" | head -5

# Check the specific file and line mentioned
echo ""
echo "=== Checking the specific file and line ==="
if [ -f "mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoContentComponents.kt" ]; then
  sed -n '260,280p' "mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoContentComponents.kt"
else
  echo "File not found at expected path"
  find . -name "NanoContentComponents.kt" -type f
fi

Repository: phodal/auto-dev

Length of output: 1071


🏁 Script executed:

#!/bin/bash

# Examine the AutoDevColors definition in the compose theme
echo "=== AutoDevColors.kt content ==="
cat "./mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/theme/AutoDevColors.kt"

echo ""
echo "=== Searching for text color definitions ==="
grep -n "Text\|text" "./mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/theme/AutoDevColors.kt" || echo "No explicit text tokens found"

Repository: phodal/auto-dev

Length of output: 11810


🏁 Script executed:

#!/bin/bash

# Check if there are other similar badge/signal usages to understand the pattern
echo "=== Searching for other Color.Black or Color.White usage in UI components ==="
rg -n "Color\.(Black|White)" mpp-ui/src/commonMain/kotlin --type kt -A 2 -B 2

echo ""
echo "=== Searching for badge-like components ==="
rg -n "Surface.*shape.*RoundedCornerShape" mpp-ui/src/commonMain/kotlin --type kt -B 3 -A 5

Repository: phodal/auto-dev

Length of output: 252


🏁 Script executed:

#!/bin/bash

# Check for Color.Black or Color.White usage without file type restriction
echo "=== Searching for Color.Black or Color.White usage ==="
rg -n "Color\.(Black|White)" mpp-ui/src/commonMain/kotlin -A 2 -B 2

echo ""
echo "=== Searching for badge-like components with rounded corners ==="
rg -n "RoundedCornerShape" mpp-ui/src/commonMain/kotlin -B 5 -A 5

Repository: phodal/auto-dev

Length of output: 50372


Replace hardcoded colors with design tokens for badge text colors.

The hardcoded Color.Black and Color.White violate the design system requirement. Replace with appropriate AutoDevColors.Text tokens:

-        val textColor = if (colorName == "yellow") Color.Black else Color.White
+        val textColor = if (colorName == "yellow") AutoDevColors.Text.inverse else AutoDevColors.Text.primary

Note: AutoDevColors.Text.onWarn and AutoDevColors.Text.onSignal don't exist in the design system. The above uses available tokens: inverse (#0B0E14) for dark text on yellow, primary (#F5F5F5) for light text on other signal backgrounds.

📝 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
val textColor = if (colorName == "yellow") Color.Black else Color.White
val textColor = if (colorName == "yellow") AutoDevColors.Text.inverse else AutoDevColors.Text.primary
🤖 Prompt for AI Agents
In
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoContentComponents.kt
around line 269, replace the hardcoded Color.Black/Color.White used for badge
text with the design tokens: use AutoDevColors.Text.inverse when colorName ==
"yellow" (dark text on yellow) and use AutoDevColors.Text.primary for all other
signal backgrounds; update the assignment to reference these tokens instead of
raw Color values and import/qualify AutoDevColors as needed.

Comment on lines +277 to +285
private fun evaluateNumericExpressionOrNull(expr: String, state: Map<String, Any>): Double? {
val parser = NumericExprParser(expr, state)
return try {
val result = parser.parseExpression()
if (!parser.isAtEnd()) null else result
} catch (_: Throwable) {
null
}
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Catching Throwable is too broad.

Catching Throwable will also catch OutOfMemoryError, StackOverflowError, and other serious errors that should propagate.

     private fun evaluateNumericExpressionOrNull(expr: String, state: Map<String, Any>): Double? {
         val parser = NumericExprParser(expr, state)
         return try {
             val result = parser.parseExpression()
             if (!parser.isAtEnd()) null else result
-        } catch (_: Throwable) {
+        } catch (_: Exception) {
             null
         }
     }
📝 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
private fun evaluateNumericExpressionOrNull(expr: String, state: Map<String, Any>): Double? {
val parser = NumericExprParser(expr, state)
return try {
val result = parser.parseExpression()
if (!parser.isAtEnd()) null else result
} catch (_: Throwable) {
null
}
}
private fun evaluateNumericExpressionOrNull(expr: String, state: Map<String, Any>): Double? {
val parser = NumericExprParser(expr, state)
return try {
val result = parser.parseExpression()
if (!parser.isAtEnd()) null else result
} catch (_: Exception) {
null
}
}
🤖 Prompt for AI Agents
In mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoRenderUtils.kt
around lines 277 to 285, the function currently catches Throwable which also
swallows serious Errors (OOM, StackOverflow) — change the catch to only catch
Exception or, better, the specific parsing exception thrown by NumericExprParser
(e.g., ParseException or the parser's runtime exception type). Replace "catch
(_: Throwable)" with "catch (e: Exception)" or "catch (e: YourParserException)"
and return null there; let Errors propagate.

Introduce PromptStreamingService interface to abstract LLM operations,
enabling better testability with deterministic fake implementations.
Add KoogPromptStreamingServiceAdapter to maintain compatibility with
existing KoogLLMService. Update
…rness

Split validation and parsing into distinct steps to better track compilation
status and provide more detailed error reporting. Also improve template
selection to avoid duplicates and handle cases where count exceeds template
size.
Add a JVM-only CLI utility to debug NanoDSL text interpolation. The tool parses NanoDSL files, initializes state, and generates reports showing raw vs rendered string properties along with template expression evaluations. It can process individual files or directories and optionally fail on unresolved expressions.
Update input components to use widthIn(min=X.dp) instead of fillMaxWidth() to prevent components from expanding to full available width.
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

♻️ Duplicate comments (1)
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoContentComponents.kt (1)

276-276: Replace hardcoded colors with design tokens for badge text colors.

The hardcoded Color.Black and Color.White violate the design system requirement. Replace with appropriate AutoDevColors.Text tokens.

Apply this diff:

-        val textColor = if (colorName == "yellow") Color.Black else Color.White
+        val textColor = if (colorName == "yellow") AutoDevColors.Text.inverse else AutoDevColors.Text.primary

As per coding guidelines, never use hardcoded colors - always use design tokens from the design system.

🧹 Nitpick comments (10)
mpp-ui/src/jvmTest/kotlin/cc/unitmesh/devins/ui/nano/NanoTextInterpolationTest.kt (1)

52-70: LGTM! Consider adding edge case tests.

The test correctly validates the distinction between literal props (which require explicit interpolation) and bound props (which are already resolved from state). The logic is sound.

Consider adding test cases for edge scenarios:

  • Missing state keys (e.g., {state.nonexistent})
  • Null or undefined values
  • Invalid expressions or malformed templates
  • Nested property access (e.g., {state.user.name})

These additions would improve test robustness and catch potential runtime errors.

mpp-core/src/commonTest/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgentScenarioCompileTest.kt (2)

4-4: Unused import.

EmptyFileSystem is imported but not used in this file. The streamPrompt method has a default value for fileSystem, so this import can be removed.

-import cc.unitmesh.devins.filesystem.EmptyFileSystem

88-116: Consider using standard test assertions.

Using throw IllegalStateException for test failures works, but standard Kotlin test assertions provide cleaner error messages and better IDE integration.

-            if (!result.success) {
-                throw IllegalStateException(
-                    "Scenario '$description' should succeed. metadata=${result.metadata} content='${result.content.take(1200)}'"
-                )
-            }
+            kotlin.test.assertTrue(result.success) {
+                "Scenario '$description' should succeed. metadata=${result.metadata} content='${result.content.take(1200)}'"
+            }

-            val isValid = result.metadata["isValid"]
-            if (isValid != "true") {
-                throw IllegalStateException(
-                    "Scenario '$description' should be valid. isValid=$isValid metadata=${result.metadata}"
-                )
-            }
+            kotlin.test.assertEquals("true", result.metadata["isValid"]) {
+                "Scenario '$description' should be valid. metadata=${result.metadata}"
+            }
mpp-core/src/jvmMain/kotlin/cc/unitmesh/devins/test/NanoDslScenarioHarness.kt (1)

310-324: Consider expanding template reference pattern coverage.

The current regex patterns cover common cases ({state.foo}, ${state.foo}, {len(state.items)}), which is reasonable for an initial implementation. However, NanoDSL might support additional template expressions like arithmetic operations ({state.price * state.quantity}) or nested function calls that won't be detected.

Consider whether enhanced coverage is needed based on NanoDSL's actual template syntax capabilities.

mpp-ui/src/iosMain/kotlin/cc/unitmesh/devins/ui/nano/NanoImageCache.ios.kt (1)

23-46: Consider adding error logging for debugging.

The implementation uses runCatching to swallow I/O errors silently. While best-effort persistence is acceptable (as noted in the common code), adding logging would help diagnose cache failures during development and debugging.

Example:

 internal actual suspend fun platformWriteCachedImageBytes(key: String, bytes: ByteArray) {
     val dir = nanoImageCacheDirPath()
     val fileManager = NSFileManager.defaultManager
-    runCatching {
+    runCatching {
         fileManager.createDirectoryAtPath(
             path = dir,
             withIntermediateDirectories = true,
             attributes = null,
             error = null
         )
+    }.onFailure { e ->
+        // Optional: log error for debugging
+        println("NanoImageCache: Failed to create cache directory: ${e.message}")
     }

     val path = nanoImageCachePath(key)
-    runCatching {
+    runCatching {
         bytes.usePinned { pinned ->
             val file = fopen(path, "wb") ?: return@usePinned
             try {
                 fwrite(pinned.addressOf(0), 1u, bytes.size.toULong(), file)
             } finally {
                 fclose(file)
             }
         }
+    }.onFailure { e ->
+        // Optional: log error for debugging
+        println("NanoImageCache: Failed to write cache file: ${e.message}")
     }
 }
mpp-ui/src/androidMain/kotlin/cc/unitmesh/devins/ui/nano/NanoImageCache.android.kt (1)

5-18: Code duplication with JVM implementation.

The read and write implementations are identical to the JVM variant (NanoImageCache.jvm.kt lines 5-18). Since both Android and JVM use java.io.File, consider extracting this logic to a shared source set or using a common internal function to reduce duplication.

If your Kotlin Multiplatform setup supports it, consider creating a shared JVM-based source set (e.g., jvmCommonMain) that both jvmMain and androidMain can depend on, or extract the I/O logic into a shared internal helper.

mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoContentComponents.kt (1)

251-259: Consider reusing HttpClient instances for better performance.

Creating a new HttpClient for each image download may be inefficient. If HttpClientFactory.create() doesn't internally pool connections, consider reusing a single client instance or implementing connection pooling.

For example, maintain a module-level client:

private val httpClient by lazy { cc.unitmesh.agent.tool.impl.http.HttpClientFactory.create() }

private suspend fun downloadImageBytes(url: String): ByteArray = withContext(Dispatchers.Default) {
    val response: io.ktor.client.statement.HttpResponse = httpClient.get(url)
    response.readBytes()
}

Note: Only implement this if HttpClientFactory doesn't already handle connection pooling internally.

mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoLayoutComponents.kt (1)

54-157: Consider extracting helper functions to reduce complexity.

The RenderHStack function contains complex branching logic for auto-distribution, wrapping, and flex handling. While correct, this high cognitive complexity could be reduced by extracting helper functions.

Consider extracting:

  • Child type classification logic (lines 96-107) into shouldAutoDistributeChildren(children)
  • Wrap decision logic (lines 87-93) into shouldWrapChildren(children, justify)
  • Child modifier calculation (lines 132-154) into calculateChildModifier(child, state, ...)

This would improve readability and make the rendering logic easier to maintain and test.

mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoInputComponents.kt (1)

178-280: Consider extracting duplicate mutation logic in Checkbox.

The checkbox state mutation logic is duplicated between the Checkbox.onCheckedChange handler (lines 210-239) and the label click handler (lines 246-276). Extracting this into a helper function would reduce duplication and improve maintainability.

Example refactor:

fun handleCheckboxToggle(newValue: Boolean, inList: InListExpression?, statePath: String?, onChange: NanoActionIR?) {
    when {
        inList != null -> {
            onAction(
                NanoActionIR(
                    type = "stateMutation",
                    payload = mapOf(
                        "path" to JsonPrimitive(inList.listPath),
                        "operation" to JsonPrimitive(if (newValue) "APPEND" else "REMOVE"),
                        "value" to JsonPrimitive(inList.item)
                    )
                )
            )
        }
        statePath != null -> {
            onAction(
                NanoActionIR(
                    type = "stateMutation",
                    payload = mapOf(
                        "path" to JsonPrimitive(statePath),
                        "operation" to JsonPrimitive("SET"),
                        "value" to JsonPrimitive(newValue.toString())
                    )
                )
            )
        }
        else -> uncontrolledChecked = newValue
    }
    onChange?.let { onAction(it) }
}

Then call handleCheckboxToggle(newValue, inList, statePath, onChange) from both handlers.

mpp-ui/src/jvmMain/kotlin/cc/unitmesh/devins/ui/nano/cli/NanoDslTextRenderCli.kt (1)

149-166: Binding resolution only handles state.* expressions.

The current implementation only resolves bindings prefixed with state. (lines 154-159). Bindings referencing props, computed values, or other sources will display the raw expression. While acceptable for a debugging tool, this limitation could be documented or expanded.

Consider adding a comment explaining the limitation:

 // Bindings (show resolved values for quick inspection)
+// Note: Only resolves state.* bindings; other expressions show raw form
 if (!ir.bindings.isNullOrEmpty()) {
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7542fb9 and 8e20abb.

📒 Files selected for processing (19)
  • mpp-core/build.gradle.kts (1 hunks)
  • mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgent.kt (4 hunks)
  • mpp-core/src/commonMain/kotlin/cc/unitmesh/llm/KoogPromptStreamingServiceAdapter.kt (1 hunks)
  • mpp-core/src/commonMain/kotlin/cc/unitmesh/llm/PromptStreamingService.kt (1 hunks)
  • mpp-core/src/commonTest/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgentScenarioCompileTest.kt (1 hunks)
  • mpp-core/src/jvmMain/kotlin/cc/unitmesh/devins/test/NanoDslScenarioHarness.kt (1 hunks)
  • mpp-ui/build.gradle.kts (2 hunks)
  • mpp-ui/src/androidMain/kotlin/cc/unitmesh/devins/ui/nano/NanoImageCache.android.kt (1 hunks)
  • mpp-ui/src/androidMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.android.kt (1 hunks)
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoContentComponents.kt (1 hunks)
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoImageCache.kt (1 hunks)
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoInputComponents.kt (1 hunks)
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoLayoutComponents.kt (1 hunks)
  • mpp-ui/src/iosMain/kotlin/cc/unitmesh/devins/ui/nano/NanoImageCache.ios.kt (1 hunks)
  • mpp-ui/src/jsMain/kotlin/cc/unitmesh/devins/ui/nano/NanoImageCache.js.kt (1 hunks)
  • mpp-ui/src/jvmMain/kotlin/cc/unitmesh/devins/ui/nano/NanoImageCache.jvm.kt (1 hunks)
  • mpp-ui/src/jvmMain/kotlin/cc/unitmesh/devins/ui/nano/cli/NanoDslTextRenderCli.kt (1 hunks)
  • mpp-ui/src/jvmTest/kotlin/cc/unitmesh/devins/ui/nano/NanoTextInterpolationTest.kt (1 hunks)
  • mpp-ui/src/wasmJsMain/kotlin/cc/unitmesh/devins/ui/nano/NanoImageCache.wasmJs.kt (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • mpp-ui/src/androidMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.android.kt
🧰 Additional context used
📓 Path-based instructions (2)
**/*.kt

📄 CodeRabbit inference engine (AGENTS.md)

**/*.kt: Use expect/actual pattern for platform-specific code (for example: Platform implementations)
Use concrete classes (not interfaces) and Promise (not Flow) for @JsExport annotations
Avoid emoji and UTF-8 characters in WASM code
When modifying CodingAgentRenderer, update ALL implementations: DefaultCodingAgentRenderer, ComposeRenderer, JewelRenderer, ServerSideRenderer, and JsRendererAdapter in Kotlin
When modifying CodingAgentRenderer, update JVM CLI implementations: CodingCliRenderer and ConsoleRenderer
Use AutoDevColors or MaterialTheme.colorScheme for Compose styling

Files:

  • mpp-core/src/commonMain/kotlin/cc/unitmesh/llm/PromptStreamingService.kt
  • mpp-ui/src/jvmTest/kotlin/cc/unitmesh/devins/ui/nano/NanoTextInterpolationTest.kt
  • mpp-ui/src/wasmJsMain/kotlin/cc/unitmesh/devins/ui/nano/NanoImageCache.wasmJs.kt
  • mpp-ui/src/jvmMain/kotlin/cc/unitmesh/devins/ui/nano/cli/NanoDslTextRenderCli.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoContentComponents.kt
  • mpp-ui/src/androidMain/kotlin/cc/unitmesh/devins/ui/nano/NanoImageCache.android.kt
  • mpp-core/src/commonMain/kotlin/cc/unitmesh/llm/KoogPromptStreamingServiceAdapter.kt
  • mpp-ui/src/iosMain/kotlin/cc/unitmesh/devins/ui/nano/NanoImageCache.ios.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoImageCache.kt
  • mpp-ui/src/jsMain/kotlin/cc/unitmesh/devins/ui/nano/NanoImageCache.js.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoLayoutComponents.kt
  • mpp-core/src/commonTest/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgentScenarioCompileTest.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoInputComponents.kt
  • mpp-core/src/jvmMain/kotlin/cc/unitmesh/devins/test/NanoDslScenarioHarness.kt
  • mpp-ui/src/jvmMain/kotlin/cc/unitmesh/devins/ui/nano/NanoImageCache.jvm.kt
  • mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgent.kt
**/*.{kt,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Never use hardcoded colors - always use design tokens from the design system

Files:

  • mpp-core/src/commonMain/kotlin/cc/unitmesh/llm/PromptStreamingService.kt
  • mpp-ui/src/jvmTest/kotlin/cc/unitmesh/devins/ui/nano/NanoTextInterpolationTest.kt
  • mpp-ui/src/wasmJsMain/kotlin/cc/unitmesh/devins/ui/nano/NanoImageCache.wasmJs.kt
  • mpp-ui/src/jvmMain/kotlin/cc/unitmesh/devins/ui/nano/cli/NanoDslTextRenderCli.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoContentComponents.kt
  • mpp-ui/src/androidMain/kotlin/cc/unitmesh/devins/ui/nano/NanoImageCache.android.kt
  • mpp-core/src/commonMain/kotlin/cc/unitmesh/llm/KoogPromptStreamingServiceAdapter.kt
  • mpp-ui/src/iosMain/kotlin/cc/unitmesh/devins/ui/nano/NanoImageCache.ios.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoImageCache.kt
  • mpp-ui/src/jsMain/kotlin/cc/unitmesh/devins/ui/nano/NanoImageCache.js.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoLayoutComponents.kt
  • mpp-core/src/commonTest/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgentScenarioCompileTest.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoInputComponents.kt
  • mpp-core/src/jvmMain/kotlin/cc/unitmesh/devins/test/NanoDslScenarioHarness.kt
  • mpp-ui/src/jvmMain/kotlin/cc/unitmesh/devins/ui/nano/NanoImageCache.jvm.kt
  • mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgent.kt
🧠 Learnings (7)
📓 Common learnings
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Applies to **/*.kt : When modifying `CodingAgentRenderer`, update ALL implementations: `DefaultCodingAgentRenderer`, `ComposeRenderer`, `JewelRenderer`, `ServerSideRenderer`, and `JsRendererAdapter` in Kotlin
📚 Learning: 2025-12-07T08:40:10.859Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Applies to **/*.kt : When modifying `CodingAgentRenderer`, update JVM CLI implementations: `CodingCliRenderer` and `ConsoleRenderer`

Applied to files:

  • mpp-ui/src/jvmMain/kotlin/cc/unitmesh/devins/ui/nano/cli/NanoDslTextRenderCli.kt
  • mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgent.kt
  • mpp-ui/build.gradle.kts
📚 Learning: 2025-12-07T08:40:10.859Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Applies to **/*.kt : When modifying `CodingAgentRenderer`, update ALL implementations: `DefaultCodingAgentRenderer`, `ComposeRenderer`, `JewelRenderer`, `ServerSideRenderer`, and `JsRendererAdapter` in Kotlin

Applied to files:

  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoContentComponents.kt
  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoLayoutComponents.kt
  • mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgent.kt
📚 Learning: 2025-12-07T08:40:10.859Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Applies to **/*.{kt,ts,tsx} : Never use hardcoded colors - always use design tokens from the design system

Applied to files:

  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoContentComponents.kt
📚 Learning: 2025-12-07T08:40:10.859Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Applies to **/*.kt : Use `AutoDevColors` or `MaterialTheme.colorScheme` for Compose styling

Applied to files:

  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoContentComponents.kt
📚 Learning: 2025-12-07T08:40:10.859Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Applies to mpp-ui/src/jsMain/typescript/**/*.ts : Use `semanticInk`/`semanticChalk` design tokens from `mpp-ui/src/jsMain/typescript/design-system/` for CLI/TUI styling

Applied to files:

  • mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoContentComponents.kt
📚 Learning: 2025-12-07T08:40:10.859Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Applies to **/*.kt : Use `expect`/`actual` pattern for platform-specific code (for example: `Platform` implementations)

Applied to files:

  • mpp-ui/src/androidMain/kotlin/cc/unitmesh/devins/ui/nano/NanoImageCache.android.kt
🧬 Code graph analysis (6)
mpp-ui/src/jvmTest/kotlin/cc/unitmesh/devins/ui/nano/NanoTextInterpolationTest.kt (1)
mpp-vscode/webview/src/types/nano.ts (1)
  • NanoBindingIR (36-40)
mpp-ui/src/jvmMain/kotlin/cc/unitmesh/devins/ui/nano/cli/NanoDslTextRenderCli.kt (1)
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/state/NanoStateManager.kt (1)
  • initFromComponent (42-44)
mpp-ui/src/androidMain/kotlin/cc/unitmesh/devins/ui/nano/NanoImageCache.android.kt (1)
mpp-ui/src/jvmMain/kotlin/cc/unitmesh/devins/ui/nano/NanoImageCache.jvm.kt (1)
  • nanoImageCacheFile (20-23)
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoImageCache.kt (5)
mpp-ui/src/androidMain/kotlin/cc/unitmesh/devins/ui/nano/NanoImageCache.android.kt (2)
  • platformReadCachedImageBytes (5-12)
  • platformWriteCachedImageBytes (14-18)
mpp-ui/src/iosMain/kotlin/cc/unitmesh/devins/ui/nano/NanoImageCache.ios.kt (2)
  • platformReadCachedImageBytes (15-20)
  • platformWriteCachedImageBytes (22-46)
mpp-ui/src/jsMain/kotlin/cc/unitmesh/devins/ui/nano/NanoImageCache.js.kt (2)
  • platformReadCachedImageBytes (3-3)
  • platformWriteCachedImageBytes (5-7)
mpp-ui/src/jvmMain/kotlin/cc/unitmesh/devins/ui/nano/NanoImageCache.jvm.kt (2)
  • platformReadCachedImageBytes (5-12)
  • platformWriteCachedImageBytes (14-18)
mpp-ui/src/wasmJsMain/kotlin/cc/unitmesh/devins/ui/nano/NanoImageCache.wasmJs.kt (2)
  • platformReadCachedImageBytes (15-25)
  • platformWriteCachedImageBytes (27-38)
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoInputComponents.kt (4)
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/state/NanoStateManager.kt (1)
  • onAction (82-84)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaInputListener.kt (1)
  • Button (10-13)
mpp-vscode/webview/src/types/nano.ts (1)
  • NanoActionIR (28-31)
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.kt (1)
  • renderNode (55-94)
mpp-ui/src/jvmMain/kotlin/cc/unitmesh/devins/ui/nano/NanoImageCache.jvm.kt (1)
mpp-ui/src/androidMain/kotlin/cc/unitmesh/devins/ui/nano/NanoImageCache.android.kt (1)
  • nanoImageCacheFile (20-24)
⏰ 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
  • GitHub Check: Test mpp-core (KMP)
🔇 Additional comments (35)
mpp-ui/src/jvmTest/kotlin/cc/unitmesh/devins/ui/nano/NanoTextInterpolationTest.kt (3)

1-8: LGTM!

The package declaration and imports are appropriate for this test file.


11-33: LGTM!

The test correctly validates both brace {...} and dollar ${} template syntaxes. The escape sequence ${'$'} on line 26 is necessary to prevent Kotlin's own string interpolation from interfering with the test string.


35-50: LGTM!

The test correctly validates the len() function with both direct property access (len(salesData)) and state-prefixed access (len(state.salesData)). The use of Chinese characters in assertions is acceptable for JVM tests.

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

324-332: LGTM!

The new Gradle task is well-configured with proper dependency on jvmMainClasses and correct classpath construction from the JVM compilation outputs.

mpp-core/src/commonMain/kotlin/cc/unitmesh/llm/PromptStreamingService.kt (1)

9-24: LGTM!

Clean interface design with sensible defaults. The abstraction enables testability by allowing test doubles to simulate streaming without network calls.

mpp-core/src/commonMain/kotlin/cc/unitmesh/llm/KoogPromptStreamingServiceAdapter.kt (1)

14-34: LGTM!

Clean adapter implementation following the standard delegation pattern. The comment correctly explains the Kotlin limitation that necessitates this adapter.

mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgent.kt (2)

41-61: LGTM!

Good refactoring for testability. The primary constructor now accepts the interface while the secondary constructor provides backward compatibility by wrapping KoogLLMService with the adapter.


529-637: Well-structured prompt documentation.

The updated DEFAULT_PROMPT comprehensively documents all new P0 components with clear categorization (Layout, Display, Form Input, Feedback, Data Components). The examples are practical and the guidance on using scene paths for images instead of external URLs is a good practice for AI-generated content.

mpp-core/src/commonTest/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgentScenarioCompileTest.kt (1)

16-27: LGTM!

Clean fake implementation using flowOf for single-emission streaming. This effectively tests the agent's response handling without network dependencies.

mpp-core/src/jvmMain/kotlin/cc/unitmesh/devins/test/NanoDslScenarioHarness.kt (7)

1-32: LGTM!

The imports are appropriate and the documentation clearly describes the harness purpose and usage.


85-140: LGTM!

The validation flow is comprehensive and well-structured, covering validator checks, compilation, and template-state consistency. The report includes all relevant diagnostic information, and file handling properly uses UTF-8 encoding.


142-151: LGTM!

The summary output is clear and appropriately uses error() to fail the build when scenarios don't pass, which is expected behavior for a test harness.


153-188: LGTM!

The configuration class is well-designed with sensible defaults. The default LLM scenario prompt is particularly well-crafted, focusing on user needs rather than UI implementation details.


190-245: LGTM!

Both scenario generation approaches are well-implemented. The local generation uses a seeded random for reproducibility, and the LLM generation includes proper error handling for empty responses.


247-254: LGTM!

The extraction logic correctly handles both code-fenced and plain NanoDSL content with an appropriate fallback.


275-308: The indentation logic at line 295 is correct. In valid NanoDSL state blocks, child keys are always indented more than the state: line itself (e.g., state: at 4 spaces, keys at 8 spaces). The condition indent <= baseIndent correctly identifies when a line at equal or lesser indentation marks the end of the state block and the start of a sibling block. This matches actual NanoDSL syntax shown in tests. No changes needed.

Likely an incorrect or invalid review comment.

mpp-ui/src/jsMain/kotlin/cc/unitmesh/devins/ui/nano/NanoImageCache.js.kt (1)

3-7: LGTM!

The stub implementations for the JS target are appropriate. The no-op behavior is clearly documented, and the implementation correctly fulfills the expect/actual contract.

mpp-ui/src/iosMain/kotlin/cc/unitmesh/devins/ui/nano/NanoImageCache.ios.kt (2)

16-20: LGTM!

The read implementation correctly handles missing files by returning null and properly converts NSData to ByteArray.


48-64: LGTM!

The helper functions correctly construct cache paths and efficiently convert NSData to ByteArray using memcpy with pinned memory.

mpp-ui/src/androidMain/kotlin/cc/unitmesh/devins/ui/nano/NanoImageCache.android.kt (1)

20-24: LGTM!

The Android-specific fallback path and explanatory comment are appropriate. The fallback to /data/local/tmp is suitable for Android's file system structure.

mpp-ui/src/wasmJsMain/kotlin/cc/unitmesh/devins/ui/nano/NanoImageCache.wasmJs.kt (2)

9-13: LGTM!

The JS interop helpers for Uint8Array manipulation and key namespacing are correctly implemented.


40-55: LGTM!

The bidirectional conversion between ByteArray and Uint8Array is correctly implemented with proper byte masking to handle unsigned values.

mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoImageCache.kt (4)

6-29: LGTM!

The cache implementation correctly uses the double-checked locking pattern for performance while maintaining thread safety. The unlocked check on Line 11 is a valid optimization with the subsequent locked double-check on Line 14 preventing races.


31-49: LGTM!

The cache key generation is well-designed:

  • Sanitizes filenames for filesystem safety
  • Uses URL hashing to prevent collisions
  • Balances readability with collision avoidance
  • Includes length limits to prevent path issues

51-60: LGTM!

The FNV-1a 64-bit hash implementation is correct:

  • Uses proper FNV-1a constants (offset basis and prime)
  • Implements FNV-1a algorithm (XOR then multiply)
  • Uses character codes for cross-platform consistency
  • Correctly converts to unsigned hex representation

62-63: LGTM!

The expect declarations correctly define the platform-specific I/O contract. All platforms provide actual implementations as verified in the other files.

Based on learnings, the expect/actual pattern is appropriate for platform-specific code.

mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoInputComponents.kt (1)

689-769: LGTM! Excellent type preservation in Slider.

The Slider implementation correctly preserves the original state type (Int vs Float) when encoding values, and formats the display value appropriately. This attention to type safety prevents unexpected type coercion issues.

mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoLayoutComponents.kt (1)

31-32: No issues found - extension functions are properly defined.

The toSpacing() and toPadding() extension functions are defined in mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoRenderUtils.kt at lines 583 and 596 respectively. Both functions correctly convert string values to Dp, and the code compiles without errors.

mpp-ui/src/jvmMain/kotlin/cc/unitmesh/devins/ui/nano/cli/NanoDslTextRenderCli.kt (5)

23-70: LGTM! Well-structured CLI entry point.

The main function correctly handles argument parsing, file discovery, and error reporting. The deterministic file sorting and clear error messages make this a solid CLI implementation.


186-193: LGTM! Correct filtering of non-string primitives.

The extension correctly filters out numeric and boolean JSON primitives, ensuring only string properties are processed for template interpolation.


205-264: LGTM! Robust argument parsing with clear usage.

The argument parsing handles both named and positional arguments, uses defensive boolean parsing, and provides a helpful usage message. Good CLI UX.


89-90: The APIs are correctly implemented. initFromComponent(ir) exists as a convenience method on NanoStateManager that delegates to initFromIR(), and getState().snapshot() correctly returns a Map<String, Any?> that can contain nulls. The conversion of nulls to empty strings via v ?: "" is intentional and appropriate for CLI rendering—the state keys remain visible on line 96. No changes needed.


195-203: The regex pattern is correct and intentional. Both ${expr} and {expr} are supported NanoDSL template syntaxes, as explicitly validated by the test suite (NanoRenderUtilsTest.kt). The interpolateText() function in NanoRenderUtils.kt uses the identical pattern and includes tests confirming both delimiters work: assertEquals("Budget: 800", NanoRenderUtils.interpolateText("Budget: {flightBudget}", state)) and assertEquals("Hotel", NanoRenderUtils.interpolateText("{state.accommodation.title()}", state)). Removing the second alternative would break support for bare-brace templates. No changes needed.

Likely an incorrect or invalid review comment.

mpp-ui/build.gradle.kts (2)

976-984: LGTM! New CLI task correctly configured.

The new runNanoDslTextRenderCli task follows the established pattern for CLI tasks in this build file, with correct classpath construction and a clear description.


987-993: LGTM! Demo task updated consistently.

The runNanoDSLDemo task has been updated to use the same classpath construction pattern as the new CLI task, with an updated description. The changes are consistent and correct.

implementation(project(":mpp-codegraph"))
implementation(project(":mpp-viewer"))
implementation(project(":mpp-viewer-web"))
implementation(project(":xiuper-ui"))
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

🧩 Analysis chain

🏁 Script executed:

# Check the file structure and locate build.gradle.kts
fd -type f -name "build.gradle.kts" | grep -E "(mpp-ui|xiuper-ui)" | head -10

Repository: phodal/auto-dev

Length of output: 229


🏁 Script executed:

# Get the full context of mpp-ui/build.gradle.kts around the dependency declarations
git ls-files | grep -E "mpp-ui/build\.gradle\.kts" | head -1 | xargs -I {} sh -c 'wc -l {} && echo "---" && sed -n "140,210p" {} && echo "---" && sed -n "265,280p" {} && echo "---" && sed -n "330,345p" {} && echo "---" && sed -n "350,365p" {} && echo "---" && sed -n "390,405p" {}'

Repository: phodal/auto-dev

Length of output: 5189


🏁 Script executed:

# Check if xiuper-ui exists and if it's a multiplatform module
find . -type d -name xiuper-ui 2>/dev/null | head -5

Repository: phodal/auto-dev

Length of output: 70


🏁 Script executed:

# Check for expect/actual patterns in xiuper-ui if it exists
rg -n "expect (class|fun|val|interface)" xiuper-ui --type=kt 2>/dev/null | head -10

Repository: phodal/auto-dev

Length of output: 41


🏁 Script executed:

# Check if xiuper-ui is a multiplatform module
ls -la xiuper-ui/ | head -20

Repository: phodal/auto-dev

Length of output: 419


🏁 Script executed:

# Look at xiuper-ui's build configuration
cat xiuper-ui/build.gradle.kts 2>/dev/null | head -50

Repository: phodal/auto-dev

Length of output: 1029


🏁 Script executed:

# Check if xiuper-ui has platform-specific sourcesets (which would justify redeclarations)
rg -A 5 "sourceSets" xiuper-ui/build.gradle.kts 2>/dev/null | head -30

Repository: phodal/auto-dev

Length of output: 316


🏁 Script executed:

# Check for expect/actual in xiuper-ui kotlin files (corrected syntax)
rg -n "expect (class|fun|val|interface)" xiuper-ui/ --glob="*.kt" 2>/dev/null | head -20

Repository: phodal/auto-dev

Length of output: 41


Remove redundant xiuper-ui redeclarations in platform sourcesets.

The xiuper-ui dependency is added to commonMain (line 150), which is sufficient since xiuper-ui is a multiplatform library supporting all declared target platforms (jvm, js, wasmJs, ios). The redeclarations in jvmMain (line 200), jsMain (line 358), and wasmJsMain (line 396) are redundant. Keep only the androidMain redeclaration (line 273) due to its exclude statements for kotlin-logging conflicts. The iosMain redeclaration (line 337) can also be removed.

🤖 Prompt for AI Agents
In mpp-ui/build.gradle.kts around line 150 and platform sourcesets (jvmMain at
~200, androidMain at ~273, iosMain at ~337, jsMain at ~358, wasmJsMain at ~396):
xiuper-ui is already declared in commonMain (line 150) so remove the redundant
implementation(project(":xiuper-ui")) lines from jvmMain (≈200), iosMain (≈337),
jsMain (≈358) and wasmJsMain (≈396); keep the androidMain redeclaration at ≈273
because it contains the necessary exclude rules for kotlin-logging conflicts.
Ensure no other platform-specific excludes are lost when removing those
redundant declarations.

}
errorMessage != null -> {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text("⚠️ Error", color = MaterialTheme.colorScheme.error)
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

Remove emoji to ensure WASM compatibility.

The emoji "⚠️" violates the coding guideline to avoid emoji and UTF-8 characters in WASM code, which may cause rendering issues on that platform.

Apply this diff to use plain text:

-                        Text("⚠️ Error", color = MaterialTheme.colorScheme.error)
+                        Text("Error", color = MaterialTheme.colorScheme.error)

As per coding guidelines, avoid emoji and UTF-8 characters in WASM code.

📝 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
Text("⚠️ Error", color = MaterialTheme.colorScheme.error)
Text("Error", color = MaterialTheme.colorScheme.error)
🤖 Prompt for AI Agents
In
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoContentComponents.kt
around line 229, the Text composable includes an emoji ("⚠️") which violates the
WASM UTF-8/emoji guideline; remove the emoji and replace the string with plain
ASCII (e.g., change "⚠️ Error" to "Error") so the Text shows plain text and
ensure any other nearby strings follow the same plain-text rule.

Comment on lines +20 to +23
private fun nanoImageCacheFile(key: String): File {
val tmpDir = System.getProperty("java.io.tmpdir").orEmpty().ifEmpty { "/tmp" }
return File(File(File(tmpDir), "autodev"), "nano-images").resolve(key)
}
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

Unix-specific fallback path may fail on Windows.

The fallback to "/tmp" on Line 21 is Unix-specific and will fail on Windows systems if java.io.tmpdir is not set. While java.io.tmpdir is typically available on all JVM platforms, consider using a more portable fallback or letting it fail explicitly if the system property is not set.

Consider using System.getProperty("user.home") as a fallback or validating that the directory is writable:

 private fun nanoImageCacheFile(key: String): File {
-    val tmpDir = System.getProperty("java.io.tmpdir").orEmpty().ifEmpty { "/tmp" }
+    val tmpDir = System.getProperty("java.io.tmpdir")
+        ?: System.getProperty("user.home")
+        ?: error("Cannot determine cache directory: java.io.tmpdir and user.home are not set")
     return File(File(File(tmpDir), "autodev"), "nano-images").resolve(key)
 }
🤖 Prompt for AI Agents
In mpp-ui/src/jvmMain/kotlin/cc/unitmesh/devins/ui/nano/NanoImageCache.jvm.kt
around lines 20 to 23, the code falls back to the Unix-only string "/tmp" when
java.io.tmpdir is empty which can be invalid on Windows; change the fallback to
a cross-platform option such as System.getProperty("user.home") (or
Paths.get(System.getProperty("user.home"), ".cache", "appname") style location)
and after resolving the final directory ensure it exists and is writable (create
directories if needed and throw or log a clear error if creation or write-check
fails).

Comment on lines +15 to +38
internal actual suspend fun platformReadCachedImageBytes(key: String): ByteArray? {
return suspendCancellableCoroutine { cont ->
IndexedDBStorage.loadBinary(namespacedKey(key)).then({ uint8 ->
cont.resume(uint8?.toByteArray())
null
}).catch({ error ->
cont.resumeWithException(RuntimeException(error.toString()))
null
})
}
}

internal actual suspend fun platformWriteCachedImageBytes(key: String, bytes: ByteArray) {
val uint8 = bytes.toUint8Array()
return suspendCancellableCoroutine { cont ->
IndexedDBStorage.saveBinary(namespacedKey(key), uint8).then({ _ ->
cont.resume(Unit)
null
}).catch({ error ->
cont.resumeWithException(RuntimeException(error.toString()))
null
})
}
}
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

Inconsistent error handling across platforms.

The WASM/JS implementation throws RuntimeException on cache failures (lines 21, 34), while other platforms (JVM, Android, iOS) silently swallow errors using runCatching. This inconsistency could lead to unexpected behavior:

  • For reads: The exception may propagate to callers since the common code doesn't wrap platformReadCachedImageBytes in error handling.
  • For writes: The common code wraps the write in runCatching (NanoImageCache.kt line 25), so the exception will be caught.

Align with other platforms by catching errors and returning null/Unit:

 internal actual suspend fun platformReadCachedImageBytes(key: String): ByteArray? {
-    return suspendCancellableCoroutine { cont ->
+    return runCatching {
+        suspendCancellableCoroutine { cont ->
-        IndexedDBStorage.loadBinary(namespacedKey(key)).then({ uint8 ->
-            cont.resume(uint8?.toByteArray())
-            null
-        }).catch({ error ->
-            cont.resumeWithException(RuntimeException(error.toString()))
-            null
-        })
+            IndexedDBStorage.loadBinary(namespacedKey(key)).then({ uint8 ->
+                cont.resume(uint8?.toByteArray())
+                null
+            }).catch({ _ ->
+                cont.resume(null)
+                null
+            })
+        }
-    }
+    }.getOrNull()
 }

 internal actual suspend fun platformWriteCachedImageBytes(key: String, bytes: ByteArray) {
     val uint8 = bytes.toUint8Array()
-    return suspendCancellableCoroutine { cont ->
+    runCatching {
+        suspendCancellableCoroutine<Unit> { cont ->
-        IndexedDBStorage.saveBinary(namespacedKey(key), uint8).then({ _ ->
-            cont.resume(Unit)
-            null
-        }).catch({ error ->
-            cont.resumeWithException(RuntimeException(error.toString()))
-            null
-        })
+            IndexedDBStorage.saveBinary(namespacedKey(key), uint8).then({ _ ->
+                cont.resume(Unit)
+                null
+            }).catch({ _ ->
+                cont.resume(Unit)
+                null
+            })
+        }
     }
 }
📝 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
internal actual suspend fun platformReadCachedImageBytes(key: String): ByteArray? {
return suspendCancellableCoroutine { cont ->
IndexedDBStorage.loadBinary(namespacedKey(key)).then({ uint8 ->
cont.resume(uint8?.toByteArray())
null
}).catch({ error ->
cont.resumeWithException(RuntimeException(error.toString()))
null
})
}
}
internal actual suspend fun platformWriteCachedImageBytes(key: String, bytes: ByteArray) {
val uint8 = bytes.toUint8Array()
return suspendCancellableCoroutine { cont ->
IndexedDBStorage.saveBinary(namespacedKey(key), uint8).then({ _ ->
cont.resume(Unit)
null
}).catch({ error ->
cont.resumeWithException(RuntimeException(error.toString()))
null
})
}
}
internal actual suspend fun platformReadCachedImageBytes(key: String): ByteArray? {
return runCatching {
suspendCancellableCoroutine { cont ->
IndexedDBStorage.loadBinary(namespacedKey(key)).then({ uint8 ->
cont.resume(uint8?.toByteArray())
null
}).catch({ _ ->
cont.resume(null)
null
})
}
}.getOrNull()
}
internal actual suspend fun platformWriteCachedImageBytes(key: String, bytes: ByteArray) {
val uint8 = bytes.toUint8Array()
runCatching {
suspendCancellableCoroutine<Unit> { cont ->
IndexedDBStorage.saveBinary(namespacedKey(key), uint8).then({ _ ->
cont.resume(Unit)
null
}).catch({ _ ->
cont.resume(Unit)
null
})
}
}
}
🤖 Prompt for AI Agents
In
mpp-ui/src/wasmJsMain/kotlin/cc/unitmesh/devins/ui/nano/NanoImageCache.wasmJs.kt
around lines 15 to 38, the WASM/JS impl currently resumes the coroutine with
RuntimeException on IndexedDB errors which is inconsistent with other platforms;
change the error handlers to swallow errors like other platforms by resuming
with null for platformReadCachedImageBytes and resuming with Unit for
platformWriteCachedImageBytes (instead of resumeWithException), ensuring the
coroutine is always completed on both success and failure (optionally log the
error before resuming).

@phodal phodal merged commit e800871 into master Dec 17, 2025
6 of 7 checks passed
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

Comments