feat: align native UI with Happy Engineering style
This commit is contained in:
@@ -46,9 +46,7 @@ import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ChatBubble
|
||||
import androidx.compose.material.icons.filled.CheckCircle
|
||||
import androidx.compose.material.icons.filled.Favorite
|
||||
import androidx.compose.material.icons.filled.Folder
|
||||
import androidx.compose.material.icons.filled.Key
|
||||
import androidx.compose.material.icons.filled.PlayArrow
|
||||
import androidx.compose.material.icons.filled.Refresh
|
||||
import androidx.compose.material.icons.filled.Save
|
||||
@@ -58,9 +56,6 @@ import androidx.compose.material.icons.filled.Wifi
|
||||
import androidx.compose.material.icons.filled.WifiOff
|
||||
import androidx.compose.material.icons.outlined.Article
|
||||
import androidx.compose.material.icons.outlined.Bolt
|
||||
import androidx.compose.material.icons.outlined.Diamond
|
||||
import androidx.compose.material.icons.outlined.LocalFireDepartment
|
||||
import androidx.compose.material.icons.outlined.EmojiEvents
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.Card
|
||||
@@ -110,11 +105,11 @@ import java.net.URLEncoder
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
enum class Screen(val label: String, val icon: ImageVector, val title: String, val kicker: String) {
|
||||
Quest("Quest", Icons.Filled.ChatBubble, "Quest", "Hermes training path"),
|
||||
Files("Files", Icons.Filled.Folder, "Backpack", "Workspace loot"),
|
||||
Terminal("Terminal", Icons.Filled.Terminal, "Power move", "Run a command"),
|
||||
Status("Status", Icons.Outlined.EmojiEvents, "League", "Companion stats"),
|
||||
Settings("Settings", Icons.Filled.Settings, "Nest", "Connection setup")
|
||||
Quest("Chat", Icons.Filled.ChatBubble, "Agent", "Claude Code mobile client"),
|
||||
Files("Files", Icons.Filled.Folder, "Workspace", "Project files"),
|
||||
Terminal("Terminal", Icons.Filled.Terminal, "Terminal", "Run commands"),
|
||||
Status("Status", Icons.Outlined.Bolt, "Sessions", "Companion status"),
|
||||
Settings("Settings", Icons.Filled.Settings, "Settings", "Pair companion")
|
||||
}
|
||||
|
||||
data class FileEntry(val name: String, val path: String, val type: String, val size: Long)
|
||||
@@ -247,8 +242,8 @@ fun HermesNativeApp() {
|
||||
colorScheme = darkColorScheme(
|
||||
background = Cream,
|
||||
surface = Paper,
|
||||
primary = Green,
|
||||
onPrimary = Color.White,
|
||||
primary = Teal,
|
||||
onPrimary = Color.Black,
|
||||
onBackground = Ink,
|
||||
onSurface = Ink
|
||||
)
|
||||
@@ -317,25 +312,23 @@ fun NativeTopBar(screen: Screen, online: Boolean) {
|
||||
.padding(horizontal = 16.dp, vertical = 10.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(10.dp)
|
||||
) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(10.dp)) {
|
||||
RaisedCircle(color = Green, shadow = GreenDark, content = "⚡")
|
||||
Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(12.dp)) {
|
||||
RaisedCircle(color = Purple, shadow = PurpleDark, content = "H")
|
||||
Column(Modifier.weight(1f)) {
|
||||
TinyLabel(screen.kicker)
|
||||
Text(screen.title, fontSize = 31.sp, fontWeight = FontWeight.Black, color = Ink, lineHeight = 31.sp)
|
||||
Text(screen.title, fontSize = 30.sp, fontWeight = FontWeight.Black, color = Ink, lineHeight = 31.sp)
|
||||
}
|
||||
StatChip(Icons.Outlined.LocalFireDepartment, "7", Orange)
|
||||
StatChip(Icons.Outlined.Diamond, "550", Blue)
|
||||
StatChip(Icons.Filled.Favorite, "3", Red)
|
||||
StatusDot(online)
|
||||
}
|
||||
RaisedCard(
|
||||
color = if (online) PaleGreen else PaleOrange,
|
||||
shadow = if (online) GreenShadow else OrangeShadow,
|
||||
border = if (online) GreenBorder else OrangeBorder,
|
||||
color = if (online) TealPanel else WarningPanel,
|
||||
shadow = PanelShadow,
|
||||
border = if (online) TealBorder else WarningBorder,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Row(Modifier.padding(12.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
Icon(if (online) Icons.Filled.Wifi else Icons.Filled.WifiOff, contentDescription = null, tint = if (online) GreenDark else Orange)
|
||||
Text(if (online) "Companion online — ready for quests" else "Companion offline — check Settings", color = if (online) GreenDark else Orange, fontWeight = FontWeight.Black)
|
||||
Icon(if (online) Icons.Filled.Wifi else Icons.Filled.WifiOff, contentDescription = null, tint = if (online) Teal else Orange)
|
||||
Text(if (online) "Companion connected" else "Set companion URL and access key", color = if (online) Teal else Orange, fontWeight = FontWeight.Black)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -359,9 +352,9 @@ fun NativeBottomBar(current: Screen, onSelect: (Screen) -> Unit) {
|
||||
icon = { Icon(item.icon, contentDescription = item.label) },
|
||||
label = { Text(item.label, fontWeight = FontWeight.Black, fontSize = 11.sp) },
|
||||
colors = NavigationBarItemDefaults.colors(
|
||||
selectedIconColor = GreenDark,
|
||||
selectedTextColor = GreenDark,
|
||||
indicatorColor = PaleGreen,
|
||||
selectedIconColor = Teal,
|
||||
selectedTextColor = Teal,
|
||||
indicatorColor = TealPanel,
|
||||
unselectedIconColor = Muted,
|
||||
unselectedTextColor = Muted
|
||||
)
|
||||
@@ -380,10 +373,10 @@ fun QuestScreen(api: CompanionApi) {
|
||||
val messages = remember { mutableStateListOf<ChatMessage>() }
|
||||
|
||||
ScreenList {
|
||||
HeroCard(icon = Icons.Filled.ChatBubble, title = "Give Hermes one clean mission.", label = "Daily quest", text = "Short, direct prompts work best. The native app calls the local companion server.")
|
||||
HeroCard(icon = Icons.Filled.ChatBubble, title = "Claude Code from anywhere.", label = "Agent control", text = "Send prompts to your local Hermes companion and continue work from your phone.")
|
||||
Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
|
||||
PathPill(Icons.Filled.CheckCircle, "Check", true)
|
||||
PathPill(Icons.Outlined.Bolt, "Ask", true, current = true)
|
||||
PathPill(Icons.Filled.CheckCircle, "Connect", true)
|
||||
PathPill(Icons.Outlined.Bolt, "Prompt", true, current = true)
|
||||
PathPill(Icons.Filled.ChatBubble, "Review", false)
|
||||
}
|
||||
if (messages.isEmpty()) EmptyLesson()
|
||||
@@ -391,8 +384,8 @@ fun QuestScreen(api: CompanionApi) {
|
||||
if (error.isNotBlank()) ErrorCard(error)
|
||||
RaisedCard(Modifier.fillMaxWidth()) {
|
||||
Column(Modifier.padding(12.dp), verticalArrangement = Arrangement.spacedBy(10.dp)) {
|
||||
AppTextField(value = prompt, onValue = { prompt = it }, placeholder = "What should Hermes do?", minLines = 3)
|
||||
PrimaryButton(text = if (busy) "Working…" else "Start quest", icon = Icons.Filled.PlayArrow, enabled = !busy && prompt.isNotBlank()) {
|
||||
AppTextField(value = prompt, onValue = { prompt = it }, placeholder = "Message Hermes", minLines = 3)
|
||||
PrimaryButton(text = if (busy) "Working…" else "Send", icon = Icons.Filled.PlayArrow, enabled = !busy && prompt.isNotBlank()) {
|
||||
val p = prompt.trim()
|
||||
prompt = ""
|
||||
messages.add(ChatMessage("You", p))
|
||||
@@ -427,7 +420,7 @@ fun FilesScreen(api: CompanionApi) {
|
||||
LaunchedEffect(Unit) { load(".") }
|
||||
|
||||
ScreenList {
|
||||
CompactQuest(title = "Workspace files", label = "Backpack", text = "Tap folders to go deeper. Files open in a native editor.", icon = Icons.Filled.Folder) { load() }
|
||||
CompactQuest(title = "Project files", label = "Workspace", text = "Browse and edit files through the companion server.", icon = Icons.Filled.Folder) { load() }
|
||||
RaisedCard(Modifier.fillMaxWidth()) {
|
||||
Column(Modifier.padding(12.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
@@ -468,7 +461,7 @@ fun TerminalScreen(api: CompanionApi) {
|
||||
var busy by remember { mutableStateOf(false) }
|
||||
|
||||
ScreenList {
|
||||
HeroCard(icon = Icons.Filled.Terminal, title = "Run one safe command.", label = "Power move", text = "Commands execute through the companion server inside the configured workspace.", orbColor = Blue, orbShadow = BlueDark)
|
||||
HeroCard(icon = Icons.Filled.Terminal, title = "Run workspace commands.", label = "Terminal", text = "Execute commands on the paired machine without leaving the app.", orbColor = Purple, orbShadow = PurpleDark)
|
||||
RaisedCard(Modifier.fillMaxWidth()) {
|
||||
Column(Modifier.padding(12.dp), verticalArrangement = Arrangement.spacedBy(10.dp)) {
|
||||
FieldLabel("Working directory")
|
||||
@@ -495,7 +488,7 @@ fun StatusScreen(api: CompanionApi) {
|
||||
fun refresh() = scope.launch { health = runCatching { api.health() }.getOrElse { HealthState(false, it.message ?: "Health check failed") } }
|
||||
|
||||
ScreenList {
|
||||
CompactQuest(title = "Companion status", label = "League board", text = health.status, icon = Icons.Outlined.EmojiEvents, orbColor = Gold, orbShadow = GoldDark) { refresh() }
|
||||
CompactQuest(title = "Companion status", label = "Sessions", text = health.status, icon = Icons.Outlined.Bolt, orbColor = Teal, orbShadow = TealDark) { refresh() }
|
||||
Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
Metric("Hermes", if (health.hermesCli.startsWith("Found")) "Ready" else "—", Modifier.weight(1f))
|
||||
Metric("Uptime", health.uptime, Modifier.weight(1f))
|
||||
@@ -522,7 +515,7 @@ fun SettingsScreen(api: CompanionApi) {
|
||||
var status by remember { mutableStateOf("Paste the companion access key, then test the connection.") }
|
||||
|
||||
ScreenList {
|
||||
CompactQuest(title = "Pair your companion", label = "Nest setup", text = status, icon = Icons.Filled.Settings, orbColor = Green, orbShadow = GreenDark) {}
|
||||
CompactQuest(title = "Pair companion", label = "Settings", text = status, icon = Icons.Filled.Settings, orbColor = Purple, orbShadow = PurpleDark) {}
|
||||
RaisedCard(Modifier.fillMaxWidth()) {
|
||||
Column(Modifier.padding(12.dp), verticalArrangement = Arrangement.spacedBy(10.dp)) {
|
||||
FieldLabel("Companion server URL")
|
||||
@@ -562,7 +555,7 @@ fun RaisedCard(modifier: Modifier = Modifier, color: Color = Paper, shadow: Colo
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun HeroCard(icon: ImageVector, title: String, label: String, text: String, orbColor: Color = Green, orbShadow: Color = GreenDark) {
|
||||
fun HeroCard(icon: ImageVector, title: String, label: String, text: String, orbColor: Color = Teal, orbShadow: Color = TealDark) {
|
||||
RaisedCard(Modifier.fillMaxWidth()) {
|
||||
Row(Modifier.padding(16.dp), horizontalArrangement = Arrangement.spacedBy(14.dp), verticalAlignment = Alignment.Top) {
|
||||
RaisedIcon(icon, orbColor, orbShadow)
|
||||
@@ -572,7 +565,7 @@ fun HeroCard(icon: ImageVector, title: String, label: String, text: String, orbC
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun CompactQuest(title: String, label: String, text: String, icon: ImageVector, orbColor: Color = Green, orbShadow: Color = GreenDark, onRefresh: () -> Unit) {
|
||||
fun CompactQuest(title: String, label: String, text: String, icon: ImageVector, orbColor: Color = Teal, orbShadow: Color = TealDark, onRefresh: () -> Unit) {
|
||||
RaisedCard(Modifier.fillMaxWidth()) {
|
||||
Row(Modifier.padding(14.dp), horizontalArrangement = Arrangement.spacedBy(12.dp), verticalAlignment = Alignment.CenterVertically) {
|
||||
RaisedIcon(icon, orbColor, orbShadow, size = 58)
|
||||
@@ -583,45 +576,46 @@ fun CompactQuest(title: String, label: String, text: String, icon: ImageVector,
|
||||
}
|
||||
|
||||
@Composable fun RaisedIcon(icon: ImageVector, color: Color, shadow: Color, size: Int = 66) { Box(Modifier.size(size.dp), contentAlignment = Alignment.Center) { Box(Modifier.matchParentSize().padding(top = 5.dp).clip(CircleShape).background(shadow)); Box(Modifier.matchParentSize().padding(bottom = 5.dp).clip(CircleShape).background(color), contentAlignment = Alignment.Center) { Icon(icon, null, tint = Color.White, modifier = Modifier.size((size / 2).dp)) } } }
|
||||
@Composable fun RaisedCircle(color: Color, shadow: Color, content: String) { Box(Modifier.size(54.dp), contentAlignment = Alignment.Center) { Box(Modifier.matchParentSize().padding(top = 5.dp).clip(RoundedCornerShape(18.dp)).background(shadow)); Box(Modifier.matchParentSize().padding(bottom = 5.dp).clip(RoundedCornerShape(18.dp)).background(color), contentAlignment = Alignment.Center) { Text(content, fontSize = 24.sp) } } }
|
||||
@Composable fun StatChip(icon: ImageVector, value: String, color: Color) { Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(2.dp), modifier = Modifier.clip(RoundedCornerShape(99.dp)).background(Paper).padding(horizontal = 7.dp, vertical = 3.dp)) { Icon(icon, null, tint = color, modifier = Modifier.size(15.dp)); Text(value, color = color, fontWeight = FontWeight.Black, fontSize = 12.sp) } }
|
||||
@Composable fun RaisedCircle(color: Color, shadow: Color, content: String) { Box(Modifier.size(54.dp), contentAlignment = Alignment.Center) { Box(Modifier.matchParentSize().padding(top = 5.dp).clip(RoundedCornerShape(18.dp)).background(shadow)); Box(Modifier.matchParentSize().padding(bottom = 5.dp).clip(RoundedCornerShape(18.dp)).background(color), contentAlignment = Alignment.Center) { Text(content, fontSize = 24.sp, fontWeight = FontWeight.Black, color = Color.White) } } }
|
||||
@Composable fun StatusDot(online: Boolean) { Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(6.dp), modifier = Modifier.clip(RoundedCornerShape(99.dp)).background(if (online) TealPanel else WarningPanel).padding(horizontal = 10.dp, vertical = 7.dp)) { Box(Modifier.size(8.dp).clip(CircleShape).background(if (online) Teal else Orange)); Text(if (online) "LIVE" else "SETUP", color = if (online) Teal else Orange, fontWeight = FontWeight.Black, fontSize = 11.sp) } }
|
||||
@Composable fun TinyLabel(text: String) { Text(text.uppercase(), color = Muted, fontSize = 11.sp, fontWeight = FontWeight.Black, letterSpacing = 0.7.sp) }
|
||||
@Composable fun FieldLabel(text: String) { Text(text.uppercase(), color = Muted, fontSize = 12.sp, fontWeight = FontWeight.Black) }
|
||||
@Composable fun PathPill(icon: ImageVector, text: String, done: Boolean, current: Boolean = false) { Row(Modifier.clip(RoundedCornerShape(99.dp)).background(if (current) Green else if (done) PaleGreen else Surface).padding(horizontal = 12.dp, vertical = 10.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(5.dp)) { Icon(icon, null, tint = if (current) Color.White else GreenDark, modifier = Modifier.size(18.dp)); Text(text, color = if (current) Color.White else GreenDark, fontWeight = FontWeight.Black, fontSize = 13.sp) } }
|
||||
@Composable fun EmptyLesson() { RaisedCard(Modifier.fillMaxWidth()) { Column(Modifier.padding(18.dp), horizontalAlignment = Alignment.CenterHorizontally) { Text("🪽", fontSize = 42.sp); Text("No mission yet", color = Ink, fontWeight = FontWeight.Black, fontSize = 20.sp); Text("Try: check PM2 status or summarize logs.", color = Muted, fontWeight = FontWeight.ExtraBold) } } }
|
||||
@Composable fun Bubble(msg: ChatMessage) { RaisedCard(Modifier.fillMaxWidth().padding(start = if (msg.role == "You") 28.dp else 0.dp, end = if (msg.role == "You") 0.dp else 28.dp), color = if (msg.role == "You") PaleGreen else PaleGold) { Column(Modifier.padding(12.dp)) { TinyLabel(msg.role); Text(msg.content, color = Ink, fontWeight = FontWeight.ExtraBold, lineHeight = 19.sp) } } }
|
||||
@Composable fun PathPill(icon: ImageVector, text: String, done: Boolean, current: Boolean = false) { Row(Modifier.clip(RoundedCornerShape(99.dp)).background(if (current) Teal else if (done) TealPanel else Surface).padding(horizontal = 12.dp, vertical = 10.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(5.dp)) { Icon(icon, null, tint = if (current) Color.Black else Teal, modifier = Modifier.size(18.dp)); Text(text, color = if (current) Color.Black else Teal, fontWeight = FontWeight.Black, fontSize = 13.sp) } }
|
||||
@Composable fun EmptyLesson() { RaisedCard(Modifier.fillMaxWidth()) { Column(Modifier.padding(18.dp), horizontalAlignment = Alignment.CenterHorizontally) { Text("\$ happy", color = Teal, fontFamily = androidx.compose.ui.text.font.FontFamily.Monospace, fontWeight = FontWeight.Black, fontSize = 24.sp); Text("No messages yet", color = Ink, fontWeight = FontWeight.Black, fontSize = 20.sp); Text("Try: check PM2 status or summarize logs.", color = Muted, fontWeight = FontWeight.ExtraBold) } } }
|
||||
@Composable fun Bubble(msg: ChatMessage) { RaisedCard(Modifier.fillMaxWidth().padding(start = if (msg.role == "You") 28.dp else 0.dp, end = if (msg.role == "You") 0.dp else 28.dp), color = if (msg.role == "You") TealPanel else Surface) { Column(Modifier.padding(12.dp)) { TinyLabel(msg.role); Text(msg.content, color = Ink, fontWeight = FontWeight.ExtraBold, lineHeight = 19.sp) } } }
|
||||
@Composable fun ErrorCard(text: String) { RaisedCard(Modifier.fillMaxWidth(), color = PaleRed, shadow = RedShadow, border = RedBorder) { Text(text, Modifier.padding(12.dp), color = Red, fontWeight = FontWeight.Black) } }
|
||||
@Composable fun CodeCard(text: String) { RaisedCard(Modifier.fillMaxWidth()) { Text(text, Modifier.padding(14.dp), color = BlueDark, fontFamily = androidx.compose.ui.text.font.FontFamily.Monospace, fontSize = 13.sp, lineHeight = 18.sp) } }
|
||||
@Composable fun FileRow(entry: FileEntry, onClick: () -> Unit) { RaisedCard(Modifier.fillMaxWidth()) { Row(Modifier.clickable(onClick = onClick).padding(10.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(10.dp)) { Box(Modifier.size(42.dp).clip(RoundedCornerShape(14.dp)).background(if (entry.type == "directory") PaleBlue else PaleGold), contentAlignment = Alignment.Center) { Icon(if (entry.type == "directory") Icons.Filled.Folder else Icons.Outlined.Article, null, tint = if (entry.type == "directory") Blue else GoldDark) }; Text(entry.name, Modifier.weight(1f), color = Ink, fontWeight = FontWeight.Black, maxLines = 1, overflow = TextOverflow.Ellipsis); Text(if (entry.type == "file") "${entry.size} B" else "dir", color = Muted, fontWeight = FontWeight.Black, fontSize = 12.sp) } } }
|
||||
@Composable fun Metric(label: String, value: String, modifier: Modifier = Modifier) { RaisedCard(modifier) { Column(Modifier.padding(10.dp), horizontalAlignment = Alignment.CenterHorizontally) { Text(value, color = GreenDark, fontWeight = FontWeight.Black, fontSize = 18.sp, maxLines = 1, overflow = TextOverflow.Ellipsis); Text(label.uppercase(), color = Muted, fontWeight = FontWeight.Black, fontSize = 10.sp) } } }
|
||||
@Composable fun AppTextField(value: String, onValue: (String) -> Unit, placeholder: String, minLines: Int = 1, keyboardType: KeyboardType = KeyboardType.Text, password: Boolean = false) { OutlinedTextField(value = value, onValueChange = onValue, modifier = Modifier.fillMaxWidth(), placeholder = { Text(placeholder, color = Muted) }, minLines = minLines, keyboardOptions = KeyboardOptions(keyboardType = keyboardType), visualTransformation = if (password) PasswordVisualTransformation() else androidx.compose.ui.text.input.VisualTransformation.None, shape = RoundedCornerShape(16.dp), colors = TextFieldDefaults.colors(focusedContainerColor = Surface, unfocusedContainerColor = Surface, focusedIndicatorColor = Green, unfocusedIndicatorColor = NeutralBorder, cursorColor = GreenDark)) }
|
||||
@Composable fun PrimaryButton(text: String, icon: ImageVector, modifier: Modifier = Modifier, enabled: Boolean = true, onClick: () -> Unit) { Button(onClick = onClick, enabled = enabled, modifier = modifier.height(54.dp), shape = RoundedCornerShape(16.dp), colors = ButtonDefaults.buttonColors(containerColor = Green, contentColor = Color.White, disabledContainerColor = NeutralBorder), elevation = ButtonDefaults.buttonElevation(defaultElevation = 6.dp, pressedElevation = 1.dp)) { Text(text, fontWeight = FontWeight.Black); Spacer(Modifier.width(6.dp)); Icon(icon, null) } }
|
||||
@Composable fun Metric(label: String, value: String, modifier: Modifier = Modifier) { RaisedCard(modifier) { Column(Modifier.padding(10.dp), horizontalAlignment = Alignment.CenterHorizontally) { Text(value, color = Teal, fontWeight = FontWeight.Black, fontSize = 18.sp, maxLines = 1, overflow = TextOverflow.Ellipsis); Text(label.uppercase(), color = Muted, fontWeight = FontWeight.Black, fontSize = 10.sp) } } }
|
||||
@Composable fun AppTextField(value: String, onValue: (String) -> Unit, placeholder: String, minLines: Int = 1, keyboardType: KeyboardType = KeyboardType.Text, password: Boolean = false) { OutlinedTextField(value = value, onValueChange = onValue, modifier = Modifier.fillMaxWidth(), placeholder = { Text(placeholder, color = Muted) }, minLines = minLines, keyboardOptions = KeyboardOptions(keyboardType = keyboardType), visualTransformation = if (password) PasswordVisualTransformation() else androidx.compose.ui.text.input.VisualTransformation.None, shape = RoundedCornerShape(16.dp), colors = TextFieldDefaults.colors(focusedContainerColor = Surface, unfocusedContainerColor = Surface, focusedIndicatorColor = Teal, unfocusedIndicatorColor = NeutralBorder, cursorColor = Teal)) }
|
||||
@Composable fun PrimaryButton(text: String, icon: ImageVector, modifier: Modifier = Modifier, enabled: Boolean = true, onClick: () -> Unit) { Button(onClick = onClick, enabled = enabled, modifier = modifier.height(54.dp), shape = RoundedCornerShape(16.dp), colors = ButtonDefaults.buttonColors(containerColor = Teal, contentColor = Color.Black, disabledContainerColor = NeutralBorder), elevation = ButtonDefaults.buttonElevation(defaultElevation = 6.dp, pressedElevation = 1.dp)) { Text(text, fontWeight = FontWeight.Black); Spacer(Modifier.width(6.dp)); Icon(icon, null) } }
|
||||
@Composable fun SecondaryButton(text: String, icon: ImageVector, modifier: Modifier = Modifier, onClick: () -> Unit) { Button(onClick = onClick, modifier = modifier.height(54.dp), shape = RoundedCornerShape(16.dp), colors = ButtonDefaults.buttonColors(containerColor = Surface, contentColor = Ink), elevation = ButtonDefaults.buttonElevation(defaultElevation = 4.dp, pressedElevation = 1.dp)) { Text(text, fontWeight = FontWeight.Black); Spacer(Modifier.width(6.dp)); Icon(icon, null) } }
|
||||
@Composable fun RoundButton(icon: ImageVector, onClick: () -> Unit) { Button(onClick = onClick, modifier = Modifier.size(50.dp), shape = RoundedCornerShape(16.dp), contentPadding = androidx.compose.foundation.layout.PaddingValues(0.dp), colors = ButtonDefaults.buttonColors(containerColor = Blue, contentColor = Color.White), elevation = ButtonDefaults.buttonElevation(defaultElevation = 5.dp, pressedElevation = 1.dp)) { Icon(icon, null) } }
|
||||
@Composable fun RoundButton(icon: ImageVector, onClick: () -> Unit) { Button(onClick = onClick, modifier = Modifier.size(50.dp), shape = RoundedCornerShape(16.dp), contentPadding = androidx.compose.foundation.layout.PaddingValues(0.dp), colors = ButtonDefaults.buttonColors(containerColor = Purple, contentColor = Color.White), elevation = ButtonDefaults.buttonElevation(defaultElevation = 5.dp, pressedElevation = 1.dp)) { Icon(icon, null) } }
|
||||
|
||||
val Green = Color(0xFF58CC02)
|
||||
val GreenDark = Color(0xFF7FE83B)
|
||||
val GreenShadow = Color(0xFF244B19)
|
||||
val GreenBorder = Color(0xFF3D7F28)
|
||||
val PaleGreen = Color(0xFF17301A)
|
||||
val Blue = Color(0xFF1CB0F6)
|
||||
val BlueDark = Color(0xFF73D4FF)
|
||||
val PaleBlue = Color(0xFF112A3A)
|
||||
val Gold = Color(0xFFFFC800)
|
||||
val GoldDark = Color(0xFFFFD95A)
|
||||
val PaleGold = Color(0xFF332A10)
|
||||
val Orange = Color(0xFFFFA033)
|
||||
val PaleOrange = Color(0xFF332312)
|
||||
val OrangeBorder = Color(0xFF7B4D1C)
|
||||
val OrangeShadow = Color(0xFF26180C)
|
||||
val Red = Color(0xFFFF6B6B)
|
||||
val PaleRed = Color(0xFF3A171B)
|
||||
val RedBorder = Color(0xFF8B3D45)
|
||||
val RedShadow = Color(0xFF271013)
|
||||
val Cream = Color(0xFF08100E)
|
||||
val Paper = Color(0xFF111B18)
|
||||
val Surface = Color(0xFF17231F)
|
||||
val Ink = Color(0xFFEAF7EE)
|
||||
val Muted = Color(0xFF9FB4A7)
|
||||
val NeutralBorder = Color(0xFF294138)
|
||||
val NeutralShadow = Color(0xFF060B0A)
|
||||
val Teal = Color(0xFF28DBC9)
|
||||
val TealDark = Color(0xFF0F8F84)
|
||||
val TealPanel = Color(0xFF0E2B2A)
|
||||
val TealBorder = Color(0xFF176C66)
|
||||
val Purple = Color(0xFF6701E6)
|
||||
val PurpleDark = Color(0xFF3B0087)
|
||||
val PurplePanel = Color(0xFF211133)
|
||||
val Blue = Color(0xFF3B82F6)
|
||||
val BlueDark = Color(0xFF8AB7FF)
|
||||
val PaleBlue = Color(0xFF111B2B)
|
||||
val GoldDark = Color(0xFFFBBF24)
|
||||
val PaleGold = Color(0xFF211B10)
|
||||
val Orange = Color(0xFFFBBF24)
|
||||
val WarningPanel = Color(0xFF2B2111)
|
||||
val WarningBorder = Color(0xFF7A5A1B)
|
||||
val Red = Color(0xFFF87171)
|
||||
val PaleRed = Color(0xFF311719)
|
||||
val RedBorder = Color(0xFF7A363A)
|
||||
val RedShadow = Color(0xFF0A0304)
|
||||
val Cream = Color(0xFF000000)
|
||||
val Paper = Color(0xFF0B0F12)
|
||||
val Surface = Color(0xFF12181D)
|
||||
val Ink = Color(0xFFF4F4F5)
|
||||
val Muted = Color(0xFF9CA3AF)
|
||||
val NeutralBorder = Color(0xFF2A3138)
|
||||
val NeutralShadow = Color(0xFF030506)
|
||||
val PanelShadow = Color(0xFF040707)
|
||||
|
||||
Reference in New Issue
Block a user