Skip to content

Commit 5a80876

Browse files
authored
Merge pull request #50 from joreilly/ui_updates
UI updates
2 parents 65002e4 + 5e17e17 commit 5a80876

File tree

4 files changed

+170
-52
lines changed

4 files changed

+170
-52
lines changed

.junie/guidelines.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Project Guidelines
2+
3+
## Project Structure
4+
This is a Kotlin Multiplatform project with Compose Multiplatform that includes:
5+
* `composeApp` - Shared Kotlin code with Compose UI
6+
* `iosApp` - iOS application
7+
8+
## Building the Project
9+
When building this project, Junie should use the following Gradle task:
10+
```
11+
:composeApp:compileKotlinJvm
12+
```

composeApp/src/commonMain/kotlin/dev/johnoreilly/vertexai/ui/App.kt

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,48 @@
11
package dev.johnoreilly.vertexai.ui
22

33
import androidx.compose.foundation.layout.Column
4+
import androidx.compose.foundation.layout.fillMaxSize
45
import androidx.compose.foundation.layout.padding
56
import androidx.compose.material3.CenterAlignedTopAppBar
67
import androidx.compose.material3.ExperimentalMaterial3Api
78
import androidx.compose.material3.MaterialTheme
89
import androidx.compose.material3.Scaffold
10+
import androidx.compose.material3.Surface
911
import androidx.compose.material3.Text
12+
import androidx.compose.material3.TopAppBarDefaults
1013
import androidx.compose.runtime.Composable
1114
import androidx.compose.ui.Modifier
15+
import androidx.compose.ui.text.font.FontWeight
16+
import dev.johnoreilly.vertexai.ui.theme.FirebaseAILogicTheme
1217

1318

1419
@OptIn(ExperimentalMaterial3Api::class)
1520
@Composable
1621
fun App() {
17-
MaterialTheme() {
18-
Scaffold(
19-
topBar = {
20-
CenterAlignedTopAppBar(title = {
21-
Text("FirebaseAILogicKMPSampe")
22-
})
23-
}
22+
FirebaseAILogicTheme {
23+
Surface(
24+
modifier = Modifier.fillMaxSize(),
25+
color = MaterialTheme.colorScheme.background
2426
) {
25-
Column(modifier = Modifier.padding(it)) {
26-
HomeScreen()
27+
Scaffold(
28+
topBar = {
29+
CenterAlignedTopAppBar(
30+
title = {
31+
Text(
32+
"Firebase AI Logic KMP Sample",
33+
fontWeight = FontWeight.Bold
34+
)
35+
},
36+
colors = TopAppBarDefaults.centerAlignedTopAppBarColors(
37+
containerColor = MaterialTheme.colorScheme.primaryContainer,
38+
titleContentColor = MaterialTheme.colorScheme.onPrimaryContainer
39+
)
40+
)
41+
}
42+
) {
43+
Column(modifier = Modifier.padding(it)) {
44+
HomeScreen()
45+
}
2746
}
2847
}
2948
}
Lines changed: 130 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,28 @@
11
package dev.johnoreilly.vertexai.ui
22

3-
import androidx.compose.foundation.border
43
import androidx.compose.foundation.layout.Box
54
import androidx.compose.foundation.layout.Column
65
import androidx.compose.foundation.layout.Row
76
import androidx.compose.foundation.layout.Spacer
87
import androidx.compose.foundation.layout.fillMaxSize
98
import androidx.compose.foundation.layout.fillMaxWidth
9+
import androidx.compose.foundation.layout.height
1010
import androidx.compose.foundation.layout.padding
1111
import androidx.compose.foundation.layout.width
1212
import androidx.compose.foundation.lazy.LazyColumn
1313
import androidx.compose.foundation.lazy.items
1414
import androidx.compose.foundation.rememberScrollState
1515
import androidx.compose.foundation.shape.RoundedCornerShape
16-
import androidx.compose.foundation.text.BasicTextField
1716
import androidx.compose.foundation.verticalScroll
18-
import androidx.compose.material3.Checkbox
17+
import androidx.compose.material3.Button
18+
import androidx.compose.material3.Card
19+
import androidx.compose.material3.CardDefaults
1920
import androidx.compose.material3.CircularProgressIndicator
21+
import androidx.compose.material3.ElevatedCard
2022
import androidx.compose.material3.ListItem
21-
import androidx.compose.material3.OutlinedButton
23+
import androidx.compose.material3.MaterialTheme
24+
import androidx.compose.material3.OutlinedTextField
25+
import androidx.compose.material3.Switch
2226
import androidx.compose.material3.Text
2327
import androidx.compose.runtime.Composable
2428
import androidx.compose.runtime.LaunchedEffect
@@ -31,13 +35,11 @@ import androidx.compose.ui.Alignment
3135
import androidx.compose.ui.Modifier
3236
import androidx.compose.ui.focus.FocusRequester
3337
import androidx.compose.ui.focus.focusRequester
34-
import androidx.compose.ui.graphics.Color
3538
import androidx.compose.ui.layout.ContentScale
3639
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
37-
import androidx.compose.ui.text.TextStyle
40+
import androidx.compose.ui.text.font.FontWeight
3841
import androidx.compose.ui.text.input.TextFieldValue
3942
import androidx.compose.ui.unit.dp
40-
import androidx.compose.ui.unit.sp
4143
import androidx.lifecycle.compose.collectAsStateWithLifecycle
4244
import coil3.compose.AsyncImage
4345
import coil3.compose.LocalPlatformContext
@@ -67,50 +69,73 @@ fun HomeScreen() {
6769
focusRequester.requestFocus()
6870
}
6971

70-
Column(modifier = Modifier.padding(8.dp)) {
71-
BasicTextField(
72+
Column(modifier = Modifier.padding(16.dp)) {
73+
Text(
74+
text = "Enter your prompt",
75+
style = MaterialTheme.typography.labelLarge,
76+
color = MaterialTheme.colorScheme.primary,
77+
modifier = Modifier.padding(bottom = 4.dp)
78+
)
79+
80+
OutlinedTextField(
7281
value = prompt,
7382
onValueChange = { prompt = it },
74-
textStyle = TextStyle(fontSize = 24.sp),
83+
placeholder = { Text("What would you like to generate?") },
7584
modifier = Modifier
76-
.border(1.dp, Color.Black, RoundedCornerShape(8.dp))
77-
.padding(8.dp)
7885
.focusRequester(focusRequester)
7986
.fillMaxWidth()
87+
.height(120.dp),
88+
shape = RoundedCornerShape(12.dp)
8089
)
81-
82-
Row(verticalAlignment = Alignment.CenterVertically) {
83-
Text("Generate JSON")
84-
Checkbox(checked = generateJson.value, onCheckedChange = { generateJson.value = it })
90+
91+
Spacer(modifier = Modifier.height(12.dp))
92+
93+
Row(
94+
verticalAlignment = Alignment.CenterVertically,
95+
modifier = Modifier.padding(vertical = 8.dp)
96+
) {
97+
Text(
98+
"Generate JSON",
99+
style = MaterialTheme.typography.bodyLarge,
100+
fontWeight = FontWeight.Medium
101+
)
102+
Spacer(modifier = Modifier.width(8.dp))
103+
Switch(checked = generateJson.value, onCheckedChange = { generateJson.value = it })
85104
}
86105

87-
Row {
88-
OutlinedButton(
106+
Row(
107+
modifier = Modifier.padding(vertical = 8.dp)
108+
) {
109+
Button(
89110
onClick = {
90111
if (prompt.text.isNotBlank()) {
91112
keyboardController?.hide()
92113
viewModel.generateContent(prompt.text, generateJson = generateJson.value)
93114
}
94115
},
95-
modifier = Modifier.padding(vertical = 8.dp)
116+
modifier = Modifier.weight(1f),
117+
shape = RoundedCornerShape(8.dp)
96118
) {
97119
Text(stringResource(Res.string.generate_content))
98120
}
99121

100122
Spacer(Modifier.width(16.dp))
101-
OutlinedButton(
123+
124+
Button(
102125
onClick = {
103126
if (prompt.text.isNotBlank()) {
104127
keyboardController?.hide()
105128
viewModel.generateImage(prompt.text)
106129
}
107130
},
108-
modifier = Modifier.padding(vertical = 8.dp)
131+
modifier = Modifier.weight(1f),
132+
shape = RoundedCornerShape(8.dp)
109133
) {
110134
Text(stringResource(Res.string.generate_image))
111135
}
112-
113136
}
137+
138+
Spacer(modifier = Modifier.height(16.dp))
114139

115140
ResponseView(uiState, prompt.text)
116141
}
@@ -133,34 +158,96 @@ fun ResponseView(uiState: GenerativeModelUIState, prompt: String) {
133158
}
134159

135160
is GenerativeModelUIState.Success -> {
136-
if (uiState.entityContent != null) {
137-
LazyColumn {
138-
items(uiState.entityContent) { item ->
139-
ListItem(
140-
headlineContent = { Text(item.name) },
141-
supportingContent = { Text(item.country) }
161+
ElevatedCard(
162+
modifier = Modifier.fillMaxWidth(),
163+
shape = RoundedCornerShape(12.dp),
164+
elevation = CardDefaults.elevatedCardElevation(defaultElevation = 4.dp)
165+
) {
166+
Column(modifier = Modifier.padding(16.dp)) {
167+
Text(
168+
text = "Generated Result",
169+
style = MaterialTheme.typography.titleMedium,
170+
color = MaterialTheme.colorScheme.primary,
171+
fontWeight = FontWeight.Bold,
172+
modifier = Modifier.padding(bottom = 8.dp)
173+
)
174+
175+
if (uiState.entityContent != null) {
176+
LazyColumn {
177+
items(uiState.entityContent) { item ->
178+
Card(
179+
modifier = Modifier
180+
.fillMaxWidth()
181+
.padding(vertical = 4.dp),
182+
shape = RoundedCornerShape(8.dp)
183+
) {
184+
ListItem(
185+
headlineContent = {
186+
Text(
187+
text = item.name,
188+
fontWeight = FontWeight.Medium
189+
)
190+
},
191+
supportingContent = {
192+
Text(
193+
text = item.country,
194+
style = MaterialTheme.typography.bodyMedium
195+
)
196+
}
197+
)
198+
}
199+
}
200+
}
201+
} else if (uiState.textContent != null) {
202+
Column(modifier = Modifier.verticalScroll(scrollState)) {
203+
Markdown(uiState.textContent)
204+
}
205+
} else if (uiState.imageData != null) {
206+
Text(
207+
text = "Generated Image",
208+
style = MaterialTheme.typography.labelLarge,
209+
modifier = Modifier.padding(bottom = 8.dp)
142210
)
211+
Card(
212+
shape = RoundedCornerShape(8.dp),
213+
modifier = Modifier.fillMaxWidth()
214+
) {
215+
AsyncImage(
216+
model = ImageRequest
217+
.Builder(LocalPlatformContext.current)
218+
.data(uiState.imageData)
219+
.build(),
220+
contentDescription = prompt,
221+
contentScale = ContentScale.Fit,
222+
modifier = Modifier
223+
.fillMaxWidth()
224+
.padding(8.dp)
225+
)
226+
}
143227
}
144228
}
145-
} else if (uiState.textContent != null) {
146-
Column(modifier = Modifier.verticalScroll(scrollState)) {
147-
Markdown(uiState.textContent)
148-
}
149-
} else if (uiState.imageData != null) {
150-
AsyncImage(
151-
model = ImageRequest
152-
.Builder(LocalPlatformContext.current)
153-
.data(uiState.imageData)
154-
.build(),
155-
contentDescription = prompt,
156-
contentScale = ContentScale.Fit,
157-
modifier = Modifier.fillMaxWidth()
158-
)
159229
}
160230
}
161231

162232
is GenerativeModelUIState.Error -> {
163-
Text(uiState.message)
233+
Card(
234+
modifier = Modifier.fillMaxWidth(),
235+
colors = CardDefaults.cardColors(
236+
containerColor = MaterialTheme.colorScheme.errorContainer
237+
),
238+
shape = RoundedCornerShape(8.dp)
239+
) {
240+
Row(
241+
verticalAlignment = Alignment.CenterVertically,
242+
modifier = Modifier.padding(16.dp)
243+
) {
244+
Text(
245+
text = uiState.message,
246+
color = MaterialTheme.colorScheme.onErrorContainer,
247+
style = MaterialTheme.typography.bodyLarge
248+
)
249+
}
250+
}
164251
}
165252
}
166253
}

0 commit comments

Comments
 (0)