diff --git a/.idea/ktlint-plugin.xml b/.idea/ktlint-plugin.xml
new file mode 100644
index 0000000..3c1c38c
--- /dev/null
+++ b/.idea/ktlint-plugin.xml
@@ -0,0 +1,6 @@
+
+
+
+ DEFAULT
+
+
\ No newline at end of file
diff --git a/httpClient/anyType.http b/httpClient/anyType.http
index 835a532..c9e9da3 100644
--- a/httpClient/anyType.http
+++ b/httpClient/anyType.http
@@ -27,6 +27,11 @@ GET http://localhost:31009/v1/spaces/{{spaceId}}/properties/{{priorityPropertyId
Authorization: Bearer {{apiKey}}
Anytype-Version: 0.1
+### Get Property Tags
+GET http://localhost:31009/v1/spaces/{{spaceId}}/properties/{{priorityPropertyId}}/tags
+Authorization: Bearer {{apiKey}}
+Anytype-Version: 0.1
+
### Get Templates
GET http://localhost:31009/v1/spaces/{{spaceId}}/types/{{taskTypeId}}/templates
Authorization: Bearer {{apiKey}}
diff --git a/src/main/kotlin/ch/ergon/anytask/Main.kt b/src/main/kotlin/ch/ergon/anytask/Main.kt
index dc2b153..e7bd0af 100644
--- a/src/main/kotlin/ch/ergon/anytask/Main.kt
+++ b/src/main/kotlin/ch/ergon/anytask/Main.kt
@@ -152,7 +152,7 @@ private fun setupSystemTray(onShow: () -> Unit, onSettings: () -> Unit, onExit:
val icon = createTrayIcon(size)
val menu = PopupMenu().apply {
- add(MenuItem("Open").also { it.addActionListener { onShow() } })
+ add(MenuItem("Create Task").also { it.addActionListener { onShow() } })
add(MenuItem("Settings").also { it.addActionListener { onSettings() } })
addSeparator()
add(MenuItem("Quit").also { it.addActionListener { onExit() } })
diff --git a/src/main/kotlin/ch/ergon/anytask/api/AnyTypeApi.kt b/src/main/kotlin/ch/ergon/anytask/api/AnyTypeApi.kt
index 432a176..8f531bd 100644
--- a/src/main/kotlin/ch/ergon/anytask/api/AnyTypeApi.kt
+++ b/src/main/kotlin/ch/ergon/anytask/api/AnyTypeApi.kt
@@ -88,6 +88,15 @@ class AnyTypeApi(
response.body().data
}
+ suspend fun getTags(propertyId: String): Result> =
+ runCatching {
+ val response = http.get("$baseUrl/v1/spaces/$spaceId/properties/$propertyId/tags") {
+ header("Anytype-Version", "2025-11-08")
+ bearerAuth(apiKey)
+ }
+ response.body().data
+ }
+
suspend fun getTemplates(typeId: String): Result> =
runCatching {
val response = http.get("$baseUrl/v1/spaces/$spaceId/types/$typeId/templates") {
diff --git a/src/main/kotlin/ch/ergon/anytask/config/Configuration.kt b/src/main/kotlin/ch/ergon/anytask/config/Configuration.kt
index b16b1d6..a30e1b5 100644
--- a/src/main/kotlin/ch/ergon/anytask/config/Configuration.kt
+++ b/src/main/kotlin/ch/ergon/anytask/config/Configuration.kt
@@ -17,10 +17,10 @@ data class Connection(
@Serializable
data class PropertyConfig(
val key: String,
+ val id: String = "",
val name: String = "",
val format: String = "",
val indicator: String = "",
- @SerialName("possible_values") val possibleValues: List = emptyList(),
@SerialName("object_type_key") val objectTypeKey: String = "",
)
diff --git a/src/main/kotlin/ch/ergon/anytask/ui/SettingsWindow.kt b/src/main/kotlin/ch/ergon/anytask/ui/SettingsWindow.kt
index 1c0fcf8..3527563 100644
--- a/src/main/kotlin/ch/ergon/anytask/ui/SettingsWindow.kt
+++ b/src/main/kotlin/ch/ergon/anytask/ui/SettingsWindow.kt
@@ -364,7 +364,6 @@ private fun PropertyTable(
Text("Name", fontWeight = FontWeight.Bold, modifier = Modifier.weight(2f))
Text("Type", fontWeight = FontWeight.Bold, modifier = Modifier.weight(1.5f))
Text("Ind.", fontWeight = FontWeight.Bold, modifier = Modifier.weight(0.7f))
- Text("Values", fontWeight = FontWeight.Bold, modifier = Modifier.weight(2f))
Text("Obj Type", fontWeight = FontWeight.Bold, modifier = Modifier.weight(1.5f))
Spacer(modifier = Modifier.width(40.dp))
}
@@ -383,11 +382,6 @@ private fun PropertyTable(
Text(prop.name, modifier = Modifier.weight(2f), maxLines = 1)
Text(prop.format, modifier = Modifier.weight(1.5f), maxLines = 1)
Text(prop.indicator, modifier = Modifier.weight(0.7f), maxLines = 1)
- Text(
- prop.possibleValues.joinToString(", "),
- modifier = Modifier.weight(2f),
- maxLines = 1,
- )
Text(prop.objectTypeKey, modifier = Modifier.weight(1.5f), maxLines = 1)
IconButton(onClick = { onDelete(index) }) {
Icon(Icons.Default.Delete, contentDescription = "Delete", tint = Color.Gray)
@@ -409,7 +403,6 @@ private fun PropertyEditPanel(
) {
var selectedPropertyKey by remember(existing) { mutableStateOf(existing?.key ?: "") }
var indicator by remember(existing) { mutableStateOf(existing?.indicator ?: "") }
- var possibleValuesText by remember(existing) { mutableStateOf(existing?.possibleValues?.joinToString("\n") ?: "") }
var objectTypeKey by remember(existing) { mutableStateOf(existing?.objectTypeKey ?: "") }
val selectedProperty = availableProperties.find { it.key == selectedPropertyKey }
@@ -446,15 +439,6 @@ private fun PropertyEditPanel(
// Format-specific fields
when (format) {
- "select", "multi_select" -> {
- OutlinedTextField(
- value = possibleValuesText,
- onValueChange = { possibleValuesText = it },
- label = { Text("Possible Values (one per line)") },
- modifier = Modifier.fillMaxWidth().height(120.dp),
- maxLines = 10,
- )
- }
"object", "objects" -> {
DropdownSelector(
label = "Object Type",
@@ -470,14 +454,13 @@ private fun PropertyEditPanel(
TextButton(onClick = onCancel) { Text("Cancel") }
Button(
onClick = {
- val values = possibleValuesText.lines().map { it.trim() }.filter { it.isNotEmpty() }
onConfirm(
PropertyConfig(
key = selectedPropertyKey,
+ id = selectedProperty?.id ?: existing?.id ?: "",
name = selectedProperty?.name ?: existing?.name ?: "",
format = format,
indicator = indicator,
- possibleValues = values,
objectTypeKey = objectTypeKey,
)
)
diff --git a/src/main/kotlin/ch/ergon/anytask/ui/TaskWindow.kt b/src/main/kotlin/ch/ergon/anytask/ui/TaskWindow.kt
index ef969ca..94e1e94 100644
--- a/src/main/kotlin/ch/ergon/anytask/ui/TaskWindow.kt
+++ b/src/main/kotlin/ch/ergon/anytask/ui/TaskWindow.kt
@@ -49,6 +49,7 @@ import androidx.compose.ui.input.key.type
import androidx.compose.ui.unit.dp
import ch.ergon.anytask.api.AnyTypeApi
import ch.ergon.anytask.api.dto.SearchObject
+import ch.ergon.anytask.api.dto.Tag
import ch.ergon.anytask.config.Configuration
import ch.ergon.anytask.api.dto.buildPropertyValue
import ch.ergon.anytask.config.PropertyConfig
@@ -62,6 +63,7 @@ fun TaskWindow(client: AnyTypeApi, config: Configuration, isVisible: Boolean, on
var isLoading by remember { mutableStateOf(false) }
var errorMessage by remember { mutableStateOf(null) }
val propertyValues = remember { mutableStateMapOf() }
+ val propertyTags = remember { mutableStateMapOf>() }
val focusRequester = remember { FocusRequester() }
@@ -144,6 +146,7 @@ fun TaskWindow(client: AnyTypeApi, config: Configuration, isVisible: Boolean, on
value = propertyValues[prop.key] ?: "",
onValueChange = { propertyValues[prop.key] = it },
client = client,
+ tags = propertyTags[prop.key] ?: emptyList(),
)
}
}
@@ -174,7 +177,19 @@ fun TaskWindow(client: AnyTypeApi, config: Configuration, isVisible: Boolean, on
}
}
- LaunchedEffect(isVisible) { if (isVisible) focusRequester.requestFocus() }
+ LaunchedEffect(isVisible) {
+ if (isVisible) {
+ focusRequester.requestFocus()
+ config.properties
+ .filter { it.format in listOf("select", "multi_select") && it.id.isNotEmpty() }
+ .forEach { prop ->
+ launch {
+ client.getTags(prop.id)
+ .onSuccess { propertyTags[prop.key] = it }
+ }
+ }
+ }
+ }
}
@Composable
@@ -183,13 +198,14 @@ private fun PropertyField(
value: String,
onValueChange: (String) -> Unit,
client: AnyTypeApi,
+ tags: List = emptyList(),
) {
when (prop.format) {
"select", "multi_select" -> {
SelectField(
label = prop.name,
value = value,
- options = prop.possibleValues,
+ tags = tags,
onValueChange = onValueChange,
)
}
@@ -231,16 +247,17 @@ private fun PropertyField(
private fun SelectField(
label: String,
value: String,
- options: List,
+ tags: List,
onValueChange: (String) -> Unit,
) {
var expanded by remember { mutableStateOf(false) }
+ val selectedName = tags.find { it.id == value }?.name ?: value
Column {
Text(label, style = MaterialTheme.typography.caption)
Box {
OutlinedButton(onClick = { expanded = true }, modifier = Modifier.fillMaxWidth()) {
- Text(value.ifEmpty { "Select..." })
+ Text(selectedName.ifEmpty { "Select..." })
}
DropdownMenu(expanded = expanded, onDismissRequest = { expanded = false }) {
DropdownMenuItem(onClick = {
@@ -249,12 +266,12 @@ private fun SelectField(
}) {
Text("(none)", style = MaterialTheme.typography.caption)
}
- options.forEach { option ->
+ tags.forEach { tag ->
DropdownMenuItem(onClick = {
- onValueChange(option)
+ onValueChange(tag.id)
expanded = false
}) {
- Text(option)
+ Text(tag.name)
}
}
}