better app icon

This commit is contained in:
Sergey Chernov 2026-05-23 16:12:23 +03:00
parent 02bde7e644
commit 75ba13bde2
39 changed files with 498 additions and 142 deletions

View File

@ -101,6 +101,19 @@ compose.desktop {
targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb) targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
packageName = "net.sergeych.toread" packageName = "net.sergeych.toread"
packageVersion = "1.0.0" packageVersion = "1.0.0"
macOS {
iconFile.set(project.file("src/jvmMain/resources/icons/icon.icns"))
}
windows {
iconFile.set(project.file("src/jvmMain/resources/icons/icon.ico"))
shortcut = true
menu = true
}
linux {
iconFile.set(project.file("src/jvmMain/resources/icons/icon.png"))
shortcut = true
}
} }
} }
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

View File

@ -1,65 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Adaptive icon foreground: 108dp x 108dp canvas.
Safe zone: inner 72dp circle (18dp..90dp). Book (200x260) scaled 0.2769x:
translateX=26.31, translateY=18. Star sub-group at book coords (67,59) scale 0.888x.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<group
android:translateX="26.31"
android:translateY="18"
android:scaleX="0.2769"
android:scaleY="0.2769">
<!-- Pages stack -->
<path android:fillColor="#e8e4d8"
android:pathData="M36,18 H178 V248 H36 Z"/>
<path android:fillColor="#ece9df"
android:pathData="M34,16 H176 V246 H34 Z"/>
<path android:fillColor="#f0ede3"
android:pathData="M32,14 H174 V244 H32 Z"/>
<!-- Spine -->
<path android:fillColor="#1a3d52"
android:pathData="M18,14 H36 V244 H18 Z"/>
<path android:fillColor="#00000000"
android:strokeColor="#30000000"
android:strokeWidth="1"
android:pathData="M35,14 V244"/>
<!-- Cover -->
<path android:fillColor="#2c5f7a"
android:pathData="M32,14 H174 V244 H32 Z"/>
<!-- Cover border -->
<path android:fillColor="#00000000"
android:strokeColor="#30ffffff"
android:strokeWidth="1.5"
android:pathData="M40,22 H166 V236 H40 Z"/>
<!-- Star arms -->
<group
android:translateX="67"
android:translateY="59"
android:scaleX="0.888"
android:scaleY="0.888">
<path android:fillColor="#6197b4"
android:pathData="m 32.120065,25.957965 -15.0296,-9.2912 c -0.3066,-0.187 -0.6532,0.1603 -0.4666,0.4676 l 8.9112,14.4745 c 0.2399,0.3874 0.5665,0.7013 0.9597,0.9284 l 14.0032,8.0555 V 0.34209525 c 0,-0.394094 -0.5532,-0.474248 -0.6665,-0.1002 z"/>
<path android:fillColor="#bdccd6"
android:pathData="m 25.901665,48.988865 -9.271,15.0556 c -0.1866,0.3073 0.16,0.6546 0.4665,0.4676 l 14.4431,-8.9305 c 0.3866,-0.2405 0.6999,-0.5678 0.9265,-0.9619 l 8.038,-14.0336 H 0.34134532 c -0.393231,0 -0.473212,0.5544 -0.09997,0.6679 z"/>
<path android:fillColor="#94bfcf"
android:pathData="m 55.101065,32.189965 9.2711,-15.0623 c 0.1866,-0.3072 -0.16,-0.6546 -0.4666,-0.4675 l -14.4431,8.9371 c -0.3865,0.2405 -0.6998,0.5678 -0.9264,0.9619 l -8.038,14.027 h 40.1634 c 0.3932,0 0.4732,-0.5544 0.1,-0.668 z"/>
<path android:fillColor="#eee9d9"
android:pathData="m 54.507965,48.641565 -14.0099,-8.0555 v 40.2507 c 0,0.3941 0.5532,0.4742 0.6665,0.1002 l 7.7181,-25.7094 15.0296,9.2912 c 0.3066,0.187 0.6532,-0.1603 0.4665,-0.4676 l -8.9177,-14.4812 c -0.2333,-0.3807 -0.5666,-0.7013 -0.9531,-0.9284 z"/>
</group>
<!-- Bottom spine accent -->
<path android:fillColor="#0e2030"
android:pathData="M18,228 H36 V244 H18 Z"/>
</group>
</vector>

View File

@ -0,0 +1,120 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="512"
android:viewportHeight="512">
<group android:scaleX="0.84"
android:scaleY="0.84"
android:translateX="40.96"
android:translateY="40.96">
<path
android:pathData="M0,0h512v512h-512z">
<aapt:attr name="android:fillColor">
<gradient
android:centerX="230.4"
android:centerY="179.2"
android:gradientRadius="384"
android:type="radial">
<item android:offset="0" android:color="#FF2E6B8A"/>
<item android:offset="1" android:color="#FF0D2535"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M116.79,259.93a145.45,160 0,1 0,290.9 0a145.45,160 0,1 0,-290.9 0z"
android:strokeAlpha="0.25"
android:strokeWidth="0.727253"
android:fillColor="#2e6b8a"
android:fillAlpha="0.25"/>
<path
android:pathData="M156.06,140.66L372.78,140.66A7.27,7.27 0,0 1,380.05 147.93L380.05,364.65A7.27,7.27 0,0 1,372.78 371.92L156.06,371.92A7.27,7.27 0,0 1,148.79 364.65L148.79,147.93A7.27,7.27 0,0 1,156.06 140.66z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="148.79"
android:startY="140.66"
android:endX="380.05"
android:endY="140.66"
android:type="linear">
<item android:offset="0" android:color="#FFF0EDE3"/>
<item android:offset="1" android:color="#FFE8E4D8"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M150.24,134.84L366.96,134.84A7.27,7.27 0,0 1,374.24 142.11L374.24,358.83A7.27,7.27 0,0 1,366.96 366.1L150.24,366.1A7.27,7.27 0,0 1,142.97 358.83L142.97,142.11A7.27,7.27 0,0 1,150.24 134.84z"
android:fillColor="#ece9df"/>
<path
android:pathData="M144.42,129.02L361.15,129.02A7.27,7.27 0,0 1,368.42 136.29L368.42,353.01A7.27,7.27 0,0 1,361.15 360.29L144.42,360.29A7.27,7.27 0,0 1,137.15 353.01L137.15,136.29A7.27,7.27 0,0 1,144.42 129.02z"
android:fillColor="#f0ede3"/>
<path
android:pathData="M129,128.77L155.69,128.77A7.85,7.85 0,0 1,163.53 136.61L163.53,382.98A7.85,7.85 0,0 1,155.69 390.83L129,390.83A7.85,7.85 0,0 1,121.15 382.98L121.15,136.61A7.85,7.85 0,0 1,129 128.77z"
android:strokeWidth="0.755863"
android:strokeColor="#00000000">
<aapt:attr name="android:fillColor">
<gradient
android:startX="121.15"
android:startY="128.77"
android:endX="163.53"
android:endY="128.77"
android:type="linear">
<item android:offset="0" android:color="#FF122C3C"/>
<item android:offset="1" android:color="#FF2E6B8A"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M158.97,129.02L380.05,129.02A7.27,7.27 0,0 1,387.33 136.29L387.33,383.56A7.27,7.27 0,0 1,380.05 390.83L158.97,390.83A7.27,7.27 0,0 1,151.7 383.56L151.7,136.29A7.27,7.27 0,0 1,158.97 129.02z"
android:strokeWidth="0.727253">
<aapt:attr name="android:fillColor">
<gradient
android:startX="151.7"
android:startY="129.02"
android:endX="387.33"
android:endY="390.83"
android:type="linear">
<item android:offset="0" android:color="#FF3A82A6"/>
<item android:offset="1" android:color="#FF1A3D52"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M158.97,129.02L380.05,129.02A7.27,7.27 0,0 1,387.33 136.29L387.33,383.56A7.27,7.27 0,0 1,380.05 390.83L158.97,390.83A7.27,7.27 0,0 1,151.7 383.56L151.7,136.29A7.27,7.27 0,0 1,158.97 129.02z"
android:strokeWidth="0.727253">
<aapt:attr name="android:fillColor">
<gradient
android:startX="151.7"
android:startY="129.02"
android:endX="222.39"
android:endY="390.83"
android:type="linear">
<item android:offset="0" android:color="#2DFFFFFF"/>
<item android:offset="1" android:color="#00FFFFFF"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M160.42,136.29L160.42,383.56"
android:strokeWidth="1.45451"
android:fillColor="#00000000"
android:strokeColor="#30000000"/>
<path
android:pathData="M166.93,141.3L373.24,141.3A4.69,4.69 0,0 1,377.93 145.99L377.93,377.33A4.69,4.69 0,0 1,373.24 382.01L166.93,382.01A4.69,4.69 0,0 1,162.24 377.33L162.24,145.99A4.69,4.69 0,0 1,166.93 141.3z"
android:strokeAlpha="0.104098"
android:strokeWidth="3.10595"
android:fillColor="#00000000"
android:strokeColor="#000000"/>
<path
android:pathData="m249.01,223.24 l-37.71,-23.31c-0.77,-0.47 -1.64,0.4 -1.17,1.17l22.36,36.32c0.6,0.97 1.42,1.76 2.41,2.33l35.13,20.21L270.03,158.97c0,-0.99 -1.39,-1.19 -1.67,-0.25z"
android:fillColor="#6197b4"/>
<path
android:pathData="m233.41,281.02 l-23.26,37.77c-0.47,0.77 0.4,1.64 1.17,1.17l36.24,-22.41c0.97,-0.6 1.76,-1.42 2.32,-2.41l20.17,-35.21L169.28,259.94c-0.99,0 -1.19,1.39 -0.25,1.68z"
android:fillColor="#bdccd6"/>
<path
android:pathData="m306.67,238.88 l23.26,-37.79c0.47,-0.77 -0.4,-1.64 -1.17,-1.17l-36.24,22.42c-0.97,0.6 -1.76,1.42 -2.32,2.41l-20.17,35.19l100.77,0c0.99,0 1.19,-1.39 0.25,-1.68z"
android:fillColor="#94bfcf"/>
<path
android:pathData="m305.19,280.15 l-35.15,-20.21l0,100.99c0,0.99 1.39,1.19 1.67,0.25l19.36,-64.51 37.71,23.31c0.77,0.47 1.64,-0.4 1.17,-1.17l-22.37,-36.33c-0.59,-0.96 -1.42,-1.76 -2.39,-2.33z"
android:fillColor="#eee9d9"/>
</group>
</vector>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background"/> <background android:drawable="@mipmap/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/> <foreground android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon> </adaptive-icon>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background"/> <background android:drawable="@mipmap/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/> <foreground android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon> </adaptive-icon>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -30,6 +30,7 @@ import kotlinx.coroutines.launch
private const val DefaultToastDurationMillis = 1_600L private const val DefaultToastDurationMillis = 1_600L
private const val DeleteUndoDurationMillis = 5_000L private const val DeleteUndoDurationMillis = 5_000L
private const val BackgroundLibraryPageSize = 50
private data class AppToastData( private data class AppToastData(
val id: Long, val id: Long,
@ -151,7 +152,7 @@ private fun BookReaderApp(
durationMillis: Long, durationMillis: Long,
) -> Unit, ) -> Unit,
) { ) {
var state by remember { mutableStateOf<AppState>(AppState.LoadingLibrary) } var state by remember { mutableStateOf<AppState>(AppState.LoadingStartup) }
var activeScan by remember { mutableStateOf<LibraryScanProgress?>(null) } var activeScan by remember { mutableStateOf<LibraryScanProgress?>(null) }
var scanJob by remember { mutableStateOf<Job?>(null) } var scanJob by remember { mutableStateOf<Job?>(null) }
var pendingDelete by remember { mutableStateOf<PendingLibraryDelete?>(null) } var pendingDelete by remember { mutableStateOf<PendingLibraryDelete?>(null) }
@ -165,6 +166,34 @@ private fun BookReaderApp(
state = loadStartupState() state = loadStartupState()
} }
val activeBookState = when (val current = state) {
is AppState.Reader -> current.fileId to current.libraryItems.isEmpty()
is AppState.BookInfo -> current.fileId to current.libraryItems.isEmpty()
else -> null
}
LaunchedEffect(activeBookState?.first, activeBookState?.second) {
val (fileId, needsLibraryItems) = activeBookState ?: return@LaunchedEffect
if (!needsLibraryItems) return@LaunchedEffect
runCatching { loadLibraryItemsPage(BackgroundLibraryPageSize, 0) }
.onSuccess { items ->
state = when (val current = state) {
is AppState.Reader ->
if (current.fileId == fileId && current.libraryItems.isEmpty()) {
current.copy(libraryItems = items)
} else {
current
}
is AppState.BookInfo ->
if (current.fileId == fileId && current.libraryItems.isEmpty()) {
current.copy(libraryItems = items)
} else {
current
}
else -> current
}
}
}
fun commitPendingDelete(delete: PendingLibraryDelete) { fun commitPendingDelete(delete: PendingLibraryDelete) {
scope.launch { scope.launch {
val result = deleteLibraryBook(delete.request.fileId, delete.request.title) val result = deleteLibraryBook(delete.request.fileId, delete.request.title)
@ -238,18 +267,18 @@ private fun BookReaderApp(
) )
is AppState.Reader -> { is AppState.Reader -> {
scope.launch { saveActiveReadingFileId(null) } scope.launch { saveActiveReadingFileId(null) }
AppState.Library(emptyList(), current.scanPath, current.message) AppState.Library(current.libraryItems, current.scanPath, current.message)
} }
is AppState.Scan -> AppState.Library(current.items, current.scanPath, current.message) is AppState.Scan -> AppState.Library(current.items, current.scanPath, current.message)
is AppState.Error -> AppState.LoadingLibrary is AppState.Error -> AppState.LoadingStartup
is AppState.Library, AppState.LoadingLibrary -> current is AppState.Library, AppState.LoadingStartup -> current
} }
} }
val navigationDepth = when (state) { val navigationDepth = when (state) {
is AppState.BookInfo -> 2 is AppState.BookInfo -> 2
is AppState.Error, is AppState.Reader, is AppState.Scan -> 1 is AppState.Error, is AppState.Reader, is AppState.Scan -> 1
is AppState.Library, AppState.LoadingLibrary -> 0 is AppState.Library, AppState.LoadingStartup -> 0
} + if (imageViewer != null) 1 else 0 } + if (imageViewer != null) 1 else 0
PlatformBackHandler( PlatformBackHandler(
@ -291,7 +320,7 @@ private fun BookReaderApp(
} else { } else {
AppState.Library(current.items, path, message) AppState.Library(current.items, path, message)
} }
AppState.LoadingLibrary -> loadLibraryState(message, path) AppState.LoadingStartup -> loadLibraryState(message, path)
is AppState.Reader -> current.copy(message = message) is AppState.Reader -> current.copy(message = message)
is AppState.BookInfo -> current.copy(message = message) is AppState.BookInfo -> current.copy(message = message)
is AppState.Error -> current is AppState.Error -> current
@ -300,7 +329,7 @@ private fun BookReaderApp(
} }
when (val current = state) { when (val current = state) {
AppState.LoadingLibrary -> LoadingScreen("Opening library") AppState.LoadingStartup -> LoadingScreen("Opening book")
is AppState.Library -> LibraryScreen( is AppState.Library -> LibraryScreen(
state = current, state = current,
activeScan = activeScan, activeScan = activeScan,

View File

@ -4,7 +4,7 @@ import net.sergeych.toread.fb2.Fb2Book
import net.sergeych.toread.fb2.Fb2Format import net.sergeych.toread.fb2.Fb2Format
internal sealed interface AppState { internal sealed interface AppState {
data object LoadingLibrary : AppState data object LoadingStartup : AppState
data class Library( data class Library(
val items: List<LibraryItem>, val items: List<LibraryItem>,
val scanPath: String, val scanPath: String,
@ -37,39 +37,40 @@ internal sealed interface AppState {
} }
internal suspend fun loadStartupState(): AppState { internal suspend fun loadStartupState(): AppState {
val library = loadLibraryState() val scanPath = try {
if (library !is AppState.Library) return library defaultLibraryScanPath().orEmpty()
} catch (t: Throwable) {
return AppState.Error(t.message ?: "Could not open library.")
}
loadPlatformOpenBookRequest()?.let { request -> loadPlatformOpenBookRequest()?.let { request ->
return runCatching { return runCatching {
AppState.Reader( AppState.Reader(
fileId = request.id, fileId = request.id,
book = Fb2Format.parse(request.bytes, request.displayName), book = Fb2Format.parse(request.bytes, request.displayName),
libraryItems = library.items, libraryItems = emptyList(),
scanPath = library.scanPath, scanPath = scanPath,
message = library.message,
) )
}.getOrElse { }.getOrElse {
AppState.Library(library.items, library.scanPath, it.message ?: "Could not open ${request.displayName}.") AppState.Library(emptyList(), scanPath, it.message ?: "Could not open ${request.displayName}.")
} }
} }
val activeFileId = loadActiveReadingFileId() ?: return library val activeFileId = loadActiveReadingFileId() ?: return AppState.Library(emptyList(), scanPath)
val item = loadLibraryItem(activeFileId) val item = loadLibraryItem(activeFileId)
if (item == null) { if (item == null) {
saveActiveReadingFileId(null) saveActiveReadingFileId(null)
return library return AppState.Library(emptyList(), scanPath)
} }
return runCatching { return runCatching {
val bytes = openLibraryBook(activeFileId) ?: error("Book file is not available.") val bytes = openLibraryBook(activeFileId) ?: error("Book file is not available.")
AppState.Reader( AppState.Reader(
fileId = activeFileId, fileId = activeFileId,
book = Fb2Format.parse(bytes, item.storageUri ?: item.title), book = Fb2Format.parse(bytes, item.storageUri ?: item.title),
libraryItems = library.items, libraryItems = emptyList(),
scanPath = library.scanPath, scanPath = scanPath,
message = library.message,
) )
}.getOrElse { }.getOrElse {
saveActiveReadingFileId(null) saveActiveReadingFileId(null)
AppState.Library(library.items, library.scanPath, it.message ?: "Could not reopen last book.") AppState.Library(emptyList(), scanPath, it.message ?: "Could not reopen last book.")
} }
} }

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 KiB

View File

@ -0,0 +1,59 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- 512x512 app icon - square book mark for launcher use -->
<svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
<defs>
<radialGradient id="bgGrad" cx="45%" cy="35%" r="75%">
<stop offset="0%" stop-color="#2e6b8a"/>
<stop offset="100%" stop-color="#0d2535"/>
</radialGradient>
<filter id="shadow" x="-10%" y="-5%" width="130%" height="120%">
<feOffset in="SourceAlpha" dx="5" dy="7" result="offset"/>
<feGaussianBlur in="offset" stdDeviation="8" result="blur"/>
<feColorMatrix in="blur" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.4 0" result="shadowBlur"/>
<feMerge>
<feMergeNode in="shadowBlur"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
<linearGradient id="pageGrad" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stop-color="#f0ede3"/>
<stop offset="100%" stop-color="#e8e4d8"/>
</linearGradient>
<linearGradient id="coverGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#3a82a6"/>
<stop offset="100%" stop-color="#1a3d52"/>
</linearGradient>
<linearGradient id="spineGrad" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stop-color="#122c3c"/>
<stop offset="100%" stop-color="#2e6b8a"/>
</linearGradient>
<linearGradient id="coverShine" x1="0%" y1="0%" x2="30%" y2="100%">
<stop offset="0%" stop-color="#ffffff" stop-opacity="0.18"/>
<stop offset="100%" stop-color="#ffffff" stop-opacity="0"/>
</linearGradient>
</defs>
<rect width="512" height="512" fill="url(#bgGrad)"/>
<ellipse cx="268" cy="256" rx="200" ry="220" fill="#2e6b8a" opacity="0.25"/>
<g filter="url(#shadow)">
<rect x="112" y="92" width="318" height="318" rx="10" fill="url(#pageGrad)"/>
<rect x="104" y="84" width="318" height="318" rx="10" fill="#ece9df"/>
<rect x="96" y="76" width="318" height="318" rx="10" fill="#f0ede3"/>
</g>
<rect x="74" y="76" width="54" height="360" rx="10" fill="url(#spineGrad)"/>
<rect x="116" y="76" width="324" height="360" rx="10" fill="url(#coverGrad)"/>
<rect x="116" y="76" width="324" height="360" rx="10" fill="url(#coverShine)"/>
<line x1="128" y1="86" x2="128" y2="426" stroke="#00000030" stroke-width="2"/>
<rect x="140" y="100" width="276" height="312" rx="6" fill="none" stroke="#ffffff30" stroke-width="4"/>
<g transform="translate(139, 116) scale(3.45)">
<path d="m 32.120065,25.957965 -15.0296,-9.2912 c -0.3066,-0.187 -0.6532,0.1603 -0.4666,0.4676 l 8.9112,14.4745 c 0.2399,0.3874 0.5665,0.7013 0.9597,0.9284 l 14.0032,8.0555 V 0.34209525 c 0,-0.394094 -0.5532,-0.474248 -0.6665,-0.1002 z" fill="#6197b4"/>
<path d="m 25.901665,48.988865 -9.271,15.0556 c -0.1866,0.3073 0.16,0.6546 0.4665,0.4676 l 14.4431,-8.9305 c 0.3866,-0.2405 0.6999,-0.5678 0.9265,-0.9619 l 8.038,-14.0336 H 0.34134532 c -0.393231,0 -0.473212,0.5544 -0.09997,0.6679 z" fill="#bdccd6"/>
<path d="m 55.101065,32.189965 9.2711,-15.0623 c 0.1866,-0.3072 -0.16,-0.6546 -0.4666,-0.4675 l -14.4431,8.9371 c -0.3865,0.2405 -0.6998,0.5678 -0.9264,0.9619 l -8.038,14.027 h 40.1634 c 0.3932,0 0.4732,-0.5544 0.1,-0.668 z" fill="#94bfcf"/>
<path d="m 54.507965,48.641565 -14.0099,-8.0555 v 40.2507 c 0,0.3941 0.5532,0.4742 0.6665,0.1002 l 7.7181,-25.7094 15.0296,9.2912 c 0.3066,0.187 0.6532,-0.1603 0.4665,-0.4676 l -8.9177,-14.4812 c -0.2333,-0.3807 -0.5666,-0.7013 -0.9531,-0.9284 z" fill="#eee9d9"/>
</g>
<rect x="74" y="390" width="54" height="46" rx="0 0 10 10" fill="#0e2030" opacity="0.75"/>
</svg>

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@ -1,65 +1,264 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- 512x512 app icon — desktop launcher & Android (keep content in inner ~340px safe zone) --> <!-- 512x512 app icon - square book mark for launcher use -->
<svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
<defs> <svg
<radialGradient id="bgGrad" cx="45%" cy="35%" r="75%"> width="512"
<stop offset="0%" stop-color="#2e6b8a"/> height="512"
<stop offset="100%" stop-color="#0d2535"/> viewBox="0 0 512 512"
fill="none"
version="1.1"
id="svg20"
sodipodi:docname="icon-app.svg"
inkscape:version="1.4.4 (1:1.4.4+202605061436+dcaf3e7d9e)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview20"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="1.7832031"
inkscape:cx="264.13144"
inkscape:cy="289.36693"
inkscape:window-width="1861"
inkscape:window-height="1163"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg20" />
<defs
id="defs10">
<radialGradient
id="bgGrad"
cx="45%"
cy="35%"
r="75%">
<stop
offset="0%"
stop-color="#2e6b8a"
id="stop1" />
<stop
offset="100%"
stop-color="#0d2535"
id="stop2" />
</radialGradient> </radialGradient>
<filter id="shadow" x="-10%" y="-5%" width="130%" height="120%"> <filter
<feDropShadow dx="5" dy="7" stdDeviation="8" flood-color="#00000066"/> id="shadow"
x="-0.05748503"
y="-0.05748503"
width="1.1299401"
height="1.1359281">
<feOffset
in="SourceAlpha"
dx="5"
dy="7"
result="offset"
id="feOffset2" />
<feGaussianBlur
in="offset"
stdDeviation="8"
result="blur"
id="feGaussianBlur2" />
<feColorMatrix
in="blur"
type="matrix"
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.4 0"
result="shadowBlur"
id="feColorMatrix2" />
<feMerge
id="feMerge3">
<feMergeNode
in="shadowBlur"
id="feMergeNode2" />
<feMergeNode
in="SourceGraphic"
id="feMergeNode3" />
</feMerge>
</filter> </filter>
<linearGradient id="pageGrad" x1="0%" y1="0%" x2="100%" y2="0%"> <linearGradient
<stop offset="0%" stop-color="#f0ede3"/> id="pageGrad"
<stop offset="100%" stop-color="#e8e4d8"/> x1="112"
y1="92"
x2="430"
y2="92"
gradientUnits="userSpaceOnUse">
<stop
offset="0%"
stop-color="#f0ede3"
id="stop3" />
<stop
offset="100%"
stop-color="#e8e4d8"
id="stop4" />
</linearGradient> </linearGradient>
<linearGradient id="coverGrad" x1="0%" y1="0%" x2="100%" y2="100%"> <linearGradient
<stop offset="0%" stop-color="#3a82a6"/> id="coverGrad"
<stop offset="100%" stop-color="#1a3d52"/> x1="122.27474"
y1="72.099931"
x2="463.80072"
y2="413.62592"
gradientTransform="matrix(0.6899326,0,0,0.76659181,67.335419,73.748798)"
gradientUnits="userSpaceOnUse">
<stop
offset="0%"
stop-color="#3a82a6"
id="stop5" />
<stop
offset="100%"
stop-color="#1a3d52"
id="stop6" />
</linearGradient> </linearGradient>
<linearGradient id="spineGrad" x1="0%" y1="0%" x2="100%" y2="0%"> <linearGradient
<stop offset="0%" stop-color="#122c3c"/> id="spineGrad"
<stop offset="100%" stop-color="#2e6b8a"/> x1="191.06718"
y1="29.434673"
x2="330.49458"
y2="29.434673"
gradientTransform="matrix(0.30396746,0,0,1.8795869,63.07392,73.440088)"
gradientUnits="userSpaceOnUse">
<stop
offset="0%"
stop-color="#122c3c"
id="stop7" />
<stop
offset="100%"
stop-color="#2e6b8a"
id="stop8" />
</linearGradient> </linearGradient>
<linearGradient id="coverShine" x1="0%" y1="0%" x2="30%" y2="100%"> <linearGradient
<stop offset="0%" stop-color="#ffffff" stop-opacity="0.18"/> id="coverShine"
<stop offset="100%" stop-color="#ffffff" stop-opacity="0"/> x1="122.27474"
y1="72.099931"
x2="224.73254"
y2="413.62592"
gradientTransform="matrix(0.6899326,0,0,0.76659181,67.335419,73.748798)"
gradientUnits="userSpaceOnUse">
<stop
offset="0%"
stop-color="#ffffff"
stop-opacity="0.18"
id="stop9" />
<stop
offset="100%"
stop-color="#ffffff"
stop-opacity="0"
id="stop10" />
</linearGradient> </linearGradient>
</defs> </defs>
<rect
<!-- Full-bleed background --> width="512"
<rect width="512" height="512" fill="url(#bgGrad)"/> height="512"
fill="url(#bgGrad)"
<!-- Subtle radial glow behind book --> id="rect10" />
<ellipse cx="268" cy="256" rx="200" ry="220" fill="#2e6b8a" opacity="0.25"/> <ellipse
cx="262.23917"
<!-- Book, scaled 1.84x and centered (book canvas 200x260 → 368x478, centered in 512x512) --> cy="259.92554"
<!-- x_offset=(512-368)/2=72, y_offset=(512-478)/2=17 --> rx="145.45056"
<g transform="translate(72, 17) scale(1.84)"> ry="159.99562"
<!-- Pages --> fill="#2e6b8a"
<rect x="36" y="18" width="142" height="230" rx="2" fill="url(#pageGrad)" filter="url(#shadow)"/> opacity="0.25"
<rect x="34" y="16" width="142" height="230" rx="2" fill="#ece9df"/> id="ellipse10"
<rect x="32" y="14" width="142" height="230" rx="2" fill="#f0ede3"/> style="stroke-width:0.727253" />
<!-- Spine --> <g
<rect x="18" y="14" width="18" height="230" rx="2" fill="url(#spineGrad)"/> filter="url(#shadow)"
<line x1="35" y1="14" x2="35" y2="244" stroke="#00000030" stroke-width="1"/> id="g13"
<!-- Cover --> transform="matrix(0.72725281,0,0,0.72725281,67.335419,73.748798)">
<rect x="32" y="14" width="142" height="230" rx="2" fill="url(#coverGrad)"/> <rect
<rect x="32" y="14" width="142" height="230" rx="2" fill="url(#coverShine)"/> x="112"
<!-- Cover border --> y="92"
<rect x="40" y="22" width="126" height="214" rx="1" fill="none" stroke="#ffffff30" stroke-width="1.5"/> width="318"
<!-- Star (scale 0.888 within book, translate 67,59) --> height="318"
<g transform="translate(67, 59) scale(0.888)"> rx="10"
<path d="m 32.120065,25.957965 -15.0296,-9.2912 c -0.3066,-0.187 -0.6532,0.1603 -0.4666,0.4676 l 8.9112,14.4745 c 0.2399,0.3874 0.5665,0.7013 0.9597,0.9284 l 14.0032,8.0555 V 0.34209525 c 0,-0.394094 -0.5532,-0.474248 -0.6665,-0.1002 z" fill="#6197b4"/> fill="url(#pageGrad)"
<path d="m 25.901665,48.988865 -9.271,15.0556 c -0.1866,0.3073 0.16,0.6546 0.4665,0.4676 l 14.4431,-8.9305 c 0.3866,-0.2405 0.6999,-0.5678 0.9265,-0.9619 l 8.038,-14.0336 H 0.34134532 c -0.393231,0 -0.473212,0.5544 -0.09997,0.6679 z" fill="#bdccd6"/> id="rect11"
<path d="m 55.101065,32.189965 9.2711,-15.0623 c 0.1866,-0.3072 -0.16,-0.6546 -0.4666,-0.4675 l -14.4431,8.9371 c -0.3865,0.2405 -0.6998,0.5678 -0.9264,0.9619 l -8.038,14.027 h 40.1634 c 0.3932,0 0.4732,-0.5544 0.1,-0.668 z" fill="#94bfcf"/> style="fill:url(#pageGrad)" />
<path d="m 54.507965,48.641565 -14.0099,-8.0555 v 40.2507 c 0,0.3941 0.5532,0.4742 0.6665,0.1002 l 7.7181,-25.7094 15.0296,9.2912 c 0.3066,0.187 0.6532,-0.1603 0.4665,-0.4676 l -8.9177,-14.4812 c -0.2333,-0.3807 -0.5666,-0.7013 -0.9531,-0.9284 z" fill="#eee9d9"/> <rect
</g> x="104"
<!-- Title --> y="84"
<text x="103" y="180" text-anchor="middle" font-family="Georgia, serif" font-size="15" font-weight="bold" fill="#ffffff" letter-spacing="1">TOREAD</text> width="318"
<line x1="55" y1="188" x2="151" y2="188" stroke="#ffffff50" stroke-width="0.8"/> height="318"
<text x="103" y="202" text-anchor="middle" font-family="Georgia, serif" font-size="9" fill="#aaccdd" letter-spacing="2">E-READER</text> rx="10"
<!-- Bottom spine detail --> fill="#ece9df"
<rect x="18" y="228" width="18" height="16" fill="#0e2030"/> id="rect12" />
<rect
x="96"
y="76"
width="318"
height="318"
rx="10"
fill="#f0ede3"
id="rect13" />
</g>
<rect
x="121.15213"
y="128.76512"
width="42.381393"
height="262.06592"
rx="7.8484058"
fill="url(#spineGrad)"
id="rect14"
style="fill:url(#spineGrad);stroke:none;stroke-width:0.755863" />
<rect
x="151.69673"
y="129.02002"
width="235.62991"
height="261.811"
rx="7.2725282"
fill="url(#coverGrad)"
id="rect15"
style="fill:url(#coverGrad);stroke-width:0.727253" />
<rect
x="151.69673"
y="129.02002"
width="235.62991"
height="261.811"
rx="7.2725282"
fill="url(#coverShine)"
id="rect16"
style="display:inline;fill:url(#coverShine);stroke-width:0.727253" />
<line
x1="160.42378"
y1="136.29254"
x2="160.42378"
y2="383.5585"
stroke="#00000030"
stroke-width="1.45451"
id="line16" />
<rect
x="162.24403"
y="141.29759"
width="215.68887"
height="240.71645"
rx="4.6888885"
fill="none"
stroke="#ffffff30"
stroke-width="3.10595"
id="rect17"
style="fill:none;stroke:#000000;stroke-opacity:0.104098" />
<g
transform="matrix(2.5090222,0,0,2.5090222,168.42356,158.11013)"
id="g20">
<path
d="m 32.120065,25.957965 -15.0296,-9.2912 c -0.3066,-0.187 -0.6532,0.1603 -0.4666,0.4676 l 8.9112,14.4745 c 0.2399,0.3874 0.5665,0.7013 0.9597,0.9284 l 14.0032,8.0555 V 0.34209525 c 0,-0.394094 -0.5532,-0.474248 -0.6665,-0.1002 z"
fill="#6197b4"
id="path17" />
<path
d="m 25.901665,48.988865 -9.271,15.0556 c -0.1866,0.3073 0.16,0.6546 0.4665,0.4676 l 14.4431,-8.9305 c 0.3866,-0.2405 0.6999,-0.5678 0.9265,-0.9619 l 8.038,-14.0336 H 0.34134532 c -0.393231,0 -0.473212,0.5544 -0.09997,0.6679 z"
fill="#bdccd6"
id="path18" />
<path
d="m 55.101065,32.189965 9.2711,-15.0623 c 0.1866,-0.3072 -0.16,-0.6546 -0.4666,-0.4675 l -14.4431,8.9371 c -0.3865,0.2405 -0.6998,0.5678 -0.9264,0.9619 l -8.038,14.027 h 40.1634 c 0.3932,0 0.4732,-0.5544 0.1,-0.668 z"
fill="#94bfcf"
id="path19" />
<path
d="m 54.507965,48.641565 -14.0099,-8.0555 v 40.2507 c 0,0.3941 0.5532,0.4742 0.6665,0.1002 l 7.7181,-25.7094 15.0296,9.2912 c 0.3066,0.187 0.6532,-0.1603 0.4665,-0.4676 l -8.9177,-14.4812 c -0.2333,-0.3807 -0.5666,-0.7013 -0.9531,-0.9284 z"
fill="#eee9d9"
id="path20" />
</g> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 KiB