Fixed project's formatting

This commit is contained in:
Abdelilah El Aissaoui 2022-02-02 00:26:46 +01:00
parent 6142ecef5b
commit a3ff073b54
43 changed files with 152 additions and 143 deletions

View File

@ -2,9 +2,10 @@
A FOSS Git client based on (Jetbrains) Compose and JGit. A FOSS Git client based on (Jetbrains) Compose and JGit.
The main goal of Gitnuro is to provide a multiplatform open source Git client without any kind of constraint to how you can use it nor relying on web technologies. The main goal of Gitnuro is to provide a multiplatform open source Git client without any kind of constraint to how you
can use it nor relying on web technologies.
The project it is still in alpha and many features are lacking or missing, but can be good for basic usage. The project it is still in alpha and many features are lacking or missing, but can be good for basic usage.
Right now you CAN: Right now you CAN:
@ -46,20 +47,19 @@ Right now you CAN'T:
I'll create releases once we hit beta stage. I'll create releases once we hit beta stage.
## Steps to build ## Steps to build
Note: Requires minimum JDK 16. Note: Requires minimum JDK 16.
- Clone the project - Clone the project
- Open terminal/shell in the project folder - Open terminal/shell in the project folder
- `./gradlew run` to run the project - `./gradlew run` to run the project
- `./gradlew tasks` to view other build options (native building requires java >=15) - `./gradlew tasks` to view other build options (native building requires java >=15)
Feel free to open issues for bugs or sugestions. Feel free to open issues for bugs or sugestions.
## Screenshots (latest update: 01 feb 2022) ## Screenshots (latest update: 01 feb 2022)
![Example 1](/res/img/gitnuro_example_1.png) ![Example 1](/res/img/gitnuro_example_1.png)
![Example 2](/res/img/gitnuro_example_2.png) ![Example 2](/res/img/gitnuro_example_2.png)
![Example 3](/res/img/gitnuro_example_3.png) ![Example 3](/res/img/gitnuro_example_3.png)

View File

@ -49,7 +49,7 @@ class App {
private val tabsFlow = MutableStateFlow<List<TabInformation>>(emptyList()) private val tabsFlow = MutableStateFlow<List<TabInformation>>(emptyList())
fun start(){ fun start() {
appStateManager.loadRepositoriesTabs() appStateManager.loadRepositoriesTabs()
loadTabs() loadTabs()

View File

@ -1,7 +1,7 @@
package app.di package app.di
import app.AppStateManager
import app.App import app.App
import app.AppStateManager
import dagger.Component import dagger.Component
import javax.inject.Singleton import javax.inject.Singleton

View File

@ -4,7 +4,7 @@ import app.ui.components.TabInformation
import dagger.Component import dagger.Component
@TabScope @TabScope
@Component(dependencies = [ AppComponent::class ]) @Component(dependencies = [AppComponent::class])
interface TabComponent { interface TabComponent {
fun inject(tabInformation: TabInformation) fun inject(tabInformation: TabInformation)
} }

View File

@ -31,6 +31,6 @@ val String.dirPath: String
} }
val String.withoutLineEnding: String val String.withoutLineEnding: String
get() = this get() = this
.removeSuffix("\n") .removeSuffix("\n")
.removeSuffix("\r\n") .removeSuffix("\r\n")

View File

@ -4,7 +4,9 @@ import app.extensions.isBranch
import app.extensions.simpleName import app.extensions.simpleName
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.eclipse.jgit.api.* import org.eclipse.jgit.api.CreateBranchCommand
import org.eclipse.jgit.api.Git
import org.eclipse.jgit.api.ListBranchCommand
import org.eclipse.jgit.lib.Ref import org.eclipse.jgit.lib.Ref
import org.eclipse.jgit.revwalk.RevCommit import org.eclipse.jgit.revwalk.RevCommit
import javax.inject.Inject import javax.inject.Inject

View File

@ -4,7 +4,6 @@ import app.di.HunkDiffGeneratorFactory
import app.di.RawFileManagerFactory import app.di.RawFileManagerFactory
import app.extensions.fullData import app.extensions.fullData
import app.git.diff.DiffResult import app.git.diff.DiffResult
import app.git.diff.Hunk
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.eclipse.jgit.api.Git import org.eclipse.jgit.api.Git

View File

@ -1,16 +1,10 @@
package app.git package app.git
import app.extensions.isBranch
import app.extensions.isMerging
import app.extensions.simpleName
import app.git.graph.GraphCommitList import app.git.graph.GraphCommitList
import app.git.graph.GraphWalk import app.git.graph.GraphWalk
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ensureActive import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.eclipse.jgit.api.CreateBranchCommand
import org.eclipse.jgit.api.Git import org.eclipse.jgit.api.Git
import org.eclipse.jgit.api.ResetCommand import org.eclipse.jgit.api.ResetCommand
import org.eclipse.jgit.lib.Constants import org.eclipse.jgit.lib.Constants
@ -26,7 +20,7 @@ class LogManager @Inject constructor(
val commitList = GraphCommitList() val commitList = GraphCommitList()
val repositoryState = git.repository.repositoryState val repositoryState = git.repository.repositoryState
println("Repository state ${repositoryState.description}") println("Repository state ${repositoryState.description}")
if(currentBranch != null || repositoryState.isRebasing) { // Current branch is null when there is no log (new repo) or rebasing if (currentBranch != null || repositoryState.isRebasing) { // Current branch is null when there is no log (new repo) or rebasing
val logList = git.log().setMaxCount(2).call().toList() val logList = git.log().setMaxCount(2).call().toList()
val walk = GraphWalk(git.repository) val walk = GraphWalk(git.repository)

View File

@ -22,10 +22,10 @@ class MergeManager @Inject constructor() {
.call() .call()
} }
suspend fun abortBranch(git: Git) = withContext(Dispatchers.IO) { suspend fun abortMerge(git: Git) = withContext(Dispatchers.IO) {
git.repository.writeMergeCommitMsg(null); git.repository.writeMergeCommitMsg(null)
git.repository.writeMergeHeads(null); git.repository.writeMergeHeads(null)
git.reset().setMode(ResetCommand.ResetType.HARD).call(); git.reset().setMode(ResetCommand.ResetType.HARD).call()
} }
} }

View File

@ -61,14 +61,18 @@ class RawFileManager @AssistedInject constructor(
return try { return try {
EntryContent.Text(RawText.load(ldr, DEFAULT_BINARY_FILE_THRESHOLD)) EntryContent.Text(RawText.load(ldr, DEFAULT_BINARY_FILE_THRESHOLD))
} catch (ex: BinaryBlobException) { } catch (ex: BinaryBlobException) {
if(isImage(entry)) { if (isImage(entry)) {
generateImageBinary(ldr, entry, side) generateImageBinary(ldr, entry, side)
} else } else
EntryContent.Binary EntryContent.Binary
} }
} }
private fun generateImageBinary(ldr: ObjectLoader, entry: DiffEntry, side: DiffEntry.Side): EntryContent.ImageBinary { private fun generateImageBinary(
ldr: ObjectLoader,
entry: DiffEntry,
side: DiffEntry.Side
): EntryContent.ImageBinary {
println("Data's size is ${ldr.size}") println("Data's size is ${ldr.size}")
val tempDir = createTempDirectory("gitnuro${repository.directory.absolutePath.replace("/", "_")}") val tempDir = createTempDirectory("gitnuro${repository.directory.absolutePath.replace("/", "_")}")
@ -99,11 +103,11 @@ class RawFileManager @AssistedInject constructor(
} }
sealed class EntryContent { sealed class EntryContent {
object Missing: EntryContent() object Missing : EntryContent()
object InvalidObjectBlob: EntryContent() object InvalidObjectBlob : EntryContent()
data class Text(val rawText: RawText): EntryContent() data class Text(val rawText: RawText) : EntryContent()
sealed class BinaryContent() : EntryContent() sealed class BinaryContent() : EntryContent()
data class ImageBinary(val tempFilePath: Path): BinaryContent() data class ImageBinary(val tempFilePath: Path) : BinaryContent()
object Binary: BinaryContent() object Binary : BinaryContent()
object TooLargeEntry: EntryContent() object TooLargeEntry : EntryContent()
} }

View File

@ -1,8 +1,6 @@
package app.git package app.git
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.eclipse.jgit.api.Git import org.eclipse.jgit.api.Git
import org.eclipse.jgit.lib.Ref import org.eclipse.jgit.lib.Ref

View File

@ -1,8 +1,6 @@
package app.git package app.git
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.eclipse.jgit.api.Git import org.eclipse.jgit.api.Git
import javax.inject.Inject import javax.inject.Inject

View File

@ -60,7 +60,7 @@ class StatusManager @Inject constructor(
val rawFileManager = rawFileManagerFactory.create(git.repository) val rawFileManager = rawFileManagerFactory.create(git.repository)
val entryContent = rawFileManager.getRawContent(DiffEntry.Side.OLD, diffEntry) val entryContent = rawFileManager.getRawContent(DiffEntry.Side.OLD, diffEntry)
if(entryContent !is EntryContent.Text) if (entryContent !is EntryContent.Text)
return@withContext return@withContext
val textLines = getTextLines(entryContent.rawText).toMutableList() val textLines = getTextLines(entryContent.rawText).toMutableList()
@ -103,7 +103,7 @@ class StatusManager @Inject constructor(
val rawFileManager = rawFileManagerFactory.create(git.repository) val rawFileManager = rawFileManagerFactory.create(git.repository)
val entryContent = rawFileManager.getRawContent(DiffEntry.Side.NEW, diffEntry) val entryContent = rawFileManager.getRawContent(DiffEntry.Side.NEW, diffEntry)
if(entryContent !is EntryContent.Text) if (entryContent !is EntryContent.Text)
return@withContext return@withContext
val textLines = getTextLines(entryContent.rawText).toMutableList() val textLines = getTextLines(entryContent.rawText).toMutableList()
@ -224,33 +224,34 @@ class StatusManager @Inject constructor(
.call() .call()
} }
suspend fun getStaged(git: Git, currentBranch: Ref?, repositoryState: RepositoryState) = withContext(Dispatchers.IO) { suspend fun getStaged(git: Git, currentBranch: Ref?, repositoryState: RepositoryState) =
return@withContext git withContext(Dispatchers.IO) {
.diff() return@withContext git
.setShowNameAndStatusOnly(true).apply { .diff()
if (currentBranch == null && !repositoryState.isMerging && !repositoryState.isRebasing) .setShowNameAndStatusOnly(true).apply {
setOldTree(EmptyTreeIterator()) // Required if the repository is empty if (currentBranch == null && !repositoryState.isMerging && !repositoryState.isRebasing)
setOldTree(EmptyTreeIterator()) // Required if the repository is empty
setCached(true) setCached(true)
} }
.call() .call()
// TODO: Grouping and fitlering allows us to remove duplicates when conflicts appear, requires more testing (what happens in windows? /dev/null is a unix thing) // TODO: Grouping and fitlering allows us to remove duplicates when conflicts appear, requires more testing (what happens in windows? /dev/null is a unix thing)
// TODO: Test if we should group by old path or new path // TODO: Test if we should group by old path or new path
.groupBy { .groupBy {
if(it.newPath != "/dev/null") if (it.newPath != "/dev/null")
it.newPath it.newPath
else else
it.oldPath it.oldPath
} }
.map { .map {
val entries = it.value val entries = it.value
val hasConflicts = val hasConflicts =
(entries.count() > 1 && (repositoryState.isMerging || repositoryState.isRebasing)) (entries.count() > 1 && (repositoryState.isMerging || repositoryState.isRebasing))
StatusEntry(entries.first(), isConflict = hasConflicts) StatusEntry(entries.first(), isConflict = hasConflicts)
} }
} }
suspend fun getUnstaged(git: Git, repositoryState: RepositoryState) = withContext(Dispatchers.IO) { suspend fun getUnstaged(git: Git, repositoryState: RepositoryState) = withContext(Dispatchers.IO) {
return@withContext git return@withContext git
@ -258,7 +259,7 @@ class StatusManager @Inject constructor(
.setShowNameAndStatusOnly(true) .setShowNameAndStatusOnly(true)
.call() .call()
.groupBy { .groupBy {
if(it.oldPath != "/dev/null") if (it.oldPath != "/dev/null")
it.oldPath it.oldPath
else else
it.newPath it.newPath

View File

@ -1,8 +1,8 @@
package app.git package app.git
import app.ErrorsManager import app.ErrorsManager
import app.newErrorNow
import app.di.TabScope import app.di.TabScope
import app.newErrorNow
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.SupervisorJob
@ -95,22 +95,23 @@ class TabState @Inject constructor(
} }
} }
fun runOperation(showError: Boolean = false, block: suspend (git: Git) -> RefreshType) = managerScope.launch(Dispatchers.IO) { fun runOperation(showError: Boolean = false, block: suspend (git: Git) -> RefreshType) =
operationRunning = true managerScope.launch(Dispatchers.IO) {
try { operationRunning = true
val refreshType = block(safeGit) try {
val refreshType = block(safeGit)
if (refreshType != RefreshType.NONE) if (refreshType != RefreshType.NONE)
_refreshData.emit(refreshType) _refreshData.emit(refreshType)
} catch (ex: Exception) { } catch (ex: Exception) {
ex.printStackTrace() ex.printStackTrace()
if (showError) if (showError)
errorsManager.addError(newErrorNow(ex, ex.localizedMessage)) errorsManager.addError(newErrorNow(ex, ex.localizedMessage))
} finally { } finally {
operationRunning = false operationRunning = false
}
} }
}
} }
enum class RefreshType { enum class RefreshType {

View File

@ -1,8 +1,6 @@
package app.git package app.git
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.eclipse.jgit.api.Git import org.eclipse.jgit.api.Git
import org.eclipse.jgit.lib.Ref import org.eclipse.jgit.lib.Ref

View File

@ -14,7 +14,6 @@ import java.io.ByteArrayOutputStream
import java.io.IOException import java.io.IOException
import java.io.InvalidObjectException import java.io.InvalidObjectException
import kotlin.contracts.ExperimentalContracts import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract
import kotlin.math.max import kotlin.math.max
import kotlin.math.min import kotlin.math.min
@ -48,7 +47,7 @@ class HunkDiffGenerator @AssistedInject constructor(
val rawOld = rawFileManager.getRawContent(DiffEntry.Side.OLD, ent) val rawOld = rawFileManager.getRawContent(DiffEntry.Side.OLD, ent)
val rawNew = rawFileManager.getRawContent(DiffEntry.Side.NEW, ent) val rawNew = rawFileManager.getRawContent(DiffEntry.Side.NEW, ent)
if(rawOld == EntryContent.InvalidObjectBlob || rawNew == EntryContent.InvalidObjectBlob) if (rawOld == EntryContent.InvalidObjectBlob || rawNew == EntryContent.InvalidObjectBlob)
throw InvalidObjectException("Invalid object in diff format") throw InvalidObjectException("Invalid object in diff format")
var diffResult: DiffResult = DiffResult.Text(emptyList()) var diffResult: DiffResult = DiffResult.Text(emptyList())
@ -84,7 +83,7 @@ class HunkDiffGenerator @AssistedInject constructor(
else -> null else -> null
} }
return if(rawOldText != null && newOldText != null) { return if (rawOldText != null && newOldText != null) {
onText(rawOldText, newOldText) onText(rawOldText, newOldText)
true true
} else } else

View File

@ -53,7 +53,7 @@ object NetworkImageLoader {
fun rememberNetworkImageOrNull(url: String, placeHolderImageRes: String? = null): ImageBitmap? { fun rememberNetworkImageOrNull(url: String, placeHolderImageRes: String? = null): ImageBitmap? {
val networkImageLoader = NetworkImageLoader val networkImageLoader = NetworkImageLoader
var image by remember(url) { var image by remember(url) {
val placeHolderImage = if(placeHolderImageRes != null) val placeHolderImage = if (placeHolderImageRes != null)
useResource(placeHolderImageRes) { useResource(placeHolderImageRes) {
Image.makeFromEncoded(it.toByteArray()).toComposeImageBitmap() Image.makeFromEncoded(it.toByteArray()).toComposeImageBitmap()
} }

View File

@ -109,8 +109,6 @@ val Colors.unstageButton: Color
get() = error get() = error
enum class Themes(val displayName: String) { enum class Themes(val displayName: String) {
LIGHT("Light"), LIGHT("Light"),
DARK("Dark") DARK("Dark")

View File

@ -22,10 +22,10 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import app.LoadingRepository import app.LoadingRepository
import app.credentials.CredentialsState import app.credentials.CredentialsState
import app.viewmodels.TabViewModel
import app.viewmodels.RepositorySelectionStatus
import app.ui.dialogs.PasswordDialog import app.ui.dialogs.PasswordDialog
import app.ui.dialogs.UserPasswordDialog import app.ui.dialogs.UserPasswordDialog
import app.viewmodels.RepositorySelectionStatus
import app.viewmodels.TabViewModel
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
// TODO onDispose sometimes is called when changing tabs, therefore losing the tab state // TODO onDispose sometimes is called when changing tabs, therefore losing the tab state

View File

@ -1,20 +1,24 @@
package app.ui package app.ui
import androidx.compose.foundation.* import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.selection.SelectionContainer import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.* import androidx.compose.material.*
import androidx.compose.runtime.* import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import app.extensions.* import app.extensions.*
import app.viewmodels.TabViewModel
import app.theme.headerBackground import app.theme.headerBackground
import app.theme.headerText import app.theme.headerText
import app.theme.primaryTextColor import app.theme.primaryTextColor
@ -34,7 +38,7 @@ fun CommitChanges(
) { ) {
val commitChangesStatusState = commitChangesViewModel.commitChangesStatus.collectAsState() val commitChangesStatusState = commitChangesViewModel.commitChangesStatus.collectAsState()
when(val commitChangesStatus = commitChangesStatusState.value) { when (val commitChangesStatus = commitChangesStatusState.value) {
CommitChangesStatus.Loading -> { CommitChangesStatus.Loading -> {
LinearProgressIndicator(modifier = Modifier.fillMaxWidth()) LinearProgressIndicator(modifier = Modifier.fillMaxWidth())
} }

View File

@ -6,8 +6,12 @@ import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.text.selection.DisableSelection import androidx.compose.foundation.text.selection.DisableSelection
import androidx.compose.foundation.text.selection.SelectionContainer import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.material.* import androidx.compose.material.IconButton
import androidx.compose.runtime.* import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
@ -92,14 +96,14 @@ fun NonTextDiff(diffResult: DiffResult.NonText) {
SideTitle("New") SideTitle("New")
SideDiff(newBinaryContent) SideDiff(newBinaryContent)
} }
} else if(oldBinaryContent != EntryContent.Missing) { } else if (oldBinaryContent != EntryContent.Missing) {
Box( Box(
modifier = Modifier.fillMaxSize() modifier = Modifier.fillMaxSize()
.padding(all = 24.dp), .padding(all = 24.dp),
) { ) {
SideDiff(oldBinaryContent) SideDiff(oldBinaryContent)
} }
} else if(newBinaryContent != EntryContent.Missing) { } else if (newBinaryContent != EntryContent.Missing) {
Column( Column(
modifier = Modifier.fillMaxSize() modifier = Modifier.fillMaxSize()
.padding(all = 24.dp), .padding(all = 24.dp),

View File

@ -2,7 +2,10 @@
package app.ui package app.ui
import androidx.compose.foundation.* import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.* import androidx.compose.material.*
@ -18,13 +21,11 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import app.theme.primaryTextColor import app.theme.primaryTextColor
import app.ui.context_menu.pullContextMenuItems
import app.viewmodels.MenuViewModel
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
import app.ui.context_menu.DropDownContent import app.ui.context_menu.DropDownContent
import app.ui.context_menu.DropDownContentData import app.ui.context_menu.DropDownContentData
import app.ui.context_menu.pullContextMenuItems
import app.ui.context_menu.pushContextMenuItems import app.ui.context_menu.pushContextMenuItems
import app.viewmodels.MenuViewModel
// TODO Add tooltips to all the buttons // TODO Add tooltips to all the buttons
@Composable @Composable
@ -199,7 +200,7 @@ fun ExtendedMenuButton(
.border(ButtonDefaults.outlinedBorder, RoundedCornerShape(topEnd = 3.dp, bottomEnd = 3.dp)) .border(ButtonDefaults.outlinedBorder, RoundedCornerShape(topEnd = 3.dp, bottomEnd = 3.dp))
.clickable { .clickable {
showDropDownMenu = true showDropDownMenu = true
}, },
contentAlignment = Alignment.Center, contentAlignment = Alignment.Center,
) { ) {
Icon( Icon(

View File

@ -7,9 +7,9 @@ import androidx.compose.runtime.*
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import app.git.DiffEntryType import app.git.DiffEntryType
import app.viewmodels.TabViewModel
import app.ui.dialogs.NewBranchDialog import app.ui.dialogs.NewBranchDialog
import app.ui.log.Log import app.ui.log.Log
import app.viewmodels.TabViewModel
import openRepositoryDialog import openRepositoryDialog
import org.eclipse.jgit.revwalk.RevCommit import org.eclipse.jgit.revwalk.RevCommit
import org.jetbrains.compose.splitpane.ExperimentalSplitPaneApi import org.jetbrains.compose.splitpane.ExperimentalSplitPaneApi

View File

@ -58,7 +58,7 @@ fun UncommitedChanges(
if (stageStatus is StageStatus.Loaded) { if (stageStatus is StageStatus.Loaded) {
staged = stageStatus.staged staged = stageStatus.staged
unstaged = stageStatus.unstaged unstaged = stageStatus.unstaged
LaunchedEffect(staged) { LaunchedEffect(staged) {
if (selectedEntryType != null) { if (selectedEntryType != null) {
checkIfSelectedEntryShouldBeUpdated( checkIfSelectedEntryShouldBeUpdated(
@ -154,7 +154,7 @@ fun UncommitedChanges(
.padding(8.dp) .padding(8.dp)
.run { .run {
// When rebasing, we don't need a fixed size as we don't show the message TextField // When rebasing, we don't need a fixed size as we don't show the message TextField
if(!repositoryState.isRebasing) { if (!repositoryState.isRebasing) {
height(192.dp) height(192.dp)
} else } else
this this

View File

@ -20,11 +20,11 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import app.extensions.dirName import app.extensions.dirName
import app.extensions.dirPath import app.extensions.dirPath
import app.viewmodels.TabViewModel
import app.theme.primaryTextColor import app.theme.primaryTextColor
import app.theme.secondaryTextColor import app.theme.secondaryTextColor
import app.ui.dialogs.CloneDialog import app.ui.dialogs.CloneDialog
import app.ui.dialogs.MaterialDialog import app.ui.dialogs.MaterialDialog
import app.viewmodels.TabViewModel
import openRepositoryDialog import openRepositoryDialog
import java.awt.Desktop import java.awt.Desktop
import java.net.URI import java.net.URI

View File

@ -28,7 +28,7 @@ fun AvatarImage(
.clip(CircleShape) .clip(CircleShape)
) { ) {
val avatar = rememberAvatar(personIdent.emailAddress) val avatar = rememberAvatar(personIdent.emailAddress)
if(avatar == null) { if (avatar == null) {
Box( Box(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()

View File

@ -2,7 +2,10 @@ package app.ui.components
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
@ -16,16 +19,15 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.shadow import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import app.AppStateManager import app.AppStateManager
import app.di.AppComponent import app.di.AppComponent
import app.di.DaggerTabComponent import app.di.DaggerTabComponent
import app.theme.primaryTextColor import app.theme.primaryTextColor
import app.viewmodels.TabViewModel
import app.theme.tabColorActive import app.theme.tabColorActive
import app.theme.tabColorInactive import app.theme.tabColorInactive
import app.ui.AppTab import app.ui.AppTab
import app.viewmodels.TabViewModel
import javax.inject.Inject import javax.inject.Inject
import kotlin.io.path.Path import kotlin.io.path.Path
import kotlin.io.path.name import kotlin.io.path.name
@ -65,7 +67,7 @@ fun RepositoriesTabPanel(
val index = tabs.indexOf(tab) val index = tabs.indexOf(tab)
val nextIndex = if (index == 0 && tabs.count() >= 2) { val nextIndex = if (index == 0 && tabs.count() >= 2) {
1 // If the first tab is selected, select the next one 1 // If the first tab is selected, select the next one
} else if (index == tabs.count() -1 && tabs.count() >= 2) } else if (index == tabs.count() - 1 && tabs.count() >= 2)
index - 1 // If the last tab is selected, select the previous one index - 1 // If the last tab is selected, select the previous one
else if (tabs.count() >= 2) else if (tabs.count() >= 2)
index + 1 // If any in between tab is selected, select the next one index + 1 // If any in between tab is selected, select the next one
@ -126,7 +128,7 @@ fun TabPanel(
@Composable @Composable
fun Tab(title: MutableState<String>, selected: Boolean, onClick: () -> Unit, onCloseTab: () -> Unit) { fun Tab(title: MutableState<String>, selected: Boolean, onClick: () -> Unit, onCloseTab: () -> Unit) {
val elevation = if(selected) { val elevation = if (selected) {
3.dp 3.dp
} else } else
0.dp 0.dp
@ -194,7 +196,7 @@ class TabInformation(
appStateManager.repositoryTabChanged(key, path) appStateManager.repositoryTabChanged(key, path)
} }
} }
if(path != null) if (path != null)
tabViewModel.openRepository(path) tabViewModel.openRepository(path)
content = { content = {
AppTab(tabViewModel) AppTab(tabViewModel)

View File

@ -5,16 +5,13 @@ import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text import androidx.compose.material.Text
import androidx.compose.material.contentColorFor
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import app.theme.primaryTextColor
@Composable @Composable
fun SecondaryButton( fun SecondaryButton(

View File

@ -1,6 +1,5 @@
package app.ui.components package app.ui.components
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height

View File

@ -13,7 +13,6 @@ import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp

View File

@ -12,8 +12,8 @@ import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import app.git.CloneStatus import app.git.CloneStatus
import app.viewmodels.TabViewModel
import app.theme.primaryTextColor import app.theme.primaryTextColor
import app.viewmodels.TabViewModel
import java.io.File import java.io.File
@Composable @Composable

View File

@ -6,8 +6,11 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.material.* import androidx.compose.material.Button
import androidx.compose.runtime.* import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight

View File

@ -210,7 +210,7 @@ fun LogDialogs(
} }
) )
is LogDialog.RebaseBranch -> { is LogDialog.RebaseBranch -> {
if(currentBranch != null) { if (currentBranch != null) {
RebaseDialog( RebaseDialog(
currentBranchName = currentBranch.simpleName, currentBranchName = currentBranch.simpleName,
rebaseBranchName = showLogDialog.ref.simpleName, rebaseBranchName = showLogDialog.ref.simpleName,

View File

@ -5,7 +5,6 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import org.eclipse.jgit.api.Git import org.eclipse.jgit.api.Git
import org.eclipse.jgit.lib.Ref import org.eclipse.jgit.lib.Ref
import org.eclipse.jgit.revwalk.RevCommit
import javax.inject.Inject import javax.inject.Inject
class BranchesViewModel @Inject constructor( class BranchesViewModel @Inject constructor(
@ -29,7 +28,7 @@ class BranchesViewModel @Inject constructor(
// set selected branch as the first one always // set selected branch as the first one always
val selectedBranch = branchesList.find { it.name == _currentBranch.value } val selectedBranch = branchesList.find { it.name == _currentBranch.value }
if(selectedBranch != null) { if (selectedBranch != null) {
branchesList.remove(selectedBranch) branchesList.remove(selectedBranch)
branchesList.add(0, selectedBranch) branchesList.add(0, selectedBranch)
} }
@ -51,7 +50,7 @@ class BranchesViewModel @Inject constructor(
return@safeProcessing RefreshType.ALL_DATA return@safeProcessing RefreshType.ALL_DATA
} }
fun deleteBranch(branch: Ref) =tabState.safeProcessing { git -> fun deleteBranch(branch: Ref) = tabState.safeProcessing { git ->
branchesManager.deleteBranch(git, branch) branchesManager.deleteBranch(git, branch)
return@safeProcessing RefreshType.ALL_DATA return@safeProcessing RefreshType.ALL_DATA

View File

@ -20,8 +20,8 @@ class DiffViewModel @Inject constructor(
val lazyListState = MutableStateFlow( val lazyListState = MutableStateFlow(
LazyListState( LazyListState(
0, 0,
0 0
) )
) )
@ -31,10 +31,10 @@ class DiffViewModel @Inject constructor(
_diffResult.value = null _diffResult.value = null
// If it's a different file or different state (index or workdir), reset the scroll state // If it's a different file or different state (index or workdir), reset the scroll state
if(oldDiffEntryType != null && if (oldDiffEntryType != null &&
(oldDiffEntryType.diffEntry.oldPath != diffEntryType.diffEntry.oldPath || (oldDiffEntryType.diffEntry.oldPath != diffEntryType.diffEntry.oldPath ||
oldDiffEntryType.diffEntry.newPath != diffEntryType.diffEntry.newPath || oldDiffEntryType.diffEntry.newPath != diffEntryType.diffEntry.newPath ||
oldDiffEntryType::class != diffEntryType::class) oldDiffEntryType::class != diffEntryType::class)
) { ) {
lazyListState.value = LazyListState( lazyListState.value = LazyListState(
0, 0,

View File

@ -75,7 +75,7 @@ class LogViewModel @Inject constructor(
return@safeProcessing RefreshType.ALL_DATA return@safeProcessing RefreshType.ALL_DATA
} }
fun deleteBranch(branch: Ref) =tabState.safeProcessing { git -> fun deleteBranch(branch: Ref) = tabState.safeProcessing { git ->
branchesManager.deleteBranch(git, branch) branchesManager.deleteBranch(git, branch)
return@safeProcessing RefreshType.ALL_DATA return@safeProcessing RefreshType.ALL_DATA
@ -100,5 +100,6 @@ class LogViewModel @Inject constructor(
sealed class LogStatus { sealed class LogStatus {
object Loading : LogStatus() object Loading : LogStatus()
class Loaded(val hasUncommitedChanges: Boolean, val plotCommitList: GraphCommitList, val currentBranch: Ref?) : LogStatus() class Loaded(val hasUncommitedChanges: Boolean, val plotCommitList: GraphCommitList, val currentBranch: Ref?) :
LogStatus()
} }

View File

@ -36,7 +36,7 @@ class MenuViewModel @Inject constructor(
return@safeProcessing RefreshType.UNCOMMITED_CHANGES return@safeProcessing RefreshType.UNCOMMITED_CHANGES
} }
fun openFolderInFileExplorer() = tabState.runOperation (showError = true) { git -> fun openFolderInFileExplorer() = tabState.runOperation(showError = true) { git ->
Desktop.getDesktop().open(git.repository.directory.parentFile) Desktop.getDesktop().open(git.repository.directory.parentFile)
return@runOperation RefreshType.NONE return@runOperation RefreshType.NONE

View File

@ -3,14 +3,11 @@ package app.viewmodels
import app.git.BranchesManager import app.git.BranchesManager
import app.git.RemoteInfo import app.git.RemoteInfo
import app.git.RemotesManager import app.git.RemotesManager
import app.git.TabState
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.eclipse.jgit.api.Git import org.eclipse.jgit.api.Git
import org.eclipse.jgit.lib.Ref
import org.eclipse.jgit.transport.RemoteConfig
import javax.inject.Inject import javax.inject.Inject
class RemotesViewModel @Inject constructor( class RemotesViewModel @Inject constructor(

View File

@ -135,7 +135,7 @@ class StatusViewModel @Inject constructor(
} }
fun abortMerge() = tabState.safeProcessing { git -> fun abortMerge() = tabState.safeProcessing { git ->
mergeManager.abortBranch(git) mergeManager.abortMerge(git)
return@safeProcessing RefreshType.ALL_DATA return@safeProcessing RefreshType.ALL_DATA
} }

View File

@ -2,10 +2,10 @@ package app.viewmodels
import app.AppStateManager import app.AppStateManager
import app.ErrorsManager import app.ErrorsManager
import app.newErrorNow
import app.credentials.CredentialsState import app.credentials.CredentialsState
import app.credentials.CredentialsStateManager import app.credentials.CredentialsStateManager
import app.git.* import app.git.*
import app.newErrorNow
import app.ui.SelectedItem import app.ui.SelectedItem
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.cancel import kotlinx.coroutines.cancel
@ -215,7 +215,7 @@ class TabViewModel @Inject constructor(
} }
fun newSelectedRef(objectId: ObjectId?) = tabState.runOperation { git -> fun newSelectedRef(objectId: ObjectId?) = tabState.runOperation { git ->
if(objectId == null) { if (objectId == null) {
newSelectedItem(SelectedItem.None) newSelectedItem(SelectedItem.None)
return@runOperation RefreshType.NONE return@runOperation RefreshType.NONE
} }
@ -233,7 +233,7 @@ class TabViewModel @Inject constructor(
fun newSelectedItem(selectedItem: SelectedItem) { fun newSelectedItem(selectedItem: SelectedItem) {
_selectedItem.value = selectedItem _selectedItem.value = selectedItem
if(selectedItem is SelectedItem.CommitBasedItem) { if (selectedItem is SelectedItem.CommitBasedItem) {
commitChangesViewModel.loadChanges(selectedItem.revCommit) commitChangesViewModel.loadChanges(selectedItem.revCommit)
} }
} }

View File

@ -1,3 +1,5 @@
<svg width="120" height="120" viewBox="0 0 120 120" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="120" height="120" viewBox="0 0 120 120" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M10 0C4.47715 0 0 4.47715 0 10V110C0 115.523 4.47715 120 10 120H110C115.523 120 120 115.523 120 110V10C120 4.47715 115.523 0 110 0H10ZM59.666 55.0039V69.2773C59.666 73.1328 59.2148 76.5371 58.3125 79.4902C57.4375 82.416 56.166 84.8633 54.498 86.832C52.8301 88.8281 50.793 90.332 48.3867 91.3438C45.9805 92.3555 43.2734 92.8613 40.2656 92.8613C37.2852 92.8613 34.5918 92.3555 32.1855 91.3438C29.7793 90.332 27.7422 88.8281 26.0742 86.832C24.3789 84.8633 23.0664 82.416 22.1367 79.4902C21.2344 76.5371 20.7832 73.1328 20.7832 69.2773V55.0039C20.7832 51.1484 21.2344 47.7578 22.1367 44.832C23.0391 41.8789 24.3379 39.4043 26.0332 37.4082C27.7012 35.4395 29.7246 33.9492 32.1035 32.9375C34.5098 31.9258 37.2031 31.4199 40.1836 31.4199C43.1914 31.4199 45.8984 31.9258 48.3047 32.9375C50.7109 33.9492 52.7617 35.4395 54.457 37.4082C56.125 39.4043 57.4102 41.8789 58.3125 44.832C59.2148 47.7578 59.666 51.1484 59.666 55.0039ZM32.2676 62.4277V64.3555L48.1816 52.502C48.127 50.2598 47.8809 48.3457 47.4434 46.7598C47.0332 45.1465 46.418 43.8613 45.5977 42.9043C44.9688 42.1387 44.1895 41.5781 43.2598 41.2227C42.3574 40.8398 41.332 40.6484 40.1836 40.6484C38.8984 40.6484 37.7637 40.8945 36.7793 41.3867C35.8223 41.8516 35.0156 42.5625 34.3594 43.5195C33.6484 44.5312 33.1152 45.8438 32.7598 47.457C32.4316 49.0703 32.2676 50.9844 32.2676 53.1992V59.8027V62.4277ZM47.6484 76.7832C48.0039 75.1426 48.1816 73.2148 48.1816 71V64.0684V62.0996V60.2539L32.2676 72.0664C32.3223 73.8711 32.5 75.4707 32.8008 76.8652C33.1289 78.2598 33.5801 79.4219 34.1543 80.3516C34.8105 81.4727 35.6445 82.3066 36.6562 82.8535C37.668 83.4004 38.8711 83.6738 40.2656 83.6738C41.5508 83.6738 42.6855 83.4414 43.6699 82.9766C44.6543 82.4844 45.4746 81.7461 46.1309 80.7617C46.8145 79.7227 47.3203 78.3965 47.6484 76.7832ZM99.1641 32.2812V92H87.6387V46.1855L72.5449 51.5996V41.6738L98.5488 32.2812H99.1641Z" fill="black"/> <path fill-rule="evenodd" clip-rule="evenodd"
d="M10 0C4.47715 0 0 4.47715 0 10V110C0 115.523 4.47715 120 10 120H110C115.523 120 120 115.523 120 110V10C120 4.47715 115.523 0 110 0H10ZM59.666 55.0039V69.2773C59.666 73.1328 59.2148 76.5371 58.3125 79.4902C57.4375 82.416 56.166 84.8633 54.498 86.832C52.8301 88.8281 50.793 90.332 48.3867 91.3438C45.9805 92.3555 43.2734 92.8613 40.2656 92.8613C37.2852 92.8613 34.5918 92.3555 32.1855 91.3438C29.7793 90.332 27.7422 88.8281 26.0742 86.832C24.3789 84.8633 23.0664 82.416 22.1367 79.4902C21.2344 76.5371 20.7832 73.1328 20.7832 69.2773V55.0039C20.7832 51.1484 21.2344 47.7578 22.1367 44.832C23.0391 41.8789 24.3379 39.4043 26.0332 37.4082C27.7012 35.4395 29.7246 33.9492 32.1035 32.9375C34.5098 31.9258 37.2031 31.4199 40.1836 31.4199C43.1914 31.4199 45.8984 31.9258 48.3047 32.9375C50.7109 33.9492 52.7617 35.4395 54.457 37.4082C56.125 39.4043 57.4102 41.8789 58.3125 44.832C59.2148 47.7578 59.666 51.1484 59.666 55.0039ZM32.2676 62.4277V64.3555L48.1816 52.502C48.127 50.2598 47.8809 48.3457 47.4434 46.7598C47.0332 45.1465 46.418 43.8613 45.5977 42.9043C44.9688 42.1387 44.1895 41.5781 43.2598 41.2227C42.3574 40.8398 41.332 40.6484 40.1836 40.6484C38.8984 40.6484 37.7637 40.8945 36.7793 41.3867C35.8223 41.8516 35.0156 42.5625 34.3594 43.5195C33.6484 44.5312 33.1152 45.8438 32.7598 47.457C32.4316 49.0703 32.2676 50.9844 32.2676 53.1992V59.8027V62.4277ZM47.6484 76.7832C48.0039 75.1426 48.1816 73.2148 48.1816 71V64.0684V62.0996V60.2539L32.2676 72.0664C32.3223 73.8711 32.5 75.4707 32.8008 76.8652C33.1289 78.2598 33.5801 79.4219 34.1543 80.3516C34.8105 81.4727 35.6445 82.3066 36.6562 82.8535C37.668 83.4004 38.8711 83.6738 40.2656 83.6738C41.5508 83.6738 42.6855 83.4414 43.6699 82.9766C44.6543 82.4844 45.4746 81.7461 46.1309 80.7617C46.8145 79.7227 47.3203 78.3965 47.6484 76.7832ZM99.1641 32.2812V92H87.6387V46.1855L72.5449 51.5996V41.6738L98.5488 32.2812H99.1641Z"
fill="black"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -1 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z"/></svg> <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000">
<path d="M0 0h24v24H0V0z" fill="none"/>
<path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z"/>
</svg>

Before

Width:  |  Height:  |  Size: 268 B

After

Width:  |  Height:  |  Size: 279 B

View File

@ -1 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><g><rect fill="none" height="24" width="24"/><path d="M20,6h-8l-2-2H4C2.9,4,2.01,4.9,2.01,6L2,18c0,1.1,0.9,2,2,2h16c1.1,0,2-0.9,2-2V8C22,6.9,21.1,6,20,6z M14,16H6v-2h8V16z M18,12H6v-2h12V12z"/></g></svg> <svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px"
fill="#000000">
<g>
<rect fill="none" height="24" width="24"/>
<path d="M20,6h-8l-2-2H4C2.9,4,2.01,4.9,2.01,6L2,18c0,1.1,0.9,2,2,2h16c1.1,0,2-0.9,2-2V8C22,6.9,21.1,6,20,6z M14,16H6v-2h8V16z M18,12H6v-2h12V12z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 339 B

After

Width:  |  Height:  |  Size: 373 B