feat: DevPanel render logging — unknown widgets, placeholders, and urlwidget failures
Add comprehensive logging to BricksRenderer for DevPanel visibility: 1. Unknown widgettypes: WARN log with full JSON dump in DevPanel Logs tab. Also shows [? WidgetType] orange placeholder on screen when no subwidgets. 2. Placeholder widgets (Html, MarkdownViewer, etc.): INFO log with widget summary. 3. urlwidget load failures: ERROR log with full widget JSON and error message. 4. widgetSummary(): compact one-line summary (type, id, options keys, counts). 5. widgetJsonDump(): pretty-printed JSON for deep inspection in DevPanel. Previously: unknown widgets silently fell through to subwidget rendering. Now: every unrecognized widget is visible in DevPanel with full context.
This commit is contained in:
parent
5ba33b9e18
commit
fe6261598b
@ -27,12 +27,51 @@ import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.bricks.mp.actions.ActionDispatcher
|
||||
import com.bricks.mp.dev.DevLogLevel
|
||||
import com.bricks.mp.dev.DevLogSource
|
||||
import com.bricks.mp.dev.DevLogStore
|
||||
import com.bricks.mp.widgets.*
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonPrimitive
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
|
||||
/**
|
||||
* Serialize a widget to a compact, readable string for dev logging.
|
||||
* Shows widgettype, id, options keys, subwidget count, and bind count.
|
||||
*/
|
||||
private fun widgetSummary(w: BricksWidget): String {
|
||||
val optKeys = w.options.keys.joinToString(", ")
|
||||
val subCount = w.subwidgets.size
|
||||
val bindCount = w.binds.size
|
||||
return buildString {
|
||||
append("widgettype=${w.widgettype}")
|
||||
if (w.id.isNotEmpty()) append(", id=${w.id}")
|
||||
if (optKeys.isNotEmpty()) append(", options=[$optKeys]")
|
||||
if (subCount > 0) append(", subwidgets=$subCount")
|
||||
if (bindCount > 0) append(", binds=$bindCount")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize the full widget JSON for detailed error inspection in DevPanel.
|
||||
*/
|
||||
private fun widgetJsonDump(w: BricksWidget): String {
|
||||
return try {
|
||||
val json = Json { encodeDefaults = true; prettyPrint = true }
|
||||
json.encodeToString(BricksWidget.serializer(), w)
|
||||
} catch (e: Exception) {
|
||||
"Failed to serialize: ${e.message}"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归渲染引擎 - 将 BricksWidget 树渲染为 Compose UI
|
||||
*
|
||||
* Error handling strategy:
|
||||
* - Unknown widgettypes → WARN log with full widget data in DevPanel
|
||||
* - Placeholder widgets → INFO log with widget summary
|
||||
* - urlwidget load failures → ERROR log with widget JSON dump
|
||||
* - RenderPlaceholder and individual render functions handle their own errors
|
||||
*/
|
||||
@Composable
|
||||
fun RenderWidget(
|
||||
@ -41,7 +80,15 @@ fun RenderWidget(
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val resolvedWidget = resolveTemplates(widget)
|
||||
dispatchRender(resolvedWidget, actionDispatcher, modifier)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun dispatchRender(
|
||||
resolvedWidget: BricksWidget,
|
||||
actionDispatcher: ActionDispatcher?,
|
||||
modifier: Modifier
|
||||
) {
|
||||
when (resolvedWidget.widgettype) {
|
||||
// 文本
|
||||
"Text" -> RenderTextWidget(resolvedWidget)
|
||||
@ -94,14 +141,30 @@ fun RenderWidget(
|
||||
"Message" -> RenderMessageWidget(resolvedWidget)
|
||||
"urlwidget" -> RenderUrlWidget(resolvedWidget, actionDispatcher)
|
||||
|
||||
// 默认: 渲染子组件
|
||||
// 默认: 未知 widgettype — 记录日志并尝试渲染子组件
|
||||
else -> {
|
||||
// Log unknown widgettype with full widget data for DevPanel inspection
|
||||
DevLogStore.log(
|
||||
level = DevLogLevel.WARN,
|
||||
message = "Unknown widgettype: ${resolvedWidget.widgettype}",
|
||||
details = "Widget data:\n${widgetJsonDump(resolvedWidget)}",
|
||||
source = DevLogSource.RENDER
|
||||
)
|
||||
if (resolvedWidget.subwidgets.isNotEmpty()) {
|
||||
Column(modifier = modifier) {
|
||||
resolvedWidget.subwidgets.forEach { child ->
|
||||
RenderWidget(child, actionDispatcher)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Show placeholder so the developer can see what was skipped
|
||||
Text(
|
||||
text = "[? ${resolvedWidget.widgettype}]",
|
||||
color = Color(0xFFFFA000),
|
||||
fontSize = 10.sp,
|
||||
fontFamily = androidx.compose.ui.text.font.FontFamily.Monospace,
|
||||
modifier = Modifier.padding(4.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -532,6 +595,13 @@ private fun RenderUrlWidget(widget: BricksWidget, actionDispatcher: ActionDispat
|
||||
}
|
||||
|
||||
if (error != null) {
|
||||
// Log the failure to DevPanel with full widget context
|
||||
DevLogStore.log(
|
||||
level = DevLogLevel.ERROR,
|
||||
message = "urlwidget load failed: $url",
|
||||
details = "Widget data:\n${widgetJsonDump(widget)}\n\nError: $error",
|
||||
source = DevLogSource.RENDER
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth().padding(16.dp),
|
||||
verticalArrangement = Arrangement.Center,
|
||||
@ -696,6 +766,13 @@ private fun RenderMessageWidget(widget: BricksWidget) {
|
||||
|
||||
@Composable
|
||||
private fun RenderPlaceholder(widget: BricksWidget, name: String) {
|
||||
// Log placeholder rendering so developers know which widgets are not yet implemented
|
||||
DevLogStore.log(
|
||||
level = DevLogLevel.INFO,
|
||||
message = "Placeholder rendered: ${widget.widgettype} ($name)",
|
||||
details = "Widget data:\n${widgetSummary(widget)}",
|
||||
source = DevLogSource.RENDER
|
||||
)
|
||||
Text(
|
||||
text = "[${widget.widgettype}: $name - TODO]",
|
||||
modifier = Modifier.padding(8.dp),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user