package cc.unitmesh.devins.ui.compose.agent.knowledge import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.selection.SelectionContainer import androidx.compose.foundation.verticalScroll import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.Tab import androidx.compose.material3.TabRow import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalClipboardManager import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.DialogProperties import cc.unitmesh.agent.tool.impl.docql.DocQLSearchStats import cc.unitmesh.devins.ui.compose.icons.AutoDevComposeIcons import cc.unitmesh.devins.ui.compose.sketch.MarkdownSketchRenderer import cc.unitmesh.devins.ui.compose.sketch.TextBlockRenderer /** * Dialog to display detailed DocQL search results. * * Shows: * - Tab 1: Detailed search results with full formatting * - Tab 2: Search statistics and configuration */ @Composable fun DocQLDetailDialog( keyword: String, stats: DocQLSearchStats, onDismiss: () -> Unit ) { var selectedTab by remember { mutableStateOf(0) } val clipboardManager = LocalClipboardManager.current val tabs = listOf("Results", "Stats", "Full Results") Dialog( onDismissRequest = onDismiss, properties = DialogProperties( dismissOnBackPress = true, dismissOnClickOutside = true, usePlatformDefaultWidth = false ) ) { Surface( modifier = Modifier .widthIn(min = 500.dp, max = 900.dp) .heightIn(min = 400.dp, max = 800.dp) .fillMaxWidth(0.9f) .fillMaxHeight(0.85f), shape = RoundedCornerShape(16.dp), color = MaterialTheme.colorScheme.surface, tonalElevation = 8.dp ) { Column(modifier = Modifier.fillMaxSize()) { // Header Row( modifier = Modifier .fillMaxWidth() .background(MaterialTheme.colorScheme.surfaceVariant) .padding(16.dp), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically ) { Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(8.dp) ) { Icon( imageVector = AutoDevComposeIcons.Search, contentDescription = "DocQL Search", tint = MaterialTheme.colorScheme.primary, modifier = Modifier.size(24.dp) ) Column { Text( text = "DocQL Search Results", style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.Bold, color = MaterialTheme.colorScheme.onSurfaceVariant ) Text( text = "Query: \"$keyword\"", style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.7f) ) } } Row(horizontalArrangement = Arrangement.spacedBy(4.dp)) { IconButton( onClick = { val content = stats.detailedResults ?: "No detailed results available" clipboardManager.setText(AnnotatedString(content)) } ) { Icon( imageVector = AutoDevComposeIcons.ContentCopy, contentDescription = "Copy Results", tint = MaterialTheme.colorScheme.onSurfaceVariant ) } // Close button IconButton(onClick = onDismiss) { Icon( imageVector = AutoDevComposeIcons.Close, contentDescription = "Close" ) } } } // Tabs TabRow( selectedTabIndex = selectedTab, modifier = Modifier.fillMaxWidth() ) { tabs.forEachIndexed { index, title -> Tab( selected = selectedTab == index, onClick = { selectedTab = index }, text = { Row( horizontalArrangement = Arrangement.spacedBy(4.dp), verticalAlignment = Alignment.CenterVertically ) { Icon( imageVector = when (index) { 0 -> AutoDevComposeIcons.Description 1 -> AutoDevComposeIcons.Analytics 2 -> AutoDevComposeIcons.Code else -> AutoDevComposeIcons.Description }, contentDescription = null, modifier = Modifier.size(16.dp) ) Text(title) } } ) } } HorizontalDivider() // Content when (selectedTab) { 0 -> DetailedResultsTab(stats) 1 -> StatsTab(stats) 2 -> FullResultsTab(stats) } } } } } @Composable private fun DetailedResultsTab(stats: DocQLSearchStats) { val scrollState = rememberScrollState() val detailedResults = stats.detailedResults Column( modifier = Modifier .fillMaxSize() .verticalScroll(scrollState) .padding(16.dp) ) { if (detailedResults.isNullOrBlank()) { // No detailed results Surface( color = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.5f), shape = RoundedCornerShape(8.dp), modifier = Modifier.fillMaxWidth() ) { Column( modifier = Modifier.padding(24.dp), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(8.dp) ) { Icon( imageVector = AutoDevComposeIcons.Search, contentDescription = null, tint = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.5f), modifier = Modifier.size(48.dp) ) Text( text = "No detailed results available", style = MaterialTheme.typography.bodyLarge, color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.7f) ) if (stats.resultsAfterRerank == 0) { Text( text = "Try a different search query", style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.5f) ) } } } } else { TextBlockRenderer(detailedResults) } } } @Composable private fun StatsTab(stats: DocQLSearchStats) { val scrollState = rememberScrollState() Column( modifier = Modifier .fillMaxSize() .verticalScroll(scrollState) .padding(16.dp) ) { // Use the existing DocQLStatsSection for consistent display DocQLStatsSection(stats = stats) Spacer(modifier = Modifier.height(16.dp)) // Additional raw stats for debugging Surface( color = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.3f), shape = RoundedCornerShape(8.dp), modifier = Modifier.fillMaxWidth() ) { Column( modifier = Modifier.padding(12.dp), verticalArrangement = Arrangement.spacedBy(8.dp) ) { Text( text = "Search Summary", style = MaterialTheme.typography.labelLarge, fontWeight = FontWeight.Bold, color = MaterialTheme.colorScheme.onSurfaceVariant ) val summaryItems = listOf( "Search Type" to stats.searchType.name, "Documents Searched" to stats.documentsSearched.toString(), "Raw Results" to stats.totalRawResults.toString(), "After Reranking" to stats.resultsAfterRerank.toString(), "Truncated" to if (stats.truncated) "Yes" else "No", "Used Fallback" to if (stats.usedFallback) "Yes" else "No" ) summaryItems.forEach { (label, value) -> Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween ) { Text( text = label, style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.7f) ) Text( text = value, style = MaterialTheme.typography.bodySmall, fontWeight = FontWeight.Medium, color = MaterialTheme.colorScheme.onSurfaceVariant ) } } // Keyword expansion info if available stats.keywordExpansion?.let { kw -> Spacer(modifier = Modifier.height(8.dp)) HorizontalDivider() Spacer(modifier = Modifier.height(8.dp)) Text( text = "Keyword Expansion", style = MaterialTheme.typography.labelLarge, fontWeight = FontWeight.Bold, color = MaterialTheme.colorScheme.onSurfaceVariant ) val kwItems = listOf( "Original Query" to kw.originalQuery, "Strategy Used" to kw.strategyUsed, "Level Used" to kw.levelUsed.toString(), "Level 1 Results" to kw.level1ResultCount.toString(), "Level 2 Results" to kw.level2ResultCount.toString() ) kwItems.forEach { (label, value) -> Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween ) { Text( text = label, style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.7f) ) Text( text = value, style = MaterialTheme.typography.bodySmall, fontWeight = FontWeight.Medium, color = MaterialTheme.colorScheme.onSurfaceVariant ) } } if (kw.primaryKeywords.isNotEmpty()) { Spacer(modifier = Modifier.height(4.dp)) Text( text = "Primary Keywords: ${kw.primaryKeywords.joinToString(", ")}", style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.7f) ) } } } } } } @Composable private fun FullResultsTab(stats: DocQLSearchStats) { val scrollState = rememberScrollState() val fullResults = stats.fullResults Column( modifier = Modifier .fillMaxSize() .verticalScroll(scrollState) .padding(16.dp) ) { if (fullResults.isNullOrBlank()) { // No full results available Surface( color = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.5f), shape = RoundedCornerShape(8.dp), modifier = Modifier.fillMaxWidth() ) { Column( modifier = Modifier.padding(24.dp), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(8.dp) ) { Icon( imageVector = AutoDevComposeIcons.Code, contentDescription = null, tint = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.5f), modifier = Modifier.size(48.dp) ) Text( text = "No full results available", style = MaterialTheme.typography.bodyLarge, color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.7f) ) Text( text = "Full results are only captured for direct DocQL queries", style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.5f) ) } } } else { // Display full unformatted results Surface( color = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.2f), shape = RoundedCornerShape(8.dp), modifier = Modifier.fillMaxWidth() ) { SelectionContainer { Text( text = fullResults, style = MaterialTheme.typography.bodySmall.copy( fontFamily = FontFamily.Monospace ), color = MaterialTheme.colorScheme.onSurface, modifier = Modifier.padding(12.dp) ) } } } } }