* fix(#391): resolve deprecation warnings - Icon: Icons.Filled.ArrowForward → Icons.AutoMirrored.Filled.ArrowForward (KanbanScreen) - Room: fallbackToDestructiveMigration() → fallbackToDestructiveMigration(false) (HermesDatabase) - Tab: ScrollableTabRow → PrimaryScrollableTabRow (SettingsScreen) - Clipboard: LocalClipboardManager → LocalClipboard + coroutine-scoped setText (ChatBubble: UserBubble, AssistantBubble, ToolBubble, CopyButton) - ClickableText: pushStringAnnotation → pushLink(LinkAnnotation.Url), ClickableText → Text with native link handling (ChatBubble: RichText) * fix(#392): replace deprecated setText with setClipEntry for new Clipboard API --------- Co-authored-by: M57 <hy4ri@users.noreply.github.com>
This commit is contained in:
1 parent
b1fca60511
commit
8d3d8a11e8
4 files changed
+30
-27
No files matched your search
@@ -39,7 +39,7 @@ abstract class HermesDatabase : RoomDatabase() {
|
|||||||
HermesDatabase::class.java,
|
HermesDatabase::class.java,
|
||||||
"hermes_control.db",
|
"hermes_control.db",
|
||||||
).openHelperFactory(factory)
|
).openHelperFactory(factory)
|
||||||
.fallbackToDestructiveMigration()
|
.fallbackToDestructiveMigration(false)
|
||||||
.build()
|
.build()
|
||||||
.also { instance = it }
|
.also { instance = it }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.m57.hermescontrol.ui.chat
|
package com.m57.hermescontrol.ui.chat
|
||||||
|
|
||||||
|
import android.content.ClipData
|
||||||
import android.text.format.DateFormat
|
import android.text.format.DateFormat
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.animation.animateContentSize
|
import androidx.compose.animation.animateContentSize
|
||||||
@@ -27,7 +28,6 @@ import androidx.compose.foundation.layout.padding
|
|||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.widthIn
|
import androidx.compose.foundation.layout.widthIn
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.foundation.text.ClickableText
|
|
||||||
import androidx.compose.foundation.text.selection.SelectionContainer
|
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Build
|
import androidx.compose.material.icons.filled.Build
|
||||||
@@ -47,6 +47,7 @@ import androidx.compose.runtime.LaunchedEffect
|
|||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
@@ -54,14 +55,15 @@ import androidx.compose.ui.draw.clip
|
|||||||
import androidx.compose.ui.graphics.Brush
|
import androidx.compose.ui.graphics.Brush
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.luminance
|
import androidx.compose.ui.graphics.luminance
|
||||||
import androidx.compose.ui.platform.LocalClipboardManager
|
import androidx.compose.ui.platform.ClipEntry
|
||||||
|
import androidx.compose.ui.platform.LocalClipboard
|
||||||
import androidx.compose.ui.platform.LocalConfiguration
|
import androidx.compose.ui.platform.LocalConfiguration
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.platform.LocalUriHandler
|
|
||||||
import androidx.compose.ui.platform.testTag
|
import androidx.compose.ui.platform.testTag
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.semantics.Role
|
import androidx.compose.ui.semantics.Role
|
||||||
import androidx.compose.ui.text.AnnotatedString
|
import androidx.compose.ui.text.AnnotatedString
|
||||||
|
import androidx.compose.ui.text.LinkAnnotation
|
||||||
import androidx.compose.ui.text.SpanStyle
|
import androidx.compose.ui.text.SpanStyle
|
||||||
import androidx.compose.ui.text.buildAnnotatedString
|
import androidx.compose.ui.text.buildAnnotatedString
|
||||||
import androidx.compose.ui.text.font.FontFamily
|
import androidx.compose.ui.text.font.FontFamily
|
||||||
@@ -82,6 +84,7 @@ import com.m57.hermescontrol.theme.ToolChipColor
|
|||||||
import com.m57.hermescontrol.theme.ToolChipColorLight
|
import com.m57.hermescontrol.theme.ToolChipColorLight
|
||||||
import com.m57.hermescontrol.theme.UserBubble
|
import com.m57.hermescontrol.theme.UserBubble
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.time.ZoneId
|
import java.time.ZoneId
|
||||||
import java.time.format.DateTimeFormatter
|
import java.time.format.DateTimeFormatter
|
||||||
@@ -141,7 +144,8 @@ private fun UserBubble(
|
|||||||
isCurrentMatch: Boolean = false,
|
isCurrentMatch: Boolean = false,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val clipboardManager = LocalClipboardManager.current
|
val clipboard = LocalClipboard.current
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
var showCopyButton by remember { mutableStateOf(false) }
|
var showCopyButton by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
// Auto-dismiss copy button after 4 seconds
|
// Auto-dismiss copy button after 4 seconds
|
||||||
@@ -255,7 +259,9 @@ private fun UserBubble(
|
|||||||
) {
|
) {
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
clipboardManager.setText(AnnotatedString(message.content))
|
scope.launch {
|
||||||
|
clipboard.setClipEntry(ClipEntry(ClipData.newPlainText(null, message.content)))
|
||||||
|
}
|
||||||
showCopyButton = false
|
showCopyButton = false
|
||||||
},
|
},
|
||||||
modifier = Modifier.size(32.dp),
|
modifier = Modifier.size(32.dp),
|
||||||
@@ -298,7 +304,8 @@ private fun AssistantBubble(
|
|||||||
Color(0xFFE8E6EE)
|
Color(0xFFE8E6EE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val clipboardManager = LocalClipboardManager.current
|
val clipboard = LocalClipboard.current
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
var showCopyButton by remember { mutableStateOf(false) }
|
var showCopyButton by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
// Auto-dismiss copy button after 4 seconds
|
// Auto-dismiss copy button after 4 seconds
|
||||||
@@ -385,7 +392,9 @@ private fun AssistantBubble(
|
|||||||
) {
|
) {
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
clipboardManager.setText(AnnotatedString(message.content))
|
scope.launch {
|
||||||
|
clipboard.setClipEntry(ClipEntry(ClipData.newPlainText(null, message.content)))
|
||||||
|
}
|
||||||
showCopyButton = false
|
showCopyButton = false
|
||||||
},
|
},
|
||||||
modifier = Modifier.size(32.dp),
|
modifier = Modifier.size(32.dp),
|
||||||
@@ -706,7 +715,8 @@ private fun ToolBubble(
|
|||||||
}
|
}
|
||||||
val config = ToolSchemaRegistry.getDisplayConfig(message.toolName)
|
val config = ToolSchemaRegistry.getDisplayConfig(message.toolName)
|
||||||
|
|
||||||
val clipboardManager = LocalClipboardManager.current
|
val clipboard = LocalClipboard.current
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
var showCopyButton by remember { mutableStateOf(false) }
|
var showCopyButton by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
// Auto-dismiss copy button after 4 seconds
|
// Auto-dismiss copy button after 4 seconds
|
||||||
@@ -775,7 +785,6 @@ private fun ToolBubble(
|
|||||||
CopyButton(
|
CopyButton(
|
||||||
visible = showCopyButton,
|
visible = showCopyButton,
|
||||||
textToCopy = message.content,
|
textToCopy = message.content,
|
||||||
clipboardManager = clipboardManager,
|
|
||||||
onCopy = { showCopyButton = false },
|
onCopy = { showCopyButton = false },
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
@@ -806,7 +815,6 @@ private fun ToolBubble(
|
|||||||
CopyButton(
|
CopyButton(
|
||||||
visible = showCopyButton,
|
visible = showCopyButton,
|
||||||
textToCopy = message.content,
|
textToCopy = message.content,
|
||||||
clipboardManager = clipboardManager,
|
|
||||||
onCopy = { showCopyButton = false },
|
onCopy = { showCopyButton = false },
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
@@ -845,7 +853,6 @@ private fun ToolBubble(
|
|||||||
CopyButton(
|
CopyButton(
|
||||||
visible = showCopyButton,
|
visible = showCopyButton,
|
||||||
textToCopy = message.content,
|
textToCopy = message.content,
|
||||||
clipboardManager = clipboardManager,
|
|
||||||
onCopy = { showCopyButton = false },
|
onCopy = { showCopyButton = false },
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
@@ -921,10 +928,11 @@ private fun HeaderRow(
|
|||||||
private fun CopyButton(
|
private fun CopyButton(
|
||||||
visible: Boolean,
|
visible: Boolean,
|
||||||
textToCopy: String,
|
textToCopy: String,
|
||||||
clipboardManager: androidx.compose.ui.platform.ClipboardManager,
|
|
||||||
onCopy: () -> Unit,
|
onCopy: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
|
val clipboard = LocalClipboard.current
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
AnimatedVisibility(
|
AnimatedVisibility(
|
||||||
visible = visible,
|
visible = visible,
|
||||||
enter = fadeIn() + scaleIn(),
|
enter = fadeIn() + scaleIn(),
|
||||||
@@ -938,7 +946,9 @@ private fun CopyButton(
|
|||||||
) {
|
) {
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
clipboardManager.setText(AnnotatedString(textToCopy))
|
scope.launch {
|
||||||
|
clipboard.setClipEntry(ClipEntry(ClipData.newPlainText(null, textToCopy)))
|
||||||
|
}
|
||||||
onCopy()
|
onCopy()
|
||||||
},
|
},
|
||||||
modifier = Modifier.size(32.dp),
|
modifier = Modifier.size(32.dp),
|
||||||
@@ -964,7 +974,6 @@ private fun RichText(
|
|||||||
searchQuery: String = "",
|
searchQuery: String = "",
|
||||||
isCurrentMatch: Boolean = false,
|
isCurrentMatch: Boolean = false,
|
||||||
) {
|
) {
|
||||||
val uriHandler = LocalUriHandler.current
|
|
||||||
val urlPattern = remember { Regex("""https?://[^\s)>\]\u0022\u0027]+""") }
|
val urlPattern = remember { Regex("""https?://[^\s)>\]\u0022\u0027]+""") }
|
||||||
val linkColor = MaterialTheme.colorScheme.primary
|
val linkColor = MaterialTheme.colorScheme.primary
|
||||||
val searchHighlightColor = if (isCurrentMatch) Color(0xFFF57C00) else Color(0xFFFFF176).copy(alpha = 0.9f)
|
val searchHighlightColor = if (isCurrentMatch) Color(0xFFF57C00) else Color(0xFFFFF176).copy(alpha = 0.9f)
|
||||||
@@ -1033,7 +1042,7 @@ private fun RichText(
|
|||||||
// URL: https://...
|
// URL: https://...
|
||||||
urlPattern.matchAt(src, i) != null -> {
|
urlPattern.matchAt(src, i) != null -> {
|
||||||
val match = urlPattern.matchAt(src, i)!!
|
val match = urlPattern.matchAt(src, i)!!
|
||||||
pushStringAnnotation(tag = "URL", annotation = match.value)
|
pushLink(LinkAnnotation.Url(match.value))
|
||||||
withStyle(
|
withStyle(
|
||||||
SpanStyle(
|
SpanStyle(
|
||||||
color = linkColor,
|
color = linkColor,
|
||||||
@@ -1064,15 +1073,9 @@ private fun RichText(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ClickableText(
|
Text(
|
||||||
text = annotated,
|
text = annotated,
|
||||||
style = MaterialTheme.typography.bodyMedium.copy(color = textColor),
|
style = MaterialTheme.typography.bodyMedium.copy(color = textColor),
|
||||||
onClick = { offset: Int ->
|
|
||||||
annotated
|
|
||||||
.getStringAnnotations("URL", offset, offset)
|
|
||||||
.firstOrNull()
|
|
||||||
?.let { uriHandler.openUri(it.item) }
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import androidx.compose.foundation.lazy.LazyRow
|
|||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||||
import androidx.compose.material.icons.filled.ArrowForward
|
import androidx.compose.material.icons.automirrored.filled.ArrowForward
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.Card
|
import androidx.compose.material3.Card
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
@@ -253,7 +253,7 @@ fun TaskCard(
|
|||||||
if (onMoveRight != null) {
|
if (onMoveRight != null) {
|
||||||
IconButton(onClick = onMoveRight) {
|
IconButton(onClick = onMoveRight) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Filled.ArrowForward,
|
imageVector = Icons.AutoMirrored.Filled.ArrowForward,
|
||||||
contentDescription = stringResource(R.string.content_desc_move_right),
|
contentDescription = stringResource(R.string.content_desc_move_right),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ import androidx.compose.material3.MaterialTheme
|
|||||||
import androidx.compose.material3.OutlinedButton
|
import androidx.compose.material3.OutlinedButton
|
||||||
import androidx.compose.material3.OutlinedTextField
|
import androidx.compose.material3.OutlinedTextField
|
||||||
import androidx.compose.material3.OutlinedTextFieldDefaults
|
import androidx.compose.material3.OutlinedTextFieldDefaults
|
||||||
import androidx.compose.material3.ScrollableTabRow
|
import androidx.compose.material3.PrimaryScrollableTabRow
|
||||||
import androidx.compose.material3.SegmentedButton
|
import androidx.compose.material3.SegmentedButton
|
||||||
import androidx.compose.material3.SegmentedButtonDefaults
|
import androidx.compose.material3.SegmentedButtonDefaults
|
||||||
import androidx.compose.material3.SingleChoiceSegmentedButtonRow
|
import androidx.compose.material3.SingleChoiceSegmentedButtonRow
|
||||||
@@ -101,7 +101,7 @@ fun SettingsScreen(
|
|||||||
Modifier
|
Modifier
|
||||||
.fillMaxSize(),
|
.fillMaxSize(),
|
||||||
) {
|
) {
|
||||||
ScrollableTabRow(
|
PrimaryScrollableTabRow(
|
||||||
selectedTabIndex = selectedTab.ordinal,
|
selectedTabIndex = selectedTab.ordinal,
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
edgePadding = 0.dp,
|
edgePadding = 0.dp,
|
||||||
|
|||||||
Reference in new issue
Block a user