Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
25a7ab4
potential apkm fix
prateek-who Jan 31, 2026
f92d1c9
Update PatchCommand.kt
prateek-who Jan 31, 2026
227bc14
Update .gitignore
prateek-who Jan 31, 2026
b2e63be
Update PatchCommand.kt
prateek-who Feb 1, 2026
ee63a14
feat: GUI Update
prateek-who Feb 6, 2026
d4ae84e
Removed hush hush svgs
prateek-who Feb 6, 2026
3c9c11a
Simplified version fixes + .apkm support fixed
prateek-who Feb 6, 2026
5199e5d
Minor UI updates
prateek-who Feb 6, 2026
73151ae
apkmirror link builder
prateek-who Feb 7, 2026
67a8aab
fix build
LisoUseInAIKyrios Feb 7, 2026
d30d4a5
Add IDE launcher preset
LisoUseInAIKyrios Feb 7, 2026
b6de86b
Use web-search api
LisoUseInAIKyrios Feb 7, 2026
afb8846
Follow redirects in non simple mode
LisoUseInAIKyrios Feb 7, 2026
3bfc223
Minor UI updates
prateek-who Feb 8, 2026
930d4b3
Connected devices update
prateek-who Feb 8, 2026
783dd7a
Patch Screen UI Improvements
prateek-who Feb 9, 2026
fdba2c6
--riplibs update
prateek-who Feb 9, 2026
ffa14f9
Minor release and windows fixes
prateek-who Feb 10, 2026
512f6f5
Patching Engine Fix
prateek-who Feb 11, 2026
bf224f6
Minor Fixes
prateek-who Feb 11, 2026
46a4577
Use non zero Java exit code if patching fails
prateek-who Feb 12, 2026
7640a80
Minor Fixes
prateek-who Feb 12, 2026
4477106
Merge remote-tracking branch 'origin/dev' into gui-update
LisoUseInAIKyrios Feb 15, 2026
5fb0e1a
Minor Fixes and new theme
prateek-who Feb 15, 2026
b8d3109
Better Offline support + Minor fixes
prateek-who Feb 19, 2026
7927c7e
Merge branch 'dev' into gui-update
prateek-who Feb 19, 2026
34eb844
Update PatchesViewModel.kt
prateek-who Feb 19, 2026
3648b94
Merge branch 'dev' into gui-update
prateek-who Feb 22, 2026
f3f80fc
conflict fix proper
prateek-who Feb 22, 2026
2cbb805
this for sure is the fix
prateek-who Feb 22, 2026
3ac83f3
Merge branch 'dev' into gui-update
prateek-who Feb 28, 2026
3100d85
partial fix
prateek-who Feb 28, 2026
a445ce6
final fix?
prateek-who Feb 28, 2026
c3b2311
final fix actually?
prateek-who Feb 28, 2026
bf8058b
Extremely minor update
prateek-who Mar 8, 2026
633fa97
revert the aapt2 libraries exclude change
prateek-who Mar 9, 2026
258ee86
backup before remote reset
prateek-who Mar 11, 2026
7eed1c6
Major Update
prateek-who Mar 13, 2026
aed56c2
Minor UI Update
prateek-who Mar 16, 2026
bcea7dd
Major Update
prateek-who Mar 17, 2026
bdc6d5b
Major Fix Update
prateek-who Mar 21, 2026
408395d
Minor UI Fixes
prateek-who Mar 22, 2026
b73a3dd
added ability to patch xapk
prateek-who Mar 23, 2026
1b123a5
apks support added
prateek-who Mar 26, 2026
9f56cb6
Minor UI fixes
prateek-who Mar 28, 2026
70a840e
Options and minor fixes
prateek-who Mar 30, 2026
c04a7a6
Minor theme fixes
prateek-who Mar 31, 2026
8cae648
Delete CLAUDE.md
prateek-who Mar 31, 2026
9873385
Merge branch 'dev' into gui-overhaul
prateek-who Apr 1, 2026
3616a97
Final FIX HOPEFULLY
prateek-who Apr 1, 2026
2b99b01
Fixed image selection fix for patch screen's patch options
prateek-who Apr 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ build/

# Local configuration file (sdk path, etc)
local.properties
old_build.gradle.kts

# Log/OS Files
*.log
Expand Down
5 changes: 4 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ group = "app.morphe"
kotlin {
jvmToolchain {
languageVersion.set(JavaLanguageVersion.of(17))
vendor.set(JvmVendorSpec.ADOPTIUM)
vendor.set(JvmVendorSpec.JETBRAINS)
}
compilerOptions {
jvmTarget.set(JvmTarget.JVM_17)
Expand Down Expand Up @@ -100,6 +100,9 @@ dependencies {
implementation(libs.voyager.koin)
implementation(libs.voyager.transitions)

// -- JBR API (macOS title bar customization) ----------------------------
implementation(libs.jbr.api)

// -- APK Parsing (GUI) -------------------------------------------------
implementation(libs.apk.parser)

Expand Down
6 changes: 6 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ voyager = "1.1.0-beta03"
coroutines = "1.10.2"
kotlinx-serialization = "1.9.0"

# JBR
jbr-api = "1.5.0"

# APK
apk-parser = "2.6.10"

Expand Down Expand Up @@ -65,6 +68,9 @@ kotlinx-coroutines-swing = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-
# Serialization
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinx-serialization" }

# JBR
jbr-api = { module = "org.jetbrains.runtime:jbr-api", version.ref = "jbr-api" }

# APK
apk-parser = { module = "net.dongliu:apk-parser", version.ref = "apk-parser" }

Expand Down
2 changes: 1 addition & 1 deletion src/main/kotlin/app/morphe/cli/command/OptionsCommand.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import java.util.logging.Logger

@Command(
name = "options-create",
description = ["Create an options JSON file for the patches and options."],
description = ["Create an options JSON file for the patches and options."] ,
)
internal object OptionsCommand : Callable<Int> {

Expand Down
95 changes: 75 additions & 20 deletions src/main/kotlin/app/morphe/gui/App.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,29 @@
package app.morphe.gui

import androidx.compose.animation.Crossfade
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Surface
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clipToBounds
import androidx.compose.ui.unit.dp
import app.morphe.gui.ui.components.LocalTitleBarInsets
import app.morphe.gui.ui.components.LottieAnimation
import app.morphe.gui.ui.components.SakuraPetals
import app.morphe.gui.ui.components.TitleBarInsets
import cafe.adriel.voyager.navigator.Navigator
import cafe.adriel.voyager.transitions.SlideTransition
import app.morphe.gui.data.repository.ConfigRepository
import app.morphe.gui.data.repository.PatchRepository
import app.morphe.gui.util.PatchService
import app.morphe.gui.data.repository.PatchSourceManager
import app.morphe.gui.di.appModule
import kotlinx.coroutines.launch
import org.koin.compose.KoinApplication
import org.koin.compose.koinInject
import app.morphe.gui.ui.screens.home.HomeScreen
import app.morphe.gui.ui.screens.quick.QuickPatchContent
import app.morphe.gui.ui.screens.quick.QuickPatchViewModel
import app.morphe.gui.util.PatchService
import app.morphe.gui.ui.theme.LocalThemeState
import app.morphe.gui.ui.theme.MorpheTheme
import app.morphe.gui.ui.theme.ThemePreference
Expand Down Expand Up @@ -57,16 +64,16 @@ fun App(initialSimplifiedMode: Boolean = true) {
@Composable
private fun AppContent(initialSimplifiedMode: Boolean) {
val configRepository: ConfigRepository = koinInject()
val patchRepository: PatchRepository = koinInject()
val patchService: PatchService = koinInject()
val patchSourceManager: PatchSourceManager = koinInject()
val scope = rememberCoroutineScope()

var themePreference by remember { mutableStateOf(ThemePreference.SYSTEM) }
var isSimplifiedMode by remember { mutableStateOf(initialSimplifiedMode) }
var isLoading by remember { mutableStateOf(true) }

// Load config on startup
// Initialize PatchSourceManager and load config on startup
LaunchedEffect(Unit) {
patchSourceManager.initialize()
val config = configRepository.loadConfig()
themePreference = config.getThemePreference()
isSimplifiedMode = config.useSimplifiedMode
Expand Down Expand Up @@ -109,27 +116,75 @@ private fun AppContent(initialSimplifiedMode: Boolean) {
}
}

val titleBarInsets = remember {
val isMac = System.getProperty("os.name")?.lowercase()?.contains("mac") == true
if (isMac) TitleBarInsets(start = 80.dp, top = 0.dp)
else TitleBarInsets()
}

MorpheTheme(themePreference = themePreference) {
CompositionLocalProvider(
LocalThemeState provides themeState,
LocalModeState provides modeState
LocalModeState provides modeState,
LocalTitleBarInsets provides titleBarInsets
) {
Surface(modifier = Modifier.fillMaxSize()) {
if (!isLoading) {
// Create QuickPatchViewModel outside Crossfade so it persists across mode switches.
// Otherwise every expert→simplified switch creates a new VM that re-fetches from GitHub.
val quickViewModel = remember {
QuickPatchViewModel(patchRepository, patchService, configRepository)
Box(modifier = Modifier.fillMaxSize()) {
if (!isLoading) {
val patchService: PatchService = koinInject()
val quickViewModel = remember {
QuickPatchViewModel(patchSourceManager, patchService, configRepository)
}

Crossfade(targetState = isSimplifiedMode) { simplified ->
if (simplified) {
QuickPatchContent(quickViewModel)
} else {
Navigator(HomeScreen()) { navigator ->
SlideTransition(navigator)
}
}
}
}

Crossfade(targetState = isSimplifiedMode) { simplified ->
if (simplified) {
// Quick/Simplified mode
QuickPatchContent(quickViewModel)
} else {
// Full mode
Navigator(HomeScreen()) { navigator ->
SlideTransition(navigator)
// Falling petals — on top of everything (Sakura)
SakuraPetals(
enabled = themePreference == ThemePreference.SAKURA
)

// Matcha cat — top-right corner
if (themePreference == ThemePreference.MATCHA) {
val catJson = remember {
try {
object {}.javaClass.getResourceAsStream("/cat2333s.json")
?.bufferedReader()?.readText()
} catch (e: Exception) {
null
}
}
catJson?.let { json ->
// 1080px canvas, rendered at 350dp (1dp ≈ 3.086 canvas px).
// Ears ~y385 → 125dp, bar bottom ~y576 → 187dp.
// Body shrunk to 85% so it hides behind bar.
// Clip from 120dp to 192dp (72dp visible) — ears to just past bar.
val renderSize = 350.dp
val clipTop = 120.dp // just above ears
val clipHeight = 72.dp // ears → just past bar bottom
Box(
modifier = Modifier
.align(Alignment.TopEnd)
.padding(top = 24.dp, end = 16.dp)
.requiredWidth(renderSize)
.requiredHeight(clipHeight)
.clipToBounds()
) {
LottieAnimation(
jsonString = json,
modifier = Modifier
.requiredSize(renderSize)
.offset(y = -clipTop),
alpha = 0.28f
)
}
}
}
Expand Down
33 changes: 32 additions & 1 deletion src/main/kotlin/app/morphe/gui/GuiMain.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@

package app.morphe.gui

import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.graphics.painter.BitmapPainter
import app.morphe.gui.ui.components.LocalFrameWindowScope
import androidx.compose.ui.graphics.toComposeImageBitmap
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
Expand Down Expand Up @@ -63,7 +65,36 @@ fun launchGui(args: Array<String>) = application {
icon = appIcon
) {
window.minimumSize = java.awt.Dimension(600, 400)
App(initialSimplifiedMode = initialSimplifiedMode)

// macOS: transparent title bar with expanded height so traffic lights
// align with our header row content. Uses JetBrains Runtime custom title bar API.
// Other OS: standard decorated window (no-op).
remember {
val isMac = System.getProperty("os.name")?.lowercase()?.contains("mac") == true
if (isMac) {
window.rootPane.putClientProperty("apple.awt.fullWindowContent", true)
window.rootPane.putClientProperty("apple.awt.transparentTitleBar", true)
window.rootPane.putClientProperty("apple.awt.windowTitleVisible", false)

// JBR: expand the title bar so traffic lights center with our header row.
// Height ~= header top padding (26dp) + half content height (~20dp) + buffer
// → traffic lights center vertically with our header icons/text.
try {
val decorations = com.jetbrains.JBR.getWindowDecorations()
val titleBar = decorations.createCustomTitleBar()
titleBar.height = 56f
titleBar.putProperty("controls.visible", true)
decorations.setCustomTitleBar(window, titleBar)
} catch (_: Exception) {
// Not running on JBR — traffic lights stay at default position
}
}
true
}

CompositionLocalProvider(LocalFrameWindowScope provides this) {
App(initialSimplifiedMode = initialSimplifiedMode)
}
}
}

Expand Down
28 changes: 27 additions & 1 deletion src/main/kotlin/app/morphe/gui/data/model/AppConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@ import app.morphe.gui.ui.theme.ThemePreference
/**
* Application configuration stored in config.json
*/

val DEFAULT_PATCH_SOURCE = PatchSource(
id = "morphe-default",
name = "Morphe Patches",
type = PatchSourceType.DEFAULT,
url = "https://github.com/MorpheApp/morphe-patches",
deletable = false
)

@Serializable
data class AppConfig(
val themePreference: String = ThemePreference.SYSTEM.name,
Expand All @@ -19,7 +28,9 @@ data class AppConfig(
val preferredPatchChannel: String = PatchChannel.STABLE.name,
val defaultOutputDirectory: String? = null,
val autoCleanupTempFiles: Boolean = true, // Default ON
val useSimplifiedMode: Boolean = true // Default to Quick/Simplified mode
val useSimplifiedMode: Boolean = true, // Default to Quick/Simplified mode
val patchSource: List<PatchSource> = listOf(DEFAULT_PATCH_SOURCE),
val activePatchSourceId: String = "morphe-default"
) {
fun getThemePreference(): ThemePreference {
return try {
Expand All @@ -38,6 +49,21 @@ data class AppConfig(
}
}

@Serializable
data class PatchSource (
val id: String,
val name: String,
val type: PatchSourceType,
val url: String? = null, // For DEFAULT (morphe) and GITHUB (other source) type
val filePath: String? = null, // For local files
val deletable: Boolean = true
)

@Serializable
enum class PatchSourceType{
DEFAULT, GITHUB, LOCAL
}

enum class PatchChannel {
STABLE,
DEV
Expand Down
3 changes: 2 additions & 1 deletion src/main/kotlin/app/morphe/gui/data/model/Patch.kt
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ enum class PatchOptionType {
INT,
LONG,
FLOAT,
LIST
LIST,
FILE
}

/**
Expand Down
13 changes: 10 additions & 3 deletions src/main/kotlin/app/morphe/gui/data/model/Release.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@ data class Release(
val id: Long,
@SerialName("tag_name")
val tagName: String,
val name: String,
val name: String? = null,
@SerialName("prerelease")
val isPrerelease: Boolean,
val isPrerelease: Boolean = false,
val draft: Boolean = false,
@SerialName("published_at")
val publishedAt: String,
val publishedAt: String? = null,
@SerialName("created_at")
val createdAt: String? = null,
val assets: List<ReleaseAsset> = emptyList(),
val body: String? = null
) {
Expand Down Expand Up @@ -62,6 +64,11 @@ data class ReleaseAsset(
*/
fun isMpp(): Boolean = name.endsWith(".mpp", ignoreCase = true)

/**
* Check if this is a patch file (.mpp or .jar)
*/
fun isPatchFile(): Boolean = isMpp() || isJar()

/**
* Get human-readable file size
*/
Expand Down
Loading
Loading