From a023a9259bd8005110c97003db02d893478cc87b Mon Sep 17 00:00:00 2001 From: leo Date: Thu, 5 Jun 2025 21:06:31 +0800 Subject: [PATCH] refactor: rewrite lfs pointer detection and image loading Signed-off-by: leo --- src/Models/ImageDecoder.cs | 8 +++ src/Models/LFSObject.cs | 18 +++++- src/Models/RevisionFile.cs | 17 +++-- src/ViewModels/CommitDetail.cs | 70 +++++++------------- src/ViewModels/DiffContext.cs | 47 ++++++-------- src/ViewModels/FileHistories.cs | 65 ++++++++----------- src/ViewModels/ImageSource.cs | 78 +++++++++++++++++++++++ src/ViewModels/LFSImageDiff.cs | 28 ++++---- src/ViewModels/Preferences.cs | 8 +-- src/ViewModels/RevisionLFSImage.cs | 34 ++++++++++ src/Views/DiffView.axaml | 42 +++++++----- src/Views/LFSDiffView.axaml | 32 ---------- src/Views/LFSDiffView.axaml.cs | 12 ---- src/Views/RevisionFileContentViewer.axaml | 26 ++++++++ 14 files changed, 286 insertions(+), 199 deletions(-) create mode 100644 src/Models/ImageDecoder.cs create mode 100644 src/ViewModels/ImageSource.cs create mode 100644 src/ViewModels/RevisionLFSImage.cs delete mode 100644 src/Views/LFSDiffView.axaml delete mode 100644 src/Views/LFSDiffView.axaml.cs diff --git a/src/Models/ImageDecoder.cs b/src/Models/ImageDecoder.cs new file mode 100644 index 00000000..3a758882 --- /dev/null +++ b/src/Models/ImageDecoder.cs @@ -0,0 +1,8 @@ +namespace SourceGit.Models +{ + public enum ImageDecoder + { + None = 0, + Builtin, + } +} diff --git a/src/Models/LFSObject.cs b/src/Models/LFSObject.cs index 0f281253..8bc2dda2 100644 --- a/src/Models/LFSObject.cs +++ b/src/Models/LFSObject.cs @@ -1,8 +1,22 @@ -namespace SourceGit.Models +using System.Text.RegularExpressions; + +namespace SourceGit.Models { - public class LFSObject + public partial class LFSObject { + [GeneratedRegex(@"^version https://git-lfs.github.com/spec/v\d+\r?\noid sha256:([0-9a-f]+)\r?\nsize (\d+)[\r\n]*$")] + private static partial Regex REG_FORMAT(); + public string Oid { get; set; } = string.Empty; public long Size { get; set; } = 0; + + public static LFSObject Parse(string content) + { + var match = REG_FORMAT().Match(content); + if (match.Success) + return new() { Oid = match.Groups[1].Value, Size = long.Parse(match.Groups[2].Value) }; + + return null; + } } } diff --git a/src/Models/RevisionFile.cs b/src/Models/RevisionFile.cs index 8cc1be2a..29a23efa 100644 --- a/src/Models/RevisionFile.cs +++ b/src/Models/RevisionFile.cs @@ -1,4 +1,6 @@ -using Avalonia.Media.Imaging; +using System.Globalization; +using System.IO; +using Avalonia.Media.Imaging; namespace SourceGit.Models { @@ -9,10 +11,17 @@ namespace SourceGit.Models public class RevisionImageFile { - public Bitmap Image { get; set; } = null; - public long FileSize { get; set; } = 0; - public string ImageType { get; set; } = string.Empty; + public Bitmap Image { get; } + public long FileSize { get; } + public string ImageType { get; } public string ImageSize => Image != null ? $"{Image.PixelSize.Width} x {Image.PixelSize.Height}" : "0 x 0"; + + public RevisionImageFile(string file, Bitmap img, long size) + { + Image = img; + FileSize = size; + ImageType = Path.GetExtension(file)!.Substring(1).ToUpper(CultureInfo.CurrentCulture); + } } public class RevisionTextFile diff --git a/src/ViewModels/CommitDetail.cs b/src/ViewModels/CommitDetail.cs index 55618897..10ee336a 100644 --- a/src/ViewModels/CommitDetail.cs +++ b/src/ViewModels/CommitDetail.cs @@ -1,13 +1,11 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.IO; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using Avalonia.Controls; -using Avalonia.Media.Imaging; using Avalonia.Platform.Storage; using Avalonia.Threading; @@ -103,9 +101,7 @@ namespace SourceGit.ViewModels set { if (SetProperty(ref _searchChangeFilter, value)) - { RefreshVisibleChanges(); - } } } @@ -205,14 +201,11 @@ namespace SourceGit.ViewModels var isBinary = new Commands.IsBinary(_repo.FullPath, _commit.SHA, file.Path).Result(); if (isBinary) { - var ext = Path.GetExtension(file.Path); - if (IMG_EXTS.Contains(ext)) + var imgDecoder = ImageSource.GetDecoder(file.Path); + if (imgDecoder != Models.ImageDecoder.None) { - var stream = Commands.QueryFileContent.Run(_repo.FullPath, _commit.SHA, file.Path); - var fileSize = stream.Length; - var bitmap = fileSize > 0 ? new Bitmap(stream) : null; - var imageType = ext!.Substring(1).ToUpper(CultureInfo.CurrentCulture); - var image = new Models.RevisionImageFile() { Image = bitmap, FileSize = fileSize, ImageType = imageType }; + var source = ImageSource.FromRevision(_repo.FullPath, _commit.SHA, file.Path, imgDecoder); + var image = new Models.RevisionImageFile(file.Path, source.Bitmap, source.Size); Dispatcher.UIThread.Invoke(() => ViewRevisionFileContent = image); } else @@ -227,13 +220,20 @@ namespace SourceGit.ViewModels var contentStream = Commands.QueryFileContent.Run(_repo.FullPath, _commit.SHA, file.Path); var content = new StreamReader(contentStream).ReadToEnd(); - var matchLFS = REG_LFS_FORMAT().Match(content); - if (matchLFS.Success) + var lfs = Models.LFSObject.Parse(content); + if (lfs != null) { - var obj = new Models.RevisionLFSObject() { Object = new Models.LFSObject() }; - obj.Object.Oid = matchLFS.Groups[1].Value; - obj.Object.Size = long.Parse(matchLFS.Groups[2].Value); - Dispatcher.UIThread.Invoke(() => ViewRevisionFileContent = obj); + var imgDecoder = ImageSource.GetDecoder(file.Path); + if (imgDecoder != Models.ImageDecoder.None) + { + var combined = new RevisionLFSImage(_repo.FullPath, file.Path, lfs, imgDecoder); + Dispatcher.UIThread.Invoke(() => ViewRevisionFileContent = combined); + } + else + { + var rlfs = new Models.RevisionLFSObject() { Object = lfs }; + Dispatcher.UIThread.Invoke(() => ViewRevisionFileContent = rlfs); + } } else { @@ -246,29 +246,15 @@ namespace SourceGit.ViewModels Task.Run(() => { var submoduleRoot = Path.Combine(_repo.FullPath, file.Path); - var commit = new Commands.QuerySingleCommit(submoduleRoot, file.SHA).Result(); - if (commit != null) + var commit = new Commands.QuerySingleCommit(submoduleRoot, _commit.SHA).Result(); + var message = commit != null ? new Commands.QueryCommitFullMessage(submoduleRoot, _commit.SHA).Result() : null; + var module = new Models.RevisionSubmodule() { - var body = new Commands.QueryCommitFullMessage(submoduleRoot, file.SHA).Result(); - var submodule = new Models.RevisionSubmodule() - { - Commit = commit, - FullMessage = new Models.CommitFullMessage { Message = body } - }; + Commit = commit ?? new Models.Commit() { SHA = _commit.SHA }, + FullMessage = new Models.CommitFullMessage { Message = message } + }; - Dispatcher.UIThread.Invoke(() => ViewRevisionFileContent = submodule); - } - else - { - Dispatcher.UIThread.Invoke(() => - { - ViewRevisionFileContent = new Models.RevisionSubmodule() - { - Commit = new Models.Commit() { SHA = file.SHA }, - FullMessage = null, - }; - }); - } + Dispatcher.UIThread.Invoke(() => ViewRevisionFileContent = module); }); break; default: @@ -897,14 +883,6 @@ namespace SourceGit.ViewModels [GeneratedRegex(@"\b([0-9a-fA-F]{6,40})\b")] private static partial Regex REG_SHA_FORMAT(); - [GeneratedRegex(@"^version https://git-lfs.github.com/spec/v\d+\r?\noid sha256:([0-9a-f]+)\r?\nsize (\d+)[\r\n]*$")] - private static partial Regex REG_LFS_FORMAT(); - - private static readonly HashSet IMG_EXTS = new HashSet() - { - ".ico", ".bmp", ".jpg", ".png", ".jpeg", ".webp" - }; - private Repository _repo = null; private Models.Commit _commit = null; private Models.CommitFullMessage _fullMessage = null; diff --git a/src/ViewModels/DiffContext.cs b/src/ViewModels/DiffContext.cs index af089635..5258f164 100644 --- a/src/ViewModels/DiffContext.cs +++ b/src/ViewModels/DiffContext.cs @@ -1,9 +1,7 @@ using System; -using System.Collections.Generic; using System.IO; using System.Threading.Tasks; -using Avalonia.Media.Imaging; using Avalonia.Threading; using CommunityToolkit.Mvvm.ComponentModel; @@ -113,9 +111,6 @@ namespace SourceGit.ViewModels Task.Run(() => { - // NOTE: Here we override the UnifiedLines value (if UseFullTextDiff is on). - // There is no way to tell a git-diff to use "ALL lines of context", - // so instead we set a very high number for the "lines of context" parameter. var numLines = Preferences.Instance.UseFullTextDiff ? 999999999 : _unifiedLines; var ignoreWS = Preferences.Instance.IgnoreWhitespaceChangesInDiff; var latest = new Commands.Diff(_repo, _option, numLines, ignoreWS).Result(); @@ -164,28 +159,39 @@ namespace SourceGit.ViewModels else if (latest.IsBinary) { var oldPath = string.IsNullOrEmpty(_option.OrgPath) ? _option.Path : _option.OrgPath; - var ext = Path.GetExtension(_option.Path); + var imgDecoder = ImageSource.GetDecoder(_option.Path); - if (IMG_EXTS.Contains(ext)) + if (imgDecoder != Models.ImageDecoder.None) { var imgDiff = new Models.ImageDiff(); + if (_option.Revisions.Count == 2) { - (imgDiff.Old, imgDiff.OldFileSize) = BitmapFromRevisionFile(_repo, _option.Revisions[0], oldPath); - (imgDiff.New, imgDiff.NewFileSize) = BitmapFromRevisionFile(_repo, _option.Revisions[1], _option.Path); + var oldImage = ImageSource.FromRevision(_repo, _option.Revisions[0], oldPath, imgDecoder); + var newImage = ImageSource.FromRevision(_repo, _option.Revisions[1], _option.Path, imgDecoder); + imgDiff.Old = oldImage.Bitmap; + imgDiff.OldFileSize = oldImage.Size; + imgDiff.New = newImage.Bitmap; + imgDiff.NewFileSize = newImage.Size; } else { if (!oldPath.Equals("/dev/null", StringComparison.Ordinal)) - (imgDiff.Old, imgDiff.OldFileSize) = BitmapFromRevisionFile(_repo, "HEAD", oldPath); + { + var oldImage = ImageSource.FromRevision(_repo, "HEAD", oldPath, imgDecoder); + imgDiff.Old = oldImage.Bitmap; + imgDiff.OldFileSize = oldImage.Size; + } var fullPath = Path.Combine(_repo, _option.Path); if (File.Exists(fullPath)) { - imgDiff.New = new Bitmap(fullPath); - imgDiff.NewFileSize = new FileInfo(fullPath).Length; + var newImage = ImageSource.FromFile(fullPath, imgDecoder); + imgDiff.New = newImage.Bitmap; + imgDiff.NewFileSize = newImage.Size; } } + rs = imgDiff; } else @@ -207,8 +213,9 @@ namespace SourceGit.ViewModels } else if (latest.IsLFS) { - if (IMG_EXTS.Contains(Path.GetExtension(_option.Path) ?? ".invalid")) - rs = new LFSImageDiff(_repo, latest.LFSDiff); + var imgDecoder = ImageSource.GetDecoder(_option.Path); + if (imgDecoder != Models.ImageDecoder.None) + rs = new LFSImageDiff(_repo, latest.LFSDiff, imgDecoder); else rs = latest.LFSDiff; } @@ -229,13 +236,6 @@ namespace SourceGit.ViewModels }); } - private (Bitmap, long) BitmapFromRevisionFile(string repo, string revision, string file) - { - var stream = Commands.QueryFileContent.Run(repo, revision, file); - var size = stream.Length; - return size > 0 ? (new Bitmap(stream), size) : (null, size); - } - private Models.RevisionSubmodule QuerySubmoduleRevision(string repo, string sha) { var commit = new Commands.QuerySingleCommit(repo, sha).Result(); @@ -256,11 +256,6 @@ namespace SourceGit.ViewModels }; } - private static readonly HashSet IMG_EXTS = new HashSet() - { - ".ico", ".bmp", ".jpg", ".png", ".jpeg", ".webp" - }; - private class Info { public string Argument { get; set; } diff --git a/src/ViewModels/FileHistories.cs b/src/ViewModels/FileHistories.cs index 0e474af2..c3e5aac1 100644 --- a/src/ViewModels/FileHistories.cs +++ b/src/ViewModels/FileHistories.cs @@ -1,11 +1,8 @@ using System.Collections.Generic; -using System.Globalization; using System.IO; -using System.Text.RegularExpressions; using System.Threading.Tasks; using Avalonia.Collections; -using Avalonia.Media.Imaging; using Avalonia.Threading; using CommunityToolkit.Mvvm.ComponentModel; @@ -18,7 +15,7 @@ namespace SourceGit.ViewModels public object Content { get; set; } = content; } - public partial class FileHistoriesSingleRevision : ObservableObject + public class FileHistoriesSingleRevision : ObservableObject { public bool IsDiffMode { @@ -78,14 +75,11 @@ namespace SourceGit.ViewModels var isBinary = new Commands.IsBinary(_repo.FullPath, _revision.SHA, _file).Result(); if (isBinary) { - var ext = Path.GetExtension(_file); - if (IMG_EXTS.Contains(ext)) + var imgDecoder = ImageSource.GetDecoder(_file); + if (imgDecoder != Models.ImageDecoder.None) { - var stream = Commands.QueryFileContent.Run(_repo.FullPath, _revision.SHA, _file); - var fileSize = stream.Length; - var bitmap = fileSize > 0 ? new Bitmap(stream) : null; - var imageType = Path.GetExtension(_file)!.TrimStart('.').ToUpper(CultureInfo.CurrentCulture); - var image = new Models.RevisionImageFile() { Image = bitmap, FileSize = fileSize, ImageType = imageType }; + var source = ImageSource.FromRevision(_repo.FullPath, _revision.SHA, _file, imgDecoder); + var image = new Models.RevisionImageFile(_file, source.Bitmap, source.Size); Dispatcher.UIThread.Invoke(() => ViewContent = new FileHistoriesRevisionFile(_file, image)); } else @@ -100,13 +94,20 @@ namespace SourceGit.ViewModels var contentStream = Commands.QueryFileContent.Run(_repo.FullPath, _revision.SHA, _file); var content = new StreamReader(contentStream).ReadToEnd(); - var matchLFS = REG_LFS_FORMAT().Match(content); - if (matchLFS.Success) + var lfs = Models.LFSObject.Parse(content); + if (lfs != null) { - var lfs = new Models.RevisionLFSObject() { Object = new() }; - lfs.Object.Oid = matchLFS.Groups[1].Value; - lfs.Object.Size = long.Parse(matchLFS.Groups[2].Value); - Dispatcher.UIThread.Invoke(() => ViewContent = new FileHistoriesRevisionFile(_file, lfs)); + var imgDecoder = ImageSource.GetDecoder(_file); + if (imgDecoder != Models.ImageDecoder.None) + { + var combined = new RevisionLFSImage(_repo.FullPath, _file, lfs, imgDecoder); + Dispatcher.UIThread.Invoke(() => ViewContent = new FileHistoriesRevisionFile(_file, combined)); + } + else + { + var rlfs = new Models.RevisionLFSObject() { Object = lfs }; + Dispatcher.UIThread.Invoke(() => ViewContent = new FileHistoriesRevisionFile(_file, rlfs)); + } } else { @@ -120,25 +121,14 @@ namespace SourceGit.ViewModels { var submoduleRoot = Path.Combine(_repo.FullPath, _file); var commit = new Commands.QuerySingleCommit(submoduleRoot, obj.SHA).Result(); - if (commit != null) + var message = commit != null ? new Commands.QueryCommitFullMessage(submoduleRoot, obj.SHA).Result() : null; + var module = new Models.RevisionSubmodule() { - var message = new Commands.QueryCommitFullMessage(submoduleRoot, obj.SHA).Result(); - var module = new Models.RevisionSubmodule() - { - Commit = commit, - FullMessage = new Models.CommitFullMessage { Message = message } - }; - Dispatcher.UIThread.Invoke(() => ViewContent = new FileHistoriesRevisionFile(_file, module)); - } - else - { - var module = new Models.RevisionSubmodule() - { - Commit = new Models.Commit() { SHA = obj.SHA }, - FullMessage = null - }; - Dispatcher.UIThread.Invoke(() => ViewContent = new FileHistoriesRevisionFile(_file, module)); - } + Commit = commit ?? new Models.Commit() { SHA = obj.SHA }, + FullMessage = new Models.CommitFullMessage { Message = message } + }; + + Dispatcher.UIThread.Invoke(() => ViewContent = new FileHistoriesRevisionFile(_file, module)); }); break; default: @@ -153,11 +143,6 @@ namespace SourceGit.ViewModels ViewContent = new DiffContext(_repo.FullPath, option, _viewContent as DiffContext); } - [GeneratedRegex(@"^version https://git-lfs.github.com/spec/v\d+\r?\noid sha256:([0-9a-f]+)\r?\nsize (\d+)[\r\n]*$")] - private static partial Regex REG_LFS_FORMAT(); - - private static readonly HashSet IMG_EXTS = [".ico", ".bmp", ".jpg", ".png", ".jpeg", ".webp"]; - private Repository _repo = null; private string _file = null; private Models.Commit _revision = null; diff --git a/src/ViewModels/ImageSource.cs b/src/ViewModels/ImageSource.cs new file mode 100644 index 00000000..ec6b6a8d --- /dev/null +++ b/src/ViewModels/ImageSource.cs @@ -0,0 +1,78 @@ +using System.IO; +using Avalonia.Media.Imaging; + +namespace SourceGit.ViewModels +{ + public class ImageSource + { + public Bitmap Bitmap { get; } + public long Size { get; } + + public ImageSource(Bitmap bitmap, long size) + { + Bitmap = bitmap; + Size = size; + } + + public static Models.ImageDecoder GetDecoder(string file) + { + var ext = Path.GetExtension(file) ?? ".invalid_img"; + + switch (ext) + { + case ".ico": + case ".bmp": + case ".jpg": + case ".jpeg": + case ".png": + case ".webp": + return Models.ImageDecoder.Builtin; + default: + return Models.ImageDecoder.None; + } + } + + public static ImageSource FromFile(string fullpath, Models.ImageDecoder decoder) + { + using (var stream = File.OpenRead(fullpath)) + return LoadFromStream(stream, decoder); + } + + public static ImageSource FromRevision(string repo, string revision, string file, Models.ImageDecoder decoder) + { + var stream = Commands.QueryFileContent.Run(repo, revision, file); + return LoadFromStream(stream, decoder); + } + + public static ImageSource FromLFSObject(string repo, Models.LFSObject lfs, Models.ImageDecoder decoder) + { + if (string.IsNullOrEmpty(lfs.Oid) || lfs.Size == 0) + return new ImageSource(null, 0); + + var stream = Commands.QueryFileContent.FromLFS(repo, lfs.Oid, lfs.Size); + return LoadFromStream(stream, decoder); + } + + private static ImageSource LoadFromStream(Stream stream, Models.ImageDecoder decoder) + { + var size = stream.Length; + if (size > 0) + { + if (decoder == Models.ImageDecoder.Builtin) + { + try + { + var bitmap = new Bitmap(stream); + return new ImageSource(bitmap, size); + } + catch + { + // Just ignore. + } + } + } + + return new ImageSource(null, 0); + } + } +} diff --git a/src/ViewModels/LFSImageDiff.cs b/src/ViewModels/LFSImageDiff.cs index 655004a6..bb69fd65 100644 --- a/src/ViewModels/LFSImageDiff.cs +++ b/src/ViewModels/LFSImageDiff.cs @@ -1,8 +1,5 @@ using System.Threading.Tasks; - -using Avalonia.Media.Imaging; using Avalonia.Threading; - using CommunityToolkit.Mvvm.ComponentModel; namespace SourceGit.ViewModels @@ -20,30 +17,27 @@ namespace SourceGit.ViewModels private set => SetProperty(ref _image, value); } - public LFSImageDiff(string repo, Models.LFSDiff lfs) + public LFSImageDiff(string repo, Models.LFSDiff lfs, Models.ImageDecoder decoder) { LFS = lfs; Task.Run(() => { - var img = new Models.ImageDiff(); - (img.Old, img.OldFileSize) = BitmapFromLFSObject(repo, lfs.Old); - (img.New, img.NewFileSize) = BitmapFromLFSObject(repo, lfs.New); + var oldImage = ImageSource.FromLFSObject(repo, lfs.Old, decoder); + var newImage = ImageSource.FromLFSObject(repo, lfs.New, decoder); + + var img = new Models.ImageDiff() + { + Old = oldImage.Bitmap, + OldFileSize = oldImage.Size, + New = newImage.Bitmap, + NewFileSize = newImage.Size + }; Dispatcher.UIThread.Invoke(() => Image = img); }); } - private (Bitmap, long) BitmapFromLFSObject(string repo, Models.LFSObject lfs) - { - if (string.IsNullOrEmpty(lfs.Oid) || lfs.Size == 0) - return (null, 0); - - var stream = Commands.QueryFileContent.FromLFS(repo, lfs.Oid, lfs.Size); - var size = stream.Length; - return size > 0 ? (new Bitmap(stream), size) : (null, size); - } - private Models.ImageDiff _image; } } diff --git a/src/ViewModels/Preferences.cs b/src/ViewModels/Preferences.cs index 76305655..2fcbb0cf 100644 --- a/src/ViewModels/Preferences.cs +++ b/src/ViewModels/Preferences.cs @@ -261,10 +261,10 @@ namespace SourceGit.ViewModels set => SetProperty(ref _useBlockNavigationInDiffView, value); } - public int LFSImageDiffActiveIdx + public int LFSImageActiveIdx { - get => _lfsImageDiffActiveIdx; - set => SetProperty(ref _lfsImageDiffActiveIdx, value); + get => _lfsImageActiveIdx; + set => SetProperty(ref _lfsImageActiveIdx, value); } public Models.ChangeViewMode UnstagedChangeViewMode @@ -693,7 +693,7 @@ namespace SourceGit.ViewModels private bool _showHiddenSymbolsInDiffView = false; private bool _useFullTextDiff = false; private bool _useBlockNavigationInDiffView = false; - private int _lfsImageDiffActiveIdx = 0; + private int _lfsImageActiveIdx = 0; private Models.ChangeViewMode _unstagedChangeViewMode = Models.ChangeViewMode.List; private Models.ChangeViewMode _stagedChangeViewMode = Models.ChangeViewMode.List; diff --git a/src/ViewModels/RevisionLFSImage.cs b/src/ViewModels/RevisionLFSImage.cs new file mode 100644 index 00000000..e0b9a348 --- /dev/null +++ b/src/ViewModels/RevisionLFSImage.cs @@ -0,0 +1,34 @@ +using System.Threading.Tasks; +using Avalonia.Threading; +using CommunityToolkit.Mvvm.ComponentModel; + +namespace SourceGit.ViewModels +{ + public class RevisionLFSImage : ObservableObject + { + public Models.RevisionLFSObject LFS + { + get; + } + + public Models.RevisionImageFile Image + { + get => _image; + private set => SetProperty(ref _image, value); + } + + public RevisionLFSImage(string repo, string file, Models.LFSObject lfs, Models.ImageDecoder decoder) + { + LFS = new Models.RevisionLFSObject() { Object = lfs }; + + Task.Run(() => + { + var source = ImageSource.FromLFSObject(repo, lfs, decoder); + var img = new Models.RevisionImageFile(file, source.Bitmap, source.Size); + Dispatcher.UIThread.Invoke(() => Image = img); + }); + } + + private Models.RevisionImageFile _image = null; + } +} diff --git a/src/Views/DiffView.axaml b/src/Views/DiffView.axaml index c12c8a62..c9291a6b 100644 --- a/src/Views/DiffView.axaml +++ b/src/Views/DiffView.axaml @@ -224,7 +224,29 @@ - + + + + + + + + + + + + + + + + + + + @@ -282,7 +304,7 @@ - + + + + + + + + + + + + + + + + + + + +