From 1a521a5d307a618fcac60e1d5a514310c0490cff Mon Sep 17 00:00:00 2001 From: Paul <41129807+Paulanerus@users.noreply.github.com> Date: Wed, 1 Jan 2025 12:55:23 +0100 Subject: [PATCH 01/37] feat: Added pool selection and getAvailablePools to retrieve every pool with indexable fields --- .../dev/paulee/api/data/IDataService.kt | 4 ++- .../dev/paulee/core/data/DataServiceImpl.kt | 30 +++++++++++++++++-- gradle.properties | 4 +-- 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/api/src/main/kotlin/dev/paulee/api/data/IDataService.kt b/api/src/main/kotlin/dev/paulee/api/data/IDataService.kt index 116bbb8..c9f2272 100644 --- a/api/src/main/kotlin/dev/paulee/api/data/IDataService.kt +++ b/api/src/main/kotlin/dev/paulee/api/data/IDataService.kt @@ -9,10 +9,12 @@ interface IDataService : Closeable { fun loadDataPools(path: Path, dataInfo: Set): Int - fun selectDataPool(pool: String) + fun selectDataPool(selection: String) fun getSelectedPool(): String + fun getAvailablePools(): Set + fun getPage(query: String, pageCount: Int): Pair>, Map>>> fun getPageCount(query: String): Pair> diff --git a/core/src/main/kotlin/dev/paulee/core/data/DataServiceImpl.kt b/core/src/main/kotlin/dev/paulee/core/data/DataServiceImpl.kt index 17c677e..4773ef8 100644 --- a/core/src/main/kotlin/dev/paulee/core/data/DataServiceImpl.kt +++ b/core/src/main/kotlin/dev/paulee/core/data/DataServiceImpl.kt @@ -78,7 +78,21 @@ private class DataPool(val indexer: Indexer, dataInfo: RequiresData) { } } - if (defaultIndexField.isNullOrEmpty()) println("${dataInfo.name} has no default index field") + if (this.defaultIndexField.isNullOrEmpty()) { + println("${dataInfo.name} has no default index field.") + + this.fields.filter { it.value } + .entries + .firstOrNull()?.key + .let { + if (it == null) { + println("${dataInfo.name} has no indexable fields.") + } else { + println("'$it' was chosen instead.") + this.defaultIndexField = it + } + } + } } fun search(query: String): IndexSearchResult { @@ -235,12 +249,22 @@ class DataServiceImpl(private val storageProvider: IStorageProvider) : IDataServ return dataPools.size } - override fun selectDataPool(pool: String) { - TODO("Not yet implemented") + override fun selectDataPool(selection: String) { + if (!selection.contains(".")) return + + val (pool, field) = selection.split(".", limit = 2) + + this.currentPool = pool + this.currentField = field } override fun getSelectedPool(): String = "${this.currentPool}.${this.currentField}" + override fun getAvailablePools(): Set = dataPools.filter { it.value.fields.any { it.value } } + .flatMap { entry -> + entry.value.fields.filter { it.value }.map { "${entry.key}.${it.key.substringBefore(".")}" } + }.toSet() + override fun getPage(query: String, pageCount: Int): PageResult { if (this.currentPool == null || this.currentField == null) return Pair(emptyList(), emptyMap()) diff --git a/gradle.properties b/gradle.properties index eebe5b6..89bfe2e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,7 +4,7 @@ kotlin.version=2.1.0 compose.version=1.7.1 lucene.version=10.0.0 -api.version=0.24.0 -core.version=0.24.0 +api.version=0.25.0 +core.version=0.25.0 ui.version=0.14.1 app.version=1.5.0 From 119fe1c6218eeae14e2721769a889b814f6f6b0b Mon Sep 17 00:00:00 2001 From: Paul <41129807+Paulanerus@users.noreply.github.com> Date: Wed, 1 Jan 2025 12:56:22 +0100 Subject: [PATCH 02/37] feat: Added pool selection interface --- gradle.properties | 2 +- .../kotlin/dev/paulee/ui/TextExplorerUI.kt | 38 +++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 89bfe2e..54de9da 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,5 +6,5 @@ lucene.version=10.0.0 api.version=0.25.0 core.version=0.25.0 -ui.version=0.14.1 +ui.version=0.15.0 app.version=1.5.0 diff --git a/ui/src/main/kotlin/dev/paulee/ui/TextExplorerUI.kt b/ui/src/main/kotlin/dev/paulee/ui/TextExplorerUI.kt index be57107..fd79cb3 100644 --- a/ui/src/main/kotlin/dev/paulee/ui/TextExplorerUI.kt +++ b/ui/src/main/kotlin/dev/paulee/ui/TextExplorerUI.kt @@ -2,6 +2,7 @@ package dev.paulee.ui import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.background +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.* @@ -64,6 +65,7 @@ class TextExplorerUI(private val pluginService: IPluginService, private val data var selectedRows by remember { mutableStateOf(setOf>()) } var displayDiffWindow by remember { mutableStateOf(false) } var showTable by remember { mutableStateOf(false) } + var showPopup by remember { mutableStateOf(false) } var isOpened by remember { mutableStateOf(false) } var totalPages by remember { mutableStateOf(0L) } var currentPage by remember { mutableStateOf(0) } @@ -75,6 +77,42 @@ class TextExplorerUI(private val pluginService: IPluginService, private val data MaterialTheme { Box(modifier = Modifier.fillMaxSize()) { + + Box(modifier = Modifier.align(Alignment.TopStart).padding(25.dp)) { + val (pool, field) = dataService.getSelectedPool().split(".", limit = 2) + + var selectedText by remember { mutableStateOf("$pool ($field)") } + + Text(selectedText, modifier = Modifier.padding(2.dp).clickable { showPopup = true }) + + DropdownMenu( + expanded = showPopup, + onDismissRequest = { showPopup = false } + ) { + val menuItems = dataService.getAvailablePools().map { + val (p, f) = it.split(".", limit = 2) + Pair(it, "$p ($f)") + } + menuItems.forEach { item -> + DropdownMenuItem( + onClick = { + dataService.selectDataPool(item.first) + + if (selectedText != item.second) { + text = "" + showTable = false + } + + selectedText = item.second + showPopup = false + } + ) { + Text(item.second) + } + } + } + } + DropDownMenu( modifier = Modifier.align(Alignment.TopEnd), items = listOf("Load Plugin"), clicked = { when (it) { From 9a949245aeef301f2fce12b4d01fd7c5da145c20 Mon Sep 17 00:00:00 2001 From: Paul <41129807+Paulanerus@users.noreply.github.com> Date: Wed, 1 Jan 2025 12:57:43 +0100 Subject: [PATCH 03/37] build: Update app version to 1.6.0 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 54de9da..b227671 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,4 +7,4 @@ lucene.version=10.0.0 api.version=0.25.0 core.version=0.25.0 ui.version=0.15.0 -app.version=1.5.0 +app.version=1.6.0 From c066e90b1e29c6b0dbed25303aa43b6d8f2957df Mon Sep 17 00:00:00 2001 From: Paul <41129807+Paulanerus@users.noreply.github.com> Date: Thu, 2 Jan 2025 21:08:34 +0100 Subject: [PATCH 04/37] refactor: Changed onRowSelect type from set to list with it associated columns TableView cells are also now selectable --- gradle.properties | 2 +- .../kotlin/dev/paulee/ui/TextExplorerUI.kt | 8 +- .../dev/paulee/ui/components/DiffViewer.kt | 4 +- .../dev/paulee/ui/components/TableView.kt | 87 ++++++++++--------- 4 files changed, 54 insertions(+), 47 deletions(-) diff --git a/gradle.properties b/gradle.properties index b227671..575c329 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,5 +6,5 @@ lucene.version=10.0.0 api.version=0.25.0 core.version=0.25.0 -ui.version=0.15.0 +ui.version=0.16.0 app.version=1.6.0 diff --git a/ui/src/main/kotlin/dev/paulee/ui/TextExplorerUI.kt b/ui/src/main/kotlin/dev/paulee/ui/TextExplorerUI.kt index fd79cb3..b3d8d18 100644 --- a/ui/src/main/kotlin/dev/paulee/ui/TextExplorerUI.kt +++ b/ui/src/main/kotlin/dev/paulee/ui/TextExplorerUI.kt @@ -62,7 +62,7 @@ class TextExplorerUI(private val pluginService: IPluginService, private val data @Composable private fun content() { var text by remember { mutableStateOf("") } - var selectedRows by remember { mutableStateOf(setOf>()) } + var selectedRows by remember { mutableStateOf(listOf>()) } var displayDiffWindow by remember { mutableStateOf(false) } var showTable by remember { mutableStateOf(false) } var showPopup by remember { mutableStateOf(false) } @@ -215,7 +215,11 @@ class TextExplorerUI(private val pluginService: IPluginService, private val data columns = header, data = data, links = links, - onRowSelect = { selectedRows = it }, + onRowSelect = { + val mask = arrayOf("") + + selectedRows = it.map { it.filterKeys { it in mask } } + }, clicked = { displayDiffWindow = true }) if (totalPages < 2) return@Column diff --git a/ui/src/main/kotlin/dev/paulee/ui/components/DiffViewer.kt b/ui/src/main/kotlin/dev/paulee/ui/components/DiffViewer.kt index 068939f..f909b66 100644 --- a/ui/src/main/kotlin/dev/paulee/ui/components/DiffViewer.kt +++ b/ui/src/main/kotlin/dev/paulee/ui/components/DiffViewer.kt @@ -13,7 +13,7 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Window @Composable -fun DiffViewerWindow(selectedRows: Set>, onClose: () -> Unit) { +fun DiffViewerWindow(selectedRows: List>, onClose: () -> Unit) { Window(onCloseRequest = onClose, title = "DiffViewer") { MaterialTheme { Column( @@ -22,7 +22,7 @@ fun DiffViewerWindow(selectedRows: Set>, onClose: () -> Unit) { verticalArrangement = Arrangement.Center ) { selectedRows.forEach { - Text(it.joinToString(" ")) + Text(it.entries.joinToString(" ")) } } } diff --git a/ui/src/main/kotlin/dev/paulee/ui/components/TableView.kt b/ui/src/main/kotlin/dev/paulee/ui/components/TableView.kt index 66d607e..e3ae925 100644 --- a/ui/src/main/kotlin/dev/paulee/ui/components/TableView.kt +++ b/ui/src/main/kotlin/dev/paulee/ui/components/TableView.kt @@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.text.selection.SelectionContainer import androidx.compose.material.* import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Delete @@ -30,7 +31,7 @@ fun TableView( columns: List, data: List>, links: Map>>, - onRowSelect: (Set>) -> Unit, + onRowSelect: (List>) -> Unit, clicked: () -> Unit = {}, ) { val scrollState = rememberScrollState() @@ -73,7 +74,7 @@ fun TableView( IconButton( onClick = { selectedRows = emptySet() - onRowSelect(emptySet()) + onRowSelect(emptyList()) }, modifier = Modifier.align(Alignment.CenterStart), enabled = selectedRows.isNotEmpty() ) { Icon(Icons.Default.Delete, contentDescription = "Delete") @@ -143,49 +144,51 @@ fun TableView( LazyColumn(state = verticalScrollState) { items(data.size) { rowIndex -> val row = data[rowIndex] - Row( - modifier = Modifier.fillMaxWidth().clickable { - val selected = if (selectedRows.contains(rowIndex)) { - selectedRows - rowIndex - } else { - selectedRows + rowIndex + SelectionContainer { + Row( + modifier = Modifier.fillMaxWidth().clickable { + val selected = if (selectedRows.contains(rowIndex)) { + selectedRows - rowIndex + } else { + selectedRows + rowIndex + } + selectedRows = selected + onRowSelect(selected.map { rowIdx -> columns.zip(data[rowIdx]).toMap() }) } - selectedRows = selected - onRowSelect(selected.map { data[it] }.toSet()) - } - .background(if (selectedRows.contains(rowIndex)) Color.LightGray else Color.Transparent) - .padding(vertical = 8.dp), - ) { - row.forEachIndexed { colIndex, cell -> - if (hiddenColumns.contains(colIndex)) return@forEachIndexed - - val col = columns[colIndex] - - val link = links[col]?.find { it[col] == cell } - - TooltipArea( - tooltip = { - if (link == null) return@TooltipArea - - Surface( - color = Color.Gray, - shape = RoundedCornerShape(4.dp) - ) { - Text( - text = link.toString(), - modifier = Modifier.padding(10.dp) - ) + .background(if (selectedRows.contains(rowIndex)) Color.LightGray else Color.Transparent) + .padding(vertical = 8.dp), + ) { + row.forEachIndexed { colIndex, cell -> + if (hiddenColumns.contains(colIndex)) return@forEachIndexed + + val col = columns[colIndex] + + val link = links[col]?.find { it[col] == cell } + + TooltipArea( + tooltip = { + if (link == null) return@TooltipArea + + Surface( + color = Color.Gray, + shape = RoundedCornerShape(4.dp) + ) { + Text( + text = link.toString(), + modifier = Modifier.padding(10.dp) + ) + } } + ) { + MarkedText( + modifier = Modifier.width(columnWidths[colIndex]) + .padding(horizontal = 4.dp), + textDecoration = if (link == null) TextDecoration.None else TextDecoration.Underline, + text = cell, + highlights = indexStrings, + color = Color.Green + ) } - ) { - MarkedText( - modifier = Modifier.width(columnWidths[colIndex]) - .padding(horizontal = 4.dp), - textDecoration = if (link == null) TextDecoration.None else TextDecoration.Underline, - text = cell, - highlights = indexStrings, - color = Color.Green - ) } } } From 47e6c5e17a37d58665d531b19195a5ee4d4246bd Mon Sep 17 00:00:00 2001 From: Paul <41129807+Paulanerus@users.noreply.github.com> Date: Thu, 2 Jan 2025 21:14:36 +0100 Subject: [PATCH 05/37] feat: Added awt to compose color ext function --- gradle.properties | 2 +- ui/src/main/kotlin/dev/paulee/ui/Utility.kt | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 575c329..47dc6a9 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,5 +6,5 @@ lucene.version=10.0.0 api.version=0.25.0 core.version=0.25.0 -ui.version=0.16.0 +ui.version=0.16.1 app.version=1.6.0 diff --git a/ui/src/main/kotlin/dev/paulee/ui/Utility.kt b/ui/src/main/kotlin/dev/paulee/ui/Utility.kt index 2f0551b..e339d76 100644 --- a/ui/src/main/kotlin/dev/paulee/ui/Utility.kt +++ b/ui/src/main/kotlin/dev/paulee/ui/Utility.kt @@ -9,6 +9,8 @@ import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.text.withStyle +fun java.awt.Color.toComposeColor() = Color(red, green, blue, alpha) + @Composable fun MarkedText( modifier: Modifier = Modifier, From 54c7f88346f7ddc43ba0e6b97ecdbd755060d77a Mon Sep 17 00:00:00 2001 From: Paul <41129807+Paulanerus@users.noreply.github.com> Date: Thu, 2 Jan 2025 21:17:15 +0100 Subject: [PATCH 06/37] feat: Added new Taggable api interface and ViewFilter annotation --- api/src/main/kotlin/dev/paulee/api/data/Data.kt | 3 +++ api/src/main/kotlin/dev/paulee/api/plugin/Plugin.kt | 8 +++++++- gradle.properties | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/api/src/main/kotlin/dev/paulee/api/data/Data.kt b/api/src/main/kotlin/dev/paulee/api/data/Data.kt index 7096d15..1094079 100644 --- a/api/src/main/kotlin/dev/paulee/api/data/Data.kt +++ b/api/src/main/kotlin/dev/paulee/api/data/Data.kt @@ -68,3 +68,6 @@ annotation class NullValue(val values: Array) @Target(AnnotationTarget.VALUE_PARAMETER) annotation class Link(val clazz: KClass<*>) + +@Target(AnnotationTarget.FUNCTION) +annotation class ViewFilter(val fields: Array) diff --git a/api/src/main/kotlin/dev/paulee/api/plugin/Plugin.kt b/api/src/main/kotlin/dev/paulee/api/plugin/Plugin.kt index 0c43d38..7415b5d 100644 --- a/api/src/main/kotlin/dev/paulee/api/plugin/Plugin.kt +++ b/api/src/main/kotlin/dev/paulee/api/plugin/Plugin.kt @@ -1,5 +1,7 @@ package dev.paulee.api.plugin +import java.awt.Color + @Target(AnnotationTarget.CLASS) annotation class PluginOrder(val order: Int) @@ -8,9 +10,13 @@ annotation class PluginMetadata( val name: String, val version: String = "", val author: String = "", - val description: String = "" + val description: String = "", ) interface IPlugin { fun init() +} + +interface Taggable { + fun tag(field: String, value: String): Map } \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 47dc6a9..96308c6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,7 +4,7 @@ kotlin.version=2.1.0 compose.version=1.7.1 lucene.version=10.0.0 -api.version=0.25.0 +api.version=0.26.0 core.version=0.25.0 ui.version=0.16.1 app.version=1.6.0 From 1a4658cc8e2dacf421712924dceeaaf1fbd86fde Mon Sep 17 00:00:00 2001 From: Paul <41129807+Paulanerus@users.noreply.github.com> Date: Thu, 2 Jan 2025 21:30:16 +0100 Subject: [PATCH 07/37] feat: Added PluginService interfaces as a DiffWindow parameter --- gradle.properties | 2 +- ui/src/main/kotlin/dev/paulee/ui/TextExplorerUI.kt | 8 ++------ ui/src/main/kotlin/dev/paulee/ui/components/DiffViewer.kt | 3 ++- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/gradle.properties b/gradle.properties index 96308c6..1dbd329 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,5 +6,5 @@ lucene.version=10.0.0 api.version=0.26.0 core.version=0.25.0 -ui.version=0.16.1 +ui.version=0.17.0 app.version=1.6.0 diff --git a/ui/src/main/kotlin/dev/paulee/ui/TextExplorerUI.kt b/ui/src/main/kotlin/dev/paulee/ui/TextExplorerUI.kt index b3d8d18..62370cd 100644 --- a/ui/src/main/kotlin/dev/paulee/ui/TextExplorerUI.kt +++ b/ui/src/main/kotlin/dev/paulee/ui/TextExplorerUI.kt @@ -215,11 +215,7 @@ class TextExplorerUI(private val pluginService: IPluginService, private val data columns = header, data = data, links = links, - onRowSelect = { - val mask = arrayOf("") - - selectedRows = it.map { it.filterKeys { it in mask } } - }, + onRowSelect = { selectedRows = it }, clicked = { displayDiffWindow = true }) if (totalPages < 2) return@Column @@ -276,7 +272,7 @@ class TextExplorerUI(private val pluginService: IPluginService, private val data color = Color.LightGray ) - if (displayDiffWindow) DiffViewerWindow(selectedRows) { displayDiffWindow = false } + if (displayDiffWindow) DiffViewerWindow(pluginService, selectedRows) { displayDiffWindow = false } } } } diff --git a/ui/src/main/kotlin/dev/paulee/ui/components/DiffViewer.kt b/ui/src/main/kotlin/dev/paulee/ui/components/DiffViewer.kt index f909b66..22bd17b 100644 --- a/ui/src/main/kotlin/dev/paulee/ui/components/DiffViewer.kt +++ b/ui/src/main/kotlin/dev/paulee/ui/components/DiffViewer.kt @@ -11,9 +11,10 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Window +import dev.paulee.api.plugin.IPluginService @Composable -fun DiffViewerWindow(selectedRows: List>, onClose: () -> Unit) { +fun DiffViewerWindow(pluginService: IPluginService, selectedRows: List>, onClose: () -> Unit) { Window(onCloseRequest = onClose, title = "DiffViewer") { MaterialTheme { Column( From dcb34f3610e1159f92073ba2fc7f013302e1cf0e Mon Sep 17 00:00:00 2001 From: Paul <41129807+Paulanerus@users.noreply.github.com> Date: Thu, 2 Jan 2025 21:31:41 +0100 Subject: [PATCH 08/37] feat: Added support for interacting with the tagging interface --- .../dev/paulee/api/plugin/IPluginService.kt | 5 +++++ .../paulee/core/plugin/PluginServiceImpl.kt | 19 +++++++++++++++---- gradle.properties | 4 ++-- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/api/src/main/kotlin/dev/paulee/api/plugin/IPluginService.kt b/api/src/main/kotlin/dev/paulee/api/plugin/IPluginService.kt index 00f6c5d..7b1fe83 100644 --- a/api/src/main/kotlin/dev/paulee/api/plugin/IPluginService.kt +++ b/api/src/main/kotlin/dev/paulee/api/plugin/IPluginService.kt @@ -1,6 +1,7 @@ package dev.paulee.api.plugin import dev.paulee.api.data.RequiresData +import java.awt.Color import java.nio.file.Path interface IPluginService { @@ -20,4 +21,8 @@ interface IPluginService { fun getAllDataInfos(): Set fun getDataSources(dataInfo: String): Set + + fun tagFields(plugin: IPlugin, field: String, value: String): Map + + fun getFilterMask(plugin: IPlugin): Array } \ No newline at end of file diff --git a/core/src/main/kotlin/dev/paulee/core/plugin/PluginServiceImpl.kt b/core/src/main/kotlin/dev/paulee/core/plugin/PluginServiceImpl.kt index 15565a2..21f7ff6 100644 --- a/core/src/main/kotlin/dev/paulee/core/plugin/PluginServiceImpl.kt +++ b/core/src/main/kotlin/dev/paulee/core/plugin/PluginServiceImpl.kt @@ -2,11 +2,10 @@ package dev.paulee.core.plugin import dev.paulee.api.data.DataSource import dev.paulee.api.data.RequiresData -import dev.paulee.api.plugin.IPlugin -import dev.paulee.api.plugin.IPluginService -import dev.paulee.api.plugin.PluginMetadata -import dev.paulee.api.plugin.PluginOrder +import dev.paulee.api.data.ViewFilter +import dev.paulee.api.plugin.* import dev.paulee.core.normalizeDataSource +import java.awt.Color import java.net.URLClassLoader import java.nio.file.Path import java.util.jar.JarFile @@ -15,6 +14,7 @@ import kotlin.io.path.extension import kotlin.io.path.isDirectory import kotlin.io.path.walk import kotlin.reflect.full.findAnnotation +import kotlin.reflect.full.functions class PluginServiceImpl : IPluginService { @@ -79,6 +79,17 @@ class PluginServiceImpl : IPluginService { return dataSources } + override fun tagFields(plugin: IPlugin, field: String, value: String): Map = + (plugin as? Taggable)?.tag(field, value) ?: emptyMap() + + override fun getFilterMask(plugin: IPlugin): Array { + val taggable = plugin as? Taggable ?: return emptyArray() + + val func = taggable::class.functions.find { it.name == "tag" } ?: return emptyArray() + + return func.findAnnotation()?.fields ?: emptyArray() + } + private fun getPluginEntryPoint(path: Path): String? = JarFile(path.toFile()).use { return it.manifest.mainAttributes.getValue("Main-Class") } diff --git a/gradle.properties b/gradle.properties index 1dbd329..dc86351 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,7 +4,7 @@ kotlin.version=2.1.0 compose.version=1.7.1 lucene.version=10.0.0 -api.version=0.26.0 -core.version=0.25.0 +api.version=0.27.0 +core.version=0.26.0 ui.version=0.17.0 app.version=1.6.0 From 780e92664e2de180e60f686d31c81e0777bec12c Mon Sep 17 00:00:00 2001 From: Paul <41129807+Paulanerus@users.noreply.github.com> Date: Thu, 2 Jan 2025 21:38:03 +0100 Subject: [PATCH 09/37] feat: Added requirement to open pool selection drop down --- gradle.properties | 2 +- ui/src/main/kotlin/dev/paulee/ui/TextExplorerUI.kt | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index dc86351..93e0e90 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,5 +6,5 @@ lucene.version=10.0.0 api.version=0.27.0 core.version=0.26.0 -ui.version=0.17.0 +ui.version=0.17.1 app.version=1.6.0 diff --git a/ui/src/main/kotlin/dev/paulee/ui/TextExplorerUI.kt b/ui/src/main/kotlin/dev/paulee/ui/TextExplorerUI.kt index 62370cd..c0b869c 100644 --- a/ui/src/main/kotlin/dev/paulee/ui/TextExplorerUI.kt +++ b/ui/src/main/kotlin/dev/paulee/ui/TextExplorerUI.kt @@ -83,7 +83,11 @@ class TextExplorerUI(private val pluginService: IPluginService, private val data var selectedText by remember { mutableStateOf("$pool ($field)") } - Text(selectedText, modifier = Modifier.padding(2.dp).clickable { showPopup = true }) + Text( + selectedText, modifier = Modifier.padding(2.dp).then( + if (dataService.getAvailablePools().size > 1) Modifier.clickable { showPopup = true } + else Modifier + )) DropdownMenu( expanded = showPopup, From 0a883a435a58e79e169692ac1b7013673b89752a Mon Sep 17 00:00:00 2001 From: Paul <41129807+Paulanerus@users.noreply.github.com> Date: Sun, 5 Jan 2025 18:43:55 +0100 Subject: [PATCH 10/37] build: Added kotlin reflect dependency --- ui/build.gradle.kts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ui/build.gradle.kts b/ui/build.gradle.kts index b538d5a..47b76e7 100644 --- a/ui/build.gradle.kts +++ b/ui/build.gradle.kts @@ -14,6 +14,8 @@ repositories { } dependencies { + implementation(kotlin("reflect")) + implementation(project(":api")) testImplementation(kotlin("test")) From 00fa4783fc6d47bb72e9e30d33586abacf0bad55 Mon Sep 17 00:00:00 2001 From: Paul <41129807+Paulanerus@users.noreply.github.com> Date: Sun, 5 Jan 2025 18:44:22 +0100 Subject: [PATCH 11/37] feat: Added Config --- ui/src/main/kotlin/dev/paulee/ui/Config.kt | 65 ++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 ui/src/main/kotlin/dev/paulee/ui/Config.kt diff --git a/ui/src/main/kotlin/dev/paulee/ui/Config.kt b/ui/src/main/kotlin/dev/paulee/ui/Config.kt new file mode 100644 index 0000000..f29a0ea --- /dev/null +++ b/ui/src/main/kotlin/dev/paulee/ui/Config.kt @@ -0,0 +1,65 @@ +package dev.paulee.ui + +import java.nio.file.Path +import kotlin.io.path.Path +import kotlin.io.path.bufferedReader +import kotlin.io.path.bufferedWriter +import kotlin.reflect.KMutableProperty +import kotlin.reflect.KVisibility +import kotlin.reflect.full.memberProperties + +object Config { + + var noWidthRestriction = false + + private var configFile = "config" + + private var configPath = Path(configFile) + + fun save() { + this.configPath.bufferedWriter().use { writer -> + + this::class.memberProperties + .filter { it.visibility == KVisibility.PUBLIC && it is KMutableProperty<*> } + .forEach { + val value = it.getter.call(this) + + writer.write("${it.name} = $value\n") + writer.newLine() + } + } + } + + fun load(path: Path) { + this.configPath = path.resolve(configFile) + + this.configPath.bufferedReader().useLines { lines -> + lines.filter { it.contains("=") }.forEach { + val (field, value) = it.split("=", limit = 2).map { it.trim() } + + val member = this::class.memberProperties.find { it.name == field } + + if (member != null && member is KMutableProperty<*>) { + val converted: Any = when (member.returnType.classifier) { + Boolean::class -> value.toBooleanStrictOrNull() ?: false + Short::class -> value.toShortOrNull() ?: 0 + Int::class -> value.toIntOrNull() ?: 0 + Long::class -> value.toLongOrNull() ?: 0L + Float::class -> value.toFloatOrNull() ?: 0f + Double::class -> value.toDoubleOrNull() ?: 0 + else -> value + } + + runCatching { + member.setter.call( + this, + converted + ) + }.onFailure { e -> println("Failed to set value for $field (${e.message}).") } + } else { + //TODO + } + } + } + } +} \ No newline at end of file From 60151885cf47c96b281215b593e06432832c1e5f Mon Sep 17 00:00:00 2001 From: Paul <41129807+Paulanerus@users.noreply.github.com> Date: Sun, 5 Jan 2025 18:44:46 +0100 Subject: [PATCH 12/37] build: Bump version --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 93e0e90..490422c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,5 +6,5 @@ lucene.version=10.0.0 api.version=0.27.0 core.version=0.26.0 -ui.version=0.17.1 +ui.version=0.18.0 app.version=1.6.0 From ab51770a55c31209a13959db2a99ac8c18d754de Mon Sep 17 00:00:00 2001 From: Paul <41129807+Paulanerus@users.noreply.github.com> Date: Sun, 5 Jan 2025 18:46:12 +0100 Subject: [PATCH 13/37] feat: Added width limit toggle --- gradle.properties | 2 +- ui/src/main/kotlin/dev/paulee/ui/TextExplorerUI.kt | 13 +++++++++++-- .../kotlin/dev/paulee/ui/components/TableView.kt | 8 ++++++-- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/gradle.properties b/gradle.properties index 490422c..5067406 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,5 +6,5 @@ lucene.version=10.0.0 api.version=0.27.0 core.version=0.26.0 -ui.version=0.18.0 +ui.version=0.18.1 app.version=1.6.0 diff --git a/ui/src/main/kotlin/dev/paulee/ui/TextExplorerUI.kt b/ui/src/main/kotlin/dev/paulee/ui/TextExplorerUI.kt index c0b869c..704c73b 100644 --- a/ui/src/main/kotlin/dev/paulee/ui/TextExplorerUI.kt +++ b/ui/src/main/kotlin/dev/paulee/ui/TextExplorerUI.kt @@ -28,6 +28,7 @@ import dev.paulee.ui.components.DiffViewerWindow import dev.paulee.ui.components.DropDownMenu import dev.paulee.ui.components.FileDialog import dev.paulee.ui.components.TableView +import dev.paulee.ui.components.widthLimitWrapper import java.nio.file.Path import kotlin.io.path.* @@ -50,8 +51,9 @@ class TextExplorerUI(private val pluginService: IPluginService, private val data init { if (!pluginsDir.exists()) pluginsDir.createDirectories() - this.pluginService.loadFromDirectory(pluginsDir) + Config.load(appDir) + this.pluginService.loadFromDirectory(pluginsDir) this.pluginService.initAll() val size = this.dataService.loadDataPools(dataDir, this.pluginService.getAllDataInfos()) @@ -118,9 +120,15 @@ class TextExplorerUI(private val pluginService: IPluginService, private val data } DropDownMenu( - modifier = Modifier.align(Alignment.TopEnd), items = listOf("Load Plugin"), clicked = { + modifier = Modifier.align(Alignment.TopEnd), + items = listOf("Load Plugin", "Width Limit"), + clicked = { when (it) { "Load Plugin" -> isOpened = true + "Width Limit" -> { + Config.noWidthRestriction = !Config.noWidthRestriction + widthLimitWrapper = !widthLimitWrapper + } } }) @@ -287,6 +295,7 @@ class TextExplorerUI(private val pluginService: IPluginService, private val data Window(title = "TextExplorer", state = windowState, onCloseRequest = { dataService.close() + Config.save() exitApplication() }) { content() diff --git a/ui/src/main/kotlin/dev/paulee/ui/components/TableView.kt b/ui/src/main/kotlin/dev/paulee/ui/components/TableView.kt index e3ae925..f4249ad 100644 --- a/ui/src/main/kotlin/dev/paulee/ui/components/TableView.kt +++ b/ui/src/main/kotlin/dev/paulee/ui/components/TableView.kt @@ -21,8 +21,11 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.rememberTextMeasurer import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.unit.dp +import dev.paulee.ui.Config import dev.paulee.ui.MarkedText +var widthLimitWrapper by mutableStateOf(Config.noWidthRestriction) + @OptIn(ExperimentalFoundationApi::class) @Composable fun TableView( @@ -46,7 +49,7 @@ fun TableView( val headerTextStyle = LocalTextStyle.current.copy(fontWeight = FontWeight.Bold) val cellTextStyle = LocalTextStyle.current - val columnWidths = remember(columns, data) { + val columnWidths = remember(columns, data, widthLimitWrapper) { columns.mapIndexed { colIndex, colName -> val headerWidthPx = textMeasurer.measure( text = AnnotatedString(colName), @@ -64,7 +67,8 @@ fun TableView( val maxDataWidth = with(density) { maxDataWidthPx.toDp() } - minOf(maxOf(headerWidth, maxDataWidth) + 16.dp, 700.dp) + if(Config.noWidthRestriction) maxOf(headerWidth, maxDataWidth) + 16.dp + else minOf(maxOf(headerWidth, maxDataWidth) + 16.dp, 700.dp) } } From 05550d746d92962e402c7e5b6923a5d5436f95c7 Mon Sep 17 00:00:00 2001 From: Paulanerus <41129807+Paulanerus@users.noreply.github.com> Date: Mon, 6 Jan 2025 09:11:01 +0100 Subject: [PATCH 14/37] fix: Fixed crash caused by a missing config file on load --- gradle.properties | 2 +- ui/src/main/kotlin/dev/paulee/ui/Config.kt | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 5067406..2185d08 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,5 +6,5 @@ lucene.version=10.0.0 api.version=0.27.0 core.version=0.26.0 -ui.version=0.18.1 +ui.version=0.18.2 app.version=1.6.0 diff --git a/ui/src/main/kotlin/dev/paulee/ui/Config.kt b/ui/src/main/kotlin/dev/paulee/ui/Config.kt index f29a0ea..6e34c4f 100644 --- a/ui/src/main/kotlin/dev/paulee/ui/Config.kt +++ b/ui/src/main/kotlin/dev/paulee/ui/Config.kt @@ -4,6 +4,7 @@ import java.nio.file.Path import kotlin.io.path.Path import kotlin.io.path.bufferedReader import kotlin.io.path.bufferedWriter +import kotlin.io.path.notExists import kotlin.reflect.KMutableProperty import kotlin.reflect.KVisibility import kotlin.reflect.full.memberProperties @@ -33,6 +34,8 @@ object Config { fun load(path: Path) { this.configPath = path.resolve(configFile) + if(this.configPath.notExists()) return + this.configPath.bufferedReader().useLines { lines -> lines.filter { it.contains("=") }.forEach { val (field, value) = it.split("=", limit = 2).map { it.trim() } From ffbfa96616e9cc5204d9a9e5c51d779f8e351951 Mon Sep 17 00:00:00 2001 From: Paulanerus <41129807+Paulanerus@users.noreply.github.com> Date: Mon, 6 Jan 2025 09:13:07 +0100 Subject: [PATCH 15/37] refactor: Changed selected pool logic --- gradle.properties | 2 +- .../kotlin/dev/paulee/ui/TextExplorerUI.kt | 39 ++++++++++++------- 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/gradle.properties b/gradle.properties index 2185d08..ddf6583 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,5 +6,5 @@ lucene.version=10.0.0 api.version=0.27.0 core.version=0.26.0 -ui.version=0.18.2 +ui.version=0.18.3 app.version=1.6.0 diff --git a/ui/src/main/kotlin/dev/paulee/ui/TextExplorerUI.kt b/ui/src/main/kotlin/dev/paulee/ui/TextExplorerUI.kt index 704c73b..1105dd6 100644 --- a/ui/src/main/kotlin/dev/paulee/ui/TextExplorerUI.kt +++ b/ui/src/main/kotlin/dev/paulee/ui/TextExplorerUI.kt @@ -24,11 +24,7 @@ import androidx.compose.ui.window.application import androidx.compose.ui.window.rememberWindowState import dev.paulee.api.data.IDataService import dev.paulee.api.plugin.IPluginService -import dev.paulee.ui.components.DiffViewerWindow -import dev.paulee.ui.components.DropDownMenu -import dev.paulee.ui.components.FileDialog -import dev.paulee.ui.components.TableView -import dev.paulee.ui.components.widthLimitWrapper +import dev.paulee.ui.components.* import java.nio.file.Path import kotlin.io.path.* @@ -48,6 +44,8 @@ class TextExplorerUI(private val pluginService: IPluginService, private val data } }" + private var poolSelected by mutableStateOf(false) + init { if (!pluginsDir.exists()) pluginsDir.createDirectories() @@ -72,6 +70,13 @@ class TextExplorerUI(private val pluginService: IPluginService, private val data var totalPages by remember { mutableStateOf(0L) } var currentPage by remember { mutableStateOf(0) } + var selectedText = remember(this.poolSelected) { + val (pool, field) = dataService.getSelectedPool().split(".", limit = 2) + + if (pool == "null") "No source available" + else "$pool ($field)" + } + var header by remember { mutableStateOf(listOf()) } var indexStrings by remember { mutableStateOf(emptySet()) } var data by remember { mutableStateOf(listOf>()) } @@ -79,14 +84,10 @@ class TextExplorerUI(private val pluginService: IPluginService, private val data MaterialTheme { Box(modifier = Modifier.fillMaxSize()) { - Box(modifier = Modifier.align(Alignment.TopStart).padding(25.dp)) { - val (pool, field) = dataService.getSelectedPool().split(".", limit = 2) - - var selectedText by remember { mutableStateOf("$pool ($field)") } - Text( - selectedText, modifier = Modifier.padding(2.dp).then( + selectedText, + modifier = Modifier.padding(2.dp).then( if (dataService.getAvailablePools().size > 1) Modifier.clickable { showPopup = true } else Modifier )) @@ -103,6 +104,7 @@ class TextExplorerUI(private val pluginService: IPluginService, private val data DropdownMenuItem( onClick = { dataService.selectDataPool(item.first) + poolSelected = !poolSelected if (selectedText != item.second) { text = "" @@ -316,8 +318,19 @@ class TextExplorerUI(private val pluginService: IPluginService, private val data this.pluginService.getDataInfo(plugin)?.let { if (it.sources.isEmpty()) return@let - if (this.dataService.createDataPool(it, dataDir)) println("Created data pool for ${it.name}") - else println("Failed to create data pool for ${it.name}") + val poolsEmpty = this.dataService.getAvailablePools().isEmpty() + + if (this.dataService.createDataPool(it, dataDir)) { + println("Created data pool for ${it.name}") + + this.dataService.getAvailablePools().firstOrNull()?.let { + if (!poolsEmpty) return@let + + this.dataService.selectDataPool(it) + this.poolSelected = !this.poolSelected + } + + } else println("Failed to create data pool for ${it.name}") } return true } From 8869e0f206036df740c4568df87400b79ed7445e Mon Sep 17 00:00:00 2001 From: Paulanerus <41129807+Paulanerus@users.noreply.github.com> Date: Mon, 6 Jan 2025 09:53:16 +0100 Subject: [PATCH 16/37] feat: Added optional 'global' parameter for view filter --- api/src/main/kotlin/dev/paulee/api/data/Data.kt | 2 +- gradle.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api/src/main/kotlin/dev/paulee/api/data/Data.kt b/api/src/main/kotlin/dev/paulee/api/data/Data.kt index 1094079..814a0ca 100644 --- a/api/src/main/kotlin/dev/paulee/api/data/Data.kt +++ b/api/src/main/kotlin/dev/paulee/api/data/Data.kt @@ -70,4 +70,4 @@ annotation class NullValue(val values: Array) annotation class Link(val clazz: KClass<*>) @Target(AnnotationTarget.FUNCTION) -annotation class ViewFilter(val fields: Array) +annotation class ViewFilter(val fields: Array, val global: Boolean = true) diff --git a/gradle.properties b/gradle.properties index ddf6583..b519bb9 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,7 +4,7 @@ kotlin.version=2.1.0 compose.version=1.7.1 lucene.version=10.0.0 -api.version=0.27.0 +api.version=0.27.1 core.version=0.26.0 ui.version=0.18.3 app.version=1.6.0 From 4e2ae9a3d32b054972b75f7b33a8735110b593a4 Mon Sep 17 00:00:00 2001 From: Paulanerus <41129807+Paulanerus@users.noreply.github.com> Date: Mon, 6 Jan 2025 09:54:33 +0100 Subject: [PATCH 17/37] refactor: Changed return type to the actual view filter object --- .../main/kotlin/dev/paulee/api/plugin/IPluginService.kt | 3 ++- .../kotlin/dev/paulee/core/plugin/PluginServiceImpl.kt | 8 ++++---- gradle.properties | 4 ++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/api/src/main/kotlin/dev/paulee/api/plugin/IPluginService.kt b/api/src/main/kotlin/dev/paulee/api/plugin/IPluginService.kt index 7b1fe83..fabbd49 100644 --- a/api/src/main/kotlin/dev/paulee/api/plugin/IPluginService.kt +++ b/api/src/main/kotlin/dev/paulee/api/plugin/IPluginService.kt @@ -1,6 +1,7 @@ package dev.paulee.api.plugin import dev.paulee.api.data.RequiresData +import dev.paulee.api.data.ViewFilter import java.awt.Color import java.nio.file.Path @@ -24,5 +25,5 @@ interface IPluginService { fun tagFields(plugin: IPlugin, field: String, value: String): Map - fun getFilterMask(plugin: IPlugin): Array + fun getViewFilter(plugin: IPlugin): ViewFilter? } \ No newline at end of file diff --git a/core/src/main/kotlin/dev/paulee/core/plugin/PluginServiceImpl.kt b/core/src/main/kotlin/dev/paulee/core/plugin/PluginServiceImpl.kt index 21f7ff6..d55f41a 100644 --- a/core/src/main/kotlin/dev/paulee/core/plugin/PluginServiceImpl.kt +++ b/core/src/main/kotlin/dev/paulee/core/plugin/PluginServiceImpl.kt @@ -82,12 +82,12 @@ class PluginServiceImpl : IPluginService { override fun tagFields(plugin: IPlugin, field: String, value: String): Map = (plugin as? Taggable)?.tag(field, value) ?: emptyMap() - override fun getFilterMask(plugin: IPlugin): Array { - val taggable = plugin as? Taggable ?: return emptyArray() + override fun getViewFilter(plugin: IPlugin): ViewFilter? { + val taggable = plugin as? Taggable ?: return null - val func = taggable::class.functions.find { it.name == "tag" } ?: return emptyArray() + val func = taggable::class.functions.find { it.name == "tag" } ?: return null - return func.findAnnotation()?.fields ?: emptyArray() + return func.findAnnotation() } private fun getPluginEntryPoint(path: Path): String? = diff --git a/gradle.properties b/gradle.properties index b519bb9..ed0a50a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,7 +4,7 @@ kotlin.version=2.1.0 compose.version=1.7.1 lucene.version=10.0.0 -api.version=0.27.1 -core.version=0.26.0 +api.version=0.28.0 +core.version=0.27.0 ui.version=0.18.3 app.version=1.6.0 From be66776779266bf45dd665d0ca3a93f09ea2b400 Mon Sep 17 00:00:00 2001 From: Paulanerus <41129807+Paulanerus@users.noreply.github.com> Date: Mon, 6 Jan 2025 09:55:43 +0100 Subject: [PATCH 18/37] feat: Added selected pool as a DiffViewer Window parameter --- gradle.properties | 2 +- .../kotlin/dev/paulee/ui/TextExplorerUI.kt | 2 +- .../dev/paulee/ui/components/DiffViewer.kt | 31 ++++++++++++------- 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/gradle.properties b/gradle.properties index ed0a50a..f342b2b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,5 +6,5 @@ lucene.version=10.0.0 api.version=0.28.0 core.version=0.27.0 -ui.version=0.18.3 +ui.version=0.18.4 app.version=1.6.0 diff --git a/ui/src/main/kotlin/dev/paulee/ui/TextExplorerUI.kt b/ui/src/main/kotlin/dev/paulee/ui/TextExplorerUI.kt index 1105dd6..4167bd7 100644 --- a/ui/src/main/kotlin/dev/paulee/ui/TextExplorerUI.kt +++ b/ui/src/main/kotlin/dev/paulee/ui/TextExplorerUI.kt @@ -286,7 +286,7 @@ class TextExplorerUI(private val pluginService: IPluginService, private val data color = Color.LightGray ) - if (displayDiffWindow) DiffViewerWindow(pluginService, selectedRows) { displayDiffWindow = false } + if (displayDiffWindow) DiffViewerWindow(pluginService, dataService.getSelectedPool(), selectedRows) { displayDiffWindow = false } } } } diff --git a/ui/src/main/kotlin/dev/paulee/ui/components/DiffViewer.kt b/ui/src/main/kotlin/dev/paulee/ui/components/DiffViewer.kt index 22bd17b..97f21e6 100644 --- a/ui/src/main/kotlin/dev/paulee/ui/components/DiffViewer.kt +++ b/ui/src/main/kotlin/dev/paulee/ui/components/DiffViewer.kt @@ -1,9 +1,6 @@ package dev.paulee.ui.components -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.* import androidx.compose.material.MaterialTheme import androidx.compose.material.Text import androidx.compose.runtime.Composable @@ -14,16 +11,26 @@ import androidx.compose.ui.window.Window import dev.paulee.api.plugin.IPluginService @Composable -fun DiffViewerWindow(pluginService: IPluginService, selectedRows: List>, onClose: () -> Unit) { +fun DiffViewerWindow( + pluginService: IPluginService, + selected: String, + selectedRows: List>, + onClose: () -> Unit +) { Window(onCloseRequest = onClose, title = "DiffViewer") { MaterialTheme { - Column( - modifier = Modifier.fillMaxSize().padding(16.dp), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center - ) { - selectedRows.forEach { - Text(it.entries.joinToString(" ")) + val associatedPlugins = pluginService.getPlugins() + .filter { pluginService.getDataInfo(it)?.name == selected.substringBefore(".") } + + Box(modifier = Modifier.fillMaxSize()) { + Column( + modifier = Modifier.padding(16.dp).align(Alignment.Center), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center + ) { + selectedRows.forEach { + Text(it.entries.joinToString(" ")) + } } } } From 8d4b970fa84e5f96e9c3920dd680cf00cf9c0f9b Mon Sep 17 00:00:00 2001 From: Paul <41129807+Paulanerus@users.noreply.github.com> Date: Mon, 6 Jan 2025 16:56:13 +0100 Subject: [PATCH 19/37] refactor: Merged highlights and color param into a map --- gradle.properties | 2 +- ui/src/main/kotlin/dev/paulee/ui/Utility.kt | 7 +++---- .../dev/paulee/ui/components/DiffViewer.kt | 18 +++++++++++++----- .../dev/paulee/ui/components/TableView.kt | 5 ++--- 4 files changed, 19 insertions(+), 13 deletions(-) diff --git a/gradle.properties b/gradle.properties index f342b2b..d1c8736 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,5 +6,5 @@ lucene.version=10.0.0 api.version=0.28.0 core.version=0.27.0 -ui.version=0.18.4 +ui.version=0.18.5 app.version=1.6.0 diff --git a/ui/src/main/kotlin/dev/paulee/ui/Utility.kt b/ui/src/main/kotlin/dev/paulee/ui/Utility.kt index e339d76..19ab788 100644 --- a/ui/src/main/kotlin/dev/paulee/ui/Utility.kt +++ b/ui/src/main/kotlin/dev/paulee/ui/Utility.kt @@ -16,19 +16,18 @@ fun MarkedText( modifier: Modifier = Modifier, textDecoration: TextDecoration = TextDecoration.None, text: String, - highlights: Set, - color: Color = Color.Blue, + highlights: Map, ) { val annotatedString = buildAnnotatedString { var currentIndex = 0 - highlights.forEach { highlight -> + highlights.keys.forEach { highlight -> val startIndex = text.indexOf(highlight, currentIndex) if (startIndex != -1) { append(text.substring(currentIndex, startIndex)) - withStyle(style = SpanStyle(background = color.copy(alpha = 0.3f))) { + withStyle(style = SpanStyle(background = (highlights[highlight] ?: Color.Blue).copy(alpha = 0.3f))) { append(highlight) } diff --git a/ui/src/main/kotlin/dev/paulee/ui/components/DiffViewer.kt b/ui/src/main/kotlin/dev/paulee/ui/components/DiffViewer.kt index 97f21e6..fbe5418 100644 --- a/ui/src/main/kotlin/dev/paulee/ui/components/DiffViewer.kt +++ b/ui/src/main/kotlin/dev/paulee/ui/components/DiffViewer.kt @@ -9,19 +9,25 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Window import dev.paulee.api.plugin.IPluginService +import dev.paulee.api.plugin.Taggable @Composable fun DiffViewerWindow( pluginService: IPluginService, selected: String, selectedRows: List>, - onClose: () -> Unit + onClose: () -> Unit, ) { + val associatedPlugins = pluginService.getPlugins() + .filter { pluginService.getDataInfo(it)?.name == selected.substringBefore(".") } + + val tagPlugins = associatedPlugins.mapNotNull { it as? Taggable } + + val viewFilter = associatedPlugins.mapNotNull { pluginService.getViewFilter(it) }.filter { it.global } + .flatMap { it.fields.toList() }.toSet() + Window(onCloseRequest = onClose, title = "DiffViewer") { MaterialTheme { - val associatedPlugins = pluginService.getPlugins() - .filter { pluginService.getDataInfo(it)?.name == selected.substringBefore(".") } - Box(modifier = Modifier.fillMaxSize()) { Column( modifier = Modifier.padding(16.dp).align(Alignment.Center), @@ -29,7 +35,9 @@ fun DiffViewerWindow( verticalArrangement = Arrangement.Center ) { selectedRows.forEach { - Text(it.entries.joinToString(" ")) + val text = it.entries.joinToString(" ") { if (it.key in viewFilter) it.value else "" } + + Text(text) } } } diff --git a/ui/src/main/kotlin/dev/paulee/ui/components/TableView.kt b/ui/src/main/kotlin/dev/paulee/ui/components/TableView.kt index f4249ad..be4d326 100644 --- a/ui/src/main/kotlin/dev/paulee/ui/components/TableView.kt +++ b/ui/src/main/kotlin/dev/paulee/ui/components/TableView.kt @@ -67,7 +67,7 @@ fun TableView( val maxDataWidth = with(density) { maxDataWidthPx.toDp() } - if(Config.noWidthRestriction) maxOf(headerWidth, maxDataWidth) + 16.dp + if (Config.noWidthRestriction) maxOf(headerWidth, maxDataWidth) + 16.dp else minOf(maxOf(headerWidth, maxDataWidth) + 16.dp, 700.dp) } } @@ -189,8 +189,7 @@ fun TableView( .padding(horizontal = 4.dp), textDecoration = if (link == null) TextDecoration.None else TextDecoration.Underline, text = cell, - highlights = indexStrings, - color = Color.Green + highlights = indexStrings.associate { it to Color.Green } ) } } From 20797ac6b5d554c7ba1d77dcd5e7493945f6652c Mon Sep 17 00:00:00 2001 From: Paul <41129807+Paulanerus@users.noreply.github.com> Date: Tue, 7 Jan 2025 11:40:05 +0100 Subject: [PATCH 20/37] feat: Added name fielt to ViewFilter annotation --- api/src/main/kotlin/dev/paulee/api/data/Data.kt | 2 +- gradle.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api/src/main/kotlin/dev/paulee/api/data/Data.kt b/api/src/main/kotlin/dev/paulee/api/data/Data.kt index 814a0ca..4960a80 100644 --- a/api/src/main/kotlin/dev/paulee/api/data/Data.kt +++ b/api/src/main/kotlin/dev/paulee/api/data/Data.kt @@ -70,4 +70,4 @@ annotation class NullValue(val values: Array) annotation class Link(val clazz: KClass<*>) @Target(AnnotationTarget.FUNCTION) -annotation class ViewFilter(val fields: Array, val global: Boolean = true) +annotation class ViewFilter(val name: String, val fields: Array, val global: Boolean = true) diff --git a/gradle.properties b/gradle.properties index d1c8736..fb39d42 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,7 +4,7 @@ kotlin.version=2.1.0 compose.version=1.7.1 lucene.version=10.0.0 -api.version=0.28.0 +api.version=0.29.0 core.version=0.27.0 ui.version=0.18.5 app.version=1.6.0 From 54e6e4170e10b2a532da1b4607ade842b84b7bf4 Mon Sep 17 00:00:00 2001 From: Paul <41129807+Paulanerus@users.noreply.github.com> Date: Tue, 7 Jan 2025 11:41:30 +0100 Subject: [PATCH 21/37] feat: Introduced Tag with a name and color as the tag map value --- api/src/main/kotlin/dev/paulee/api/plugin/IPluginService.kt | 3 +-- api/src/main/kotlin/dev/paulee/api/plugin/Plugin.kt | 4 +++- .../main/kotlin/dev/paulee/core/plugin/PluginServiceImpl.kt | 3 +-- gradle.properties | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/api/src/main/kotlin/dev/paulee/api/plugin/IPluginService.kt b/api/src/main/kotlin/dev/paulee/api/plugin/IPluginService.kt index fabbd49..665f1a6 100644 --- a/api/src/main/kotlin/dev/paulee/api/plugin/IPluginService.kt +++ b/api/src/main/kotlin/dev/paulee/api/plugin/IPluginService.kt @@ -2,7 +2,6 @@ package dev.paulee.api.plugin import dev.paulee.api.data.RequiresData import dev.paulee.api.data.ViewFilter -import java.awt.Color import java.nio.file.Path interface IPluginService { @@ -23,7 +22,7 @@ interface IPluginService { fun getDataSources(dataInfo: String): Set - fun tagFields(plugin: IPlugin, field: String, value: String): Map + fun tagFields(plugin: IPlugin, field: String, value: String): Map fun getViewFilter(plugin: IPlugin): ViewFilter? } \ No newline at end of file diff --git a/api/src/main/kotlin/dev/paulee/api/plugin/Plugin.kt b/api/src/main/kotlin/dev/paulee/api/plugin/Plugin.kt index 7415b5d..2a538c6 100644 --- a/api/src/main/kotlin/dev/paulee/api/plugin/Plugin.kt +++ b/api/src/main/kotlin/dev/paulee/api/plugin/Plugin.kt @@ -17,6 +17,8 @@ interface IPlugin { fun init() } +data class Tag(val name: String, val color: Color) + interface Taggable { - fun tag(field: String, value: String): Map + fun tag(field: String, value: String): Map } \ No newline at end of file diff --git a/core/src/main/kotlin/dev/paulee/core/plugin/PluginServiceImpl.kt b/core/src/main/kotlin/dev/paulee/core/plugin/PluginServiceImpl.kt index d55f41a..ad15a26 100644 --- a/core/src/main/kotlin/dev/paulee/core/plugin/PluginServiceImpl.kt +++ b/core/src/main/kotlin/dev/paulee/core/plugin/PluginServiceImpl.kt @@ -5,7 +5,6 @@ import dev.paulee.api.data.RequiresData import dev.paulee.api.data.ViewFilter import dev.paulee.api.plugin.* import dev.paulee.core.normalizeDataSource -import java.awt.Color import java.net.URLClassLoader import java.nio.file.Path import java.util.jar.JarFile @@ -79,7 +78,7 @@ class PluginServiceImpl : IPluginService { return dataSources } - override fun tagFields(plugin: IPlugin, field: String, value: String): Map = + override fun tagFields(plugin: IPlugin, field: String, value: String): Map = (plugin as? Taggable)?.tag(field, value) ?: emptyMap() override fun getViewFilter(plugin: IPlugin): ViewFilter? { diff --git a/gradle.properties b/gradle.properties index fb39d42..70aac77 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,7 +4,7 @@ kotlin.version=2.1.0 compose.version=1.7.1 lucene.version=10.0.0 -api.version=0.29.0 -core.version=0.27.0 +api.version=0.30.0 +core.version=0.28.0 ui.version=0.18.5 app.version=1.6.0 From 14cbae1580ec1bf8a3a604543485568139d476f3 Mon Sep 17 00:00:00 2001 From: Paul <41129807+Paulanerus@users.noreply.github.com> Date: Tue, 7 Jan 2025 11:42:59 +0100 Subject: [PATCH 22/37] refactor: Trimmed csv fields --- .../main/kotlin/dev/paulee/core/data/io/BufferedCSVReader.kt | 5 ++--- gradle.properties | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/core/src/main/kotlin/dev/paulee/core/data/io/BufferedCSVReader.kt b/core/src/main/kotlin/dev/paulee/core/data/io/BufferedCSVReader.kt index 085bec0..cf93f44 100644 --- a/core/src/main/kotlin/dev/paulee/core/data/io/BufferedCSVReader.kt +++ b/core/src/main/kotlin/dev/paulee/core/data/io/BufferedCSVReader.kt @@ -6,7 +6,6 @@ import kotlin.io.path.bufferedReader internal class BufferedCSVReader(path: Path, private val delimiter: Char = ',') { - var lineCount: Long = 0 var errorCount: Long = 0 private var reader = path.bufferedReader() @@ -30,9 +29,9 @@ internal class BufferedCSVReader(path: Path, private val delimiter: Char = ',') if (split.size == this.headSize) { val headToValue = mutableMapOf() - split.forEachIndexed { index, entry -> headToValue[header[index]] = entry } + split.forEachIndexed { index, entry -> headToValue[header[index]] = entry.trim('"') } - batch.add(headToValue).also { lineCount++ } + batch.add(headToValue) } else errorCount++ if (batch.size == 100) callback(batch).also { batch.clear() } diff --git a/gradle.properties b/gradle.properties index 70aac77..e0020f4 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,6 +5,6 @@ compose.version=1.7.1 lucene.version=10.0.0 api.version=0.30.0 -core.version=0.28.0 +core.version=0.28.1 ui.version=0.18.5 app.version=1.6.0 From 54ca94a02ff8faf6469fd3d4d94d919cacfd6ff4 Mon Sep 17 00:00:00 2001 From: Paul <41129807+Paulanerus@users.noreply.github.com> Date: Tue, 7 Jan 2025 11:44:15 +0100 Subject: [PATCH 23/37] refactor: Updated MarkedText to work with 'Tag' classes instead of colors --- gradle.properties | 2 +- ui/src/main/kotlin/dev/paulee/ui/Utility.kt | 32 +++++++++++++++---- .../dev/paulee/ui/components/TableView.kt | 8 ++++- 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/gradle.properties b/gradle.properties index e0020f4..7e51a7f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,5 +6,5 @@ lucene.version=10.0.0 api.version=0.30.0 core.version=0.28.1 -ui.version=0.18.5 +ui.version=0.18.6 app.version=1.6.0 diff --git a/ui/src/main/kotlin/dev/paulee/ui/Utility.kt b/ui/src/main/kotlin/dev/paulee/ui/Utility.kt index 19ab788..7e51ab2 100644 --- a/ui/src/main/kotlin/dev/paulee/ui/Utility.kt +++ b/ui/src/main/kotlin/dev/paulee/ui/Utility.kt @@ -6,8 +6,11 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.buildAnnotatedString +import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.text.withStyle +import androidx.compose.ui.unit.sp +import dev.paulee.api.plugin.Tag fun java.awt.Color.toComposeColor() = Color(red, green, blue, alpha) @@ -16,26 +19,41 @@ fun MarkedText( modifier: Modifier = Modifier, textDecoration: TextDecoration = TextDecoration.None, text: String, - highlights: Map, + highlights: Map, ) { val annotatedString = buildAnnotatedString { var currentIndex = 0 - highlights.keys.forEach { highlight -> - val startIndex = text.indexOf(highlight, currentIndex) + highlights.forEach { (highlightedWord, tagAndColor) -> + val (tag, color) = tagAndColor + val startIndex = text.indexOf(highlightedWord, currentIndex) + + val composeColor = color.toComposeColor() if (startIndex != -1) { append(text.substring(currentIndex, startIndex)) - withStyle(style = SpanStyle(background = (highlights[highlight] ?: Color.Blue).copy(alpha = 0.3f))) { - append(highlight) + withStyle(style = SpanStyle(background = composeColor.copy(alpha = 0.3f))) { + append(highlightedWord) + } + + if (tag.isNotEmpty()) { + withStyle( + style = SpanStyle( + fontSize = 13.sp, + background = composeColor.copy(alpha = 0.8f), + fontWeight = FontWeight.Bold + ) + ) { + append(" $tag ") + } } - currentIndex = startIndex + highlight.length + currentIndex = startIndex + highlightedWord.length } } if (currentIndex < text.length) append(text.substring(currentIndex)) } - Text(annotatedString, modifier = modifier, textDecoration = textDecoration) + Text(text = annotatedString, modifier = modifier, textDecoration = textDecoration) } \ No newline at end of file diff --git a/ui/src/main/kotlin/dev/paulee/ui/components/TableView.kt b/ui/src/main/kotlin/dev/paulee/ui/components/TableView.kt index be4d326..23b87c9 100644 --- a/ui/src/main/kotlin/dev/paulee/ui/components/TableView.kt +++ b/ui/src/main/kotlin/dev/paulee/ui/components/TableView.kt @@ -21,6 +21,7 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.rememberTextMeasurer import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.unit.dp +import dev.paulee.api.plugin.Tag import dev.paulee.ui.Config import dev.paulee.ui.MarkedText @@ -189,7 +190,12 @@ fun TableView( .padding(horizontal = 4.dp), textDecoration = if (link == null) TextDecoration.None else TextDecoration.Underline, text = cell, - highlights = indexStrings.associate { it to Color.Green } + highlights = indexStrings.associate { + it to Tag( + "", + java.awt.Color.green + ) + } ) } } From c2ef1fdbd8293ac5d8b79da295f720c94ac9d7d8 Mon Sep 17 00:00:00 2001 From: Paul <41129807+Paulanerus@users.noreply.github.com> Date: Tue, 7 Jan 2025 19:55:45 +0100 Subject: [PATCH 24/37] refactor: Updated DiffViewer window to support selection of different tagging variants from plugins --- gradle.properties | 2 +- .../kotlin/dev/paulee/ui/TextExplorerUI.kt | 6 +- .../dev/paulee/ui/components/DiffViewer.kt | 87 +++++++++++++++++-- 3 files changed, 86 insertions(+), 9 deletions(-) diff --git a/gradle.properties b/gradle.properties index 7e51a7f..ff6e6e6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,5 +6,5 @@ lucene.version=10.0.0 api.version=0.30.0 core.version=0.28.1 -ui.version=0.18.6 +ui.version=0.18.7 app.version=1.6.0 diff --git a/ui/src/main/kotlin/dev/paulee/ui/TextExplorerUI.kt b/ui/src/main/kotlin/dev/paulee/ui/TextExplorerUI.kt index 4167bd7..d0adc8b 100644 --- a/ui/src/main/kotlin/dev/paulee/ui/TextExplorerUI.kt +++ b/ui/src/main/kotlin/dev/paulee/ui/TextExplorerUI.kt @@ -286,7 +286,11 @@ class TextExplorerUI(private val pluginService: IPluginService, private val data color = Color.LightGray ) - if (displayDiffWindow) DiffViewerWindow(pluginService, dataService.getSelectedPool(), selectedRows) { displayDiffWindow = false } + if (displayDiffWindow) DiffViewerWindow( + pluginService, + dataService.getSelectedPool(), + selectedRows + ) { displayDiffWindow = false } } } } diff --git a/ui/src/main/kotlin/dev/paulee/ui/components/DiffViewer.kt b/ui/src/main/kotlin/dev/paulee/ui/components/DiffViewer.kt index fbe5418..27a5c3f 100644 --- a/ui/src/main/kotlin/dev/paulee/ui/components/DiffViewer.kt +++ b/ui/src/main/kotlin/dev/paulee/ui/components/DiffViewer.kt @@ -1,15 +1,25 @@ package dev.paulee.ui.components +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Text -import androidx.compose.runtime.Composable +import androidx.compose.material.* +import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Window +import dev.paulee.api.plugin.IPlugin import dev.paulee.api.plugin.IPluginService +import dev.paulee.api.plugin.Tag import dev.paulee.api.plugin.Taggable +import dev.paulee.ui.MarkedText + +fun generateHeatMap(selectedRows: List>): List> { + + if (selectedRows.size <= 1) return emptyList() + + return emptyList() +} @Composable fun DiffViewerWindow( @@ -18,26 +28,89 @@ fun DiffViewerWindow( selectedRows: List>, onClose: () -> Unit, ) { + fun getTagName(taggable: Taggable?): String? { + val plugin = taggable as? IPlugin ?: return null + + val pluginName = pluginService.getPluginMetadata(plugin)?.name ?: return null + + val viewFilterName = pluginService.getViewFilter(plugin)?.name ?: pluginName + + return when { + viewFilterName.isNotEmpty() -> viewFilterName + pluginName.isNotEmpty() -> pluginName + else -> null + } + } + + val (pool, select) = selected.split(".", limit = 2) + + val heatmap = generateHeatMap(selectedRows) + val associatedPlugins = pluginService.getPlugins() - .filter { pluginService.getDataInfo(it)?.name == selected.substringBefore(".") } + .filter { pluginService.getDataInfo(it)?.name == pool } val tagPlugins = associatedPlugins.mapNotNull { it as? Taggable } + var selectedTagger = tagPlugins.firstOrNull() val viewFilter = associatedPlugins.mapNotNull { pluginService.getViewFilter(it) }.filter { it.global } .flatMap { it.fields.toList() }.toSet() + var selectedText by remember { mutableStateOf(getTagName(selectedTagger) ?: "") } + var showPopup by remember { mutableStateOf(false) } + var selected by remember { mutableStateOf(false) } + Window(onCloseRequest = onClose, title = "DiffViewer") { MaterialTheme { Box(modifier = Modifier.fillMaxSize()) { + Box(modifier = Modifier.align(Alignment.TopStart).padding(16.dp)) { + + if (selectedTagger == null) return@Box + + Row(verticalAlignment = Alignment.CenterVertically) { + Text( + selectedText, + modifier = Modifier.padding(2.dp).then( + if (tagPlugins.size > 1) Modifier.clickable { showPopup = true } + else Modifier + )) + + Checkbox(checked = selected, onCheckedChange = { selected = it }) + } + + DropdownMenu( + expanded = showPopup, + onDismissRequest = { showPopup = false } + ) { + val menuItems = tagPlugins.mapNotNull { + val name = getTagName(it) ?: return@mapNotNull null + + Pair(name, it) + } + menuItems.forEach { item -> + DropdownMenuItem( + onClick = { + selectedTagger = item.second + selectedText = item.first + showPopup = false + } + ) { + Text(item.first) + } + } + } + } + Column( modifier = Modifier.padding(16.dp).align(Alignment.Center), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center + verticalArrangement = Arrangement.spacedBy(8.dp) ) { selectedRows.forEach { val text = it.entries.joinToString(" ") { if (it.key in viewFilter) it.value else "" } - Text(text) + val tags = + selectedTagger?.tag(select, text).orEmpty() + + MarkedText(text = text, highlights = if (selected) tags else heatmap.first()) } } } From 6599921656d9c19e0516edab131bb51882bf6267 Mon Sep 17 00:00:00 2001 From: Paulanerus <41129807+Paulanerus@users.noreply.github.com> Date: Thu, 9 Jan 2025 10:16:40 +0100 Subject: [PATCH 25/37] feat(core): Added java-diff-utils as a dependency --- core/build.gradle.kts | 2 ++ gradle.properties | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 1b69ab4..7b422a8 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -16,6 +16,8 @@ dependencies { implementation("org.xerial:sqlite-jdbc:3.47.0.0") + implementation("io.github.java-diff-utils:java-diff-utils:4.15") + implementation("org.apache.lucene:lucene-core:${rootProject.extra["lucene.version"]}") implementation("org.apache.lucene:lucene-analysis-common:${rootProject.extra["lucene.version"]}") implementation("org.apache.lucene:lucene-analysis-kuromoji:${rootProject.extra["lucene.version"]}") diff --git a/gradle.properties b/gradle.properties index ff6e6e6..ae26c31 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,6 +5,6 @@ compose.version=1.7.1 lucene.version=10.0.0 api.version=0.30.0 -core.version=0.28.1 +core.version=0.28.2 ui.version=0.18.7 app.version=1.6.0 From 087abf9e18aabce2020d3f19de8a1c899c902b6d Mon Sep 17 00:00:00 2001 From: Paulanerus <41129807+Paulanerus@users.noreply.github.com> Date: Thu, 9 Jan 2025 10:19:29 +0100 Subject: [PATCH 26/37] feat: Added diff service and implementation The implementation is based on the java-diff-utils, which is an implementation of the Myer's diff algorithm. --- .../kotlin/dev/paulee/api/data/DiffService.kt | 8 ++++ .../dev/paulee/core/data/DiffServiceImpl.kt | 37 +++++++++++++++++++ gradle.properties | 4 +- 3 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 api/src/main/kotlin/dev/paulee/api/data/DiffService.kt create mode 100644 core/src/main/kotlin/dev/paulee/core/data/DiffServiceImpl.kt diff --git a/api/src/main/kotlin/dev/paulee/api/data/DiffService.kt b/api/src/main/kotlin/dev/paulee/api/data/DiffService.kt new file mode 100644 index 0000000..b6dd1c2 --- /dev/null +++ b/api/src/main/kotlin/dev/paulee/api/data/DiffService.kt @@ -0,0 +1,8 @@ +package dev.paulee.api.data + +data class Change(val str: String, val tokens: List>) + +interface DiffService { + + fun getDiff(strings: List): Set +} \ No newline at end of file diff --git a/core/src/main/kotlin/dev/paulee/core/data/DiffServiceImpl.kt b/core/src/main/kotlin/dev/paulee/core/data/DiffServiceImpl.kt new file mode 100644 index 0000000..738bbad --- /dev/null +++ b/core/src/main/kotlin/dev/paulee/core/data/DiffServiceImpl.kt @@ -0,0 +1,37 @@ +package dev.paulee.core.data + +import com.github.difflib.text.DiffRowGenerator +import dev.paulee.api.data.Change +import dev.paulee.api.data.DiffService + +class DiffServiceImpl : DiffService { + + private val generator = + DiffRowGenerator.create().mergeOriginalRevised(true).showInlineDiffs(true).oldTag { f -> "~~" } + .newTag { f -> "**" }.build() + + override fun getDiff(strings: List): Set { + + if (strings.size <= 1) return emptySet() + + val first = listOf(strings[0]) + + return (1 until strings.size).map { + val output = generator.generateDiffRows(first, listOf(strings[it])) + + val oldLine = output.first().oldLine + + Change(oldLine, extractToken(oldLine)) + }.toSet() + } + + private fun extractToken(str: String): List> { + val patternOld = Regex("~~([^~]*)~~") + val patternNew = Regex("\\*\\*([^*]*)\\*\\*") + + val entriesOld = patternOld.findAll(str).map { it.groupValues[0] to it.range }.toList() + val entriesNew = patternNew.findAll(str).map { it.groupValues[0] to it.range }.toList() + + return entriesOld + entriesNew + } +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index ae26c31..82f63db 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,7 +4,7 @@ kotlin.version=2.1.0 compose.version=1.7.1 lucene.version=10.0.0 -api.version=0.30.0 -core.version=0.28.2 +api.version=0.31.0 +core.version=0.29.0 ui.version=0.18.7 app.version=1.6.0 From 5e79ada756b3ed26236da08b95a1fadd6043a9f9 Mon Sep 17 00:00:00 2001 From: Paulanerus <41129807+Paulanerus@users.noreply.github.com> Date: Thu, 9 Jan 2025 10:28:15 +0100 Subject: [PATCH 27/37] feat: Added diff service as a dependency for the ui --- gradle.properties | 2 +- .../kotlin/dev/paulee/ui/TextExplorerUI.kt | 8 ++++- .../dev/paulee/ui/components/DiffViewer.kt | 33 +++++++++++-------- 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/gradle.properties b/gradle.properties index 82f63db..8e4027b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,5 +6,5 @@ lucene.version=10.0.0 api.version=0.31.0 core.version=0.29.0 -ui.version=0.18.7 +ui.version=0.19.0 app.version=1.6.0 diff --git a/ui/src/main/kotlin/dev/paulee/ui/TextExplorerUI.kt b/ui/src/main/kotlin/dev/paulee/ui/TextExplorerUI.kt index d0adc8b..0c62f04 100644 --- a/ui/src/main/kotlin/dev/paulee/ui/TextExplorerUI.kt +++ b/ui/src/main/kotlin/dev/paulee/ui/TextExplorerUI.kt @@ -22,13 +22,18 @@ import androidx.compose.ui.window.Window import androidx.compose.ui.window.WindowPosition import androidx.compose.ui.window.application import androidx.compose.ui.window.rememberWindowState +import dev.paulee.api.data.DiffService import dev.paulee.api.data.IDataService import dev.paulee.api.plugin.IPluginService import dev.paulee.ui.components.* import java.nio.file.Path import kotlin.io.path.* -class TextExplorerUI(private val pluginService: IPluginService, private val dataService: IDataService) { +class TextExplorerUI( + private val pluginService: IPluginService, + private val dataService: IDataService, + private val diffService: DiffService +) { private val appDir = Path(System.getProperty("user.home")).resolve(".textexplorer") @@ -287,6 +292,7 @@ class TextExplorerUI(private val pluginService: IPluginService, private val data ) if (displayDiffWindow) DiffViewerWindow( + diffService, pluginService, dataService.getSelectedPool(), selectedRows diff --git a/ui/src/main/kotlin/dev/paulee/ui/components/DiffViewer.kt b/ui/src/main/kotlin/dev/paulee/ui/components/DiffViewer.kt index 27a5c3f..6686739 100644 --- a/ui/src/main/kotlin/dev/paulee/ui/components/DiffViewer.kt +++ b/ui/src/main/kotlin/dev/paulee/ui/components/DiffViewer.kt @@ -8,21 +8,16 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Window +import dev.paulee.api.data.DiffService import dev.paulee.api.plugin.IPlugin import dev.paulee.api.plugin.IPluginService import dev.paulee.api.plugin.Tag import dev.paulee.api.plugin.Taggable import dev.paulee.ui.MarkedText -fun generateHeatMap(selectedRows: List>): List> { - - if (selectedRows.size <= 1) return emptyList() - - return emptyList() -} - @Composable fun DiffViewerWindow( + diffService: DiffService, pluginService: IPluginService, selected: String, selectedRows: List>, @@ -44,8 +39,6 @@ fun DiffViewerWindow( val (pool, select) = selected.split(".", limit = 2) - val heatmap = generateHeatMap(selectedRows) - val associatedPlugins = pluginService.getPlugins() .filter { pluginService.getDataInfo(it)?.name == pool } @@ -55,10 +48,17 @@ fun DiffViewerWindow( val viewFilter = associatedPlugins.mapNotNull { pluginService.getViewFilter(it) }.filter { it.global } .flatMap { it.fields.toList() }.toSet() + val heatmap = generateHeatMap(diffService, selectedRows.map { it.filterKeys { key -> key in viewFilter } }) + var selectedText by remember { mutableStateOf(getTagName(selectedTagger) ?: "") } var showPopup by remember { mutableStateOf(false) } var selected by remember { mutableStateOf(false) } + val entries = remember(selected) { + if (selected) emptyList>() // TODO apply plugin specific view filter. + else selectedRows.map { it.filterKeys { key -> key in viewFilter } } + } + Window(onCloseRequest = onClose, title = "DiffViewer") { MaterialTheme { Box(modifier = Modifier.fillMaxSize()) { @@ -104,16 +104,23 @@ fun DiffViewerWindow( modifier = Modifier.padding(16.dp).align(Alignment.Center), verticalArrangement = Arrangement.spacedBy(8.dp) ) { - selectedRows.forEach { - val text = it.entries.joinToString(" ") { if (it.key in viewFilter) it.value else "" } + entries.forEachIndexed { index, entry -> + val text = entry.entries.joinToString(" ") { if (it.key in viewFilter) it.value else "" } val tags = selectedTagger?.tag(select, text).orEmpty() - MarkedText(text = text, highlights = if (selected) tags else heatmap.first()) + MarkedText(text = text, highlights = if (selected) tags else heatmap[index]) } } } } } -} \ No newline at end of file +} + +private fun generateHeatMap(diffService: DiffService, entries: List>): List> { + + //TODO Replace entries and color correctly. + + return emptyList() +} From 9f9769897a67024ad93a5165c306c37006f23d0b Mon Sep 17 00:00:00 2001 From: Paulanerus <41129807+Paulanerus@users.noreply.github.com> Date: Thu, 9 Jan 2025 11:25:24 +0100 Subject: [PATCH 28/37] feat: Added old and new value retrieval functions --- .../main/kotlin/dev/paulee/api/data/DiffService.kt | 4 ++++ .../kotlin/dev/paulee/core/data/DiffServiceImpl.kt | 14 ++++++++++++++ gradle.properties | 4 ++-- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/api/src/main/kotlin/dev/paulee/api/data/DiffService.kt b/api/src/main/kotlin/dev/paulee/api/data/DiffService.kt index b6dd1c2..5e5c0e3 100644 --- a/api/src/main/kotlin/dev/paulee/api/data/DiffService.kt +++ b/api/src/main/kotlin/dev/paulee/api/data/DiffService.kt @@ -5,4 +5,8 @@ data class Change(val str: String, val tokens: List>) interface DiffService { fun getDiff(strings: List): Set + + fun oldValue(change: Change): String + + fun newValue(change: Change): String } \ No newline at end of file diff --git a/core/src/main/kotlin/dev/paulee/core/data/DiffServiceImpl.kt b/core/src/main/kotlin/dev/paulee/core/data/DiffServiceImpl.kt index 738bbad..3d3e2b1 100644 --- a/core/src/main/kotlin/dev/paulee/core/data/DiffServiceImpl.kt +++ b/core/src/main/kotlin/dev/paulee/core/data/DiffServiceImpl.kt @@ -25,6 +25,20 @@ class DiffServiceImpl : DiffService { }.toSet() } + override fun oldValue(change: Change): String = with(change) { + tokens.fold(str) { acc, token -> + if (token.first.startsWith("**")) acc.replace(token.first, "") + else acc.replace(token.first, token.first.trim('~')) + } + } + + override fun newValue(change: Change): String = with(change) { + tokens.fold(str) { acc, token -> + if (token.first.startsWith("~~")) acc.replace(token.first, "") + else acc.replace(token.first, token.first.trim('*')) + } + } + private fun extractToken(str: String): List> { val patternOld = Regex("~~([^~]*)~~") val patternNew = Regex("\\*\\*([^*]*)\\*\\*") diff --git a/gradle.properties b/gradle.properties index 8e4027b..e027d99 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,7 +4,7 @@ kotlin.version=2.1.0 compose.version=1.7.1 lucene.version=10.0.0 -api.version=0.31.0 -core.version=0.29.0 +api.version=0.32.0 +core.version=0.30.0 ui.version=0.19.0 app.version=1.6.0 From ec0dcc12298ff7526a455453459ad7f2e962a389 Mon Sep 17 00:00:00 2001 From: Paulanerus <41129807+Paulanerus@users.noreply.github.com> Date: Thu, 9 Jan 2025 12:06:33 +0100 Subject: [PATCH 29/37] refactor: Updated ui logic --- gradle.properties | 2 +- .../dev/paulee/ui/components/DiffViewer.kt | 54 ++++++++++++------- 2 files changed, 37 insertions(+), 19 deletions(-) diff --git a/gradle.properties b/gradle.properties index e027d99..cbc1085 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,5 +6,5 @@ lucene.version=10.0.0 api.version=0.32.0 core.version=0.30.0 -ui.version=0.19.0 +ui.version=0.19.1 app.version=1.6.0 diff --git a/ui/src/main/kotlin/dev/paulee/ui/components/DiffViewer.kt b/ui/src/main/kotlin/dev/paulee/ui/components/DiffViewer.kt index 6686739..19fee0d 100644 --- a/ui/src/main/kotlin/dev/paulee/ui/components/DiffViewer.kt +++ b/ui/src/main/kotlin/dev/paulee/ui/components/DiffViewer.kt @@ -39,8 +39,7 @@ fun DiffViewerWindow( val (pool, select) = selected.split(".", limit = 2) - val associatedPlugins = pluginService.getPlugins() - .filter { pluginService.getDataInfo(it)?.name == pool } + val associatedPlugins = pluginService.getPlugins().filter { pluginService.getDataInfo(it)?.name == pool } val tagPlugins = associatedPlugins.mapNotNull { it as? Taggable } var selectedTagger = tagPlugins.firstOrNull() @@ -55,7 +54,7 @@ fun DiffViewerWindow( var selected by remember { mutableStateOf(false) } val entries = remember(selected) { - if (selected) emptyList>() // TODO apply plugin specific view filter. + if (selected) selectedRows // TODO apply plugin specific view filter. else selectedRows.map { it.filterKeys { key -> key in viewFilter } } } @@ -69,18 +68,15 @@ fun DiffViewerWindow( Row(verticalAlignment = Alignment.CenterVertically) { Text( selectedText, - modifier = Modifier.padding(2.dp).then( - if (tagPlugins.size > 1) Modifier.clickable { showPopup = true } - else Modifier - )) + modifier = Modifier.padding(2.dp) + .then(if (tagPlugins.size > 1) Modifier.clickable { showPopup = true } + else Modifier)) Checkbox(checked = selected, onCheckedChange = { selected = it }) } DropdownMenu( - expanded = showPopup, - onDismissRequest = { showPopup = false } - ) { + expanded = showPopup, onDismissRequest = { showPopup = false }) { val menuItems = tagPlugins.mapNotNull { val name = getTagName(it) ?: return@mapNotNull null @@ -92,8 +88,7 @@ fun DiffViewerWindow( selectedTagger = item.second selectedText = item.first showPopup = false - } - ) { + }) { Text(item.first) } } @@ -104,13 +99,20 @@ fun DiffViewerWindow( modifier = Modifier.padding(16.dp).align(Alignment.Center), verticalArrangement = Arrangement.spacedBy(8.dp) ) { + Text(entries.first().entries.joinToString(" ") { if (it.key in viewFilter) it.value else "" }) + entries.forEachIndexed { index, entry -> + if (index == 0) return@forEachIndexed + val text = entry.entries.joinToString(" ") { if (it.key in viewFilter) it.value else "" } - val tags = - selectedTagger?.tag(select, text).orEmpty() + val tags = selectedTagger?.tag(select, text).orEmpty() - MarkedText(text = text, highlights = if (selected) tags else heatmap[index]) + val colors = heatmap["text"] ?: emptyList() + MarkedText( + text = text, + highlights = if (selected) tags else if (index - 1 < colors.size) colors[index - 1] else emptyMap() + ) } } } @@ -118,9 +120,25 @@ fun DiffViewerWindow( } } -private fun generateHeatMap(diffService: DiffService, entries: List>): List> { +private fun generateHeatMap( + diffService: DiffService, entries: List> +): Map>> { + + val grouped: Map> = entries.flatMap { it.entries }.groupBy({ it.key }, { it.value }) + + val colors = mutableMapOf>>() + grouped.forEach { key, value -> + + if (value.size <= 1) return@forEach - //TODO Replace entries and color correctly. + val changes = diffService.getDiff(value) + + //TODO handle 3 cases (added, removed, updated) + + changes.forEach { + println(it) + } + } - return emptyList() + return colors } From 95733c733f44e0999c9ff65cd34d399316e4085f Mon Sep 17 00:00:00 2001 From: Paul <41129807+Paulanerus@users.noreply.github.com> Date: Thu, 9 Jan 2025 18:46:37 +0100 Subject: [PATCH 30/37] feat: Added default value for links --- gradle.properties | 2 +- ui/src/main/kotlin/dev/paulee/ui/components/TableView.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index cbc1085..8c93176 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,5 +6,5 @@ lucene.version=10.0.0 api.version=0.32.0 core.version=0.30.0 -ui.version=0.19.1 +ui.version=0.19.2 app.version=1.6.0 diff --git a/ui/src/main/kotlin/dev/paulee/ui/components/TableView.kt b/ui/src/main/kotlin/dev/paulee/ui/components/TableView.kt index 23b87c9..191f5c9 100644 --- a/ui/src/main/kotlin/dev/paulee/ui/components/TableView.kt +++ b/ui/src/main/kotlin/dev/paulee/ui/components/TableView.kt @@ -34,7 +34,7 @@ fun TableView( indexStrings: Set = emptySet(), columns: List, data: List>, - links: Map>>, + links: Map>> = emptyMap(), onRowSelect: (List>) -> Unit, clicked: () -> Unit = {}, ) { From c1c279f56f14874208132e1fe2ee84098b0aa261 Mon Sep 17 00:00:00 2001 From: Paul <41129807+Paulanerus@users.noreply.github.com> Date: Thu, 9 Jan 2025 18:47:24 +0100 Subject: [PATCH 31/37] feat: Updated main entry to create the diff service object --- src/main/kotlin/dev/paulee/Main.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/dev/paulee/Main.kt b/src/main/kotlin/dev/paulee/Main.kt index 1c043c6..f1a849c 100644 --- a/src/main/kotlin/dev/paulee/Main.kt +++ b/src/main/kotlin/dev/paulee/Main.kt @@ -2,11 +2,15 @@ package dev.paulee import dev.paulee.api.data.provider.StorageType import dev.paulee.core.data.DataServiceImpl +import dev.paulee.core.data.DiffServiceImpl import dev.paulee.core.data.provider.StorageProvider import dev.paulee.core.plugin.PluginServiceImpl import dev.paulee.ui.TextExplorerUI fun main() { - val explorerUI = TextExplorerUI(PluginServiceImpl(), DataServiceImpl(StorageProvider.of(StorageType.SQLITE))) + val explorerUI = TextExplorerUI( + PluginServiceImpl(), + DataServiceImpl(StorageProvider.of(StorageType.SQLITE)), + DiffServiceImpl()) explorerUI.start() } \ No newline at end of file From ac391f95f8118243d1c6dfd4fd07e62bef4fe775 Mon Sep 17 00:00:00 2001 From: Paul <41129807+Paulanerus@users.noreply.github.com> Date: Thu, 9 Jan 2025 18:48:16 +0100 Subject: [PATCH 32/37] feat: Add single compare variant for getDiff --- api/src/main/kotlin/dev/paulee/api/data/DiffService.kt | 2 ++ core/src/main/kotlin/dev/paulee/core/data/DiffServiceImpl.kt | 2 ++ gradle.properties | 4 ++-- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/api/src/main/kotlin/dev/paulee/api/data/DiffService.kt b/api/src/main/kotlin/dev/paulee/api/data/DiffService.kt index 5e5c0e3..7b9eeb3 100644 --- a/api/src/main/kotlin/dev/paulee/api/data/DiffService.kt +++ b/api/src/main/kotlin/dev/paulee/api/data/DiffService.kt @@ -6,6 +6,8 @@ interface DiffService { fun getDiff(strings: List): Set + fun getDiff(original: String, str: String): Change? + fun oldValue(change: Change): String fun newValue(change: Change): String diff --git a/core/src/main/kotlin/dev/paulee/core/data/DiffServiceImpl.kt b/core/src/main/kotlin/dev/paulee/core/data/DiffServiceImpl.kt index 3d3e2b1..db66a31 100644 --- a/core/src/main/kotlin/dev/paulee/core/data/DiffServiceImpl.kt +++ b/core/src/main/kotlin/dev/paulee/core/data/DiffServiceImpl.kt @@ -25,6 +25,8 @@ class DiffServiceImpl : DiffService { }.toSet() } + override fun getDiff(original: String, str: String): Change? = this.getDiff(listOf(original, str)).firstOrNull() + override fun oldValue(change: Change): String = with(change) { tokens.fold(str) { acc, token -> if (token.first.startsWith("**")) acc.replace(token.first, "") diff --git a/gradle.properties b/gradle.properties index 8c93176..281a5f9 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,7 +4,7 @@ kotlin.version=2.1.0 compose.version=1.7.1 lucene.version=10.0.0 -api.version=0.32.0 -core.version=0.30.0 +api.version=0.32.1 +core.version=0.30.1 ui.version=0.19.2 app.version=1.6.0 From 8308e7ee6559732a1274f8bd66dde2ba376b2c6d Mon Sep 17 00:00:00 2001 From: Paul <41129807+Paulanerus@users.noreply.github.com> Date: Thu, 9 Jan 2025 18:49:10 +0100 Subject: [PATCH 33/37] feat: Added initial HeatmapText implementation --- gradle.properties | 2 +- ui/src/main/kotlin/dev/paulee/ui/Utility.kt | 42 ++++++++++++++++++++- 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/gradle.properties b/gradle.properties index 281a5f9..c93c440 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,5 +6,5 @@ lucene.version=10.0.0 api.version=0.32.1 core.version=0.30.1 -ui.version=0.19.2 +ui.version=0.20.0 app.version=1.6.0 diff --git a/ui/src/main/kotlin/dev/paulee/ui/Utility.kt b/ui/src/main/kotlin/dev/paulee/ui/Utility.kt index 7e51ab2..1836aa0 100644 --- a/ui/src/main/kotlin/dev/paulee/ui/Utility.kt +++ b/ui/src/main/kotlin/dev/paulee/ui/Utility.kt @@ -1,5 +1,6 @@ package dev.paulee.ui +import androidx.compose.material.MaterialTheme import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier @@ -7,9 +8,11 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.text.withStyle import androidx.compose.ui.unit.sp +import dev.paulee.api.data.Change import dev.paulee.api.plugin.Tag fun java.awt.Color.toComposeColor() = Color(red, green, blue, alpha) @@ -18,6 +21,7 @@ fun java.awt.Color.toComposeColor() = Color(red, green, blue, alpha) fun MarkedText( modifier: Modifier = Modifier, textDecoration: TextDecoration = TextDecoration.None, + textAlign: TextAlign = TextAlign.Start, text: String, highlights: Map, ) { @@ -55,5 +59,39 @@ fun MarkedText( if (currentIndex < text.length) append(text.substring(currentIndex)) } - Text(text = annotatedString, modifier = modifier, textDecoration = textDecoration) -} \ No newline at end of file + Text(text = annotatedString, modifier = modifier, textDecoration = textDecoration, textAlign = textAlign) +} + +@Composable +fun HeatmapText( + change: Change?, + fallback: String, + modifier: Modifier = Modifier, + textDecoration: TextDecoration = TextDecoration.None, + textAlign: TextAlign = TextAlign.Center, +) { + val text = change?.str ?: fallback + + val annotatedString = buildAnnotatedString { + var currentIndex = 0 + + change?.tokens.orEmpty().forEach { (token, range) -> + val startIndex = text.indexOf(token, currentIndex) + + val composeColor = if (token.startsWith("**")) Color.Green else Color.Red + + if (startIndex != -1) { + append(text.substring(currentIndex, startIndex)) + + withStyle(style = SpanStyle(background = composeColor.copy(alpha = 0.3f))) { + append(token) + } + + currentIndex = startIndex + token.length + } + } + + if (currentIndex < text.length) append(text.substring(currentIndex)) + } + Text(text = annotatedString, modifier = modifier, textDecoration = textDecoration, textAlign = textAlign) +} From 0370700536e5df17715b56f3fda51e613655e883 Mon Sep 17 00:00:00 2001 From: Paul <41129807+Paulanerus@users.noreply.github.com> Date: Thu, 9 Jan 2025 18:54:48 +0100 Subject: [PATCH 34/37] refactor: Updated DiffViewer ui logic --- gradle.properties | 2 +- .../dev/paulee/ui/components/DiffViewer.kt | 84 +++++++++---------- 2 files changed, 43 insertions(+), 43 deletions(-) diff --git a/gradle.properties b/gradle.properties index c93c440..28485f0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,5 +6,5 @@ lucene.version=10.0.0 api.version=0.32.1 core.version=0.30.1 -ui.version=0.20.0 +ui.version=0.20.1 app.version=1.6.0 diff --git a/ui/src/main/kotlin/dev/paulee/ui/components/DiffViewer.kt b/ui/src/main/kotlin/dev/paulee/ui/components/DiffViewer.kt index 19fee0d..ef981a7 100644 --- a/ui/src/main/kotlin/dev/paulee/ui/components/DiffViewer.kt +++ b/ui/src/main/kotlin/dev/paulee/ui/components/DiffViewer.kt @@ -6,13 +6,16 @@ import androidx.compose.material.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import androidx.compose.ui.window.Window import dev.paulee.api.data.DiffService import dev.paulee.api.plugin.IPlugin import dev.paulee.api.plugin.IPluginService -import dev.paulee.api.plugin.Tag import dev.paulee.api.plugin.Taggable +import dev.paulee.ui.HeatmapText import dev.paulee.ui.MarkedText @Composable @@ -47,14 +50,12 @@ fun DiffViewerWindow( val viewFilter = associatedPlugins.mapNotNull { pluginService.getViewFilter(it) }.filter { it.global } .flatMap { it.fields.toList() }.toSet() - val heatmap = generateHeatMap(diffService, selectedRows.map { it.filterKeys { key -> key in viewFilter } }) - var selectedText by remember { mutableStateOf(getTagName(selectedTagger) ?: "") } var showPopup by remember { mutableStateOf(false) } var selected by remember { mutableStateOf(false) } val entries = remember(selected) { - if (selected) selectedRows // TODO apply plugin specific view filter. + if (selected) selectedRows.map { it.filterKeys { key -> key in viewFilter } } // TODO apply plugin specific view filter. else selectedRows.map { it.filterKeys { key -> key in viewFilter } } } @@ -65,10 +66,13 @@ fun DiffViewerWindow( if (selectedTagger == null) return@Box - Row(verticalAlignment = Alignment.CenterVertically) { + Text("Tagger:", fontWeight = FontWeight.Bold) + + Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(start = 4.dp, top = 8.dp)) { Text( selectedText, - modifier = Modifier.padding(2.dp) + fontSize = 14.sp, + modifier = Modifier .then(if (tagPlugins.size > 1) Modifier.clickable { showPopup = true } else Modifier)) @@ -95,50 +99,46 @@ fun DiffViewerWindow( } } - Column( - modifier = Modifier.padding(16.dp).align(Alignment.Center), - verticalArrangement = Arrangement.spacedBy(8.dp) - ) { - Text(entries.first().entries.joinToString(" ") { if (it.key in viewFilter) it.value else "" }) - - entries.forEachIndexed { index, entry -> - if (index == 0) return@forEachIndexed + Box(modifier = Modifier.fillMaxSize()) { + if (selected) { + Column( + modifier = Modifier.align(Alignment.Center), + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + entries.forEach { entry -> - val text = entry.entries.joinToString(" ") { if (it.key in viewFilter) it.value else "" } + val tags = selectedTagger?.tag("text", entry.values.first()).orEmpty() - val tags = selectedTagger?.tag(select, text).orEmpty() - - val colors = heatmap["text"] ?: emptyList() - MarkedText( - text = text, - highlights = if (selected) tags else if (index - 1 < colors.size) colors[index - 1] else emptyMap() - ) - } - } - } - } - } -} - -private fun generateHeatMap( - diffService: DiffService, entries: List> -): Map>> { + MarkedText(text = entry.values.first(), highlights = tags, textAlign = TextAlign.Center) + } + } + } else { + val first = entries.first() - val grouped: Map> = entries.flatMap { it.entries }.groupBy({ it.key }, { it.value }) + Column(modifier = Modifier.align(Alignment.Center)) { + Text( + first.entries.joinToString(" "), + textAlign = TextAlign.Center, + fontWeight = FontWeight.Bold, + modifier = Modifier.fillMaxWidth() + ) - val colors = mutableMapOf>>() - grouped.forEach { key, value -> + Spacer(Modifier.height(24.dp)) - if (value.size <= 1) return@forEach + Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { + entries.forEachIndexed { index, entry -> - val changes = diffService.getDiff(value) + if (index == 0) return@forEachIndexed - //TODO handle 3 cases (added, removed, updated) + val change = diffService.getDiff(first.values.first(), entry.values.first()) - changes.forEach { - println(it) + HeatmapText(change, entry.values.first()) + } + } + } + } + } + } } } - - return colors } From 7a0a30c30f110392a132aa174d4848532a1fb4db Mon Sep 17 00:00:00 2001 From: Paul <41129807+Paulanerus@users.noreply.github.com> Date: Thu, 9 Jan 2025 19:26:54 +0100 Subject: [PATCH 35/37] refactor: Added SelectionContainer for Text fields --- gradle.properties | 2 +- .../dev/paulee/ui/components/DiffViewer.kt | 32 +++++++++++++------ 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/gradle.properties b/gradle.properties index 28485f0..9493cdf 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,5 +6,5 @@ lucene.version=10.0.0 api.version=0.32.1 core.version=0.30.1 -ui.version=0.20.1 +ui.version=0.20.2 app.version=1.6.0 diff --git a/ui/src/main/kotlin/dev/paulee/ui/components/DiffViewer.kt b/ui/src/main/kotlin/dev/paulee/ui/components/DiffViewer.kt index ef981a7..f742c6f 100644 --- a/ui/src/main/kotlin/dev/paulee/ui/components/DiffViewer.kt +++ b/ui/src/main/kotlin/dev/paulee/ui/components/DiffViewer.kt @@ -2,6 +2,7 @@ package dev.paulee.ui.components import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* +import androidx.compose.foundation.text.selection.SelectionContainer import androidx.compose.material.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment @@ -68,7 +69,10 @@ fun DiffViewerWindow( Text("Tagger:", fontWeight = FontWeight.Bold) - Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(start = 4.dp, top = 8.dp)) { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.padding(start = 4.dp, top = 8.dp) + ) { Text( selectedText, fontSize = 14.sp, @@ -109,19 +113,27 @@ fun DiffViewerWindow( val tags = selectedTagger?.tag("text", entry.values.first()).orEmpty() - MarkedText(text = entry.values.first(), highlights = tags, textAlign = TextAlign.Center) + SelectionContainer { + MarkedText( + text = entry.values.first(), + highlights = tags, + textAlign = TextAlign.Center + ) + } } } } else { val first = entries.first() Column(modifier = Modifier.align(Alignment.Center)) { - Text( - first.entries.joinToString(" "), - textAlign = TextAlign.Center, - fontWeight = FontWeight.Bold, - modifier = Modifier.fillMaxWidth() - ) + SelectionContainer { + Text( + first.entries.joinToString(" "), + textAlign = TextAlign.Center, + fontWeight = FontWeight.Bold, + modifier = Modifier.fillMaxWidth() + ) + } Spacer(Modifier.height(24.dp)) @@ -132,7 +144,9 @@ fun DiffViewerWindow( val change = diffService.getDiff(first.values.first(), entry.values.first()) - HeatmapText(change, entry.values.first()) + SelectionContainer { + HeatmapText(change, entry.values.first()) + } } } } From c841c30f769065ad305a91c58921cf3668561897 Mon Sep 17 00:00:00 2001 From: Paul <41129807+Paulanerus@users.noreply.github.com> Date: Thu, 9 Jan 2025 20:01:34 +0100 Subject: [PATCH 36/37] feat: Center aligned diff texts --- gradle.properties | 2 +- ui/src/main/kotlin/dev/paulee/ui/components/DiffViewer.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index 9493cdf..f5c51a5 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,5 +6,5 @@ lucene.version=10.0.0 api.version=0.32.1 core.version=0.30.1 -ui.version=0.20.2 +ui.version=0.20.3 app.version=1.6.0 diff --git a/ui/src/main/kotlin/dev/paulee/ui/components/DiffViewer.kt b/ui/src/main/kotlin/dev/paulee/ui/components/DiffViewer.kt index f742c6f..0ecea4a 100644 --- a/ui/src/main/kotlin/dev/paulee/ui/components/DiffViewer.kt +++ b/ui/src/main/kotlin/dev/paulee/ui/components/DiffViewer.kt @@ -145,7 +145,7 @@ fun DiffViewerWindow( val change = diffService.getDiff(first.values.first(), entry.values.first()) SelectionContainer { - HeatmapText(change, entry.values.first()) + HeatmapText(change, entry.values.first(), Modifier.fillMaxWidth()) } } } From ba77ac7a87dde2b6e4eccc7e09fb57537beade20 Mon Sep 17 00:00:00 2001 From: Paul <41129807+Paulanerus@users.noreply.github.com> Date: Thu, 9 Jan 2025 20:02:45 +0100 Subject: [PATCH 37/37] refactor: Updated HeatmapText to resemble a diff view --- gradle.properties | 2 +- ui/src/main/kotlin/dev/paulee/ui/Utility.kt | 27 +++++++++++++++++---- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/gradle.properties b/gradle.properties index f5c51a5..caf894d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,5 +6,5 @@ lucene.version=10.0.0 api.version=0.32.1 core.version=0.30.1 -ui.version=0.20.3 +ui.version=0.20.4 app.version=1.6.0 diff --git a/ui/src/main/kotlin/dev/paulee/ui/Utility.kt b/ui/src/main/kotlin/dev/paulee/ui/Utility.kt index 1836aa0..9465a87 100644 --- a/ui/src/main/kotlin/dev/paulee/ui/Utility.kt +++ b/ui/src/main/kotlin/dev/paulee/ui/Utility.kt @@ -75,18 +75,35 @@ fun HeatmapText( val annotatedString = buildAnnotatedString { var currentIndex = 0 - change?.tokens.orEmpty().forEach { (token, range) -> - val startIndex = text.indexOf(token, currentIndex) + val sortedTokens = change?.tokens.orEmpty().sortedBy { it.second.first } - val composeColor = if (token.startsWith("**")) Color.Green else Color.Red + sortedTokens.forEach { (token, _) -> + val startIndex = text.indexOf(token, currentIndex) if (startIndex != -1) { append(text.substring(currentIndex, startIndex)) - withStyle(style = SpanStyle(background = composeColor.copy(alpha = 0.3f))) { - append(token) + val processedToken = when { + token.startsWith("~~") && token.endsWith("~~") -> { + withStyle(style = SpanStyle(background = Color.Red.copy(alpha = 0.3f))) { + append(" ".repeat(token.trim('~').length)) + } + "" + } + + token.startsWith("**") && token.endsWith("**") -> { + val trimmedToken = token.removeSurrounding("**") + withStyle(style = SpanStyle(background = Color.Green.copy(alpha = 0.3f))) { + append(trimmedToken) + } + "" + } + + else -> token } + if (processedToken.isNotEmpty()) append(processedToken) + currentIndex = startIndex + token.length } }