diff --git a/BuildTime.txt b/BuildTime.txt
index 020ecb9..577d9b4 100644
--- a/BuildTime.txt
+++ b/BuildTime.txt
@@ -1 +1 @@
-2026-03-23 21:30:51
\ No newline at end of file
+2026-03-23 22:09:30
\ No newline at end of file
diff --git a/GitCommit.txt b/GitCommit.txt
index 62e8da4..aa69fc8 100644
--- a/GitCommit.txt
+++ b/GitCommit.txt
@@ -1 +1 @@
-9162e2d
\ No newline at end of file
+9197dfb
\ No newline at end of file
diff --git a/Gui/AppManager.cs b/Gui/AppManager.cs
index b416e2a..8cc0da3 100644
--- a/Gui/AppManager.cs
+++ b/Gui/AppManager.cs
@@ -137,6 +137,7 @@ namespace CMLeonOS.Gui
RegisterApp(new AppMetadata("Paint", () => { return new Apps.Paint.Paint(); }, Icons.Icon_Paint, Color.FromArgb(0, 115, 186)));
RegisterApp(new AppMetadata("Memory Statistics", () => { return new Apps.MemoryStatistics(); }, Icons.Icon_MemoryStatistics, Color.FromArgb(25, 25, 25)));
RegisterApp(new AppMetadata("CodeStudio", () => { return new Apps.CodeStudio.CodeStudio(); }, Icons.Icon_CodeStudio, Color.FromArgb(14, 59, 76)));
+ RegisterApp(new AppMetadata("Image Viewer", () => { return new ImageViewer(); }, Icons.Icon_Default, Color.FromArgb(25, 25, 25)));
Logger.Logger.Instance.Info("AppManager", $"{AppMetadatas.Count} apps were registered.");
diff --git a/Gui/Apps/ImageViewer.cs b/Gui/Apps/ImageViewer.cs
new file mode 100644
index 0000000..9935d8a
--- /dev/null
+++ b/Gui/Apps/ImageViewer.cs
@@ -0,0 +1,274 @@
+// The CMLeonOS Project (https://github.com/Leonmmcoset/CMLeonOS)
+// Copyright (C) 2025-present LeonOS 2 Developer Team
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+using Cosmos.System.Graphics;
+using CMLeonOS;
+using CMLeonOS.Gui.UILib;
+using CMLeonOS.Logger;
+using CMLeonOS.Utils;
+using System;
+using System.Drawing;
+using System.IO;
+
+namespace CMLeonOS.Gui.Apps
+{
+ internal class ImageViewer : Process
+ {
+ internal ImageViewer() : base("Image Viewer", ProcessType.Application) { }
+
+ internal ImageViewer(string path) : base("Image Viewer", ProcessType.Application)
+ {
+ initialPath = path;
+ }
+
+ private AppWindow window;
+ private Window canvas;
+ private TextBox pathBox;
+ private Button openButton;
+ private Button loadButton;
+ private Button clearButton;
+
+ private WindowManager wm = ProcessManager.GetProcess();
+ private FileBrowser fileBrowser;
+
+ private Bitmap currentBitmap;
+ private string currentPath = string.Empty;
+ private string initialPath = string.Empty;
+
+ private const int toolbarHeight = 30;
+ private const int padding = 6;
+ private const int buttonWidth = 64;
+
+ private void ShowError(string text)
+ {
+ MessageBox messageBox = new MessageBox(this, "Image Viewer", text);
+ messageBox.Show();
+ }
+
+ private void Relayout()
+ {
+ int y = padding;
+ int buttonY = y;
+
+ openButton.MoveAndResize(window.Width - (buttonWidth * 3 + padding * 3), buttonY, buttonWidth, toolbarHeight);
+ loadButton.MoveAndResize(window.Width - (buttonWidth * 2 + padding * 2), buttonY, buttonWidth, toolbarHeight);
+ clearButton.MoveAndResize(window.Width - (buttonWidth + padding), buttonY, buttonWidth, toolbarHeight);
+
+ int pathWidth = window.Width - (buttonWidth * 3 + padding * 5);
+ pathBox.MoveAndResize(padding, y + 3, pathWidth, toolbarHeight - 6);
+
+ int canvasY = toolbarHeight + (padding * 2);
+ int canvasHeight = window.Height - canvasY - padding;
+ canvas.MoveAndResize(padding, canvasY, window.Width - (padding * 2), canvasHeight);
+ RenderCanvas();
+ }
+
+ private void DrawBitmapFit(Bitmap bitmap, Window target)
+ {
+ int srcWidth = (int)bitmap.Width;
+ int srcHeight = (int)bitmap.Height;
+ if (srcWidth <= 0 || srcHeight <= 0)
+ {
+ return;
+ }
+
+ int dstWidth = target.Width - 2;
+ int dstHeight = target.Height - 2;
+ if (dstWidth <= 0 || dstHeight <= 0)
+ {
+ return;
+ }
+
+ int scaledWidth = dstWidth;
+ int scaledHeight = (srcHeight * dstWidth) / srcWidth;
+ if (scaledHeight > dstHeight)
+ {
+ scaledHeight = dstHeight;
+ scaledWidth = (srcWidth * dstHeight) / srcHeight;
+ }
+
+ scaledWidth = Math.Max(1, scaledWidth);
+ scaledHeight = Math.Max(1, scaledHeight);
+
+ int startX = (target.Width - scaledWidth) / 2;
+ int startY = (target.Height - scaledHeight) / 2;
+
+ for (int y = 0; y < scaledHeight; y++)
+ {
+ int srcY = (y * srcHeight) / scaledHeight;
+ int sourceRowOffset = srcY * srcWidth;
+ int targetY = startY + y;
+ for (int x = 0; x < scaledWidth; x++)
+ {
+ int srcX = (x * srcWidth) / scaledWidth;
+ int sourceIndex = sourceRowOffset + srcX;
+ Color color = Color.FromArgb(bitmap.RawData[sourceIndex]);
+ target.DrawPoint(startX + x, targetY, color);
+ }
+ }
+ }
+
+ private void RenderCanvas()
+ {
+ canvas.Clear(Color.FromArgb(20, 20, 20));
+ canvas.DrawRectangle(0, 0, canvas.Width, canvas.Height, Color.FromArgb(70, 70, 70));
+
+ if (currentBitmap == null)
+ {
+ const string hint = "Open a BMP file to preview.";
+ canvas.DrawString(hint, Color.LightGray, Math.Max(2, (canvas.Width / 2) - ((hint.Length * 8) / 2)), canvas.Height / 2 - 8);
+ wm.Update(canvas);
+ return;
+ }
+
+ DrawBitmapFit(currentBitmap, canvas);
+
+ string fileName = Path.GetFileName(currentPath);
+ int srcWidth = (int)currentBitmap.Width;
+ int srcHeight = (int)currentBitmap.Height;
+ string info = $"{fileName} {srcWidth}x{srcHeight}";
+ canvas.DrawFilledRectangle(6, canvas.Height - 22, Math.Min(canvas.Width - 12, (info.Length * 8) + 8), 16, Color.FromArgb(0, 0, 0));
+ canvas.DrawString(info, Color.White, 10, canvas.Height - 20);
+
+ wm.Update(canvas);
+ }
+
+ private bool LoadImage(string path, bool showPopup = true)
+ {
+ if (string.IsNullOrWhiteSpace(path))
+ {
+ if (showPopup)
+ {
+ ShowError("Path is empty.");
+ }
+ return false;
+ }
+
+ string sanitizedPath = PathUtil.Sanitize(path.Trim());
+ if (!File.Exists(sanitizedPath))
+ {
+ if (showPopup)
+ {
+ ShowError("File not found.");
+ }
+ return false;
+ }
+
+ if (!Path.GetExtension(sanitizedPath).Equals(".bmp", StringComparison.OrdinalIgnoreCase))
+ {
+ if (showPopup)
+ {
+ ShowError("Only BMP images are supported.");
+ }
+ return false;
+ }
+
+ try
+ {
+ byte[] bmpBytes = File.ReadAllBytes(sanitizedPath);
+ currentBitmap = new Bitmap(bmpBytes);
+ currentPath = sanitizedPath;
+ pathBox.Text = sanitizedPath;
+ window.Title = $"Image Viewer - {Path.GetFileName(sanitizedPath)}";
+ RenderCanvas();
+ return true;
+ }
+ catch (Exception ex)
+ {
+ Logger.Logger.Instance.Error("ImageViewer", $"Failed to load BMP: {ex.Message}");
+ if (showPopup)
+ {
+ ShowError("Cannot load this BMP file.");
+ }
+ return false;
+ }
+ }
+
+ private void OpenDialog()
+ {
+ fileBrowser = new FileBrowser(this, wm, (string selectedPath) =>
+ {
+ if (!string.IsNullOrWhiteSpace(selectedPath))
+ {
+ LoadImage(selectedPath);
+ }
+ }, selectDirectoryOnly: false);
+ fileBrowser.Show();
+ }
+
+ private void LoadFromPath()
+ {
+ LoadImage(pathBox.Text);
+ }
+
+ private void ClearImage()
+ {
+ currentBitmap = null;
+ currentPath = string.Empty;
+ window.Title = "Image Viewer";
+ RenderCanvas();
+ }
+
+ public override void Start()
+ {
+ base.Start();
+
+ window = new AppWindow(this, 170, 90, 760, 520);
+ window.Title = "Image Viewer";
+ window.Icon = AppManager.DefaultAppIcon;
+ window.CanResize = true;
+ window.UserResized = Relayout;
+ window.Closing = TryStop;
+ wm.AddWindow(window);
+
+ pathBox = new TextBox(window, 0, 0, 1, 1);
+ pathBox.PlaceholderText = @"0:\path\image.bmp";
+ pathBox.Submitted = LoadFromPath;
+ wm.AddWindow(pathBox);
+
+ openButton = new Button(window, 0, 0, 1, 1);
+ openButton.Text = "Open";
+ openButton.OnClick = (_, _) => OpenDialog();
+ wm.AddWindow(openButton);
+
+ loadButton = new Button(window, 0, 0, 1, 1);
+ loadButton.Text = "Load";
+ loadButton.OnClick = (_, _) => LoadFromPath();
+ wm.AddWindow(loadButton);
+
+ clearButton = new Button(window, 0, 0, 1, 1);
+ clearButton.Text = "Clear";
+ clearButton.OnClick = (_, _) => ClearImage();
+ wm.AddWindow(clearButton);
+
+ canvas = new Window(this, window, 0, 0, 1, 1);
+ wm.AddWindow(canvas);
+
+ Relayout();
+
+ if (!string.IsNullOrWhiteSpace(initialPath))
+ {
+ LoadImage(initialPath, showPopup: false);
+ }
+
+ wm.Update(window);
+ }
+
+ public override void Run()
+ {
+ }
+ }
+}