去掉记忆功能

This commit is contained in:
DelLevin-Home
2026-08-18 11:28:46 +08:00
parent 2f7afeb4fa
commit e2f01a90a6
15 changed files with 7 additions and 657 deletions

View File

@@ -1,9 +0,0 @@
package fuck.andes.agent.memory
data class MemoryContext(
val memories: List<String> = emptyList(),
) {
companion object {
val EMPTY = MemoryContext()
}
}

View File

@@ -1,6 +1,5 @@
package fuck.andes.agent.model
import fuck.andes.agent.memory.MemoryContext
import fuck.andes.agent.skill.SkillContext
import org.json.JSONArray
import org.json.JSONObject
@@ -103,25 +102,4 @@ internal object AgentPromptBuilder {
JSONObject()
.put("role", "system")
.put("content", content)
private const val MEMORY_CHAR_BUDGET = 1500
fun buildMemoryAppendText(memoryContext: MemoryContext): String? {
val budgeted = budgetedMemories(memoryContext.memories, MEMORY_CHAR_BUDGET)
if (budgeted.isEmpty()) return null
return "\u3010\u8bb0\u5fc6\u3011\n" + budgeted.joinToString("\n") { "- $it" } +
"\n\n\u4ee5\u4e0a\u662f\u5173\u4e8e\u7528\u6237\u7684\u5df2\u77e5\u4fe1\u606f\uff0c\u5728\u76f8\u5173\u65f6\u53c2\u8003\uff0c\u4f46\u4e0d\u8981\u4e3b\u52a8\u63d0\u53ca\u300c\u8bb0\u5fc6\u300d\u3002"
}
private fun budgetedMemories(memories: List<String>, budget: Int): List<String> {
val result = mutableListOf<String>()
var remaining = budget
for (memory in memories) {
val line = "- $memory\n"
if (line.length > remaining) break
result.add(memory)
remaining -= line.length
}
return result
}
}

View File

@@ -18,9 +18,8 @@ import androidx.room.migration.Migration
RuntimeArchiveRunEntity::class,
RuntimeArchiveEventEntity::class,
SkillRegistryEntity::class,
MemoryEntity::class,
],
version = 13,
version = 14,
exportSchema = false,
)
internal abstract class FuckAndesDatabase : RoomDatabase() {
@@ -28,7 +27,6 @@ internal abstract class FuckAndesDatabase : RoomDatabase() {
abstract fun providerDao(): ProviderDao
abstract fun runtimeRunDao(): RuntimeRunDao
abstract fun skillDao(): SkillDao
abstract fun memoryDao(): MemoryDao
companion object {
@Volatile
@@ -41,7 +39,7 @@ internal abstract class FuckAndesDatabase : RoomDatabase() {
FuckAndesDatabase::class.java,
"fuck_andes.db",
)
.addMigrations(MIGRATION_6_7, MIGRATION_7_8, MIGRATION_8_9, MIGRATION_9_10, MIGRATION_10_11, MIGRATION_11_12, MIGRATION_12_13)
.addMigrations(MIGRATION_6_7, MIGRATION_7_8, MIGRATION_8_9, MIGRATION_9_10, MIGRATION_10_11, MIGRATION_11_12, MIGRATION_12_13, MIGRATION_13_14)
.fallbackToDestructiveMigration(dropAllTables = true)
.build()
.also { instance = it }
@@ -163,5 +161,9 @@ internal abstract class FuckAndesDatabase : RoomDatabase() {
internal val MIGRATION_12_13 = Migration(12, 13) { database ->
database.execSQL("ALTER TABLE memories ADD COLUMN priority INTEGER NOT NULL DEFAULT 0")
}
internal val MIGRATION_13_14 = Migration(13, 14) { database ->
database.execSQL("DROP TABLE IF EXISTS memories")
}
}
}

View File

@@ -1,39 +0,0 @@
package fuck.andes.data.db
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
@Dao
internal interface MemoryDao {
@Query("SELECT * FROM memories WHERE scope = 'global' AND enabled = 1 ORDER BY priority ASC, updated_at DESC")
suspend fun enabledGlobalMemories(): List<MemoryEntity>
@Query("SELECT * FROM memories WHERE scope = :scope ORDER BY priority ASC, updated_at DESC")
suspend fun memoriesByScope(scope: String): List<MemoryEntity>
@Query("SELECT * FROM memories ORDER BY priority ASC, updated_at DESC")
suspend fun allMemories(): List<MemoryEntity>
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun upsert(memory: MemoryEntity)
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun upsertAll(memories: List<MemoryEntity>)
@Query("UPDATE memories SET enabled = :enabled WHERE id = :id")
suspend fun setEnabled(id: String, enabled: Boolean)
@Query("DELETE FROM memories WHERE id = :id")
suspend fun delete(id: String)
@Query("SELECT COUNT(*) FROM memories WHERE scope = 'global' AND enabled = 1")
suspend fun enabledGlobalCount(): Int
@Query("UPDATE memories SET priority = :priority WHERE id = :id")
suspend fun setPriority(id: String, priority: Int)
@Query("SELECT MAX(priority) FROM memories WHERE scope = :scope")
suspend fun maxPriority(scope: String): Int?
}

View File

@@ -1,22 +0,0 @@
package fuck.andes.data.db
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.Index
import androidx.room.PrimaryKey
@Entity(
tableName = "memories",
indices = [
Index("scope"),
],
)
internal data class MemoryEntity(
@PrimaryKey val id: String,
val content: String,
val scope: String,
val enabled: Boolean = true,
@ColumnInfo(name = "priority") val priority: Int = 0,
@ColumnInfo(name = "created_at") val createdAt: Long,
@ColumnInfo(name = "updated_at") val updatedAt: Long,
)

View File

@@ -1,51 +0,0 @@
package fuck.andes.data.repository
import android.content.Context
import fuck.andes.data.db.FuckAndesDatabase
import fuck.andes.data.db.MemoryEntity
import java.util.concurrent.atomic.AtomicReference
internal object MemoryRepository {
private val globalCache = AtomicReference<List<MemoryEntity>>(null)
suspend fun loadGlobalMemories(context: Context): List<MemoryEntity> {
globalCache.get()?.let { return it }
val memories = FuckAndesDatabase.get(context).memoryDao().enabledGlobalMemories()
globalCache.set(memories)
return memories
}
fun invalidateGlobalCache() { globalCache.set(null) }
suspend fun upsert(context: Context, memory: MemoryEntity) {
FuckAndesDatabase.get(context).memoryDao().upsert(memory)
invalidateGlobalCache()
}
suspend fun delete(context: Context, id: String) {
FuckAndesDatabase.get(context).memoryDao().delete(id)
invalidateGlobalCache()
}
suspend fun setEnabled(context: Context, id: String, enabled: Boolean) {
FuckAndesDatabase.get(context).memoryDao().setEnabled(id, enabled)
invalidateGlobalCache()
}
suspend fun allMemoriesForManagement(context: Context, scope: String): List<MemoryEntity> =
FuckAndesDatabase.get(context).memoryDao().memoriesByScope(scope)
suspend fun enabledGlobalCount(context: Context): Int =
FuckAndesDatabase.get(context).memoryDao().enabledGlobalCount()
suspend fun setPriority(context: Context, id: String, priority: Int) {
FuckAndesDatabase.get(context).memoryDao().setPriority(id, priority)
invalidateGlobalCache()
}
suspend fun maxPriority(context: Context, scope: String): Int? =
FuckAndesDatabase.get(context).memoryDao().maxPriority(scope)
suspend fun totalEnabledCharCount(context: Context): Int =
loadGlobalMemories(context).sumOf { it.content.length }
}

View File

@@ -33,7 +33,6 @@ import androidx.lifecycle.compose.LocalLifecycleOwner
import fuck.andes.FuckAndesApp
import fuck.andes.agent.accessibility.AgentAccessibilityService
import fuck.andes.config.Prefs
import fuck.andes.data.repository.MemoryRepository
import fuck.andes.data.repository.ProviderRepository
import fuck.andes.data.repository.RuntimeConfigRepository
import fuck.andes.ui.components.MiuixBackButton
@@ -237,18 +236,6 @@ internal fun SettingsScreen(
onClick = { onNavigate(AppRoute.Skills) },
)
PrefDivider()
ArrowPreference(
title = "记忆管理",
summary = rememberMemorySummary(context),
startAction = {
TintedIcon(
icon = LucideR.drawable.lucide_ic_brain,
tint = ColorOSRoyalBlue,
)
},
onClick = { onNavigate(AppRoute.Memory) },
)
PrefDivider()
ArrowPreference(
title = "权限健康",
startAction = {
@@ -498,19 +485,3 @@ private fun isAgentAccessibilityEnabled(context: Context): Boolean {
).orEmpty()
return enabledServices.split(':').any { it.equals(expected, ignoreCase = true) }
}
@Composable
private fun rememberMemorySummary(context: Context): String {
var summary by remember { mutableStateOf("AI 跨会话记忆用户偏好与事实") }
LaunchedEffect(Unit) {
val result = withContext(Dispatchers.IO) {
runCatching {
val count = MemoryRepository.enabledGlobalCount(context)
val chars = MemoryRepository.totalEnabledCharCount(context)
"$count 条记忆 / $chars / 1500 字预算"
}.getOrElse { "AI 跨会话记忆用户偏好与事实" }
}
summary = result
}
return summary
}

View File

@@ -60,8 +60,6 @@ import fuck.andes.ui.model.AgentSkillsAction
import fuck.andes.ui.model.AgentSystemEnhanceAction
import fuck.andes.ui.model.AgentToolsAction
import fuck.andes.ui.model.ConversationSearchAction
import fuck.andes.ui.model.MemoryDetailAction
import fuck.andes.ui.model.MemoryManagementAction
import fuck.andes.ui.model.PermissionHealthAction
import fuck.andes.ui.model.RunReplayAction
import fuck.andes.ui.navigation.AgentNavigator
@@ -72,8 +70,6 @@ import fuck.andes.ui.screens.enhance.SystemEnhanceScreen
import fuck.andes.ui.screens.home.AgentHomeScreen
import fuck.andes.ui.screens.permissions.PermissionHealthScreen
import fuck.andes.ui.screens.search.ConversationSearchScreen
import fuck.andes.ui.screens.memory.MemoryDetailScreen
import fuck.andes.ui.screens.memory.MemoryManagementScreen
import fuck.andes.ui.screens.replay.RunReplayScreen
import fuck.andes.ui.screens.skills.AgentSkillsScreen
import fuck.andes.ui.screens.terminal.LinuxEnvironmentScreen
@@ -383,58 +379,6 @@ fun AgentAppRoot() {
},
)
}
entry<AppRoute.Memory> {
LaunchedEffect(Unit) {
agentState.refreshMemories()
}
MemoryManagementScreen(
state = agentState.memoryManagementState,
onAction = { action ->
when (action) {
MemoryManagementAction.NavigateBack -> popRoute()
is MemoryManagementAction.ToggleMemory -> agentState.toggleMemory(action.memoryId, action.enabled)
is MemoryManagementAction.DeleteMemory -> agentState.deleteMemory(action.memoryId)
is MemoryManagementAction.EditMemory -> agentState.editMemory(action.memoryId, action.content)
is MemoryManagementAction.AddMemory -> agentState.addMemory(action.content)
is MemoryManagementAction.OpenMemoryDetail -> pushRoute(AppRoute.MemoryDetail(action.memoryId))
MemoryManagementAction.OpenMemoryNew -> pushRoute(AppRoute.MemoryDetail(null))
}
},
)
}
entry<AppRoute.MemoryDetail> { route ->
val memory = if (route.memoryId != null) {
agentState.memoryManagementState.memories.find { it.id == route.memoryId }
} else {
agentState.draftNewMemory
}
if (memory != null) {
MemoryDetailScreen(
memory = memory,
isNew = route.memoryId == null,
onAction = { action ->
when (action) {
MemoryDetailAction.NavigateBack -> popRoute()
is MemoryDetailAction.Save -> {
agentState.editMemory(route.memoryId!!, action.content)
}
is MemoryDetailAction.SaveNew -> {
agentState.addMemory(action.content)
}
MemoryDetailAction.Delete -> {
agentState.deleteMemory(route.memoryId!!)
}
MemoryDetailAction.MoveUp -> {
agentState.moveMemoryUp(route.memoryId!!)
}
MemoryDetailAction.MoveDown -> {
agentState.moveMemoryDown(route.memoryId!!)
}
}
},
)
}
}
entry<AppRoute.ConversationSearch> { route ->
ConversationSearchScreen(
state = agentState.conversationSearchState,

View File

@@ -146,10 +146,8 @@ private fun titleForRoute(route: AppRoute?): String = when (route) {
is AppRoute.Settings -> "设置"
is AppRoute.LinuxEnvironment -> "Linux 工具环境"
is AppRoute.ModelProviders -> "模型提供商"
is AppRoute.Memory -> "记忆管理"
is AppRoute.ConversationSearch -> "搜索对话"
is AppRoute.RunReplay -> "执行回放"
is AppRoute.MemoryDetail -> if (route.memoryId == null) "添加记忆" else "编辑记忆"
is AppRoute.ModelProviderDetail -> route.providerId.let { "Provider 详情" }
is AppRoute.ModelProviderNew -> "新建提供商"
null -> "Eta"

View File

@@ -15,7 +15,6 @@ import fuck.andes.FuckAndesApp
import fuck.andes.agent.accessibility.AgentAccessibilityService
import fuck.andes.agent.device.DeviceLocationProvider
import fuck.andes.agent.media.AgentImageCodec
import fuck.andes.agent.memory.MemoryContext
import fuck.andes.agent.runtime.AgentEvent
import fuck.andes.agent.runtime.AgentExternalArchivePayload
import fuck.andes.agent.runtime.AgentRunArchiveStore
@@ -30,8 +29,6 @@ import fuck.andes.config.Prefs
import fuck.andes.core.AndroidAgentLogger
import fuck.andes.core.safeLogType
import fuck.andes.data.db.FuckAndesDatabase
import fuck.andes.data.db.MemoryEntity
import fuck.andes.data.repository.MemoryRepository
import fuck.andes.data.repository.RuntimeConfigRepository
import fuck.andes.ui.model.AgentChatHomeUiState
import fuck.andes.ui.model.AgentChatMessageUi
@@ -50,8 +47,6 @@ import fuck.andes.ui.model.ConversationSearchMatchUi
import fuck.andes.ui.model.ConversationSearchUiState
import fuck.andes.ui.model.ConversationPaneUiState
import fuck.andes.ui.model.ConversationSummaryUi
import fuck.andes.ui.model.MemoryItemUi
import fuck.andes.ui.model.MemoryManagementUiState
import fuck.andes.ui.model.PermissionHealthItemUi
import fuck.andes.ui.model.PermissionHealthUiState
import fuck.andes.ui.model.PermissionStatusUi
@@ -140,20 +135,6 @@ internal class AgentAppState(
var systemEnhanceState by mutableStateOf(buildSystemEnhanceState())
private set
var memoryManagementState by mutableStateOf(MemoryManagementUiState(isLoading = true))
private set
val draftNewMemory = MemoryItemUi(
id = "new",
content = "",
enabled = true,
createdAt = 0L,
priority = 0,
charCount = 0,
)
private var cachedGlobalMemoryContents: List<String> = emptyList()
init {
refreshConversationSummaries()
runtimeRecoveryInProgress.set(true)
@@ -404,15 +385,7 @@ internal class AgentAppState(
terminalTools = remoteBooleanForUi(Prefs.Keys.AGENT_TERMINAL_TOOLS),
browserTools = remoteBooleanForUi(Prefs.Keys.AGENT_BROWSER_TOOLS),
thinkingEnabled = thinkingEnabled,
)?.let { cfg ->
val memoryContext = MemoryContext(memories = cachedGlobalMemoryContents)
val memoryAppendText = AgentPromptBuilder.buildMemoryAppendText(memoryContext)
if (memoryAppendText != null) {
cfg.copy(systemPrompt = cfg.systemPrompt + "\n\n" + memoryAppendText)
} else {
cfg
}
}
)
if (config == null) {
withContext(Dispatchers.Main) {
applyRunResult(
@@ -516,101 +489,6 @@ internal class AgentAppState(
permissionHealthState = buildPermissionHealthState(appContext, mediaProjectionGranted)
}
fun refreshMemories() {
scope.launch(Dispatchers.IO) {
val memories = runCatching {
MemoryRepository.allMemoriesForManagement(appContext, "global")
}.getOrDefault(emptyList())
val totalEnabledChars = memories.filter { it.enabled }.sumOf { it.content.length }
memoryManagementState = MemoryManagementUiState(
memories = memories.map { it.toItemUi() },
isLoading = false,
totalEnabledChars = totalEnabledChars,
)
refreshMemoryCache()
}
}
fun toggleMemory(memoryId: String, enabled: Boolean) {
scope.launch(Dispatchers.IO) {
val item = memoryManagementState.memories.find { it.id == memoryId } ?: return@launch
MemoryRepository.setEnabled(appContext, memoryId, enabled)
refreshMemories()
}
}
fun deleteMemory(memoryId: String) {
scope.launch(Dispatchers.IO) {
MemoryRepository.delete(appContext, memoryId)
refreshMemories()
}
}
fun editMemory(memoryId: String, content: String) {
scope.launch(Dispatchers.IO) {
val item = memoryManagementState.memories.find { it.id == memoryId } ?: return@launch
val now = System.currentTimeMillis()
MemoryRepository.upsert(appContext, MemoryEntity(
id = memoryId,
content = content,
scope = "global",
enabled = item.enabled,
priority = item.priority,
createdAt = item.createdAt,
updatedAt = now,
))
refreshMemories()
}
}
fun addMemory(content: String) {
scope.launch(Dispatchers.IO) {
val now = System.currentTimeMillis()
val maxP = MemoryRepository.maxPriority(appContext, "global") ?: -1
MemoryRepository.upsert(appContext, MemoryEntity(
id = java.util.UUID.randomUUID().toString(),
content = content,
scope = "global",
enabled = true,
priority = maxP + 1,
createdAt = now,
updatedAt = now,
))
refreshMemories()
}
}
fun moveMemoryUp(memoryId: String) {
val memories = memoryManagementState.memories
val index = memories.indexOfFirst { it.id == memoryId }
if (index <= 0) return
val current = memories[index]
val above = memories[index - 1]
scope.launch(Dispatchers.IO) {
MemoryRepository.setPriority(appContext, current.id, above.priority)
MemoryRepository.setPriority(appContext, above.id, current.priority)
refreshMemories()
}
}
fun moveMemoryDown(memoryId: String) {
val memories = memoryManagementState.memories
val index = memories.indexOfFirst { it.id == memoryId }
if (index < 0 || index >= memories.lastIndex) return
val current = memories[index]
val below = memories[index + 1]
scope.launch(Dispatchers.IO) {
MemoryRepository.setPriority(appContext, current.id, below.priority)
MemoryRepository.setPriority(appContext, below.id, current.priority)
refreshMemories()
}
}
private suspend fun refreshMemoryCache() {
val globalContents = MemoryRepository.loadGlobalMemories(appContext).map { it.content }
cachedGlobalMemoryContents = globalContents
}
var pendingShareIntent by mutableStateOf<Intent?>(null)
private set
@@ -1803,12 +1681,6 @@ private fun buildSystemEnhanceState(): AgentSystemEnhanceUiState =
id = "future",
title = "后续能力",
items = listOf(
SystemEnhanceItemUi(
id = "memory",
title = "记忆系统",
summary = "长期记忆和定时触发器后续接入",
status = SystemEnhanceStatusUi.Inactive,
),
SystemEnhanceItemUi(
id = "hook",
title = "Hook 二级能力",
@@ -1847,15 +1719,6 @@ private fun isRootAvailable(): Boolean {
}
}
private fun MemoryEntity.toItemUi() = MemoryItemUi(
id = id,
content = content,
enabled = enabled,
createdAt = createdAt,
priority = priority,
charCount = content.length,
)
private fun hasAppListAccess(context: Context): Boolean {
return try {
val pm = context.packageManager

View File

@@ -53,25 +53,6 @@ sealed interface AgentSystemEnhanceAction {
data class ToggleItem(val itemId: String) : AgentSystemEnhanceAction
}
sealed interface MemoryManagementAction {
data object NavigateBack : MemoryManagementAction
data class ToggleMemory(val memoryId: String, val enabled: Boolean) : MemoryManagementAction
data class DeleteMemory(val memoryId: String) : MemoryManagementAction
data class EditMemory(val memoryId: String, val content: String) : MemoryManagementAction
data class AddMemory(val content: String) : MemoryManagementAction
data class OpenMemoryDetail(val memoryId: String) : MemoryManagementAction
data object OpenMemoryNew : MemoryManagementAction
}
sealed interface MemoryDetailAction {
data object NavigateBack : MemoryDetailAction
data class Save(val content: String) : MemoryDetailAction
data class SaveNew(val content: String) : MemoryDetailAction
data object Delete : MemoryDetailAction
data object MoveUp : MemoryDetailAction
data object MoveDown : MemoryDetailAction
}
sealed interface ConversationSearchAction {
data object NavigateBack : ConversationSearchAction
data class QueryChanged(val query: String) : ConversationSearchAction

View File

@@ -1,21 +0,0 @@
package fuck.andes.ui.model
import androidx.compose.runtime.Immutable
@Immutable
data class MemoryManagementUiState(
val memories: List<MemoryItemUi> = emptyList(),
val isLoading: Boolean = false,
val totalEnabledChars: Int = 0,
val charBudget: Int = 1500,
)
@Immutable
data class MemoryItemUi(
val id: String,
val content: String,
val enabled: Boolean,
val createdAt: Long,
val priority: Int = 0,
val charCount: Int = 0,
)

View File

@@ -13,8 +13,6 @@ sealed interface AppRoute : NavKey {
data object Settings : AppRoute
data object LinuxEnvironment : AppRoute
data object ModelProviders : AppRoute
data object Memory : AppRoute
data class MemoryDetail(val memoryId: String?) : AppRoute
data class ConversationSearch(val initialQuery: String = "") : AppRoute
data class RunReplay(val runId: String) : AppRoute
data class ModelProviderDetail(val providerId: String) : AppRoute

View File

@@ -1,140 +0,0 @@
package fuck.andes.ui.screens.memory
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import fuck.andes.ui.components.MiuixScaffoldPage
import fuck.andes.ui.components.PrefDivider
import fuck.andes.ui.model.MemoryDetailAction
import fuck.andes.ui.model.MemoryItemUi
import top.yukonga.miuix.kmp.basic.BasicComponent
import top.yukonga.miuix.kmp.basic.Button
import top.yukonga.miuix.kmp.basic.ButtonDefaults
import top.yukonga.miuix.kmp.basic.Card
import top.yukonga.miuix.kmp.basic.SmallTitle
import top.yukonga.miuix.kmp.basic.Text
import top.yukonga.miuix.kmp.basic.TextButton
import top.yukonga.miuix.kmp.basic.TextField
import top.yukonga.miuix.kmp.theme.MiuixTheme
import top.yukonga.miuix.kmp.window.WindowDialog
@Composable
fun MemoryDetailScreen(
memory: MemoryItemUi,
isNew: Boolean,
onAction: (MemoryDetailAction) -> Unit,
modifier: Modifier = Modifier,
) {
var content by remember(memory.id) { mutableStateOf(memory.content) }
var showDeleteConfirm by remember { mutableStateOf(false) }
val hasChanged = content.trim() != memory.content
MiuixScaffoldPage(
title = if (isNew) "添加记忆" else "编辑记忆",
onBack = { onAction(MemoryDetailAction.NavigateBack) },
modifier = modifier,
) {
item(key = "section_content") {
SmallTitle("内容")
}
item(key = "card_content") {
Card(modifier = Modifier.padding(horizontal = 12.dp)) {
TextField(
value = content,
onValueChange = { content = it },
maxLines = 15,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 12.dp),
)
Text(
text = "${content.length}",
fontSize = MiuixTheme.textStyles.body2.fontSize,
color = if (content.length > 1500) MiuixTheme.colorScheme.error
else MiuixTheme.colorScheme.onSurfaceVariantSummary,
modifier = Modifier.padding(horizontal = 16.dp, vertical = 4.dp),
)
}
}
item(key = "section_actions") {
SmallTitle("操作")
}
item(key = "card_actions") {
Card(modifier = Modifier.padding(horizontal = 12.dp)) {
if (hasChanged || isNew) {
BasicComponent(
title = if (isNew) "添加" else "保存",
insideMargin = PaddingValues(16.dp),
onClick = {
if (isNew) {
onAction(MemoryDetailAction.SaveNew(content.trim()))
} else {
onAction(MemoryDetailAction.Save(content.trim()))
}
onAction(MemoryDetailAction.NavigateBack)
},
)
PrefDivider()
}
if (!isNew) {
BasicComponent(
title = "上移",
insideMargin = PaddingValues(16.dp),
onClick = { onAction(MemoryDetailAction.MoveUp) },
)
PrefDivider()
BasicComponent(
title = "下移",
insideMargin = PaddingValues(16.dp),
onClick = { onAction(MemoryDetailAction.MoveDown) },
)
PrefDivider()
BasicComponent(
title = "删除",
insideMargin = PaddingValues(16.dp),
onClick = { showDeleteConfirm = true },
)
}
}
}
}
if (showDeleteConfirm) {
WindowDialog(
show = true,
title = "删除后,该记忆将不可恢复",
onDismissRequest = { showDeleteConfirm = false },
) {
Column(
modifier = Modifier.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(8.dp),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Button(
onClick = {
onAction(MemoryDetailAction.Delete)
onAction(MemoryDetailAction.NavigateBack)
},
colors = ButtonDefaults.buttonColorsPrimary(),
) {
Text("删除该记忆")
}
TextButton(
text = "取消",
onClick = { showDeleteConfirm = false },
)
}
}
}
}

View File

@@ -1,103 +0,0 @@
package fuck.andes.ui.screens.memory
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import com.composables.icons.lucide.R as LucideR
import fuck.andes.ui.components.MiuixScaffoldPage
import fuck.andes.ui.components.PrefDivider
import fuck.andes.ui.model.MemoryItemUi
import fuck.andes.ui.model.MemoryManagementAction
import fuck.andes.ui.model.MemoryManagementUiState
import top.yukonga.miuix.kmp.basic.BasicComponent
import top.yukonga.miuix.kmp.basic.Card
import top.yukonga.miuix.kmp.basic.Icon
import top.yukonga.miuix.kmp.basic.SmallTitle
import top.yukonga.miuix.kmp.theme.MiuixTheme
@Composable
fun MemoryManagementScreen(
state: MemoryManagementUiState,
onAction: (MemoryManagementAction) -> Unit,
modifier: Modifier = Modifier,
) {
MiuixScaffoldPage(
title = "记忆管理",
onBack = { onAction(MemoryManagementAction.NavigateBack) },
modifier = modifier,
) {
item(key = "section_title") {
SmallTitle("记忆")
}
item(key = "budget_summary") {
SmallTitle("${state.totalEnabledChars} / ${state.charBudget}")
}
item(key = "card_memories") {
Card(modifier = Modifier.padding(horizontal = 12.dp)) {
if (state.memories.isEmpty()) {
BasicComponent(
title = "暂无记忆",
summary = "手动添加记忆AI 对话时会参考",
insideMargin = PaddingValues(16.dp),
)
} else {
state.memories.forEachIndexed { index, memory ->
MemoryItemRow(
memory = memory,
onToggle = { onAction(MemoryManagementAction.ToggleMemory(memory.id, it)) },
onClick = { onAction(MemoryManagementAction.OpenMemoryDetail(memory.id)) },
onDelete = { onAction(MemoryManagementAction.DeleteMemory(memory.id)) },
)
if (index < state.memories.lastIndex) {
PrefDivider()
}
}
}
PrefDivider()
BasicComponent(
title = "添加记忆",
insideMargin = PaddingValues(16.dp),
onClick = { onAction(MemoryManagementAction.OpenMemoryNew) },
)
}
}
}
}
@Composable
private fun MemoryItemRow(
memory: MemoryItemUi,
onToggle: (Boolean) -> Unit,
onClick: () -> Unit,
onDelete: () -> Unit,
) {
val firstLine = memory.content.lineSequence().firstOrNull().orEmpty()
BasicComponent(
title = firstLine,
summary = buildString {
append("${memory.charCount}")
val secondLine = if (memory.content.lines().size > 1 || memory.content.length > firstLine.length) {
memory.content.drop(firstLine.length).trimStart().lineSequence().firstOrNull()?.take(40)?.plus("")?.ifBlank { null }
} else null
if (secondLine != null) {
append(" · ")
append(secondLine)
}
},
insideMargin = PaddingValues(16.dp),
endActions = {
Icon(
painter = painterResource(LucideR.drawable.lucide_ic_chevron_right),
contentDescription = null,
modifier = Modifier.size(16.dp),
tint = MiuixTheme.colorScheme.onSurfaceVariantSummary,
)
},
onClick = onClick,
)
}