package cc.unitmesh.devins.ui.compose.agent.artifact import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.jsonObject import kotlinx.serialization.json.jsonPrimitive /** * JVM-only helper for Artifact WebView <-> Kotlin console bridge. * * Why this exists: * - Streaming uses repeated HTML injections; naive console patching will wrap console.log multiple times, * causing duplicate log lines. This bridge is idempotent (stores original console once on window). * - compose-webview-multiplatform has a known callbackId = -1 spam; we suppress it (same as WebEdit). */ internal object ArtifactConsoleBridgeJvm { const val METHOD_NAME: String = "artifactConsole" /** * Inject console capture script into HTML. * * The injected JS: * - Installs a single console wrapper (idempotent via window.__autodevArtifactConsoleBridgeState) * - Buffers logs until kmpJsBridge is ready, then flushes * - Suppresses noisy kmpJsBridge.onCallback(-1, ...) spam */ fun injectConsoleCapture(html: String): String { val consoleScript = """ """.trimIndent() return when { html.contains("
", ignoreCase = true) -> { html.replaceFirst(Regex("", RegexOption.IGNORE_CASE), "\n$consoleScript\n") } html.contains(" { val bodyRegex = Regex("]*>", RegexOption.IGNORE_CASE) val match = bodyRegex.find(html) if (match != null) { html.replaceFirst(bodyRegex, "${match.value}\n$consoleScript\n") } else { "$consoleScript\n$html" } } html.contains(" { val htmlRegex = Regex("]*>", RegexOption.IGNORE_CASE) val match = htmlRegex.find(html) if (match != null) { html.replaceFirst(htmlRegex, "${match.value}\n$consoleScript\n") } else { "$consoleScript\n$html" } } else -> { "$consoleScript\n$html" } } } /** * Parse params from JsMessage to (level, message). * Supports: * - JSON object string: {"level":"log","message":"..."} * - JSON primitive containing JSON string (double encoded) * - Plain string */ fun parseConsoleParams(params: String): Pair