package cc.unitmesh.viewer.web.webedit import kotlinx.serialization.Serializable /** * Represents a DOM element from the web page * * @param id Unique identifier for the element * @param tagName HTML tag name (e.g., "div", "span", "button") * @param selector CSS selector to locate this element * @param textContent Text content of the element (truncated if too long) * @param attributes Key attributes of the element (id, class, etc.) * @param boundingBox Bounding box of the element (x, y, width, height) * @param children Child elements (for tree structure) * @param isShadowHost Whether this element has a shadow root attached * @param inShadowRoot Whether this element is inside a shadow root */ @Serializable data class DOMElement( val id: String, val tagName: String, val selector: String, val textContent: String? = null, val attributes: Map = emptyMap(), val boundingBox: BoundingBox? = null, val children: List = emptyList(), val isShadowHost: Boolean = false, val inShadowRoot: Boolean = false ) { /** * Generate a display name for the DOM tree */ fun getDisplayName(): String { val classAttr = attributes["class"]?.split(" ")?.firstOrNull()?.let { ".$it" } ?: "" val idAttr = attributes["id"]?.let { "#$it" } ?: "" val shadowIndicator = if (isShadowHost) " [shadow-host]" else if (inShadowRoot) " [shadow]" else "" val text = textContent?.take(30)?.let { " \"$it\"" } ?: "" return "$tagName$idAttr$classAttr$shadowIndicator$text" } } /** * Bounding box for element positioning */ @Serializable data class BoundingBox( val x: Double, val y: Double, val width: Double, val height: Double ) /** * Message types from WebView to Kotlin */ @Serializable sealed class WebEditMessage { /** * DOM tree updated message */ @Serializable data class DOMTreeUpdated(val root: DOMElement) : WebEditMessage() /** * D2Snap compressed DOM tree updated */ @Serializable data class D2SnapTreeUpdated(val root: D2SnapElement) : WebEditMessage() /** * Accessibility tree updated */ @Serializable data class AccessibilityTreeUpdated(val root: AccessibilityNode) : WebEditMessage() /** * Actionable elements updated (from accessibility tree) */ @Serializable data class ActionableElementsUpdated(val elements: List) : WebEditMessage() /** * Element selected by user */ @Serializable data class ElementSelected(val element: DOMElement) : WebEditMessage() /** * Page loaded event */ @Serializable data class PageLoaded(val url: String, val title: String) : WebEditMessage() /** * Error message */ @Serializable data class Error(val message: String) : WebEditMessage() /** * Page load progress */ @Serializable data class LoadProgress(val progress: Int) : WebEditMessage() /** * DOM changed notification (from MutationObserver) */ @Serializable data class DOMChanged(val mutationCount: Int) : WebEditMessage() /** * Result of an action executed in the browser (click/type/select/etc.) */ @Serializable data class ActionResult( val action: String, val ok: Boolean, val selector: String? = null, val message: String? = null, val id: String? = null ) : WebEditMessage() /** * Screenshot captured from the WebView (for Vision LLM fallback) */ @Serializable data class ScreenshotCaptured( val base64: String? = null, val mimeType: String? = null, val width: Int? = null, val height: Int? = null, val url: String? = null, val title: String? = null, val error: String? = null ) : WebEditMessage() } /** * Commands from Kotlin to WebView */ @Serializable sealed class WebEditCommand { /** * Enable/disable selection mode */ @Serializable data class SetSelectionMode(val enabled: Boolean) : WebEditCommand() /** * Highlight a specific element */ @Serializable data class HighlightElement(val selector: String) : WebEditCommand() /** * Clear all highlights */ @Serializable object ClearHighlights : WebEditCommand() /** * Get DOM tree */ @Serializable object GetDOMTree : WebEditCommand() /** * Scroll to element */ @Serializable data class ScrollToElement(val selector: String) : WebEditCommand() }