diff --git a/app/build.gradle.kts b/app/build.gradle.kts index ea90037..6bbb52f 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -46,6 +46,7 @@ dependencies { implementation(libs.androidx.activity.compose) implementation(libs.androidx.compose.material3) implementation(libs.androidx.compose.material.icons.extended) + implementation(libs.compose.icons.lucide) implementation(libs.androidx.compose.ui) implementation(libs.androidx.compose.ui.graphics) implementation(libs.androidx.compose.ui.tooling.preview) diff --git a/app/src/main/java/dev/kaizen/app/chat/ChatComponents.kt b/app/src/main/java/dev/kaizen/app/chat/ChatComponents.kt index 5ad3456..c8a1483 100644 --- a/app/src/main/java/dev/kaizen/app/chat/ChatComponents.kt +++ b/app/src/main/java/dev/kaizen/app/chat/ChatComponents.kt @@ -39,10 +39,6 @@ import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.text.BasicTextField import androidx.compose.foundation.verticalScroll -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.rounded.Add -import androidx.compose.material.icons.rounded.CameraAlt -import androidx.compose.material.icons.rounded.Call import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text @@ -79,28 +75,12 @@ import java.time.format.DateTimeFormatter import java.util.Locale import android.graphics.BitmapFactory import androidx.compose.foundation.Image -import androidx.compose.material.icons.rounded.AudioFile -import androidx.compose.material.icons.rounded.Close -import androidx.compose.material.icons.rounded.Code -import androidx.compose.material.icons.rounded.Image -import androidx.compose.material.icons.automirrored.rounded.InsertDriveFile -import androidx.compose.material.icons.rounded.PictureAsPdf -import androidx.compose.material.icons.rounded.VideoFile import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue -import androidx.compose.material.icons.rounded.Bolt -import androidx.compose.material.icons.rounded.Check -import androidx.compose.material.icons.rounded.ContentCopy -import androidx.compose.material.icons.rounded.Delete -import androidx.compose.material.icons.rounded.Mic -import androidx.compose.material.icons.rounded.Refresh import androidx.compose.ui.platform.LocalClipboard import dev.kaizen.app.haptics.rememberHaptics import androidx.compose.runtime.rememberCoroutineScope import kotlinx.coroutines.launch -import androidx.compose.material.icons.rounded.ExpandMore -import androidx.compose.material.icons.rounded.Psychology -import androidx.compose.material.icons.rounded.Tune import androidx.compose.runtime.setValue import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.graphics.asImageBitmap @@ -111,6 +91,7 @@ import kotlinx.coroutines.withContext import okhttp3.OkHttpClient import okhttp3.Request import java.util.concurrent.TimeUnit +import dev.kaizen.app.ui.icon.KaizenIcons @Composable fun KaizenHeader(modifier: Modifier = Modifier) { @@ -199,11 +180,11 @@ fun ModePillsRow( .padding(horizontal = 13.dp, vertical = 8.dp), verticalAlignment = Alignment.CenterVertically, ) { - Icon(Icons.Rounded.Psychology, null, tint = cs.onSurfaceVariant, modifier = Modifier.size(16.dp)) + Icon(KaizenIcons.Brain, null, tint = cs.onSurfaceVariant, modifier = Modifier.size(16.dp)) Spacer(Modifier.width(6.dp)) Text(stringResource(reasoningPreset.label), color = cs.onSurfaceVariant, fontSize = 13.sp, fontWeight = FontWeight.Medium) Spacer(Modifier.width(4.dp)) - Icon(Icons.Rounded.ExpandMore, null, tint = cs.onSurfaceVariant.copy(alpha = 0.5f), modifier = Modifier.size(14.dp)) + Icon(KaizenIcons.ChevronDown, null, tint = cs.onSurfaceVariant.copy(alpha = 0.5f), modifier = Modifier.size(14.dp)) } DropdownMenu(expanded = showReasoningMenu, onDismissRequest = { showReasoningMenu = false }) { ReasoningPreset.entries.forEach { preset -> @@ -234,11 +215,11 @@ fun ModePillsRow( .padding(horizontal = 13.dp, vertical = 8.dp), verticalAlignment = Alignment.CenterVertically, ) { - Icon(Icons.Rounded.Tune, null, tint = if (anyEnabled) accent.primary else cs.onSurfaceVariant, modifier = Modifier.size(16.dp)) + Icon(KaizenIcons.SlidersHorizontal, null, tint = if (anyEnabled) accent.primary else cs.onSurfaceVariant, modifier = Modifier.size(16.dp)) Spacer(Modifier.width(6.dp)) Text(stringResource(R.string.mode_sampling), color = if (anyEnabled) cs.onBackground else cs.onSurfaceVariant, fontSize = 13.sp, fontWeight = FontWeight.Medium) Spacer(Modifier.width(4.dp)) - Icon(Icons.Rounded.ExpandMore, null, tint = cs.onSurfaceVariant.copy(alpha = 0.5f), modifier = Modifier.size(14.dp)) + Icon(KaizenIcons.ChevronDown, null, tint = cs.onSurfaceVariant.copy(alpha = 0.5f), modifier = Modifier.size(14.dp)) } DropdownMenu(expanded = showSamplingPopup, onDismissRequest = { showSamplingPopup = false }) { Column(Modifier.padding(horizontal = 16.dp, vertical = 8.dp).width(240.dp)) { @@ -332,7 +313,7 @@ fun TokenBadge(used: Int, contextLength: Int, modifier: Modifier = Modifier) { Modifier.padding(horizontal = 10.dp, vertical = 7.dp), verticalAlignment = Alignment.CenterVertically, ) { - Icon(Icons.Rounded.Bolt, null, tint = accent.primary, modifier = Modifier.size(14.dp)) + Icon(KaizenIcons.Zap, null, tint = accent.primary, modifier = Modifier.size(14.dp)) Spacer(Modifier.width(4.dp)) Text( "${formatK(used)} / ${formatK(contextLength)}", @@ -416,7 +397,7 @@ fun MessageRow( horizontalArrangement = Arrangement.spacedBy(2.dp), ) { MessageActionButton( - icon = if (copied) Icons.Rounded.Check else Icons.Rounded.ContentCopy, + icon = if (copied) KaizenIcons.Check else KaizenIcons.Copy, tint = if (copied) Color(0xFF10B981) else cs.onSurfaceVariant.copy(alpha = 0.35f), ) { haptics.tick() @@ -428,7 +409,7 @@ fun MessageRow( } } onDelete?.let { del -> - MessageActionButton(Icons.Rounded.Delete, Color(0xFFEF4444).copy(alpha = 0.6f)) { + MessageActionButton(KaizenIcons.Trash, Color(0xFFEF4444).copy(alpha = 0.6f)) { haptics.tick(); del(message.wireId) } } @@ -469,7 +450,7 @@ fun MessageRow( horizontalArrangement = Arrangement.spacedBy(2.dp), ) { MessageActionButton( - icon = if (copied) Icons.Rounded.Check else Icons.Rounded.ContentCopy, + icon = if (copied) KaizenIcons.Check else KaizenIcons.Copy, tint = if (copied) Color(0xFF10B981) else cs.onSurfaceVariant.copy(alpha = 0.35f), ) { haptics.tick() @@ -481,12 +462,12 @@ fun MessageRow( } } onRegenerate?.let { regen -> - MessageActionButton(Icons.Rounded.Refresh, cs.onSurfaceVariant.copy(alpha = 0.35f)) { + MessageActionButton(KaizenIcons.RefreshCw, cs.onSurfaceVariant.copy(alpha = 0.35f)) { haptics.tick(); regen(message.wireId) } } onDelete?.let { del -> - MessageActionButton(Icons.Rounded.Delete, Color(0xFFEF4444).copy(alpha = 0.6f)) { + MessageActionButton(KaizenIcons.Trash, Color(0xFFEF4444).copy(alpha = 0.6f)) { haptics.tick(); del(message.wireId) } } @@ -575,7 +556,7 @@ fun ChatInput( } Row(Modifier.padding(start = 6.dp, end = 8.dp, top = 10.dp, bottom = 10.dp), verticalAlignment = Alignment.Bottom) { Box(Modifier.size(42.dp).clip(KaizenShapes.circle).clickable(onClick = onAddClick), contentAlignment = Alignment.Center) { - Icon(Icons.Rounded.Add, stringResource(R.string.chat_attachment), tint = cs.onSurfaceVariant, modifier = Modifier.size(22.dp)) + Icon(KaizenIcons.Plus, stringResource(R.string.chat_attachment), tint = cs.onSurfaceVariant, modifier = Modifier.size(22.dp)) } Box(Modifier.weight(1f).heightIn(min = 42.dp).padding(horizontal = 6.dp), contentAlignment = Alignment.CenterStart) { if (value.isEmpty()) Text(stringResource(R.string.chat_input_placeholder), color = cs.onSurfaceVariant.copy(alpha = 0.6f), fontSize = 15.sp) @@ -591,7 +572,7 @@ fun ChatInput( SendButton(enabled = canSend, onClick = onSend) } else { Box(Modifier.size(44.dp).clip(KaizenShapes.circle), contentAlignment = Alignment.Center) { - Icon(Icons.Rounded.Mic, stringResource(R.string.chat_call), tint = cs.onSurfaceVariant, modifier = Modifier.size(22.dp)) + Icon(KaizenIcons.Mic, stringResource(R.string.chat_call), tint = cs.onSurfaceVariant, modifier = Modifier.size(22.dp)) } } } @@ -611,11 +592,11 @@ private fun PendingFileChip(pf: PendingFile, onRemove: () -> Unit) { NetworkImage(url = pf.uploaded.url, contentDescription = pf.name, modifier = Modifier.fillMaxSize().clip(KaizenShapes.xs)) } else { val icon = when { - pf.mimeType.startsWith("image/") -> Icons.Rounded.Image - pf.mimeType.startsWith("video/") -> Icons.Rounded.VideoFile - pf.mimeType.startsWith("audio/") -> Icons.Rounded.AudioFile - pf.mimeType == "application/pdf" -> Icons.Rounded.PictureAsPdf - else -> Icons.AutoMirrored.Rounded.InsertDriveFile + pf.mimeType.startsWith("image/") -> KaizenIcons.Image + pf.mimeType.startsWith("video/") -> KaizenIcons.FileVideo + pf.mimeType.startsWith("audio/") -> KaizenIcons.FileAudio + pf.mimeType == "application/pdf" -> KaizenIcons.FileText + else -> KaizenIcons.File } Column(horizontalAlignment = Alignment.CenterHorizontally) { Icon(icon, null, tint = cs.onSurfaceVariant, modifier = Modifier.size(20.dp)) @@ -629,14 +610,14 @@ private fun PendingFileChip(pf: PendingFile, onRemove: () -> Unit) { } if (pf.error != null) { Box(Modifier.fillMaxSize().background(Color(0xFFDC2626).copy(alpha = 0.3f)), contentAlignment = Alignment.Center) { - Icon(Icons.Rounded.Close, stringResource(R.string.chat_error), tint = Color.White, modifier = Modifier.size(18.dp)) + Icon(KaizenIcons.X, stringResource(R.string.chat_error), tint = Color.White, modifier = Modifier.size(18.dp)) } } Box( Modifier.align(Alignment.TopEnd).padding(2.dp).size(18.dp).clip(KaizenShapes.circle).background(Color.Black.copy(alpha = 0.5f)).clickable(onClick = onRemove), contentAlignment = Alignment.Center, ) { - Icon(Icons.Rounded.Close, stringResource(R.string.chat_remove), tint = Color.White, modifier = Modifier.size(12.dp)) + Icon(KaizenIcons.X, stringResource(R.string.chat_remove), tint = Color.White, modifier = Modifier.size(12.dp)) } } } @@ -718,11 +699,11 @@ private fun FileChip(attachment: Attachment) { val isDark = isSystemInDarkTheme() val bg = if (isDark) Color.White.copy(alpha = 0.08f) else Color.Black.copy(alpha = 0.06f) val icon = when (attachment.kind) { - "pdf" -> Icons.Rounded.PictureAsPdf - "audio" -> Icons.Rounded.AudioFile - "video" -> Icons.Rounded.VideoFile - "code" -> Icons.Rounded.Code - else -> Icons.AutoMirrored.Rounded.InsertDriveFile + "pdf" -> KaizenIcons.FileText + "audio" -> KaizenIcons.FileAudio + "video" -> KaizenIcons.FileVideo + "code" -> KaizenIcons.Code + else -> KaizenIcons.File } Row(Modifier.clip(KaizenShapes.xs).background(bg).padding(horizontal = 10.dp, vertical = 6.dp), verticalAlignment = Alignment.CenterVertically) { Icon(icon, null, tint = cs.onSurfaceVariant, modifier = Modifier.size(16.dp)) @@ -746,9 +727,9 @@ fun AttachmentMenu( verticalArrangement = Arrangement.spacedBy(8.dp), horizontalAlignment = Alignment.Start, ) { - AttachPill(Icons.Rounded.CameraAlt, stringResource(R.string.attach_camera), accent.primary, isDark, onCamera) - AttachPill(Icons.Rounded.Image, stringResource(R.string.attach_gallery), Color(0xFF6366F1), isDark, onGallery) - AttachPill(Icons.AutoMirrored.Rounded.InsertDriveFile, stringResource(R.string.attach_file), Color(0xFF10B981), isDark, onFile) + AttachPill(KaizenIcons.Camera, stringResource(R.string.attach_camera), accent.primary, isDark, onCamera) + AttachPill(KaizenIcons.Image, stringResource(R.string.attach_gallery), Color(0xFF6366F1), isDark, onGallery) + AttachPill(KaizenIcons.File, stringResource(R.string.attach_file), Color(0xFF10B981), isDark, onFile) } } diff --git a/app/src/main/java/dev/kaizen/app/chat/ChatModels.kt b/app/src/main/java/dev/kaizen/app/chat/ChatModels.kt index f988668..6251b46 100644 --- a/app/src/main/java/dev/kaizen/app/chat/ChatModels.kt +++ b/app/src/main/java/dev/kaizen/app/chat/ChatModels.kt @@ -1,13 +1,5 @@ package dev.kaizen.app.chat -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.rounded.Adjust -import androidx.compose.material.icons.rounded.AutoAwesome -import androidx.compose.material.icons.rounded.Image -import androidx.compose.material.icons.rounded.Language -import androidx.compose.material.icons.rounded.Movie -import androidx.compose.material.icons.rounded.MusicNote -import androidx.compose.material.icons.rounded.Tune import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.stringResource @@ -15,6 +7,7 @@ import dev.kaizen.app.R import dev.kaizen.app.net.Attachment import dev.kaizen.app.net.SearchSource import dev.kaizen.app.net.ToolEvent +import dev.kaizen.app.ui.icon.KaizenIcons enum class Role { User, Assistant } @@ -54,21 +47,21 @@ data class Message( data class Suggestion(val labelRes: Int, val promptRes: Int, val icon: ImageVector) val suggestions = listOf( - Suggestion(R.string.suggest_brainstorm, R.string.suggest_brainstorm_prompt, Icons.Rounded.AutoAwesome), - Suggestion(R.string.suggest_image, R.string.suggest_image_prompt, Icons.Rounded.Image), - Suggestion(R.string.suggest_video, R.string.suggest_video_prompt, Icons.Rounded.Movie), - Suggestion(R.string.suggest_web, R.string.suggest_web_prompt, Icons.Rounded.Language), + Suggestion(R.string.suggest_brainstorm, R.string.suggest_brainstorm_prompt, KaizenIcons.Sparkles), + Suggestion(R.string.suggest_image, R.string.suggest_image_prompt, KaizenIcons.Image), + Suggestion(R.string.suggest_video, R.string.suggest_video_prompt, KaizenIcons.Video), + Suggestion(R.string.suggest_web, R.string.suggest_web_prompt, KaizenIcons.Globe), ) data class ChatMode(val labelRes: Int, val icon: ImageVector) val chatModes = listOf( - ChatMode(R.string.mode_standard, Icons.Rounded.Adjust), - ChatMode(R.string.mode_sampling, Icons.Rounded.Tune), - ChatMode(R.string.mode_image, Icons.Rounded.Image), - ChatMode(R.string.mode_search, Icons.Rounded.Language), - ChatMode(R.string.mode_video, Icons.Rounded.Movie), - ChatMode(R.string.mode_audio, Icons.Rounded.MusicNote), + ChatMode(R.string.mode_standard, KaizenIcons.CircleDot), + ChatMode(R.string.mode_sampling, KaizenIcons.SlidersHorizontal), + ChatMode(R.string.mode_image, KaizenIcons.Image), + ChatMode(R.string.mode_search, KaizenIcons.Globe), + ChatMode(R.string.mode_video, KaizenIcons.Video), + ChatMode(R.string.mode_audio, KaizenIcons.Music), ) @Composable diff --git a/app/src/main/java/dev/kaizen/app/chat/ChatScreen.kt b/app/src/main/java/dev/kaizen/app/chat/ChatScreen.kt index 22bc549..5eea00b 100644 --- a/app/src/main/java/dev/kaizen/app/chat/ChatScreen.kt +++ b/app/src/main/java/dev/kaizen/app/chat/ChatScreen.kt @@ -29,10 +29,7 @@ import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.rounded.KeyboardArrowDown -import androidx.compose.material.icons.rounded.KeyboardArrowUp -import androidx.compose.material.icons.rounded.Menu +import dev.kaizen.app.ui.icon.KaizenIcons import androidx.compose.material3.DrawerValue import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme @@ -665,7 +662,7 @@ fun ChatScreen( .clickable { scope.launch { listState.animateScrollToItem(0) } }, ) { Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { - Icon(Icons.Rounded.KeyboardArrowUp, null, tint = MaterialTheme.colorScheme.onBackground, modifier = Modifier.size(20.dp)) + Icon(KaizenIcons.ArrowUp, null, tint = MaterialTheme.colorScheme.onBackground, modifier = Modifier.size(20.dp)) } } } else if (!atBottom && canScrollDown) { @@ -681,7 +678,7 @@ fun ChatScreen( .clickable { scope.launch { listState.animateScrollToItem(messages.size - 1) } }, ) { Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { - Icon(Icons.Rounded.KeyboardArrowDown, null, tint = MaterialTheme.colorScheme.onBackground, modifier = Modifier.size(20.dp)) + Icon(KaizenIcons.ArrowDown, null, tint = MaterialTheme.colorScheme.onBackground, modifier = Modifier.size(20.dp)) } } } @@ -730,7 +727,7 @@ fun ChatScreen( contentAlignment = Alignment.Center, ) { Icon( - imageVector = Icons.Rounded.Menu, + imageVector = KaizenIcons.Menu, contentDescription = context.getString(R.string.chat_open_menu), tint = MaterialTheme.colorScheme.onSurfaceVariant, modifier = Modifier.size(17.dp), diff --git a/app/src/main/java/dev/kaizen/app/chat/Markdown.kt b/app/src/main/java/dev/kaizen/app/chat/Markdown.kt index fcbb79f..a4d36b4 100644 --- a/app/src/main/java/dev/kaizen/app/chat/Markdown.kt +++ b/app/src/main/java/dev/kaizen/app/chat/Markdown.kt @@ -19,9 +19,6 @@ import androidx.compose.foundation.layout.width import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape import dev.kaizen.app.ui.shape.KaizenShapes -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.rounded.Check -import androidx.compose.material.icons.rounded.ContentCopy import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text @@ -49,6 +46,7 @@ import androidx.compose.ui.unit.sp import dev.kaizen.app.haptics.rememberHaptics import kotlinx.coroutines.delay import kotlinx.coroutines.launch +import dev.kaizen.app.ui.icon.KaizenIcons private sealed interface MdBlock { data class Paragraph(val text: String) : MdBlock @@ -526,7 +524,7 @@ private fun CopyButton(code: String) { contentAlignment = Alignment.Center, ) { Icon( - imageVector = if (copied) Icons.Rounded.Check else Icons.Rounded.ContentCopy, + imageVector = if (copied) KaizenIcons.Check else KaizenIcons.Copy, contentDescription = "Kopieren", tint = if (copied) Color(0xFF22C55E) else cs.onSurfaceVariant.copy(alpha = 0.5f), modifier = Modifier.size(14.dp), diff --git a/app/src/main/java/dev/kaizen/app/chat/ModelSheet.kt b/app/src/main/java/dev/kaizen/app/chat/ModelSheet.kt index 14299e3..8619837 100644 --- a/app/src/main/java/dev/kaizen/app/chat/ModelSheet.kt +++ b/app/src/main/java/dev/kaizen/app/chat/ModelSheet.kt @@ -8,8 +8,6 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import dev.kaizen.app.ui.shape.KaizenShapes import androidx.compose.foundation.text.BasicTextField -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.rounded.* import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment @@ -25,6 +23,7 @@ import dev.kaizen.app.R import androidx.compose.ui.res.stringResource import dev.kaizen.app.ui.theme.LocalKaizenAccent import dev.kaizen.app.haptics.rememberHaptics +import dev.kaizen.app.ui.icon.KaizenIcons /** Human label + composite-id resolver for the top pill — falls back to a de-prefixed id. */ fun modelDisplayName(modelId: String, models: List): String = @@ -52,7 +51,7 @@ fun ModelPill(label: String, onClick: () -> Unit, modifier: Modifier = Modifier) maxLines = 1, ) Icon( - Icons.Rounded.ExpandMore, + KaizenIcons.ChevronDown, contentDescription = null, tint = cs.onSurfaceVariant, modifier = Modifier.size(18.dp), @@ -139,7 +138,7 @@ fun ModelSheet( verticalAlignment = Alignment.CenterVertically ) { Icon( - Icons.Rounded.Search, + KaizenIcons.Search, contentDescription = null, tint = cs.onSurfaceVariant.copy(alpha = 0.6f), modifier = Modifier.size(18.dp) @@ -167,7 +166,7 @@ fun ModelSheet( } if (query.isNotEmpty()) { Icon( - Icons.Rounded.Close, + KaizenIcons.X, contentDescription = stringResource(R.string.model_search_clear), tint = cs.onSurfaceVariant.copy(alpha = 0.6f), modifier = Modifier @@ -294,7 +293,7 @@ private fun ModelRow( contentAlignment = Alignment.Center, ) { Icon( - imageVector = if (favorite) Icons.Rounded.Star else Icons.Rounded.StarBorder, + imageVector = if (favorite) KaizenIcons.Star else KaizenIcons.StarOff, contentDescription = stringResource(R.string.model_favorite), tint = if (favorite) accent.primary else cs.onSurfaceVariant.copy(alpha = 0.35f), modifier = Modifier.size(20.dp), @@ -304,7 +303,7 @@ private fun ModelRow( if (selected) { Spacer(Modifier.size(4.dp)) Icon( - Icons.Rounded.Check, + KaizenIcons.Check, contentDescription = stringResource(R.string.model_selected), tint = accent.primary, modifier = Modifier.size(20.dp) diff --git a/app/src/main/java/dev/kaizen/app/chat/Sidebar.kt b/app/src/main/java/dev/kaizen/app/chat/Sidebar.kt index 3cca414..79e7ac0 100644 --- a/app/src/main/java/dev/kaizen/app/chat/Sidebar.kt +++ b/app/src/main/java/dev/kaizen/app/chat/Sidebar.kt @@ -22,17 +22,6 @@ import androidx.compose.foundation.lazy.items import androidx.compose.foundation.text.BasicTextField import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.automirrored.rounded.Logout -import androidx.compose.material.icons.rounded.Delete -import androidx.compose.material.icons.rounded.Edit -import androidx.compose.material.icons.rounded.Lock -import androidx.compose.material.icons.rounded.LockOpen -import androidx.compose.material.icons.rounded.MoreVert -import androidx.compose.material.icons.rounded.PushPin -import androidx.compose.material.icons.rounded.Search -import androidx.compose.material.icons.rounded.Settings -import androidx.compose.material.icons.rounded.Star import androidx.compose.material3.AlertDialog import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.material3.Icon @@ -70,6 +59,7 @@ import java.time.LocalDate import java.time.ZoneId import java.time.format.DateTimeFormatter import java.util.Locale +import dev.kaizen.app.ui.icon.KaizenIcons private data class DateLabels(val today: String, val yesterday: String, val earlier: String, val pinned: String) @@ -156,7 +146,7 @@ fun KaizenSidebar( .clickable { onNewChat() }, contentAlignment = Alignment.Center ) { - Icon(Icons.Rounded.Edit, stringResource(R.string.chat_new), tint = cs.onBackground, modifier = Modifier.size(17.dp)) + Icon(KaizenIcons.Pencil, stringResource(R.string.chat_new), tint = cs.onBackground, modifier = Modifier.size(17.dp)) } } @@ -171,7 +161,7 @@ fun KaizenSidebar( .padding(horizontal = 14.dp, vertical = 9.dp), verticalAlignment = Alignment.CenterVertically ) { - Icon(Icons.Rounded.Search, null, tint = cs.onSurfaceVariant.copy(alpha = 0.45f), modifier = Modifier.size(16.dp)) + Icon(KaizenIcons.Search, null, tint = cs.onSurfaceVariant.copy(alpha = 0.45f), modifier = Modifier.size(16.dp)) Spacer(Modifier.width(8.dp)) Text(stringResource(R.string.chat_search), color = cs.onSurfaceVariant.copy(alpha = 0.45f), fontSize = 13.sp) } @@ -261,7 +251,7 @@ fun KaizenSidebar( modifier = Modifier.weight(1f), ) Icon( - Icons.Rounded.Settings, + KaizenIcons.Settings, stringResource(R.string.sidebar_settings), tint = cs.onSurfaceVariant.copy(alpha = 0.5f), modifier = Modifier.size(18.dp), @@ -275,7 +265,7 @@ fun KaizenSidebar( contentAlignment = Alignment.Center, ) { Icon( - Icons.AutoMirrored.Rounded.Logout, + KaizenIcons.LogOut, stringResource(R.string.sidebar_logout), tint = cs.onSurfaceVariant.copy(alpha = 0.5f), modifier = Modifier.size(16.dp), @@ -389,7 +379,7 @@ private fun HistoryItemRow( verticalAlignment = Alignment.CenterVertically ) { if (item.pinned) { - Icon(Icons.Rounded.Star, null, tint = accent.primary.copy(alpha = 0.7f), modifier = Modifier.size(13.dp)) + Icon(KaizenIcons.Star, null, tint = accent.primary.copy(alpha = 0.7f), modifier = Modifier.size(13.dp)) Spacer(Modifier.width(6.dp)) } Text( @@ -403,7 +393,7 @@ private fun HistoryItemRow( modifier = Modifier.weight(1f), ) if (item.locked) { - Icon(Icons.Rounded.Lock, null, tint = accent.primary.copy(alpha = 0.5f), modifier = Modifier.size(13.dp)) + Icon(KaizenIcons.Lock, null, tint = accent.primary.copy(alpha = 0.5f), modifier = Modifier.size(13.dp)) Spacer(Modifier.width(2.dp)) } Box( @@ -414,7 +404,7 @@ private fun HistoryItemRow( contentAlignment = Alignment.Center, ) { Icon( - Icons.Rounded.MoreVert, + KaizenIcons.MoreVertical, stringResource(R.string.sidebar_more), tint = cs.onSurfaceVariant.copy(alpha = 0.35f), modifier = Modifier.size(16.dp), @@ -430,15 +420,15 @@ private fun HistoryItemRow( shape = KaizenShapes.md, ) { if (!item.locked) { - MenuRow(Icons.Rounded.Edit, stringResource(R.string.sidebar_rename), cs.onBackground) { showMenu = false; onRename() } + MenuRow(KaizenIcons.Pencil, stringResource(R.string.sidebar_rename), cs.onBackground) { showMenu = false; onRename() } } - MenuRow(Icons.Rounded.PushPin, stringResource(if (item.pinned) R.string.sidebar_unpin else R.string.sidebar_pin), cs.onBackground) { showMenu = false; onTogglePin() } + MenuRow(KaizenIcons.Pin, stringResource(if (item.pinned) R.string.sidebar_unpin else R.string.sidebar_pin), cs.onBackground) { showMenu = false; onTogglePin() } MenuRow( - if (item.locked) Icons.Rounded.LockOpen else Icons.Rounded.Lock, + if (item.locked) KaizenIcons.LockOpen else KaizenIcons.Lock, stringResource(if (item.locked) R.string.sidebar_unlock else R.string.sidebar_lock), cs.onBackground, ) { showMenu = false; onToggleLock() } - MenuRow(Icons.Rounded.Delete, stringResource(R.string.sidebar_delete), Color(0xFFEF4444)) { showMenu = false; onDelete() } + MenuRow(KaizenIcons.Trash, stringResource(R.string.sidebar_delete), Color(0xFFEF4444)) { showMenu = false; onDelete() } } } } diff --git a/app/src/main/java/dev/kaizen/app/chat/StreamBlocks.kt b/app/src/main/java/dev/kaizen/app/chat/StreamBlocks.kt index ffc96b3..d186d59 100644 --- a/app/src/main/java/dev/kaizen/app/chat/StreamBlocks.kt +++ b/app/src/main/java/dev/kaizen/app/chat/StreamBlocks.kt @@ -17,16 +17,6 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.rounded.Check -import androidx.compose.material.icons.rounded.Error -import androidx.compose.material.icons.rounded.ExpandLess -import androidx.compose.material.icons.rounded.ExpandMore -import androidx.compose.material.icons.rounded.Language -import androidx.compose.material.icons.rounded.Psychology -import androidx.compose.material.icons.rounded.AutoAwesome -import androidx.compose.material.icons.rounded.Build -import androidx.compose.material.icons.rounded.Description import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text @@ -50,6 +40,7 @@ import dev.kaizen.app.ui.shape.KaizenShapes import dev.kaizen.app.ui.theme.LocalKaizenAccent import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource +import dev.kaizen.app.ui.icon.KaizenIcons @Composable fun ReasoningBlock(reasoning: String, streaming: Boolean = false) { @@ -75,7 +66,7 @@ fun ReasoningBlock(reasoning: String, streaming: Boolean = false) { .padding(horizontal = 12.dp, vertical = 8.dp), verticalAlignment = Alignment.CenterVertically, ) { - Icon(Icons.Rounded.Psychology, null, tint = accent.primary, modifier = Modifier.size(16.dp)) + Icon(KaizenIcons.Brain, null, tint = accent.primary, modifier = Modifier.size(16.dp)) Spacer(Modifier.width(8.dp)) Text( stringResource(R.string.stream_reasoning), @@ -85,7 +76,7 @@ fun ReasoningBlock(reasoning: String, streaming: Boolean = false) { modifier = Modifier.weight(1f), ) Icon( - if (expanded) Icons.Rounded.ExpandLess else Icons.Rounded.ExpandMore, + if (expanded) KaizenIcons.ChevronUp else KaizenIcons.ChevronDown, null, tint = cs.onSurfaceVariant, modifier = Modifier.size(16.dp), ) } @@ -134,21 +125,21 @@ fun ToolEventsBlock(tools: List, streaming: Boolean = false) { Column(verticalArrangement = Arrangement.spacedBy(4.dp)) { if (analyzing != null) { Row(verticalAlignment = Alignment.CenterVertically) { - Icon(Icons.Rounded.Description, null, tint = accent.primary.copy(alpha = 0.6f), modifier = Modifier.size(14.dp)) + Icon(KaizenIcons.FileText, null, tint = accent.primary.copy(alpha = 0.6f), modifier = Modifier.size(14.dp)) Spacer(Modifier.width(6.dp)) Text(stringResource(R.string.stream_tool_analyzing, analyzing!!), color = cs.onSurfaceVariant.copy(alpha = 0.6f), fontSize = 12.sp) } } if (thinking && analyzing == null && completed.isEmpty() && inProgress.isEmpty()) { Row(verticalAlignment = Alignment.CenterVertically) { - Icon(Icons.Rounded.AutoAwesome, null, tint = accent.primary.copy(alpha = 0.6f), modifier = Modifier.size(14.dp)) + Icon(KaizenIcons.Sparkles, null, tint = accent.primary.copy(alpha = 0.6f), modifier = Modifier.size(14.dp)) Spacer(Modifier.width(6.dp)) Text(stringResource(R.string.stream_tool_thinking), color = cs.onSurfaceVariant.copy(alpha = 0.6f), fontSize = 12.sp) } } completed.forEach { tool -> Row(verticalAlignment = Alignment.CenterVertically) { - val icon = if (tool.type == "error") Icons.Rounded.Error else Icons.Rounded.Check + val icon = if (tool.type == "error") KaizenIcons.AlertCircle else KaizenIcons.Check val tint = if (tool.type == "error") Color(0xFFEF4444) else Color(0xFF10B981) Icon(icon, null, tint = tint, modifier = Modifier.size(14.dp)) Spacer(Modifier.width(6.dp)) @@ -161,7 +152,7 @@ fun ToolEventsBlock(tools: List, streaming: Boolean = false) { } inProgress.forEach { name -> Row(verticalAlignment = Alignment.CenterVertically) { - Icon(Icons.Rounded.Build, null, tint = accent.primary.copy(alpha = 0.6f), modifier = Modifier.size(14.dp)) + Icon(KaizenIcons.Wrench, null, tint = accent.primary.copy(alpha = 0.6f), modifier = Modifier.size(14.dp)) Spacer(Modifier.width(6.dp)) Text(name, color = cs.onSurfaceVariant.copy(alpha = 0.7f), fontSize = 12.sp, fontWeight = FontWeight.Medium) } @@ -184,7 +175,7 @@ fun SourcesBlock(sources: List, query: String = "") { .padding(vertical = 4.dp), verticalAlignment = Alignment.CenterVertically, ) { - Icon(Icons.Rounded.Language, null, tint = cs.onSurfaceVariant.copy(alpha = 0.6f), modifier = Modifier.size(13.dp)) + Icon(KaizenIcons.Globe, null, tint = cs.onSurfaceVariant.copy(alpha = 0.6f), modifier = Modifier.size(13.dp)) Spacer(Modifier.width(6.dp)) Text( stringResource(R.string.stream_sources) + " (${sources.size})", @@ -194,7 +185,7 @@ fun SourcesBlock(sources: List, query: String = "") { ) Spacer(Modifier.width(4.dp)) Icon( - if (expanded) Icons.Rounded.ExpandLess else Icons.Rounded.ExpandMore, + if (expanded) KaizenIcons.ChevronUp else KaizenIcons.ChevronDown, null, tint = cs.onSurfaceVariant.copy(alpha = 0.4f), modifier = Modifier.size(12.dp), ) } diff --git a/app/src/main/java/dev/kaizen/app/settings/SecuritySection.kt b/app/src/main/java/dev/kaizen/app/settings/SecuritySection.kt index 695a9a0..7aa2921 100644 --- a/app/src/main/java/dev/kaizen/app/settings/SecuritySection.kt +++ b/app/src/main/java/dev/kaizen/app/settings/SecuritySection.kt @@ -19,10 +19,6 @@ import androidx.compose.foundation.layout.width import androidx.compose.foundation.text.BasicTextField import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.rounded.Fingerprint -import androidx.compose.material.icons.rounded.Key -import androidx.compose.material.icons.rounded.PhoneAndroid import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme @@ -57,6 +53,7 @@ import dev.kaizen.app.net.AppDevice import dev.kaizen.app.net.ServerConfig import dev.kaizen.app.ui.shape.KaizenShapes import dev.kaizen.app.ui.theme.LocalKaizenAccent +import dev.kaizen.app.ui.icon.KaizenIcons @Composable fun SecuritySection( @@ -100,7 +97,7 @@ fun SecuritySection( title = stringResource(R.string.settings_biometric_title), description = if (biometricNotEnrolled) stringResource(R.string.settings_biometric_not_enrolled) else stringResource(R.string.settings_biometric_desc), - icon = Icons.Rounded.Fingerprint, + icon = KaizenIcons.Fingerprint, ) { Switch( checked = biometricEnabled && biometricAvailable, @@ -119,7 +116,7 @@ fun SecuritySection( SettingsOptionRow( title = stringResource(R.string.settings_devices_title), description = stringResource(R.string.settings_devices_desc), - icon = Icons.Rounded.PhoneAndroid, + icon = KaizenIcons.Smartphone, ) { if (devicesLoading) { CircularProgressIndicator( @@ -152,7 +149,7 @@ fun SecuritySection( SettingsOptionRow( title = stringResource(R.string.settings_password_title), description = "", - icon = Icons.Rounded.Key, + icon = KaizenIcons.Key, ) { PasswordChangeForm( onSubmit = { current, new_ -> diff --git a/app/src/main/java/dev/kaizen/app/settings/SettingsComponents.kt b/app/src/main/java/dev/kaizen/app/settings/SettingsComponents.kt index ad71181..6c51788 100644 --- a/app/src/main/java/dev/kaizen/app/settings/SettingsComponents.kt +++ b/app/src/main/java/dev/kaizen/app/settings/SettingsComponents.kt @@ -31,8 +31,6 @@ import dev.kaizen.app.ui.effect.KaizenShadows import dev.kaizen.app.ui.shape.KaizenShapes import androidx.compose.foundation.text.BasicTextField import androidx.compose.foundation.verticalScroll -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.rounded.Check import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text @@ -57,6 +55,7 @@ import androidx.compose.ui.unit.sp import dev.kaizen.app.ui.theme.LocalKaizenAccent import dev.kaizen.app.ui.theme.Oklab import dev.kaizen.app.ui.theme.themes.KaizenThemeId +import dev.kaizen.app.ui.icon.KaizenIcons /** Converts polar OKLCH coordinates (Lightness, Chroma, Hue in degrees) to a standard Color */ private fun oklch(l: Float, c: Float, h: Float, alpha: Float = 1.0f): Color { @@ -250,7 +249,7 @@ fun ThemeOptionPill( if (selected) { Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { Icon( - imageVector = Icons.Rounded.Check, + imageVector = KaizenIcons.Check, contentDescription = null, tint = if (themeId == KaizenThemeId.MONO && isDark.not()) Color.White else Color.Black, modifier = Modifier.size(16.dp) diff --git a/app/src/main/java/dev/kaizen/app/settings/SettingsScreen.kt b/app/src/main/java/dev/kaizen/app/settings/SettingsScreen.kt index 8d638f2..5726c2e 100644 --- a/app/src/main/java/dev/kaizen/app/settings/SettingsScreen.kt +++ b/app/src/main/java/dev/kaizen/app/settings/SettingsScreen.kt @@ -22,13 +22,6 @@ import dev.kaizen.app.ui.effect.GlassTiers import dev.kaizen.app.ui.effect.KaizenShadows import dev.kaizen.app.ui.shape.KaizenShapes import androidx.compose.foundation.verticalScroll -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.automirrored.rounded.ArrowBack -import androidx.compose.material.icons.rounded.AutoAwesome -import androidx.compose.material.icons.rounded.Language -import androidx.compose.material.icons.rounded.Palette -import androidx.compose.material.icons.rounded.Person -import androidx.compose.material.icons.rounded.Settings import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text @@ -47,6 +40,7 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.compose.ui.platform.LocalContext import dev.kaizen.app.chat.MeshBackground +import dev.kaizen.app.ui.icon.KaizenIcons /** * Premium, fully responsive, glassmorphic Settings Screen. @@ -95,7 +89,7 @@ fun SettingsScreen( modifier = Modifier.size(44.dp).clickable { onBack() }, ) { Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { - Icon(Icons.AutoMirrored.Rounded.ArrowBack, stringResource(R.string.settings_back), tint = cs.onBackground, modifier = Modifier.size(20.dp)) + Icon(KaizenIcons.ArrowLeft, stringResource(R.string.settings_back), tint = cs.onBackground, modifier = Modifier.size(20.dp)) } } @@ -107,7 +101,7 @@ fun SettingsScreen( Text(stringResource(R.string.settings_section_appearance), color = cs.onSurfaceVariant.copy(alpha = 0.6f), fontSize = 12.sp, fontWeight = FontWeight.Bold, modifier = Modifier.padding(start = 8.dp, bottom = 8.dp)) SettingsCard(modifier = Modifier.padding(bottom = 24.dp)) { - SettingsOptionRow(title = stringResource(R.string.settings_accent_title), description = stringResource(R.string.settings_accent_desc), icon = Icons.Rounded.Palette) { + SettingsOptionRow(title = stringResource(R.string.settings_accent_title), description = stringResource(R.string.settings_accent_desc), icon = KaizenIcons.Palette) { Row(horizontalArrangement = Arrangement.spacedBy(10.dp), verticalAlignment = Alignment.CenterVertically) { KaizenThemeId.entries.forEach { theme -> ThemeOptionPill(themeId = theme, selected = selectedTheme == theme, onClick = { viewModel.selectTheme(theme) }) @@ -115,7 +109,7 @@ fun SettingsScreen( } } Spacer(Modifier.height(12.dp)) - SettingsOptionRow(title = stringResource(R.string.settings_appearance_title), description = stringResource(R.string.settings_appearance_desc), icon = Icons.Rounded.Settings) { + SettingsOptionRow(title = stringResource(R.string.settings_appearance_title), description = stringResource(R.string.settings_appearance_desc), icon = KaizenIcons.Settings) { Row(horizontalArrangement = Arrangement.spacedBy(6.dp), verticalAlignment = Alignment.CenterVertically) { AppAppearance.entries.forEach { mode -> val label = when (mode) { @@ -128,7 +122,7 @@ fun SettingsScreen( } } Spacer(Modifier.height(12.dp)) - SettingsOptionRow(title = stringResource(R.string.settings_language_title), description = stringResource(R.string.settings_language_desc), icon = Icons.Rounded.Language) { + SettingsOptionRow(title = stringResource(R.string.settings_language_title), description = stringResource(R.string.settings_language_desc), icon = KaizenIcons.Globe) { Row(horizontalArrangement = Arrangement.spacedBy(6.dp), verticalAlignment = Alignment.CenterVertically) { listOf("system" to R.string.settings_language_system, "de" to R.string.settings_language_de, "en" to R.string.settings_language_en).forEach { (code, labelRes) -> val ctx = LocalContext.current @@ -141,17 +135,17 @@ fun SettingsScreen( Text(stringResource(R.string.settings_section_profile), color = cs.onSurfaceVariant.copy(alpha = 0.6f), fontSize = 12.sp, fontWeight = FontWeight.Bold, modifier = Modifier.padding(start = 8.dp, bottom = 8.dp)) SettingsCard(modifier = Modifier.padding(bottom = 24.dp)) { - SettingsOptionRow(title = stringResource(R.string.settings_name_title), description = stringResource(R.string.settings_name_desc), icon = Icons.Rounded.Person) { + SettingsOptionRow(title = stringResource(R.string.settings_name_title), description = stringResource(R.string.settings_name_desc), icon = KaizenIcons.User) { SettingsInputCapsule(value = userName, onValueChange = { viewModel.updateUserName(it) }) } Spacer(Modifier.height(12.dp)) - SettingsOptionRow(title = stringResource(R.string.settings_email_title), description = userEmail, icon = Icons.Rounded.Settings) + SettingsOptionRow(title = stringResource(R.string.settings_email_title), description = userEmail, icon = KaizenIcons.Settings) } Text(stringResource(R.string.settings_section_ai), color = cs.onSurfaceVariant.copy(alpha = 0.6f), fontSize = 12.sp, fontWeight = FontWeight.Bold, modifier = Modifier.padding(start = 8.dp, bottom = 8.dp)) SettingsCard(modifier = Modifier.padding(bottom = 24.dp)) { - SettingsOptionRow(title = stringResource(R.string.settings_ai_title), description = "", icon = Icons.Rounded.AutoAwesome) + SettingsOptionRow(title = stringResource(R.string.settings_ai_title), description = "", icon = KaizenIcons.Sparkles) } SecuritySection(viewModel = viewModel, config = config) diff --git a/app/src/main/java/dev/kaizen/app/ui/icon/KaizenIcons.kt b/app/src/main/java/dev/kaizen/app/ui/icon/KaizenIcons.kt new file mode 100644 index 0000000..32b0106 --- /dev/null +++ b/app/src/main/java/dev/kaizen/app/ui/icon/KaizenIcons.kt @@ -0,0 +1,98 @@ +package dev.kaizen.app.ui.icon + +import androidx.compose.ui.graphics.vector.ImageVector +import com.composables.icons.lucide.* + +/** + * Central icon registry for the entire app. Every UI component references icons + * through this object — never directly from Material, Lucide, or any other + * library. This gives us: + * + * 1. **One-line swap**: change the backing icon here, every usage updates + * 2. **Library independence**: migrate from Lucide to custom SVGs gradually + * 3. **Semantic naming**: `KaizenIcons.Send` not `Icons.Rounded.Send` + * 4. **Brand layer**: the [Custom] section holds hand-drawn Kaizen originals + * that override the base library for brand-critical touchpoints + * + * Convention: group by function, alphabetize within groups. + * When adding a custom icon, keep the Lucide fallback as a comment. + */ +object KaizenIcons { + + // ── Navigation ────────────────────────────────────────────────────────── + val ArrowLeft: ImageVector get() = Lucide.ArrowLeft + val ChevronDown: ImageVector get() = Lucide.ChevronDown + val ChevronUp: ImageVector get() = Lucide.ChevronUp + val Menu: ImageVector get() = Lucide.Menu + val MoreVertical: ImageVector get() = Lucide.EllipsisVertical + val Settings: ImageVector get() = Lucide.Settings + val X: ImageVector get() = Lucide.X + + // ── Chat ──────────────────────────────────────────────────────────────── + val Mic: ImageVector get() = Lucide.Mic + val Paperclip: ImageVector get() = Lucide.Paperclip + val Plus: ImageVector get() = Lucide.Plus + val Send: ImageVector get() = Lucide.SendHorizontal + val Sparkles: ImageVector get() = Lucide.Sparkles + val Square: ImageVector get() = Lucide.Square + val Zap: ImageVector get() = Lucide.Zap + + // ── Modes ─────────────────────────────────────────────────────────────── + val Brain: ImageVector get() = Lucide.Brain + val CircleDot: ImageVector get() = Lucide.CircleDot + val Globe: ImageVector get() = Lucide.Globe + val Image: ImageVector get() = Lucide.Image + val Music: ImageVector get() = Lucide.Music + val SlidersHorizontal: ImageVector get() = Lucide.SlidersHorizontal + val Video: ImageVector get() = Lucide.Video + + // ── Conversation management ───────────────────────────────────────────── + val Check: ImageVector get() = Lucide.Check + val Copy: ImageVector get() = Lucide.Copy + val Lock: ImageVector get() = Lucide.Lock + val LockOpen: ImageVector get() = Lucide.LockOpen + val Pencil: ImageVector get() = Lucide.Pencil + val Pin: ImageVector get() = Lucide.Pin + val PinOff: ImageVector get() = Lucide.PinOff + val RefreshCw: ImageVector get() = Lucide.RefreshCw + val Star: ImageVector get() = Lucide.Star + val StarOff: ImageVector get() = Lucide.StarOff + val Trash: ImageVector get() = Lucide.Trash2 + + // ── Files & media ─────────────────────────────────────────────────────── + val Camera: ImageVector get() = Lucide.Camera + val Code: ImageVector get() = Lucide.Code + val File: ImageVector get() = Lucide.File + val FileAudio: ImageVector get() = Lucide.FileAudio + val FileText: ImageVector get() = Lucide.FileText + val FileVideo: ImageVector get() = Lucide.FileVideo + val FileImage: ImageVector get() = Lucide.FileImage + + // ── Search & reasoning ────────────────────────────────────────────────── + val Search: ImageVector get() = Lucide.Search + val ExternalLink: ImageVector get() = Lucide.ExternalLink + val Wrench: ImageVector get() = Lucide.Wrench + val AlertCircle: ImageVector get() = Lucide.CircleAlert + + // ── Settings ──────────────────────────────────────────────────────────── + val Fingerprint: ImageVector get() = Lucide.Fingerprint + val Key: ImageVector get() = Lucide.Key + val Languages: ImageVector get() = Lucide.Languages + val LogOut: ImageVector get() = Lucide.LogOut + val Monitor: ImageVector get() = Lucide.Monitor + val Moon: ImageVector get() = Lucide.Moon + val Palette: ImageVector get() = Lucide.Palette + val Smartphone: ImageVector get() = Lucide.Smartphone + val Sun: ImageVector get() = Lucide.Sun + val User: ImageVector get() = Lucide.User + + // ── Scroll ────────────────────────────────────────────────────────────── + val ArrowDown: ImageVector get() = Lucide.ArrowDown + val ArrowUp: ImageVector get() = Lucide.ArrowUp + + // ── Custom (brand overrides) ──────────────────────────────────────────── + // Add hand-drawn SVGs here as ImageVector literals when ready. + // Example: + // val Send: ImageVector get() = KaizenCustomIcons.Send + // The Lucide fallback above is then removed. +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 94df565..268ae59 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -14,6 +14,7 @@ kotlinxSerialization = "1.7.3" securityCrypto = "1.1.0-alpha06" room = "2.8.4" biometric = "1.4.0-alpha02" +composeIcons = "1.0.0" [libraries] androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } @@ -23,6 +24,7 @@ androidx-security-crypto = { group = "androidx.security", name = "security-crypt androidx-room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" } androidx-room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" } androidx-biometric = { group = "androidx.biometric", name = "biometric", version.ref = "biometric" } +compose-icons-lucide = { group = "com.composables", name = "icons-lucide-android", version.ref = "composeIcons" } junit = { group = "junit", name = "junit", version.ref = "junit" } androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }