diff --git a/composeApp/src/androidMain/kotlin/com/github/jvsena42/echo/ui/home/HomeEmptyContent.kt b/composeApp/src/androidMain/kotlin/com/github/jvsena42/echo/ui/home/HomeEmptyContent.kt
index bdc7856..be766df 100644
--- a/composeApp/src/androidMain/kotlin/com/github/jvsena42/echo/ui/home/HomeEmptyContent.kt
+++ b/composeApp/src/androidMain/kotlin/com/github/jvsena42/echo/ui/home/HomeEmptyContent.kt
@@ -4,12 +4,14 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.automirrored.filled.MenuBook
+import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
@@ -29,7 +31,8 @@ import com.github.jvsena42.echo.ui.components.EchoSecondaryButton
import com.github.jvsena42.echo.ui.theme.EchoTheme
/**
- * Mirrors Pencil node `nwHYV` — "No decks yet" empty state.
+ * Mirrors Pencil node `nwHYV` — "Nothing to study yet" empty state: a white card with a circular
+ * book-open badge, title + subtitle, followed by the "Create a deck" / "Browse examples" actions.
*/
@Composable
fun HomeEmptyContent(
@@ -48,18 +51,23 @@ fun HomeEmptyContent(
)
.clip(RoundedCornerShape(28.dp))
.background(colors.surfaceCard)
- .padding(horizontal = 28.dp, vertical = 36.dp),
+ .padding(horizontal = 24.dp, vertical = 32.dp),
horizontalAlignment = Alignment.CenterHorizontally,
- verticalArrangement = Arrangement.spacedBy(20.dp),
+ verticalArrangement = Arrangement.spacedBy(16.dp),
) {
Box(
modifier = Modifier
- .size(140.dp)
- .clip(RoundedCornerShape(28.dp))
+ .size(100.dp)
+ .clip(CircleShape)
.background(colors.accentPrimarySoft),
contentAlignment = Alignment.Center,
) {
- Text(text = "\uD83D\uDCDA", fontSize = 64.sp)
+ Icon(
+ imageVector = Icons.AutoMirrored.Filled.MenuBook,
+ contentDescription = null,
+ tint = colors.accentPrimary,
+ modifier = Modifier.size(48.dp),
+ )
}
Text(
text = stringResource(R.string.home_empty_title),
@@ -74,12 +82,10 @@ fun HomeEmptyContent(
fontSize = 14.sp,
fontWeight = FontWeight.Medium,
textAlign = TextAlign.Center,
- lineHeight = 20.sp,
+ lineHeight = 21.sp,
)
}
- Spacer(Modifier.height(4.dp))
-
Column(
modifier = Modifier.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(12.dp),
diff --git a/composeApp/src/androidMain/kotlin/com/github/jvsena42/echo/ui/home/HomeScreen.kt b/composeApp/src/androidMain/kotlin/com/github/jvsena42/echo/ui/home/HomeScreen.kt
index f14e149..f2c067b 100644
--- a/composeApp/src/androidMain/kotlin/com/github/jvsena42/echo/ui/home/HomeScreen.kt
+++ b/composeApp/src/androidMain/kotlin/com/github/jvsena42/echo/ui/home/HomeScreen.kt
@@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.statusBars
import androidx.compose.foundation.layout.windowInsetsPadding
@@ -123,6 +124,17 @@ private fun HomeScreenContent(
onDeckClick: (String) -> Unit,
onRetry: () -> Unit,
) {
+ // The empty state centers its card + actions in the space below the greeting (design `nwHYV`),
+ // so it gets a non-scrolling full-height layout instead of the shared scroll column.
+ if (state is HomeUiState.Empty) {
+ HomeEmptyScreen(
+ greetingName = state.greetingName,
+ onCreateDeckClick = onCreateDeckClick,
+ onBrowseExamplesClick = onBrowseExamplesClick,
+ )
+ return
+ }
+
Column(
modifier = Modifier
.fillMaxSize()
@@ -131,14 +143,6 @@ private fun HomeScreenContent(
verticalArrangement = Arrangement.spacedBy(24.dp),
) {
when (state) {
- HomeUiState.Loading -> Unit
- is HomeUiState.Empty -> {
- GreetingHeader(name = state.greetingName)
- HomeEmptyContent(
- onCreateDeckClick = onCreateDeckClick,
- onBrowseExamplesClick = onBrowseExamplesClick,
- )
- }
is HomeUiState.Content -> {
GreetingHeader(name = state.greetingName)
HomeContent(
@@ -151,6 +155,34 @@ private fun HomeScreenContent(
GreetingHeader(name = state.greetingName)
ErrorBlock(message = state.message, onRetry = onRetry)
}
+ HomeUiState.Loading, is HomeUiState.Empty -> Unit
+ }
+ }
+}
+
+@Composable
+private fun HomeEmptyScreen(
+ greetingName: String,
+ onCreateDeckClick: () -> Unit,
+ onBrowseExamplesClick: () -> Unit,
+) {
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(PaddingValues(start = 20.dp, end = 20.dp, top = 8.dp, bottom = 24.dp)),
+ verticalArrangement = Arrangement.spacedBy(24.dp),
+ ) {
+ GreetingHeader(name = greetingName)
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .weight(1f),
+ verticalArrangement = Arrangement.spacedBy(24.dp, Alignment.CenterVertically),
+ ) {
+ HomeEmptyContent(
+ onCreateDeckClick = onCreateDeckClick,
+ onBrowseExamplesClick = onBrowseExamplesClick,
+ )
}
}
}
diff --git a/composeApp/src/androidMain/kotlin/com/github/jvsena42/echo/ui/nav/MainScreen.kt b/composeApp/src/androidMain/kotlin/com/github/jvsena42/echo/ui/nav/MainScreen.kt
index f6f434e..ffadd17 100644
--- a/composeApp/src/androidMain/kotlin/com/github/jvsena42/echo/ui/nav/MainScreen.kt
+++ b/composeApp/src/androidMain/kotlin/com/github/jvsena42/echo/ui/nav/MainScreen.kt
@@ -53,6 +53,9 @@ fun MainScreen(
EchoTab.STUDY -> HomeRoute(
onOpenDeck = { deckId -> onNavigateDeckDetail(deckId, null) },
onCreateDeck = onNavigateCreateDeck,
+ onBrowseExamples = {
+ scope.launch { pagerState.animateScrollToPage(EchoTab.DISCOVER.ordinal) }
+ },
onStartStudy = { onNavigateStudy(null) },
onSignedOut = onSignOut,
)
diff --git a/composeApp/src/androidMain/res/values/strings.xml b/composeApp/src/androidMain/res/values/strings.xml
index d20ae90..c9a07a7 100644
--- a/composeApp/src/androidMain/res/values/strings.xml
+++ b/composeApp/src/androidMain/res/values/strings.xml
@@ -15,9 +15,9 @@
Today\'s decks
See all
%1$d due · %2$d cards
- No decks yet
- Paste a list, import from a file, or start from scratch — your first deck is one tap away.
- Create your first deck
+ Nothing to study yet
+ Create or import a deck to start your first study session. Cards will appear here when they\'re ready for review.
+ Create a deck
Browse examples
diff --git a/design/main/phone-echo.pen b/design/main/phone-echo.pen
index bb1dd7f..2049af9 100644
--- a/design/main/phone-echo.pen
+++ b/design/main/phone-echo.pen
@@ -9059,113 +9059,127 @@
},
{
"type": "frame",
- "id": "6wNJm",
- "name": "emptyCard",
+ "id": "IB66Z",
+ "name": "centerWrap",
"width": "fill_container",
- "fill": "$surface-card",
- "cornerRadius": "$radius-xl",
- "effect": {
- "type": "shadow",
- "shadowType": "outer",
- "color": "#1A132614",
- "offset": {
- "x": 0,
- "y": 10
- },
- "blur": 24
- },
+ "height": "fill_container",
+ "fill": "#00000000",
"layout": "vertical",
- "gap": 20,
- "padding": [
- 36,
- 28
- ],
+ "gap": 24,
+ "justifyContent": "center",
"alignItems": "center",
"children": [
{
"type": "frame",
- "id": "7maWS",
- "name": "iconWrap",
- "width": 140,
- "height": 140,
- "fill": "$accent-primary-soft",
+ "id": "6wNJm",
+ "name": "emptyCard",
+ "width": "fill_container",
+ "fill": "$surface-card",
"cornerRadius": "$radius-xl",
- "justifyContent": "center",
+ "effect": {
+ "type": "shadow",
+ "shadowType": "outer",
+ "color": "#1A132614",
+ "offset": {
+ "x": 0,
+ "y": 10
+ },
+ "blur": 24
+ },
+ "layout": "vertical",
+ "gap": 16,
+ "padding": [
+ 32,
+ 24
+ ],
"alignItems": "center",
"children": [
{
- "type": "icon",
- "id": "pqHop",
- "width": 72,
- "height": 72,
- "icon": "layers",
- "library": "lucide",
- "fill": "$accent-primary"
+ "type": "frame",
+ "id": "7maWS",
+ "name": "iconWrap",
+ "width": 100,
+ "height": 100,
+ "fill": "$accent-primary-soft",
+ "cornerRadius": 50,
+ "justifyContent": "center",
+ "alignItems": "center",
+ "children": [
+ {
+ "type": "icon",
+ "id": "pqHop",
+ "width": 48,
+ "height": 48,
+ "icon": "book-open",
+ "library": "lucide",
+ "fill": "$accent-primary"
+ }
+ ]
+ },
+ {
+ "type": "text",
+ "id": "dGTtr",
+ "name": "title",
+ "fill": "$foreground-primary",
+ "textGrowth": "fixed-width",
+ "width": "fill_container",
+ "content": "Nothing to study yet",
+ "textAlign": "center",
+ "fontFamily": "Funnel Sans",
+ "fontSize": 24,
+ "fontWeight": "800"
+ },
+ {
+ "type": "text",
+ "id": "PIB3B",
+ "name": "sub",
+ "fill": "$foreground-muted",
+ "textGrowth": "fixed-width",
+ "width": "fill_container",
+ "content": "Create or import a deck to start your first study session. Cards will appear here when they're ready for review.",
+ "lineHeight": 1.5,
+ "textAlign": "center",
+ "fontFamily": "Inter",
+ "fontSize": 14,
+ "fontWeight": "500"
}
]
},
{
- "type": "text",
- "id": "dGTtr",
- "name": "title",
- "fill": "$foreground-primary",
- "textGrowth": "fixed-width",
- "width": "fill_container",
- "content": "No decks yet",
- "textAlign": "center",
- "fontFamily": "Funnel Sans",
- "fontSize": 24,
- "fontWeight": "800"
- },
- {
- "type": "text",
- "id": "PIB3B",
- "name": "sub",
- "fill": "$foreground-muted",
- "textGrowth": "fixed-width",
- "width": "fill_container",
- "content": "Paste a list, import from a file, or start from scratch — your first deck is one tap away.",
- "lineHeight": 1.4,
- "textAlign": "center",
- "fontFamily": "Inter",
- "fontSize": 14,
- "fontWeight": "500"
- }
- ]
- },
- {
- "type": "frame",
- "id": "5loUQ",
- "name": "btnGroup",
- "width": "fill_container",
- "fill": "#00000000",
- "layout": "vertical",
- "gap": 12,
- "alignItems": "center",
- "children": [
- {
- "id": "bmQqo",
- "type": "ref",
- "ref": "1sozq",
- "width": "fill_container",
- "name": "primary",
- "descendants": {
- "h1MHC": {
- "content": "Create your first deck"
- }
- }
- },
- {
- "id": "2aEsV",
- "type": "ref",
- "ref": "7KZM9",
+ "type": "frame",
+ "id": "5loUQ",
+ "name": "btnGroup",
"width": "fill_container",
- "name": "secondary",
- "descendants": {
- "YjRk8": {
- "content": "Browse examples"
+ "fill": "#00000000",
+ "layout": "vertical",
+ "gap": 12,
+ "alignItems": "center",
+ "children": [
+ {
+ "id": "bmQqo",
+ "type": "ref",
+ "ref": "1sozq",
+ "width": "fill_container",
+ "name": "primary",
+ "descendants": {
+ "h1MHC": {
+ "content": "Create a deck"
+ }
+ }
+ },
+ {
+ "id": "2aEsV",
+ "type": "ref",
+ "ref": "7KZM9",
+ "width": "fill_container",
+ "name": "secondary",
+ "descendants": {
+ "YjRk8": {
+ "content": "Browse examples"
+ }
+ }
}
- }
+ ]
}
]
}