feat: Add P0 core components and GenUI components to NanoDSL#517
feat: Add P0 core components and GenUI components to NanoDSL#517
Conversation
- 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.
|
Note Other AI code review bot(s) detectedCodeRabbit 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. WalkthroughRefactors 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
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes
Possibly related issues
Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
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.
convertBindingandconvertActionare duplicated betweenComponentConverterUtilsandNanoIRConverter(lines 322-390). While the comment states this is for avoiding duplication in component files, having two identical implementations creates maintenance risk.Consider having
NanoIRConverterdelegate toComponentConverterUtilsfor 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
convertToIRimplementation duplicates the logic inNanoIRConverter.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 privateconvertBindinghelper. For consistency withDatePickerComponent(which has bothconvertBindingandconvertActionhelpers), consider adding a privateconvertActionhelper:+ 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 duplicatesNanoIRConverter.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 variableoption.The
optionproperty is extracted on line 402 but never used in the composable. Either remove it or use it (e.g., as theRadioButtonvalue 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:RenderDateRangePickerpasses identical IR to both child pickers.Both date pickers receive the same
irobject, 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-basedprogressoverload for Material 3LinearProgressIndicator.The
progress: Floatsignature is deprecated in Material 3, use the overload that takesprogressas 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-standardformatattribute on HTML date input.The
formatattribute 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 adata-formatattribute 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 usesgetOrNull.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
📒 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: Useexpect/actualpattern for platform-specific code (for example:Platformimplementations)
Use concrete classes (not interfaces) andPromise(notFlow) for @JsExport annotations
Avoid emoji and UTF-8 characters in WASM code
When modifyingCodingAgentRenderer, update ALL implementations:DefaultCodingAgentRenderer,ComposeRenderer,JewelRenderer,ServerSideRenderer, andJsRendererAdapterin Kotlin
When modifyingCodingAgentRenderer, update JVM CLI implementations:CodingCliRendererandConsoleRenderer
UseAutoDevColorsorMaterialTheme.colorSchemefor Compose styling
Files:
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/ComponentDefinition.ktmpp-ui/src/iosMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.ios.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/ComponentRegistry.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverter.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/feedback/ModalComponent.ktmpp-ui/src/wasmJsMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.wasmJs.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/input/ButtonComponent.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/parser/IndentParser.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/ComponentConverterUtils.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ast/NanoNode.ktmpp-ui/src/androidMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.android.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/input/DatePickerComponent.ktmpp-ui/src/jvmMain/kotlin/cc/unitmesh/devins/ui/nano/ComposeNanoRenderer.ktxiuper-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.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/ComponentDefinition.ktmpp-ui/src/iosMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.ios.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/ComponentRegistry.ktmpp-vscode/webview/src/components/nano/NanoRenderer.tsxxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverter.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/feedback/ModalComponent.ktmpp-ui/src/wasmJsMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.wasmJs.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/input/ButtonComponent.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/parser/IndentParser.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/ComponentConverterUtils.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ast/NanoNode.ktmpp-ui/src/androidMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.android.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/input/DatePickerComponent.ktmpp-ui/src/jvmMain/kotlin/cc/unitmesh/devins/ui/nano/ComposeNanoRenderer.ktxiuper-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.ktmpp-ui/src/androidMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.android.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.ktmpp-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.ktmpp-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 newdisabled_ifproperty.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
ComponentDefinitioninterface 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
disabledIfparameter to the Button component is correctly integrated, checking both args and props for thedisabled_ifproperty.
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_ifproperty- Extracts all properties in
createASTNodeincluding the conditional disable expression- Serializes properties to IR format in
convertToIR- Uses
ComponentConverterUtilsfor action conversionThe 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
disabledIfproperty addition toButtonenables 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
NanoNodevariants. 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: includesSplitViewcontainerComponents: includesGenCanvas,ModalcontentComponents: includesDataChart,DataTable,Alert,Progress,SpinnerinputComponents: includes all form input componentsThe organization is consistent and aligns with the component specs defined above.
106-109: Buttondisabled_ifenhancement looks good.The new
disabled_ifprop of typeEXPRESSIONenables 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, andcolumnsfor DataTable) and optional configuration props. The use ofPropType.EXPRESSIONfor 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: EnsureStatefulNanoRendererhandles all new component types added toComposeNanoRenderer.The new components (DatePicker, RadioGroup, NumberInput, Modal, Alert, Progress, Spinner, GenCanvas, SplitView, SmartTextField, Slider, DateRangePicker, DataChart, DataTable) are added to
ComposeNanoRenderer.RenderNodebut may not be handled inStatefulNanoRenderer.kt. Verify thatStatefulNanoRenderer'sRenderNodecontains matching case branches for each new component type, or these will fall through toRenderUnknownat runtime.
| @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)) | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
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.
| 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> | ||
| ); | ||
| }; |
There was a problem hiding this comment.
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.
| 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> | ||
| ); | ||
| }; |
There was a problem hiding this comment.
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.
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.kt
Show resolved
Hide resolved
🤖 Augment PR SummarySummary: This PR expands NanoDSL with P0 form/feedback components plus GenUI foundation components, and introduces a component-definition file structure to improve maintainability. Changes:
Technical Notes: Adds 🤖 Was this summary useful? React with 👍 or 👎 |
| type = "showToast", | ||
| payload = mapOf("message" to JsonPrimitive(action.message)) | ||
| ) | ||
| is NanoAction.Sequence -> NanoActionIR( |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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 👎
There was a problem hiding this comment.
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.
| // TODO: Add more components as they are refactored | ||
| // Feedback components | ||
| ModalComponent.name to ModalComponent, | ||
| // TODO: Add Alert, Progress, Spinner, etc. |
There was a problem hiding this comment.
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.
| // 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, |
| 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) |
There was a problem hiding this comment.
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.
| 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") |
There was a problem hiding this comment.
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.
| "Progress" -> { | ||
| NanoNode.Progress( | ||
| value = args["value"]?.toFloatOrNull() ?: props["value"]?.toFloatOrNull(), | ||
| max = args["max"]?.toFloatOrNull() ?: props["max"]?.toFloatOrNull() ?: 100f, |
There was a problem hiding this comment.
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.
| max = args["max"]?.toFloatOrNull() ?: props["max"]?.toFloatOrNull() ?: 100f, | |
| max = args["max"]?.toFloatOrNull() ?: props["max"]?.toFloatOrNull(), |
| Column(modifier = modifier) { | ||
| if (label != null) { | ||
| Text(text = label, style = MaterialTheme.typography.labelMedium) | ||
| Spacer(modifier = Modifier.height(4.dp)) | ||
| } | ||
| MaterialSlider( | ||
| value = min, | ||
| onValueChange = {}, |
There was a problem hiding this comment.
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.
| 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 }, |
| "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"] | ||
| ) |
There was a problem hiding this comment.
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.
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.
There was a problem hiding this comment.
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: UseDispatchers.IOinstead ofDispatchers.Defaultfor the HTTP request.
Dispatchers.Defaultis optimized for CPU-bound work with a limited thread pool (equal to CPU core count). Network I/O operations likeclient.get()should useDispatchers.IO, which provides a much larger thread pool (64+ threads) designed for blocking I/O. UsingDispatchers.Defaultfor network calls risks starving the thread pool if multiple concurrent image loads occur. This applies to all platforms in this multiplatform code, asDispatchers.IOis 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. Ifflexcontains 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
closableis 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=bindingorchecked=binding(e.g.,DatePicker,Switch), while GenUI components on lines 51-53 usebind=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=vsvalue=/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", andaria-labelledbyfor the title. Alert should includerole="alert"for screen reader announcements. Close buttons should havearia-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
📒 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.tsxxiuper-ui/src/iosMain/kotlin/cc/unitmesh/xuiper/prompt/ResourceLoader.ios.ktmpp-core/src/commonMain/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgent.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.ktxiuper-ui/src/wasmJsMain/kotlin/cc/unitmesh/xuiper/prompt/ResourceLoader.wasmJs.ktxiuper-ui/src/jsMain/kotlin/cc/unitmesh/xuiper/prompt/ResourceLoader.js.kt
**/*.kt
📄 CodeRabbit inference engine (AGENTS.md)
**/*.kt: Useexpect/actualpattern for platform-specific code (for example:Platformimplementations)
Use concrete classes (not interfaces) andPromise(notFlow) for @JsExport annotations
Avoid emoji and UTF-8 characters in WASM code
When modifyingCodingAgentRenderer, update ALL implementations:DefaultCodingAgentRenderer,ComposeRenderer,JewelRenderer,ServerSideRenderer, andJsRendererAdapterin Kotlin
When modifyingCodingAgentRenderer, update JVM CLI implementations:CodingCliRendererandConsoleRenderer
UseAutoDevColorsorMaterialTheme.colorSchemefor Compose styling
Files:
xiuper-ui/src/iosMain/kotlin/cc/unitmesh/xuiper/prompt/ResourceLoader.ios.ktmpp-core/src/commonMain/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgent.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.ktxiuper-ui/src/wasmJsMain/kotlin/cc/unitmesh/xuiper/prompt/ResourceLoader.wasmJs.ktxiuper-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.ktmpp-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 ofexpect/actualpattern 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.
| 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} />; | ||
| }; |
There was a problem hiding this comment.
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.
| 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> | ||
| ); | ||
| }; |
There was a problem hiding this comment.
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).
| ### 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 |
There was a problem hiding this comment.
🧩 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 -5Repository: 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 -50Repository: 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:
-
Progress component (line 62): Documentation shows
value=binding, but the actual implementation usesvalue: Float?(numeric, not binding). Should beProgress(value=0-100, max=100, showText=true, status="normal|success|error"). -
Alert component (line 61): Documentation doesn't reflect that Alert has no binding properties. The actual implementation only supports
type,message,closable,icon, andonClose. Remove any binding examples for this component. -
Modal component (line 60): Confirm the
open=bindingproperty is correct; actual implementation usesopen: 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.
There was a problem hiding this comment.
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
iconandonCloseproperties 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, useAutoDevColors.Signal(as demonstrated inRenderBadgeon lines 241-248) orMaterialTheme.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
ratiois ≤ 0 or ≥ 1,Modifier.weight(ratio)orModifier.weight(1f - ratio)can become 0 or negative, potentially causing layout crashes. Consider clampingratioto 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:onClickmapped toonChangelimits DSL expressiveness.The parser collects only
on_clickactions but assigns them toonChangefields. DSL props likeon_changewon't be recognized, silently losing the intended action behavior. This applies to DatePicker, Switch, NumberInput, Slider, and DateRangePicker.
980-987: Progressmaxdefault handling inconsistency.The parser defaults
maxto100fwhen not provided, but this default is only applied during parsing and not reflected inNanoNode.Progress. Consider whether the default should be in the data class or if the spec's default value should benullto match.
1035-1054:dataproperty marked required in spec but nullable in parser.Both
DataChartandDataTablespecifydataas a required property in the ComponentSpec, but the parser allows it to benullwithout 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
maxis 0 (or parses to 0),value / maxproducesInfinity/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
dataorcolumnsprops are missing, the nullable?.contentreturnsnull, 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_VALUESfor 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_ifproperty 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_ifhas 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=tsxmpp-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
placeholderattribute oninput[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
closableis 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:
Parameter naming: Some components use
value=binding(Input, DatePicker, NumberInput) while others usebind=binding(SmartTextField, Slider, DateRangePicker).Binding syntax: The component examples here show
value=bindingbut the Bindings section (line 577) demonstratesInput(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=bindingbut 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 += 1mpp-ui/src/jvmMain/kotlin/cc/unitmesh/devins/ui/nano/ComposeNanoRenderer.kt (1)
398-410: Remove unused property extraction.Line 401 extracts the
optionproperty 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 consolidatingconvertBindingandconvertActionwithComponentConverterUtils.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 dedicatedSIZE_VALUESconstant would be more semantically clear.mpp-vscode/webview/src/components/nano/NanoRenderer.tsx (1)
133-135: Consider handling explicitflex: 0values.Line 135 uses
Number(flex) || 1, which means an explicitflex: 0would be coerced to1. Whileflex: 0is 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:
- Adding a brief mention of the new component categories to MINIMAL_PROMPT (while keeping it minimal)
- 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
📒 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.tsxxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverter.ktxiuper-ui/src/wasmJsMain/kotlin/cc/unitmesh/xuiper/prompt/ResourceLoader.wasmJs.ktxiuper-ui/src/iosMain/kotlin/cc/unitmesh/xuiper/prompt/ResourceLoader.ios.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/parser/IndentParser.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ast/NanoNode.ktmpp-ui/src/jvmMain/kotlin/cc/unitmesh/devins/ui/nano/ComposeNanoRenderer.ktmpp-core/src/commonMain/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgent.ktxiuper-ui/src/jsMain/kotlin/cc/unitmesh/xuiper/prompt/ResourceLoader.js.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/spec/v1/NanoSpecV1.kt
**/*.kt
📄 CodeRabbit inference engine (AGENTS.md)
**/*.kt: Useexpect/actualpattern for platform-specific code (for example:Platformimplementations)
Use concrete classes (not interfaces) andPromise(notFlow) for @JsExport annotations
Avoid emoji and UTF-8 characters in WASM code
When modifyingCodingAgentRenderer, update ALL implementations:DefaultCodingAgentRenderer,ComposeRenderer,JewelRenderer,ServerSideRenderer, andJsRendererAdapterin Kotlin
When modifyingCodingAgentRenderer, update JVM CLI implementations:CodingCliRendererandConsoleRenderer
UseAutoDevColorsorMaterialTheme.colorSchemefor Compose styling
Files:
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverter.ktxiuper-ui/src/wasmJsMain/kotlin/cc/unitmesh/xuiper/prompt/ResourceLoader.wasmJs.ktxiuper-ui/src/iosMain/kotlin/cc/unitmesh/xuiper/prompt/ResourceLoader.ios.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/parser/IndentParser.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ast/NanoNode.ktmpp-ui/src/jvmMain/kotlin/cc/unitmesh/devins/ui/nano/ComposeNanoRenderer.ktmpp-core/src/commonMain/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgent.ktxiuper-ui/src/jsMain/kotlin/cc/unitmesh/xuiper/prompt/ResourceLoader.js.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.ktxiuper-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.ktmpp-ui/src/jvmMain/kotlin/cc/unitmesh/devins/ui/nano/ComposeNanoRenderer.ktmpp-core/src/commonMain/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgent.ktxiuper-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.ktmpp-core/src/commonMain/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgent.ktxiuper-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, andon_errorparameters. 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_ifproperty 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
MaterialSliderand the extended dispatch routing for new components are correctly implemented.Also applies to: 72-89
597-635: LGTM!The placeholder implementations for
RenderDateRangePicker,RenderDataChart, andRenderDataTableare 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
disabledIfproperty 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 (
bind→value).xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverter.kt (3)
171-171: LGTM! Button IR now includesdisabled_if.The
disabledIfproperty is correctly propagated to the IR asdisabled_if.
38-57: LGTM! Comprehensive dispatch for new components.The
convert()dispatcher is well-organized with clear tier/category comments and exhaustively handles all newNanoNodevariants.
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 withdisabledIf.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.FLOATfor numeric constraints (min, max, step) andPropType.INTfor 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_VALUESconstant for size validation and appropriately sets bothallowsChildrenandallowsActions.
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 ofPropType.FLOATfor 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.FLOATfor the ratio property- SmartTextField and others appropriately use
PropType.BINDINGfor state management- Slider uses
PropType.FLOATfor numeric constraints
314-340: LGTM!The data artifact components (DataChart and DataTable) are well-designed:
- Both correctly use
PropType.EXPRESSIONfor the requireddataproperty, allowing flexible data sources- DataChart uses a dedicated
CHART_TYPE_VALUESconstant 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_ifproperty 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.
| 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> | ||
| ); | ||
| }; |
There was a problem hiding this comment.
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.
| 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.
There was a problem hiding this comment.
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
shouldAutoDistributeis true and some children have explicitweightwhile others don't, both the explicit weight and the auto-assignedweight(1f)will be applied, which may produce unexpected space distribution.For example, if you have two VStack children where one specifies
weight=2fand the other has no weight, the second VStack will getweight(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 captureson_clickactions, breakingon_changeDSL syntax.As noted in previous reviews, the parser only collects
on_clickactions (line 282, 296-307) and assigns them to fields likeonChangefor DatePicker, Switch, and NumberInput. This means DSL properties likeon_change(which are valid according to NanoSpecV1) will be silently ignored, breaking the intended action behavior.Users writing
on_change: state.value = newValuewill 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
onCloseand Alert'sonCloseare assigned fromonClick, meaning DSL syntax likeon_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.
Action mapping: Slider, DateRangePicker, and DataTable assign their action fields from
onClick, which means DSL properties likeon_changeoron_row_click(from NanoSpecV1) won't work.Required properties: DataChart and DataTable allow
datato benull(line 1040, 1051), but the ComponentSpec marksdataas required. This creates a mismatch between the spec and parser behavior, potentially causing runtime issues in renderers that assumedatais present.Consider either:
- Making
datarequired 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
dataorcolumnsprops are missing,?.contentreturnsnull, 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
📒 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: Useexpect/actualpattern for platform-specific code (for example:Platformimplementations)
Use concrete classes (not interfaces) andPromise(notFlow) for @JsExport annotations
Avoid emoji and UTF-8 characters in WASM code
When modifyingCodingAgentRenderer, update ALL implementations:DefaultCodingAgentRenderer,ComposeRenderer,JewelRenderer,ServerSideRenderer, andJsRendererAdapterin Kotlin
When modifyingCodingAgentRenderer, update JVM CLI implementations:CodingCliRendererandConsoleRenderer
UseAutoDevColorsorMaterialTheme.colorSchemefor Compose styling
Files:
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.ktmpp-core/src/commonMain/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgent.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ast/NanoNode.ktxiuper-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.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.ktmpp-core/src/commonMain/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgent.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ast/NanoNode.ktxiuper-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.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.ktmpp-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.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.ktmpp-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.IOis recommended for I/O-bound operations.Please confirm whether switching to
Dispatchers.Defaultis 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
bindingsmap- Action handling through
onActioncallback- 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
DataChartandDataTableare 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
disabledIfproperty 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"), passingnullinstead. 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
maxto 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
disabledIfproperty is correctly typed asString?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_ifpropertyThe 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
renderProgressmethod correctly guards against division by zero on line 408 withif (max > 0f), addressing the concern raised in previous reviews. The method also properly handles both literal values and binding expressions through theisBindingcheck.
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.
There was a problem hiding this comment.
Actionable comments posted: 6
♻️ Duplicate comments (2)
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/parser/IndentParser.kt (2)
1036-1055: Requireddataproperty not validated.As noted in a previous review,
DataChartandDataTablespecifydataas 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_clickactions but assigns them to fields likeonChange. This means DSL props likeon_changefor DatePicker, Switch, NumberInput, Slider, DateRangePicker, and DataTable won't be parsed and will be silently ignored.Consider adding parsing support for
on_changeor document thaton_clickshould 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
2024should use the current year fromClock.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 unchangedmpp-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
DataTableorDataChartnodes 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
forEachIndexedcapturesindexbut doesn't add it toitemState. 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
parseColumnsmethod is marked as deprecated but remains in the codebase. Consider removing it entirely if all usages have been migrated toparseColumnDefs.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
buildChartCodefunction 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 withtoDoubleOrNull()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 = falseto make it read-only, but this might prevent the clickable modifier from working on some platforms. Consider usingreadOnly = truewithoutenabled = false.Test if removing
enabled = falseworks 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
📒 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: Useexpect/actualpattern for platform-specific code (for example:Platformimplementations)
Use concrete classes (not interfaces) andPromise(notFlow) for @JsExport annotations
Avoid emoji and UTF-8 characters in WASM code
When modifyingCodingAgentRenderer, update ALL implementations:DefaultCodingAgentRenderer,ComposeRenderer,JewelRenderer,ServerSideRenderer, andJsRendererAdapterin Kotlin
When modifyingCodingAgentRenderer, update JVM CLI implementations:CodingCliRendererandConsoleRenderer
UseAutoDevColorsorMaterialTheme.colorSchemefor Compose styling
Files:
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoDataComponents.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoContentComponents.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoControlFlowComponents.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoFeedbackComponents.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/parser/IndentParser.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoRenderUtils.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoInputComponents.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverter.ktmpp-ui/src/jvmTest/kotlin/cc/unitmesh/devins/ui/nano/NanoDataComponentsTest.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoLayoutComponents.ktxiuper-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.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoContentComponents.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoControlFlowComponents.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoFeedbackComponents.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/parser/IndentParser.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoRenderUtils.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoInputComponents.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverter.ktmpp-ui/src/jvmTest/kotlin/cc/unitmesh/devins/ui/nano/NanoDataComponentsTest.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoLayoutComponents.ktxiuper-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.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoFeedbackComponents.ktmpp-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
whenexpression 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. TheonClose = onClickmapping 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
renderNodecallback 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
expectfunction already hasactualimplementations 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
@Deprecatedannotations andReplaceWithprovide a clear migration path to NanoDataComponents.
64-129: LGTM - Proper use of design tokens.RenderAlert and RenderProgress correctly use
AutoDevColors.SignalandMaterialTheme.colorSchemefor 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.Signalcolors withColor.Blackfor 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 functionstoSpacing()andtoPadding()are already defined inNanoRenderUtils.ktin the same package. Since they are String extension functions, they are automatically available inNanoLayoutComponents.ktwithout 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.interpolateTextfor 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
disabledIfis 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
labelparameter 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.
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoControlFlowComponents.kt
Show resolved
Hide resolved
| ) | ||
| } else { | ||
| // Fallback for platforms without chart support | ||
| RenderChartFallback(chartType, resolvedData, modifier) |
There was a problem hiding this comment.
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.
| 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.
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoDataComponents.kt
Outdated
Show resolved
Hide resolved
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoFeedbackComponents.kt
Outdated
Show resolved
Hide resolved
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoInputComponents.kt
Show resolved
Hide resolved
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoRenderUtils.kt
Show resolved
Hide resolved
- 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.
There was a problem hiding this comment.
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 deprecatedDividerwithHorizontalDivider.The
Dividercomponent is deprecated in Material3. UseHorizontalDivider()instead.- Divider() + HorizontalDivider()mpp-vscode/webview/src/components/nano/NanoRenderer.tsx (4)
692-704: NumberInput increment/decrement buttons lack functionality.The buttons have no
onClickhandlers, 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
closableis 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 / 365anddays % 30will produce incorrect dates due to leap years and varying month lengths.kotlinx-datetimeis 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
columnsis extracted viaextractBracketLiteral. Other components likeRadioGroupwithoptions=["a","b"]orDataChartwith complexdatamight 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
convertBindingmethod here (lines 328-343) appears identical toComponentConverterUtils.convertBindingshown 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
📒 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.tsxmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoDataComponents.ktmpp-ui/src/jsMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.js.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoFeedbackComponents.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverter.ktmpp-ui/src/jvmTest/kotlin/cc/unitmesh/devins/ui/nano/NanoRenderUtilsTest.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoInputComponents.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/parser/IndentParser.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoLayoutComponents.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoRenderUtils.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ast/NanoNode.kt**/*.kt
📄 CodeRabbit inference engine (AGENTS.md)
**/*.kt: Useexpect/actualpattern for platform-specific code (for example:Platformimplementations)
Use concrete classes (not interfaces) andPromise(notFlow) for @JsExport annotations
Avoid emoji and UTF-8 characters in WASM code
When modifyingCodingAgentRenderer, update ALL implementations:DefaultCodingAgentRenderer,ComposeRenderer,JewelRenderer,ServerSideRenderer, andJsRendererAdapterin Kotlin
When modifyingCodingAgentRenderer, update JVM CLI implementations:CodingCliRendererandConsoleRenderer
UseAutoDevColorsorMaterialTheme.colorSchemefor Compose stylingFiles:
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoDataComponents.ktmpp-ui/src/jsMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.js.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoFeedbackComponents.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverter.ktmpp-ui/src/jvmTest/kotlin/cc/unitmesh/devins/ui/nano/NanoRenderUtilsTest.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoInputComponents.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/parser/IndentParser.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoLayoutComponents.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoRenderUtils.ktxiuper-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 documentationLearnt 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.tsxmpp-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.tsxmpp-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 KotlinApplied to files:
mpp-ui/src/jsMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.js.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoFeedbackComponents.ktmpp-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 stylingApplied 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
parseMultilineInlineComponentCallfunction 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/checkedargs, 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
extractBracketLiteralfunction correctly handles nested brackets, string escaping, and mixed delimiters. This enables parsing of complex inline literals likecolumns=["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"andvalue="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
openbinding for visibility control,onCloseaction, 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
getByPathutility correctly handles thestate.prefix and nested path resolution.
162-273: Solid arithmetic expression parser.The
NumericExprParserclass 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
@Deprecatedand include appropriateReplaceWithguidance pointing toNanoDataComponents.
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
parseStructuredDefaulthelper cleanly handles both JSON and YAML literals viaYamlUtils.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, andobjecttypes, 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 offilterNotfor REMOVE are correct. The string trimming and quote removal handle common edge cases.
169-173: Good use of composable lambda for recursive rendering.The
renderNodelambda pattern cleanly enables cross-module composition while maintaining the same signature across all component renderers.
175-218: Comprehensive component type dispatch.The
whenblock 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
getBuiltInVariablesfunction correctly useskotlinx.datetimefor accurate date/time values, providing useful template variables likecurrentYear,today, andnow.
245-363: Well-structured recursive-descent expression parser.The
NumericExprParsercorrectly 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()andtoPadding()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
lenientJsonconfiguration withisLenient = trueandignoreUnknownKeys = trueis appropriate for parsing user-provided DSL options that may not be strictly formatted.
74-81: Solid validation of state paths.The
resolveStatePathFromBindingfunction correctly validates that paths are simple identifiers or dotted paths, preventing expression strings like"x" in state.itemsfrom being misinterpreted as writable state paths.
119-122: Proper use of MaterialTheme for button colors.The button colors use
MaterialTheme.colorScheme.errorfor danger intent, following the coding guideline to useMaterialTheme.colorSchemefor 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
FlowRowis 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 ifwrapContentHeight(unbounded = true)is intentional.Using
unbounded = trueallows 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.
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoFeedbackComponents.kt
Show resolved
Hide resolved
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoFeedbackComponents.kt
Show resolved
Hide resolved
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoInputComponents.kt
Outdated
Show resolved
Hide resolved
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoInputComponents.kt
Outdated
Show resolved
Hide resolved
| 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() |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find . -name "NanoNode.kt" -o -name "*Renderer*.kt" | head -20Repository: phodal/auto-dev
Length of output: 1792
🏁 Script executed:
find . -name "NanoNode.kt" -type fRepository: phodal/auto-dev
Length of output: 127
🏁 Script executed:
sed -n '365,395p' ./xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ast/NanoNode.ktRepository: 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 -80Repository: phodal/auto-dev
Length of output: 85
🏁 Script executed:
rg -n "DataChart|DataTable" -A 10 xiuper-ui/src | head -100Repository: phodal/auto-dev
Length of output: 11278
🏁 Script executed:
rg -n "renderDataChart|renderDataTable" xiuper-ui/src -A 20 | head -150Repository: phodal/auto-dev
Length of output: 6019
🏁 Script executed:
rg -n "class.*Renderer.*DataChart|class.*Renderer.*DataTable" xiuper-ui/src -A 5Repository: 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 -20Repository: phodal/auto-dev
Length of output: 355
🏁 Script executed:
rg -n "renderDataChart|renderDataTable" xiuper-ui/src --type-list | head -5Repository: phodal/auto-dev
Length of output: 448
🏁 Script executed:
cat xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/NanoRenderer.kt | head -100Repository: 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 5Repository: phodal/auto-dev
Length of output: 41
🏁 Script executed:
wc -l xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/NanoRenderer.ktRepository: phodal/auto-dev
Length of output: 136
🏁 Script executed:
sed -n '100,269p' xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/NanoRenderer.ktRepository: 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.ktRepository: phodal/auto-dev
Length of output: 41
🏁 Script executed:
wc -l xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.ktRepository: 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.
|
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 |
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (10)
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/parser/IndentParser.kt (1)
1015-1163: Parser only recognizeson_click:syntax for all action handlers.Multiple components (DatePicker, Switch, NumberInput, Slider, DateRangePicker, DataTable) assign the
onClickaction to their respective change handlers (onChange,onRowClick). This means the DSL only supportson_click:syntax, and properties likeon_change:oron_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_clickare 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 -C3xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ast/NanoNode.kt (3)
147-153: Verify that Button.disabledIf is rendered in all target renderers.The
disabledIfproperty 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.colorSchemereferences instead ofString?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.colorSchemereferences instead ofString?for color properties. This applies to all components with color properties (Badge, Icon, DataChart, Spinner).Based on learnings: Use
AutoDevColorsorMaterialTheme.colorSchemefor 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_ifproperty 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
sizeproperty usesSPACING_VALUES(xs, sm, md, lg, xl) which are defined for spacing purposes. While these values may work for sizing, consider whether a dedicatedSIZE_VALUESconstant 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
maxwith default value"100"(string), but the parser at IndentParser.kt:1091 uses100f(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
dataas 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) * 100but doesn't guard againstmaxbeing 0, which would produceInfinityorNaN. While line 421 defaultsmaxto 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 0Actually, 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
contentvalues directly into HTML attributes. When the property is missing, this producesdata-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 detectionparseKeyIdentifier()to extract the binding keyparseBindingExpression()to capture the expression valueThis is a readability improvement, not a functional issue. The current implementation works correctly.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 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: Useexpect/actualpattern for platform-specific code (for example:Platformimplementations)
Use concrete classes (not interfaces) andPromise(notFlow) for @JsExport annotations
Avoid emoji and UTF-8 characters in WASM code
When modifyingCodingAgentRenderer, update ALL implementations:DefaultCodingAgentRenderer,ComposeRenderer,JewelRenderer,ServerSideRenderer, andJsRendererAdapterin Kotlin
When modifyingCodingAgentRenderer, update JVM CLI implementations:CodingCliRendererandConsoleRenderer
UseAutoDevColorsorMaterialTheme.colorSchemefor Compose styling
Files:
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverter.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/spec/v1/NanoSpecV1.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.ktxiuper-ui/src/commonTest/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverterCommonTest.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/parser/IndentParser.ktxiuper-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.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/spec/v1/NanoSpecV1.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.ktxiuper-ui/src/commonTest/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverterCommonTest.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/parser/IndentParser.ktxiuper-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.ktxiuper-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.ktxiuper-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
extractBracketLiteralhelper 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
letensures 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
renderFlexStyleAttributehelper includes good defensive guards against invalid values and correctly applies the flex style withmin-width: 0to 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
There was a problem hiding this comment.
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.Sequenceconverter returns a NanoActionIR with only thetypefield, omitting thepayloadwith the nestedactionsarray. This is inconsistent withComponentConverterUtils.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
actionIrToJsonhelper 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:
Progress component (line 62):
- Change
value=state.progresstovalue=50(or document asvalue=0-100) since the implementation uses numericFloat?, not a binding- Change
status="normal|success|exception|active"tostatus="normal|success|error"Alert component (line 61):
- Add missing props:
iconandonClose- 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 barmpp-vscode/webview/src/components/nano/NanoRenderer.tsx (6)
696-699: Placeholder attribute won't work on date inputs.Most browsers ignore the
placeholderattribute oninput[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
closableis 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
validationproperty is passed directly to the HTMLpatternattribute without checking if it's a valid string or regex pattern.This issue was previously flagged. Consider validating that
validationis a non-empty string before assigning it topattern.
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
OutlinedTextFieldhas been addressed by wrapping the field in aBoxwith theclickablemodifier. 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
DateRangePickeruses the sameBoxwrapper pattern asDatePickerto handle clicks on a disabledOutlinedTextField, 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.fromEpochMillisecondsandtoLocalDateTime(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
percentagecalculation now includesif (max > 0f)guard and usescoerceIn(0, 100)to ensure valid output, addressing the past review concern about potential Infinity/NaN values.
557-576: Data attributes now properly handled.The
renderDataChartandrenderDataTablemethods now:
- Use conditional attribute building (
data?.let { ... } ?: "")- Apply
escapeHtmlAttrto prevent XSS and attribute injection- No longer render literal
"null"stringsThis 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
colorproperty acceptsString?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.colorSchemefor 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
getByPathfunction traverses object properties using bracket notation without checking for dangerous keys like__proto__,constructor, orprototype. 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.BlackandColor.Whitefor badge text colors. Per coding guidelines, always use design tokens likeMaterialTheme.colorScheme.onPrimary,MaterialTheme.colorScheme.onSurface, orAutoDevColorsfor 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
AutoDevColorsorMaterialTheme.colorSchemefor 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
maxproperty 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
dataas 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
RenderProgressimplementation 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
parseStructuredDefaultfunction 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
📒 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.tsxxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/ComponentConverterUtils.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverter.ktxiuper-ui/src/jvmTest/kotlin/cc/unitmesh/xuiper/render/HtmlRendererTest.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoInputComponents.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoFeedbackComponents.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoContentComponents.ktxiuper-ui/src/commonTest/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverterCommonTest.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/spec/v1/NanoSpecV1.ktxiuper-ui/src/iosMain/kotlin/cc/unitmesh/xuiper/prompt/ResourceLoader.ios.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoRenderUtils.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoDataComponents.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoControlFlowComponents.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ast/NanoNode.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/parser/IndentParser.kt
**/*.kt
📄 CodeRabbit inference engine (AGENTS.md)
**/*.kt: Useexpect/actualpattern for platform-specific code (for example:Platformimplementations)
Use concrete classes (not interfaces) andPromise(notFlow) for @JsExport annotations
Avoid emoji and UTF-8 characters in WASM code
When modifyingCodingAgentRenderer, update ALL implementations:DefaultCodingAgentRenderer,ComposeRenderer,JewelRenderer,ServerSideRenderer, andJsRendererAdapterin Kotlin
When modifyingCodingAgentRenderer, update JVM CLI implementations:CodingCliRendererandConsoleRenderer
UseAutoDevColorsorMaterialTheme.colorSchemefor Compose styling
Files:
xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/components/ComponentConverterUtils.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverter.ktxiuper-ui/src/jvmTest/kotlin/cc/unitmesh/xuiper/render/HtmlRendererTest.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoInputComponents.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoFeedbackComponents.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoContentComponents.ktxiuper-ui/src/commonTest/kotlin/cc/unitmesh/xuiper/ir/NanoIRConverterCommonTest.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/spec/v1/NanoSpecV1.ktxiuper-ui/src/iosMain/kotlin/cc/unitmesh/xuiper/prompt/ResourceLoader.ios.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoRenderUtils.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoDataComponents.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoControlFlowComponents.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/ast/NanoNode.ktxiuper-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.tsxmpp-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.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoFeedbackComponents.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoContentComponents.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.ktxiuper-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.ktxiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.ktxiuper-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.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoControlFlowComponents.ktxiuper-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.ktxiuper-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.ktxiuper-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. Thevalidationprop 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
tryParseStructuredLiteraland 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
valueandmaxfrom 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_ifattribute rendering for Button components and properly asserts the presence ofdata-disabled-ifin 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
wrapandflexlayout 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
convertBindingmethod correctly handles all binding variants (Subscribe, TwoWay, Static) and produces properly structured NanoBindingIR objects.
36-90: LGTM! Past review concern addressed.The
convertActionmethod now correctly encodes the nestedactionsarray in the payload forNanoAction.Sequence(lines 83-88), addressing the previous review comment about dropped sequence actions.
92-110: LGTM!The private helper methods
actionIrToJsonandconvertBodyFieldare 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
flexproperties to VStack, HStack, and Card, pluswrapsupport for HStack, enabling responsive flex-based layouts.
170-210: LGTM!The Button and Checkbox enhancements add important functionality:
disabled_iffor 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
RenderDataChartfunction correctly converts resolved data to a String (line 61) before passing it toRenderChartFallback, avoiding the type mismatch mentioned in past comments. The implementation properly handles chart rendering with fallback support.
300-387: LGTM!The
RenderDataTableimplementation correctly usesHorizontalDivider()(line 361) instead of the deprecatedDividercomponent, 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
RenderTextcomposable properly handles content binding resolution, state interpolation, and style mapping usingMaterialTheme.typography, following design system guidelines.
71-253: LGTM!The
RenderImagecomposable provides comprehensive image handling with generation, loading, error states, and adaptive sizing. It correctly usesMaterialTheme.colorSchemefor colors and implements proper async/await patterns.
280-357: LGTM!The
RenderIconcomposable provides comprehensive icon name mapping and correctly uses design tokens (MaterialTheme.colorScheme,AutoDevColors.Signal) for icon colors. TheRenderDivideris 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
createNodesignature now accepts anactionsmap, 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
parseArgsmethod (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
AutoDevColorsfor alert styling complies with the coding guidelines.
32-94: LGTM! Modal rendering with proper race condition handling.The
llmServiceis correctly captured into a localval servicevariable 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
@Deprecatedannotations andReplaceWithclauses provide a clear migration path toNanoDataComponents.mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.kt (2)
35-61: LGTM! Clean state initialization from IR.The
initStateFromIRfunction properly handles various types including primitives, lists, and maps. The use ofparseStructuredDefaultfor complex types is a good approach.
178-227: LGTM! Clean modular dispatch pattern.The
renderNodelambda for recursive rendering is well-designed, enabling clean delegation to external component modules. Thewhendispatch 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
lenientJsonconfiguration 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.itemsfrom being treated as writable state paths.
100-138: LGTM! Button with disabled_if support.The
RenderButtoncorrectly evaluates thedisabled_ifcondition and applies the disabled state consistently to both the click handler guard and theenabledprop.
688-769: LGTM! Well-implemented Slider component.The
RenderSliderimplementation 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
getBuiltInVariablesfunction provides useful date/time variables for text interpolation.
45-82: LGTM! Robust expression evaluation.The
interpolateTextandevaluateExpressionfunctions 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
NumericExprParsercorrectly implements:
- Standard operator precedence (+ - lower than * /)
- Parentheses for grouping
- Division by near-zero protection (returns 0.0)
- State variable resolution with
state.prefix handlingThe implementation mirrors the TypeScript version in
NanoRenderer.tsxappropriately.
530-551: LGTM! Consistent spacing/padding utilities.The extension functions provide a clean mapping from string tokens to
Dpvalues, with sensible defaults.xiuper-ui/src/commonMain/kotlin/cc/unitmesh/xuiper/render/HtmlRenderer.kt (3)
18-39: LGTM! Useful helper functions.The
renderFlexStyleAttributeproperly handles flex styling with min-width:0 to prevent overflow. TheescapeHtmlAttrfunction correctly escapes all necessary HTML special characters.
197-218: LGTM! Button with disabled_if support.The
disabled_ifattribute 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
flexproperties across layout components (VStack, HStack, Card, Form, SplitView, GenCanvas) and thewrapproperty 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
disabledIfproperty enables conditional disabling based on expressions, which is consistent with the DSL's binding patterns. Since all parameters afterlabelhave default values, this is backward compatible when using named parameters (idiomatic in Kotlin).
169-173: LGTM: Checkbox extension improves usability.Adding
labelandonChangeproperties 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?forcolumnsanddataproperties to support both literal values and state binding expressions, consistent with the DSL's binding philosophy. TheonRowClickhandler enables row interaction.
511-516: LGTM: Progress component design supports flexible value binding.The explicit documentation (lines 508-509) clarifying that
valueandmaxaccept both literal numbers and state binding expressions is helpful. UsingString?for these properties is appropriate given the DSL's dual-mode binding semantics.
| PropSpec("onSubmit", PropType.STRING), | ||
| PropSpec("flex", PropType.FLOAT, description = "Flex factor when used inside HStack/SplitView") |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's locate and examine the NanoSpecV1.kt file
find . -name "NanoSpecV1.kt" -type fRepository: 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"
fiRepository: 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" -A1Repository: 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"
fiRepository: 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 -30Repository: 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.
| | VStack | spacing, align | Vertical stack | | ||
| | HStack | spacing, align, justify | Horizontal stack | | ||
| | Card | padding, shadow, radius | Container card | |
There was a problem hiding this comment.
🧩 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 -20Repository: phodal/auto-dev
Length of output: 1754
🏁 Script executed:
git ls-files | grep -E "(VStack|HStack|Card)" | head -20Repository: phodal/auto-dev
Length of output: 505
🏁 Script executed:
rg -l "class (VStack|HStack|Card)" --type-listRepository: phodal/auto-dev
Length of output: 448
🏁 Script executed:
rg "wrap|flex" xiuper-ui/src/commonMain --type-listRepository: phodal/auto-dev
Length of output: 448
🏁 Script executed:
find . -type f -name "detailed.txt" 2>/dev/nullRepository: phodal/auto-dev
Length of output: 168
🏁 Script executed:
find . -type d -name "xiuper-ui" 2>/dev/nullRepository: phodal/auto-dev
Length of output: 70
🏁 Script executed:
rg "VStack|HStack|Card" --type kotlin -A 5 | head -40Repository: phodal/auto-dev
Length of output: 4244
🏁 Script executed:
rg "wrap.*flex|flex.*wrap" . --type kotlin --type txt | head -20Repository: phodal/auto-dev
Length of output: 181
🏁 Script executed:
cat -n ./xiuper-ui/src/commonMain/resources/prompts/detailed.txtRepository: phodal/auto-dev
Length of output: 7528
🏁 Script executed:
rg "wrap|flex" ./xiuper-ui/src/commonMain/kotlin --type kotlin -B 2 -A 2Repository: phodal/auto-dev
Length of output: 29627
🏁 Script executed:
find ./xiuper-ui -type f -name "*Component*" -o -name "*Stack*" | head -20Repository: 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 10Repository: 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.
| | 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.
| | SplitView | ratio | Left/right split layout | | ||
| | GenCanvas | bind | AI-generated canvas/preview area | |
There was a problem hiding this comment.
Consolidate duplicated SplitView documentation.
SplitView is documented twice with conflicting prop signatures:
- Section 2 (line 30):
ratioonly - 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".
| ### 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") | ||
| ``` |
There was a problem hiding this comment.
🛠️ 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.
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (9)
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoContentComponents.kt (3)
18-18: Unused import.
Layoutis 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
HttpClientfor 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
useextension 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 -C2mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoRenderUtils.kt (2)
583-604:toSpacing()andtoPadding()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 cachinggetBuiltInVariables()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:rawis redeclared in inner scope.The variable
rawon line 75 shadows the outerrawfrom 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 fromNanoStateRuntime.// 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-uiavailable 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
📒 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: Useexpect/actualpattern for platform-specific code (for example:Platformimplementations)
Use concrete classes (not interfaces) andPromise(notFlow) for @JsExport annotations
Avoid emoji and UTF-8 characters in WASM code
When modifyingCodingAgentRenderer, update ALL implementations:DefaultCodingAgentRenderer,ComposeRenderer,JewelRenderer,ServerSideRenderer, andJsRendererAdapterin Kotlin
When modifyingCodingAgentRenderer, update JVM CLI implementations:CodingCliRendererandConsoleRenderer
UseAutoDevColorsorMaterialTheme.colorSchemefor Compose styling
Files:
mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoStateRuntime.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoContentComponents.ktmpp-ui/src/commonTest/kotlin/cc/unitmesh/devins/ui/nano/NanoStateRuntimeTest.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.ktmpp-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.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoContentComponents.ktmpp-ui/src/commonTest/kotlin/cc/unitmesh/devins/ui/nano/NanoStateRuntimeTest.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/StatefulNanoRenderer.ktmpp-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.ktmpp-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!
RenderTextcorrectly resolves bindings, interpolates state expressions, and maps style props to Material typography. Good use ofMaterialTheme.typographyfor text styles.
281-352: LGTM!
RenderIconprovides comprehensive icon name mapping and usesAutoDevColors.SignalandMaterialTheme.colorSchemefor colors per coding guidelines. The fallback toIcons.Default.Infois reasonable.mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoRenderUtils.kt (3)
544-554: LGTM!The
formatDateFromMillisimplementation correctly useskotlinx.datetimefor proper date conversion from epoch milliseconds. This addresses the previous review feedback.
287-405: Well-structured recursive descent parser.
NumericExprParseris a clean implementation following standard expression parsing patterns with proper precedence handling. The structure mirrors the TypeScript implementation inNanoRenderer.tsxappropriately for cross-platform consistency.
464-537: LGTM!
evaluateConditionprovides 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
stateMutationwithSEToperation for both integer and boolean types. Tests bothstate.prefixed and bare path formats.mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoStateRuntime.kt (5)
126-136:APPENDoperation silently deduplicates entries.The
APPENDoperation checksif (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:
- Documenting this behavior in the class KDoc
- Adding a separate
ADD_UNIQUEoperation for dedup behavior- 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 theMap<String, Any>contract for renderer consumption.
152-189: LGTM!
coerceSetValueprovides 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.loadfor 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
renderNodecomposable 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 decodeImageBytesToBitmapfollows 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 |
There was a problem hiding this comment.
🧩 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
fiRepository: 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 5Repository: 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 5Repository: 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.primaryNote: 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.
| 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.
| 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 | ||
| } | ||
| } |
There was a problem hiding this comment.
🛠️ 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.
| 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.
There was a problem hiding this comment.
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.BlackandColor.Whiteviolate the design system requirement. Replace with appropriateAutoDevColors.Texttokens.Apply this diff:
- val textColor = if (colorName == "yellow") Color.Black else Color.White + val textColor = if (colorName == "yellow") AutoDevColors.Text.inverse else AutoDevColors.Text.primaryAs 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.
EmptyFileSystemis imported but not used in this file. ThestreamPromptmethod has a default value forfileSystem, so this import can be removed.-import cc.unitmesh.devins.filesystem.EmptyFileSystem
88-116: Consider using standard test assertions.Using
throw IllegalStateExceptionfor 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
runCatchingto 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 bothjvmMainandandroidMaincan 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
HttpClientfor each image download may be inefficient. IfHttpClientFactory.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
HttpClientFactorydoesn'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
RenderHStackfunction 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.onCheckedChangehandler (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 handlesstate.*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
📒 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: Useexpect/actualpattern for platform-specific code (for example:Platformimplementations)
Use concrete classes (not interfaces) andPromise(notFlow) for @JsExport annotations
Avoid emoji and UTF-8 characters in WASM code
When modifyingCodingAgentRenderer, update ALL implementations:DefaultCodingAgentRenderer,ComposeRenderer,JewelRenderer,ServerSideRenderer, andJsRendererAdapterin Kotlin
When modifyingCodingAgentRenderer, update JVM CLI implementations:CodingCliRendererandConsoleRenderer
UseAutoDevColorsorMaterialTheme.colorSchemefor Compose styling
Files:
mpp-core/src/commonMain/kotlin/cc/unitmesh/llm/PromptStreamingService.ktmpp-ui/src/jvmTest/kotlin/cc/unitmesh/devins/ui/nano/NanoTextInterpolationTest.ktmpp-ui/src/wasmJsMain/kotlin/cc/unitmesh/devins/ui/nano/NanoImageCache.wasmJs.ktmpp-ui/src/jvmMain/kotlin/cc/unitmesh/devins/ui/nano/cli/NanoDslTextRenderCli.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoContentComponents.ktmpp-ui/src/androidMain/kotlin/cc/unitmesh/devins/ui/nano/NanoImageCache.android.ktmpp-core/src/commonMain/kotlin/cc/unitmesh/llm/KoogPromptStreamingServiceAdapter.ktmpp-ui/src/iosMain/kotlin/cc/unitmesh/devins/ui/nano/NanoImageCache.ios.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoImageCache.ktmpp-ui/src/jsMain/kotlin/cc/unitmesh/devins/ui/nano/NanoImageCache.js.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoLayoutComponents.ktmpp-core/src/commonTest/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgentScenarioCompileTest.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoInputComponents.ktmpp-core/src/jvmMain/kotlin/cc/unitmesh/devins/test/NanoDslScenarioHarness.ktmpp-ui/src/jvmMain/kotlin/cc/unitmesh/devins/ui/nano/NanoImageCache.jvm.ktmpp-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.ktmpp-ui/src/jvmTest/kotlin/cc/unitmesh/devins/ui/nano/NanoTextInterpolationTest.ktmpp-ui/src/wasmJsMain/kotlin/cc/unitmesh/devins/ui/nano/NanoImageCache.wasmJs.ktmpp-ui/src/jvmMain/kotlin/cc/unitmesh/devins/ui/nano/cli/NanoDslTextRenderCli.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoContentComponents.ktmpp-ui/src/androidMain/kotlin/cc/unitmesh/devins/ui/nano/NanoImageCache.android.ktmpp-core/src/commonMain/kotlin/cc/unitmesh/llm/KoogPromptStreamingServiceAdapter.ktmpp-ui/src/iosMain/kotlin/cc/unitmesh/devins/ui/nano/NanoImageCache.ios.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoImageCache.ktmpp-ui/src/jsMain/kotlin/cc/unitmesh/devins/ui/nano/NanoImageCache.js.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoLayoutComponents.ktmpp-core/src/commonTest/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgentScenarioCompileTest.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoInputComponents.ktmpp-core/src/jvmMain/kotlin/cc/unitmesh/devins/test/NanoDslScenarioHarness.ktmpp-ui/src/jvmMain/kotlin/cc/unitmesh/devins/ui/nano/NanoImageCache.jvm.ktmpp-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.ktmpp-core/src/commonMain/kotlin/cc/unitmesh/agent/subagent/NanoDSLAgent.ktmpp-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.ktmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoLayoutComponents.ktmpp-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
jvmMainClassesand 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
KoogLLMServicewith the adapter.
529-637: Well-structured prompt documentation.The updated
DEFAULT_PROMPTcomprehensively 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
flowOffor 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 thestate:line itself (e.g.,state:at 4 spaces, keys at 8 spaces). The conditionindent <= baseIndentcorrectly 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/tmpis 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
ByteArrayandUint8Arrayis 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()andtoPadding()extension functions are defined inmpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/nano/NanoRenderUtils.ktat lines 583 and 596 respectively. Both functions correctly convert string values toDp, 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 toinitFromIR(), andgetState().snapshot()correctly returns aMap<String, Any?>that can contain nulls. The conversion of nulls to empty strings viav ?: ""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). TheinterpolateText()function inNanoRenderUtils.ktuses the identical pattern and includes tests confirming both delimiters work:assertEquals("Budget: 800", NanoRenderUtils.interpolateText("Budget: {flightBudget}", state))andassertEquals("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
runNanoDslTextRenderClitask 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")) |
There was a problem hiding this comment.
🧩 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 -10Repository: 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 -5Repository: 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 -10Repository: phodal/auto-dev
Length of output: 41
🏁 Script executed:
# Check if xiuper-ui is a multiplatform module
ls -la xiuper-ui/ | head -20Repository: 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 -50Repository: 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 -30Repository: 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 -20Repository: 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) |
There was a problem hiding this comment.
Remove emoji to ensure WASM compatibility.
The emoji "
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.
| 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.
| 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) | ||
| } |
There was a problem hiding this comment.
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).
| 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 | ||
| }) | ||
| } | ||
| } |
There was a problem hiding this comment.
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
platformReadCachedImageBytesin 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.
| 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).
All components are fully integrated into:
Compiles successfully on JVM and JS platforms.
Summary by CodeRabbit
New Features
Behavior Changes
Platform
Docs
Tests
Style
✏️ Tip: You can customize this high-level summary in your review settings.