feat: KaizenIcons system — Lucide base, full Material Icons migration
Introduces a centralized icon registry (ui/icon/KaizenIcons.kt) that decouples every UI component from the underlying icon library. All 10 source files migrated from Material Icons Rounded to KaizenIcons backed by Lucide (1.5px stroke, same library as the web frontend). Architecture: - KaizenIcons object with semantic getters (Send, Lock, Brain, etc.) - Grouped by function (Navigation, Chat, Modes, Files, Settings, etc.) - Custom brand section ready for hand-drawn SVG overrides - One-line swap: change the getter, every usage updates - Zero Material Icon imports remaining in app source Dependency: com.composables:icons-lucide-android:1.0.0
This commit is contained in:
parent
1362daee53
commit
68721d8ddf
13 changed files with 187 additions and 147 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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<KaizenModel>): 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)
|
||||
|
|
|
|||
|
|
@ -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() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<ToolEvent>, 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<ToolEvent>, 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<SearchSource>, 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<SearchSource>, 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),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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_ ->
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
98
app/src/main/java/dev/kaizen/app/ui/icon/KaizenIcons.kt
Normal file
98
app/src/main/java/dev/kaizen/app/ui/icon/KaizenIcons.kt
Normal file
|
|
@ -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.
|
||||
}
|
||||
|
|
@ -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" }
|
||||
|
|
|
|||
Loading…
Reference in a new issue