Android: ChatGPT-style menu with Camera/Photos/Files
- Top row of attachment buttons: Camera | Photos | Files - Dark menu background (0xFF1E1E2E) for contrast - Styled menu items with icons and trailing values - Session and Thinking level show current value on right - Dividers between sections - TODO: Implement camera capture and file picker
This commit is contained in:
parent
40a5b0ce1e
commit
fbf18d55b8
@ -28,6 +28,8 @@ import androidx.compose.foundation.text.BasicTextField
|
|||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.ArrowUpward
|
import androidx.compose.material.icons.filled.ArrowUpward
|
||||||
import androidx.compose.material.icons.filled.AttachFile
|
import androidx.compose.material.icons.filled.AttachFile
|
||||||
|
import androidx.compose.material.icons.filled.CameraAlt
|
||||||
|
import androidx.compose.material.icons.filled.Image
|
||||||
import androidx.compose.material.icons.filled.Menu
|
import androidx.compose.material.icons.filled.Menu
|
||||||
import androidx.compose.material.icons.filled.Mic
|
import androidx.compose.material.icons.filled.Mic
|
||||||
import androidx.compose.material.icons.filled.Refresh
|
import androidx.compose.material.icons.filled.Refresh
|
||||||
@ -75,7 +77,9 @@ fun ChatComposer(
|
|||||||
attachments: List<PendingImageAttachment>,
|
attachments: List<PendingImageAttachment>,
|
||||||
seamColor: Color = Color(0xFF3B82F6),
|
seamColor: Color = Color(0xFF3B82F6),
|
||||||
talkEnabled: Boolean = false,
|
talkEnabled: Boolean = false,
|
||||||
|
onOpenCamera: () -> Unit = {},
|
||||||
onPickImages: () -> Unit,
|
onPickImages: () -> Unit,
|
||||||
|
onPickFiles: () -> Unit = {},
|
||||||
onRemoveAttachment: (id: String) -> Unit,
|
onRemoveAttachment: (id: String) -> Unit,
|
||||||
onSetThinkingLevel: (level: String) -> Unit,
|
onSetThinkingLevel: (level: String) -> Unit,
|
||||||
onSelectSession: (sessionKey: String) -> Unit,
|
onSelectSession: (sessionKey: String) -> Unit,
|
||||||
@ -143,20 +147,63 @@ fun ChatComposer(
|
|||||||
DropdownMenu(
|
DropdownMenu(
|
||||||
expanded = showMenu,
|
expanded = showMenu,
|
||||||
onDismissRequest = { showMenu = false },
|
onDismissRequest = { showMenu = false },
|
||||||
|
modifier = Modifier.background(Color(0xFF1E1E2E)), // Darker shade
|
||||||
) {
|
) {
|
||||||
|
// Attachment buttons row: Camera | Photos | Files
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 12.dp, vertical = 8.dp),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
|
) {
|
||||||
|
AttachmentButton(
|
||||||
|
icon = Icons.Default.CameraAlt,
|
||||||
|
label = "Camera",
|
||||||
|
onClick = {
|
||||||
|
showMenu = false
|
||||||
|
onOpenCamera()
|
||||||
|
},
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
)
|
||||||
|
AttachmentButton(
|
||||||
|
icon = Icons.Default.Image,
|
||||||
|
label = "Photos",
|
||||||
|
onClick = {
|
||||||
|
showMenu = false
|
||||||
|
onPickImages()
|
||||||
|
},
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
)
|
||||||
|
AttachmentButton(
|
||||||
|
icon = Icons.Default.AttachFile,
|
||||||
|
label = "Files",
|
||||||
|
onClick = {
|
||||||
|
showMenu = false
|
||||||
|
onPickFiles()
|
||||||
|
},
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
HorizontalDivider(
|
||||||
|
modifier = Modifier.padding(vertical = 8.dp),
|
||||||
|
color = Color.White.copy(alpha = 0.1f),
|
||||||
|
)
|
||||||
|
|
||||||
// Session selector
|
// Session selector
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
text = { Text("Session: $currentSessionLabel") },
|
text = { Text("Session", color = Color.White) },
|
||||||
onClick = {
|
onClick = {
|
||||||
showMenu = false
|
showMenu = false
|
||||||
showSessionMenu = true
|
showSessionMenu = true
|
||||||
},
|
},
|
||||||
leadingIcon = { Icon(Icons.Default.Settings, contentDescription = null) },
|
leadingIcon = { Icon(Icons.Default.Settings, contentDescription = null, tint = Color.White.copy(alpha = 0.7f)) },
|
||||||
|
trailingIcon = { Text(currentSessionLabel, color = Color.White.copy(alpha = 0.5f), style = MaterialTheme.typography.bodySmall) },
|
||||||
)
|
)
|
||||||
|
|
||||||
// Thinking level
|
// Thinking level
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
text = { Text("Thinking: ${thinkingLabel(thinkingLevel)}") },
|
text = { Text("Thinking", color = Color.White) },
|
||||||
onClick = {
|
onClick = {
|
||||||
showMenu = false
|
showMenu = false
|
||||||
showThinkingMenu = true
|
showThinkingMenu = true
|
||||||
@ -164,28 +211,22 @@ fun ChatComposer(
|
|||||||
leadingIcon = {
|
leadingIcon = {
|
||||||
Text("🧠", style = MaterialTheme.typography.bodyLarge)
|
Text("🧠", style = MaterialTheme.typography.bodyLarge)
|
||||||
},
|
},
|
||||||
|
trailingIcon = { Text(thinkingLabel(thinkingLevel), color = Color.White.copy(alpha = 0.5f), style = MaterialTheme.typography.bodySmall) },
|
||||||
)
|
)
|
||||||
|
|
||||||
HorizontalDivider()
|
HorizontalDivider(
|
||||||
|
modifier = Modifier.padding(vertical = 8.dp),
|
||||||
// Attach file
|
color = Color.White.copy(alpha = 0.1f),
|
||||||
DropdownMenuItem(
|
|
||||||
text = { Text("Attach image") },
|
|
||||||
onClick = {
|
|
||||||
showMenu = false
|
|
||||||
onPickImages()
|
|
||||||
},
|
|
||||||
leadingIcon = { Icon(Icons.Default.AttachFile, contentDescription = null) },
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Refresh
|
// Refresh
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
text = { Text("Refresh") },
|
text = { Text("Refresh", color = Color.White) },
|
||||||
onClick = {
|
onClick = {
|
||||||
showMenu = false
|
showMenu = false
|
||||||
onRefresh()
|
onRefresh()
|
||||||
},
|
},
|
||||||
leadingIcon = { Icon(Icons.Default.Refresh, contentDescription = null) },
|
leadingIcon = { Icon(Icons.Default.Refresh, contentDescription = null, tint = Color.White.copy(alpha = 0.7f)) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -294,6 +335,40 @@ fun ChatComposer(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun AttachmentButton(
|
||||||
|
icon: androidx.compose.ui.graphics.vector.ImageVector,
|
||||||
|
label: String,
|
||||||
|
onClick: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
Surface(
|
||||||
|
onClick = onClick,
|
||||||
|
modifier = modifier.height(80.dp),
|
||||||
|
shape = RoundedCornerShape(12.dp),
|
||||||
|
color = Color(0xFF2A2A3E),
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.padding(12.dp),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
verticalArrangement = Arrangement.Center,
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
icon,
|
||||||
|
contentDescription = label,
|
||||||
|
tint = Color.White.copy(alpha = 0.8f),
|
||||||
|
modifier = Modifier.size(24.dp),
|
||||||
|
)
|
||||||
|
Spacer(Modifier.height(6.dp))
|
||||||
|
Text(
|
||||||
|
label,
|
||||||
|
style = MaterialTheme.typography.labelSmall,
|
||||||
|
color = Color.White.copy(alpha = 0.8f),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun MiniOrb(
|
private fun MiniOrb(
|
||||||
seamColor: Color,
|
seamColor: Color,
|
||||||
|
|||||||
@ -97,7 +97,9 @@ fun ChatSheetContent(viewModel: MainViewModel) {
|
|||||||
attachments = attachments,
|
attachments = attachments,
|
||||||
seamColor = seamColor,
|
seamColor = seamColor,
|
||||||
talkEnabled = talkEnabled,
|
talkEnabled = talkEnabled,
|
||||||
|
onOpenCamera = { /* TODO: Implement camera capture */ },
|
||||||
onPickImages = { pickImages.launch("image/*") },
|
onPickImages = { pickImages.launch("image/*") },
|
||||||
|
onPickFiles = { /* TODO: Implement file picker */ },
|
||||||
onRemoveAttachment = { id -> attachments.removeAll { it.id == id } },
|
onRemoveAttachment = { id -> attachments.removeAll { it.id == id } },
|
||||||
onSetThinkingLevel = { level -> viewModel.setChatThinkingLevel(level) },
|
onSetThinkingLevel = { level -> viewModel.setChatThinkingLevel(level) },
|
||||||
onSelectSession = { key -> viewModel.switchChatSession(key) },
|
onSelectSession = { key -> viewModel.switchChatSession(key) },
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user