@@ -9,16 +9,22 @@ import androidx.compose.foundation.layout.Column
99import androidx.compose.foundation.layout.PaddingValues
1010import androidx.compose.foundation.layout.Row
1111import androidx.compose.foundation.layout.Spacer
12+import androidx.compose.foundation.layout.WindowInsets
1213import androidx.compose.foundation.layout.fillMaxSize
1314import androidx.compose.foundation.layout.fillMaxWidth
15+import androidx.compose.foundation.layout.imePadding
16+import androidx.compose.foundation.layout.navigationBars
1417import androidx.compose.foundation.layout.padding
1518import androidx.compose.foundation.layout.size
19+import androidx.compose.foundation.layout.union
20+import androidx.compose.foundation.layout.windowInsetsPadding
1621import androidx.compose.foundation.lazy.LazyColumn
1722import androidx.compose.foundation.lazy.items
1823import androidx.compose.foundation.lazy.rememberLazyListState
1924import androidx.compose.material.icons.Icons
2025import androidx.compose.material.icons.automirrored.filled.ArrowBack
2126import androidx.compose.material.icons.automirrored.filled.Send
27+import androidx.compose.material.icons.filled.AutoAwesome
2228import androidx.compose.material.icons.filled.KeyboardArrowDown
2329import androidx.compose.material.icons.filled.SmartToy
2430import androidx.compose.material.icons.filled.Stop
@@ -48,6 +54,8 @@ import androidx.compose.runtime.mutableStateOf
4854import androidx.compose.runtime.remember
4955import androidx.compose.runtime.rememberCoroutineScope
5056import androidx.compose.runtime.setValue
57+import androidx.compose.foundation.text.KeyboardActions
58+import androidx.compose.foundation.text.KeyboardOptions
5159import androidx.compose.ui.Alignment
5260import androidx.compose.ui.Modifier
5361import androidx.compose.ui.input.key.Key
@@ -58,6 +66,10 @@ import androidx.compose.ui.input.key.onPreviewKeyEvent
5866import androidx.compose.ui.input.key.type
5967import androidx.compose.ui.platform.LocalHapticFeedback
6068import androidx.compose.ui.res.stringResource
69+import androidx.compose.ui.semantics.Role
70+import androidx.compose.ui.semantics.role
71+import androidx.compose.ui.semantics.semantics
72+import androidx.compose.ui.text.input.ImeAction
6173import androidx.compose.ui.unit.dp
6274import androidx.lifecycle.compose.collectAsStateWithLifecycle
6375import androidx.lifecycle.viewmodel.compose.viewModel
@@ -137,7 +149,10 @@ fun ChatScreen(
137149 topBar = {
138150 TopAppBar(
139151 title = {
140− Column(modifier = Modifier.clickable(enabled = models.isNotEmpty()) { showModelPicker = true }) {
152+ Column(modifier = Modifier
153+ .clickable(enabled = models.isNotEmpty()) { showModelPicker = true }
154+ .semantics { role = Role.Button }
155+ ) {
141156 Text(sessionTitle ?: stringResource(R.string.session))
142157 Text(
143158 buildString {
@@ -193,6 +208,11 @@ fun ChatScreen(
193208 state = connectionState,
194209 modifier = Modifier.align(Alignment.TopCenter),
195210 )
211+ if (messages.isEmpty() && !running) {
212+ EmptyConversation(
213+ modifier = Modifier.align(Alignment.Center),
214+ )
215+ } else {
196216 LazyColumn(
197217 state = listState,
198218 modifier = Modifier.fillMaxSize(),
@@ -222,11 +242,12 @@ fun ChatScreen(
222242 scope.launch { listState.animateScrollToItem(messages.lastIndex) }
223243 }
224244 },
225− icon = { Icon(Icons.Filled.KeyboardArrowDown, contentDescription = null) },
245+ icon = { Icon(Icons.Filled.KeyboardArrowDown, contentDescription = stringResource(R.string.latest)) },
226246 text = { Text(stringResource(R.string.latest)) },
227247 modifier = Modifier.padding(end = 16.dp, bottom = 16.dp),
228248 )
229249 }
250+ }
230251 }
231252 }
232253 }
@@ -323,9 +344,11 @@ private fun ChatInputBar(
323344 onSend: () -> Unit,
324345 onAbort: () -> Unit,
325346) {
326− Surface(tonalElevation = 3.dp) {
347+ Surface(tonalElevation = 3.dp, modifier = Modifier.imePadding()) {
327348 Row(
328− modifier = Modifier.fillMaxWidth().padding(8.dp),
349+ modifier = Modifier.fillMaxWidth()
350+ .windowInsetsPadding(WindowInsets.navigationBars)
351+ .padding(8.dp),
329352 verticalAlignment = Alignment.Bottom,
330353 ) {
331354 OutlinedTextField(
@@ -348,6 +371,8 @@ private fun ChatInputBar(
348371 placeholder = { Text(stringResource(R.string.message_placeholder)) },
349372 enabled = enabled,
350373 maxLines = 6,
374+ keyboardOptions = KeyboardOptions(imeAction = ImeAction.Send),
375+ keyboardActions = KeyboardActions(onSend = { if (enabled && value.isNotBlank()) onSend() }),
351376 )
352377 if (running) {
353378 IconButton(onClick = onAbort, modifier = Modifier.padding(start = 4.dp)) {
@@ -365,3 +390,29 @@ private fun ChatInputBar(
365390 }
366391 }
367392}
393+
394+@Composable
395+private fun EmptyConversation(modifier: Modifier = Modifier) {
396+ Column(
397+ modifier = modifier.padding(32.dp),
398+ horizontalAlignment = Alignment.CenterHorizontally,
399+ ) {
400+ Icon(
401+ Icons.Filled.AutoAwesome,
402+ contentDescription = null,
403+ modifier = Modifier.size(48.dp),
404+ tint = MaterialTheme.colorScheme.onSurfaceVariant,
405+ )
406+ Spacer(Modifier.size(16.dp))
407+ Text(
408+ stringResource(R.string.empty_chat_title),
409+ style = MaterialTheme.typography.titleMedium,
410+ )
411+ Spacer(Modifier.size(8.dp))
412+ Text(
413+ stringResource(R.string.empty_chat_hint),
414+ style = MaterialTheme.typography.bodyMedium,
415+ color = MaterialTheme.colorScheme.onSurfaceVariant,
416+ )
417+ }
418+}