14 Commits
v1.4 ... v1.5

35 changed files with 612 additions and 87 deletions

View File

@@ -5,6 +5,21 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.5] - 2026-03-12
### Added
- More OS download links
- Sub-categories for OS downloads (Desktop, Server, etc.)
### Changed
- Replaced refresh button with pull-to-refresh gesture
- Disable ISO directory change when root access is not available
- Status card now shows "Tap to request root" and updates when granted
- Improved monet/dynamic theme support for icons and text colors
### Fixed
- Sanitize user input for folder creation, IMG file creation, and file renaming to prevent shell escape vulnerabilities
## [1.4] - 2026-03-11
### Added

View File

@@ -12,8 +12,8 @@ android {
applicationId = "sh.sar.isodroid"
minSdk = 26
targetSdk = 36
versionCode = 4
versionName = "1.4"
versionCode = 5
versionName = "1.5"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}

View File

@@ -2,13 +2,23 @@
{
"name": "Arch Linux",
"category": "Linux",
"subcategory": "Enthusiast",
"description": "A simple, lightweight distribution",
"icon": "archlinux.svg",
"url": "https://archlinux.org/download/"
},
{
"name": "Gentoo Linux",
"category": "Linux",
"subcategory": "Enthusiast",
"description": "A highly flexible, source-based Linux distribution.",
"icon": "gentoo.svg",
"url": "https://www.gentoo.org/downloads/"
},
{
"name": "Debian",
"category": "Linux",
"subcategory": "Server",
"description": "Debian is a complete Free Operating System!",
"icon": "debian.svg",
"url": "https://www.debian.org/download"
@@ -16,6 +26,7 @@
{
"name": "Fedora",
"category": "Linux",
"subcategory": "Desktop",
"description": "The Fedora Project is a community of people working together to build a free and open source software platform and to collaborate on and share user-focused solutions built on that platform.",
"icon": "fedora.svg",
"url": "https://www.fedoraproject.org/"
@@ -23,6 +34,7 @@
{
"name": "Linux Mint",
"category": "Linux",
"subcategory": "Desktop",
"description": "Linux Mint is an operating system for desktop and laptop computers. It is designed to work 'out of the box' and comes fully equipped with the apps most people need.",
"icon": "linuxmint.svg",
"url": "https://linuxmint.com/download.php"
@@ -30,6 +42,7 @@
{
"name": "NixOS",
"category": "Linux",
"subcategory": "Enthusiast",
"description": "Declarative builds and deployments.",
"icon": "nixos.svg",
"url": "https://nixos.org/download/#nixos-iso"
@@ -37,6 +50,7 @@
{
"name": "Pop!_OS",
"category": "Linux",
"subcategory": "Desktop",
"description": "Unleash your potential on a Linux operating system made to be productive and personal.",
"icon": "popos.svg",
"url": "https://system76.com/pop/download/"
@@ -44,6 +58,7 @@
{
"name": "Tails",
"category": "Linux",
"subcategory": "Security",
"description": "Tails is a portable operating system that protects against surveillance and censorship.",
"icon": "tails.svg",
"url": "https://tails.net/install/download/index.en.html"
@@ -51,36 +66,281 @@
{
"name": "Ubuntu",
"category": "Linux",
"description": "",
"subcategory": "Desktop",
"description": "Ubuntu Desktop delivers new tools and enhancements for developers, creators, gamers, and administrators.",
"icon": "ubuntu.svg",
"url": "https://ubuntu.com/download"
},
{
"name": "openSUSE",
"category": "Linux",
"subcategory": "Desktop",
"description": "openSUSE makes open source Linux operating systems for desktops, servers and containers.",
"icon": "opensuse.svg",
"url": "https://get.opensuse.org/"
},
{
"name": "Rocky Linux",
"category": "Linux",
"subcategory": "Server",
"description": "Rocky Linux is an open-source enterprise operating system designed to be 100% bug-for-bug compatible with Red Hat Enterprise Linux®.",
"icon": "rockylinux.svg",
"url": "https://rockylinux.org/download"
},
{
"name": "AlmaLinux",
"category": "Linux",
"subcategory": "Server",
"description": "An Open Source, community owned and governed, forever-free enterprise Linux distribution, focused on long-term stability, providing a robust production-grade platform. AlmaLinux OS is binary compatible with RHEL®.",
"icon": "almalinux.svg",
"url": "https://almalinux.org/get-almalinux/"
},
{
"name": "Ubuntu Server",
"category": "Linux",
"subcategory": "Server",
"description": "Whether you want to configure a simple file server or build a fifty thousand-node cloud, you can rely on Ubuntu Server and its five years of free updates.",
"icon": "ubuntu.svg",
"url": "https://ubuntu.com/download/server"
},
{
"name": "Alpine Linux",
"category": "Linux",
"subcategory": "Server",
"description": "Alpine Linux is a security-oriented, lightweight Linux distribution based on musl libc and busybox.",
"icon": "alpinelinux.svg",
"url": "https://alpinelinux.org/downloads/"
},
{
"name": "FreeBSD",
"category": "BSD",
"subcategory": null,
"description": "FreeBSD is an operating system for a variety of platforms which focuses on features, speed, and stability.",
"icon": "freebsd.svg",
"url": "https://www.freebsd.org/where/"
},
{
"name": "OpenBSD",
"category": "BSD",
"subcategory": null,
"description": "A free, multi-platform 4.4BSD-based UNIX-like operating system emphasizing security and correctness.",
"icon": "openbsd.svg",
"url": "https://www.openbsd.org/faq/faq4.html#Download"
},
{
"name": "GhostBSD",
"category": "BSD",
"subcategory": "Desktop",
"description": "A simple, elegant desktop BSD Operating System",
"icon": null,
"url": "https://ghostbsd.org/download"
},
{
"name": "TrueNAS",
"category": "BSD",
"subcategory": "Networking",
"description": "The world's most popular software-defined storage operating system.",
"icon": "truenas.svg",
"url": "https://www.truenas.com/download-truenas-core/"
},
{
"name": "OPNsense",
"category": "BSD",
"subcategory": "Networking",
"description": "OPNsense® is an open source, feature rich firewall and routing platform, offering cutting-edge network protection.",
"icon": "opnsense.svg",
"url": "https://opnsense.org/download/"
},
{
"name": "pfSense",
"category": "BSD",
"subcategory": "Networking",
"description": "Secure networks start here.™",
"icon": "pfsense.svg",
"url": "https://www.pfsense.org/download/"
},
{
"name": "Windows 11",
"category": "Windows",
"subcategory": null,
"description": "",
"icon": "windows11.svg",
"url": "https://www.microsoft.com/en-us/software-download/windows11"
},
{
"name": "Windows 10",
"category": "Windows",
"subcategory": null,
"description": "",
"icon": "windows10.svg",
"url": "https://www.microsoft.com/en-us/software-download/windows10ISO"
},
{
"name": "Hiren's BootCD PE",
"category": "Recovery",
"subcategory": "Toolkit",
"description": "Hiren's BootCD PE (Preinstallation Environment) is a restored edition of Hiren's BootCD based on Windows PE",
"icon": null,
"url": "https://www.hirensbootcd.org/download/"
},
{
"name": "Clonezilla",
"category": "Recovery",
"subcategory": "Disk",
"description": "Clonezilla is a partition and disk imaging/cloning program.",
"icon": null,
"url": "https://clonezilla.org/downloads.php"
},
{
"name": "SystemRescue",
"category": "Recovery",
"subcategory": "Toolkit",
"description": "A Linux system rescue toolkit for administering or repairing your system and data.",
"icon": null,
"url": "https://www.system-rescue.org/Download/"
},
{
"name": "GParted Live",
"category": "Recovery",
"subcategory": "Disk",
"description": "A small bootable GNU/Linux distribution for x86 based computers with the GParted partition editor.",
"icon": null,
"url": "https://gparted.org/download.php"
},
{
"name": "Rescuezilla",
"category": "Recovery",
"subcategory": "Disk",
"description": "The Swiss Army Knife of system recovery. Easy-to-use disk imaging and cloning with a friendly GUI.",
"icon": null,
"url": "https://rescuezilla.com/download"
},
{
"name": "Memtest86+",
"category": "Recovery",
"subcategory": "Hardware",
"description": "Free, open-source memory testing software for x86 and x86-64 computers.",
"icon": null,
"url": "https://memtest.org/"
},
{
"name": "ShredOS",
"category": "Recovery",
"subcategory": "Disk",
"description": "A USB bootable small linux distro for securely erasing your disks using nwipe.",
"icon": null,
"url": "https://github.com/PartialVolume/shredos.x86_64/releases"
},
{
"name": "Super Grub2",
"category": "Recovery",
"subcategory": "Boot",
"description": "Get back to your GNU/Linux & Windows computers !",
"icon": null,
"url": "https://www.supergrubdisk.org/category/download/"
},
{
"name": "Bazzite",
"category": "Linux",
"subcategory": "Desktop",
"description": "Bazzite makes gaming and everyday use smoother and simpler across desktop PCs, handhelds, tablets, and home theater PCs.",
"icon": null,
"url": "https://bazzite.gg/#image-picker"
},
{
"name": "Manjaro",
"category": "Linux",
"subcategory": "Desktop",
"description": "Taking the raw power and flexibility of Arch Linux and making it more accessible for a greater audience.",
"icon": "manjaro.svg",
"url": "https://manjaro.org/products"
},
{
"name": "Kali Linux",
"category": "Linux",
"subcategory": "Security",
"description": "The most advanced penetration testing platform ever made.",
"icon": "kalilinux.svg",
"url": "https://www.kali.org/get-kali/#kali-installer-images"
},
{
"name": "ParrotOS",
"category": "Linux",
"subcategory": "Security",
"description": "The ultimate framework for your Cyber Security operations",
"icon": "parrotsecurity.svg",
"url": "https://www.parrotsec.org/download/"
},
{
"name": "ProxmoxVE",
"category": "Linux",
"subcategory": "Server",
"description": "Proxmox Virtual Environment is a complete open-source platform for enterprise virtualization.",
"icon": "proxmox.svg",
"url": "https://www.proxmox.com/en/downloads/proxmox-virtual-environment/iso"
},
{
"name": "Android-x86",
"category": "Android",
"subcategory": null,
"description": "This is a project to port Android Open Source Project to x86 platform",
"icon": "android.svg",
"url": "https://www.android-x86.org/download"
},
{
"name": "RemixOS",
"category": "Android",
"subcategory": null,
"description": "RemixOS brings Android to laptops and PCs with a powerful desktop-like experience.",
"icon": null,
"url": "https://www.fosshub.com/Remix-OS.html"
},
{
"name": "TempleOS",
"category": "Other",
"subcategory": null,
"description": "A biblical-themed lightweight operating system designed to be the Third Temple.",
"icon": null,
"url": "https://templeos.org/Downloads/"
},
{
"name": "Haiku",
"category": "Other",
"subcategory": null,
"description": "An open-source operating system inspired by BeOS, targeting personal computing.",
"icon": null,
"url": "https://www.haiku-os.org/get-haiku/"
},
{
"name": "ReactOS",
"category": "Other",
"subcategory": null,
"description": "A free, open-source operating system aiming for Windows compatibility.",
"icon": "reactos.svg",
"url": "https://reactos.org/download/"
},
{
"name": "ChromeOS Flex",
"category": "ChromeOS",
"subcategory": null,
"description": "Google's cloud-first operating system for PCs and Macs.",
"icon": "chromeos.svg",
"url": "https://chromeenterprise.google/os/chromeosflex/"
},
{
"name": "FydeOS",
"category": "ChromeOS",
"subcategory": null,
"description": "A ChromiumOS-based operating system with Android app support.",
"icon": null,
"url": "https://fydeos.io/download"
},
{
"name": "Brunch",
"category": "ChromeOS",
"subcategory": null,
"description": "A framework to boot ChromeOS on generic x86 hardware.",
"icon": null,
"url": "https://github.com/sebanc/brunch/releases"
}
]

View File

@@ -0,0 +1 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>AlmaLinux</title><path d="M23.994 15.133c.079 1.061-.668 1.927-1.69 2.005a1.8 1.8 0 0 1-1.928-1.651c-.078-1.062.63-1.849 1.691-1.967 1.023-.078 1.849.59 1.927 1.613zm-12.623 4.955c-.944 0-1.73.786-1.73 1.809 0 1.14.747 1.848 1.887 1.848.904-.04 1.691-.865 1.691-1.809 0-.983-.904-1.848-1.848-1.848zm1.061-9.675c-.039-.865-.078-1.73.08-2.556.156-.944.314-1.887.904-2.674.707-.983 1.809-.944 2.399.118.314.511.432 1.062.471 1.652 0 .354.158.432.472.393.944-.157 1.888-.157 2.792.197.118.039.236.118.394 0 .314-.276.393-1.652.196-2.006-.354-.63-.904-.55-1.455-.55-.629.039-1.18-.158-1.612-.67-.393-.471-.511-1.06-.59-1.65-.04-.276-.079-.512-.315-.709-.55-.55-1.809-.432-2.477.118-2.556 2.045-2.989 5.467-1.534 8.18.04.118.118.236.275.157zm7.984 3.658c.354-.511.865-.747 1.415-.983a.973.973 0 0 0 .59-.472c.354-.669-.078-1.81-.747-2.36-2.595-2.006-5.938-1.612-8.18.433-.118.078-.157.196-.078.314.786-.236 1.612-.472 2.477-.51.905-.08 1.848-.158 2.753.235 1.14.472 1.337 1.534.472 2.36-.393.393-.905.668-1.455.825-.315.08-.354.236-.236.551.354.865.59 1.77.472 2.753-.04.157-.079.275.078.393.354.236 1.691 0 1.967-.275.511-.472.314-1.023.196-1.534-.157-.63-.078-1.219.276-1.73zm-7.197-2.045c-.118-.079-.197-.118-.315 0 .472.708.905 1.455 1.259 2.241.314.866.668 1.73.55 2.714-.118 1.18-1.1 1.69-2.123 1.101-.511-.275-.905-.669-1.22-1.14-.196-.276-.393-.276-.629-.08-.747.63-1.533 1.102-2.516 1.26-.158 0-.315 0-.394.157-.118.393.472 1.612.826 1.809.59.354 1.062 0 1.534-.276.55-.314 1.101-.432 1.73-.236.59.197.983.63 1.337 1.102.158.196.315.353.63.432.747.197 1.77-.59 2.084-1.376 1.18-3.028-.157-6.135-2.753-7.708zm-2.556 2.438c.472-.669.826-1.416.983-2.202-.157-.04-.197.04-.315.078-.904.944-1.848 1.849-3.067 2.478-.472.236-.983.433-1.534.433-.865 0-1.376-.551-1.298-1.416a2.92 2.92 0 0 1 .787-1.849c.236-.275.236-.432-.04-.668-.786-.55-1.494-1.22-1.848-2.124-.078-.275-.275-.275-.51-.157a4.293 4.293 0 0 0-.434.236c-1.022.63-1.14 1.416-.275 2.28.63.63.944 1.338.708 2.203-.118.433-.354.747-.63 1.101a.95.95 0 0 0-.235.787c.079.747.826 1.494 1.73 1.573 2.517.236 4.562-.63 5.978-2.753zm-4.68-5.152c1.376 1.18 3.067 1.455 4.837 1.377.157 0 .315 0 .354-.118.04-.197-.157-.197-.275-.236-.826-.354-1.691-.63-2.438-1.14S6.848 8.25 6.534 7.266c-.236-.747.078-1.415.825-1.651.669-.236 1.337-.236 1.967 0 .393.157.55.078.629-.354.118-.747.354-1.455.826-2.085.55-.786.55-.865-.354-1.376-.04 0-.04-.04-.079-.04-.865-.471-1.534-.196-1.848.709-.472 1.376-1.377 1.887-2.832 1.612-.196-.04-.393-.079-.472-.079-.747.118-1.18.55-1.297 1.14-.158 1.81.786 3.107 2.084 4.17zm-2.32 3.658c-.079-.944-1.023-1.652-2.045-1.534-.905.079-1.691 1.022-1.613 1.966.08.983 1.023 1.77 1.967 1.652 1.14-.079 1.73-1.18 1.69-2.084zm15.18-8.298c.943-.079 1.73-.983 1.651-1.927-.078-.983-1.022-1.77-2.005-1.691-1.023.079-1.73.983-1.652 1.966s.983 1.73 2.006 1.652zm-12.27-.826c1.062-.157 1.77-1.023 1.652-2.045C8.107.897 7.163.149 6.18.267c-1.062.118-1.691.944-1.573 2.085.118.865 1.061 1.612 1.966 1.494z"/></svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@@ -0,0 +1 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Android</title><path d="M18.4395 5.5586c-.675 1.1664-1.352 2.3318-2.0274 3.498-.0366-.0155-.0742-.0286-.1113-.043-1.8249-.6957-3.484-.8-4.42-.787-1.8551.0185-3.3544.4643-4.2597.8203-.084-.1494-1.7526-3.021-2.0215-3.4864a1.1451 1.1451 0 0 0-.1406-.1914c-.3312-.364-.9054-.4859-1.379-.203-.475.282-.7136.9361-.3886 1.5019 1.9466 3.3696-.0966-.2158 1.9473 3.3593.0172.031-.4946.2642-1.3926 1.0177C2.8987 12.176.452 14.772 0 18.9902h24c-.119-1.1108-.3686-2.099-.7461-3.0683-.7438-1.9118-1.8435-3.2928-2.7402-4.1836a12.1048 12.1048 0 0 0-2.1309-1.6875c.6594-1.122 1.312-2.2559 1.9649-3.3848.2077-.3615.1886-.7956-.0079-1.1191a1.1001 1.1001 0 0 0-.8515-.5332c-.5225-.0536-.9392.3128-1.0488.5449zm-.0391 8.461c.3944.5926.324 1.3306-.1563 1.6503-.4799.3197-1.188.0985-1.582-.4941-.3944-.5927-.324-1.3307.1563-1.6504.4727-.315 1.1812-.1086 1.582.4941zM7.207 13.5273c.4803.3197.5506 1.0577.1563 1.6504-.394.5926-1.1038.8138-1.584.4941-.48-.3197-.5503-1.0577-.1563-1.6504.4008-.6021 1.1087-.8106 1.584-.4941z"/></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Asahi Linux</title><path d="m13.835 0-1.72 1.323v.97h2.178zm-1.95.057L9.81 1.095l2.076 4.153zm.23 3.768V6.22l-1.057-2.113L6.43 5.678 12 8.009l5.57-2.331zM6.21 5.835.533 15.957 11.885 24V8.21L6.222 5.84Zm11.58 0-.012.004-5.6 2.345 7.512 10.449 3.777-2.675zm-3.955 7.926v5.422l1.952-2.711zm2.864 3.981-4.411 6.135 5.846-4.14z"/></svg>

After

Width:  |  Height:  |  Size: 410 B

View File

@@ -0,0 +1 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Google Chrome</title><path d="M12 0C8.21 0 4.831 1.757 2.632 4.501l3.953 6.848A5.454 5.454 0 0 1 12 6.545h10.691A12 12 0 0 0 12 0zM1.931 5.47A11.943 11.943 0 0 0 0 12c0 6.012 4.42 10.991 10.189 11.864l3.953-6.847a5.45 5.45 0 0 1-6.865-2.29zm13.342 2.166a5.446 5.446 0 0 1 1.45 7.09l.002.001h-.002l-5.344 9.257c.206.01.413.016.621.016 6.627 0 12-5.373 12-12 0-1.54-.29-3.011-.818-4.364zM12 16.364a4.364 4.364 0 1 1 0-8.728 4.364 4.364 0 0 1 0 8.728Z"/></svg>

After

Width:  |  Height:  |  Size: 535 B

View File

@@ -0,0 +1 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>elementary</title><path d="M12 0a12 12 0 1 0 0 24 12 12 0 0 0 0-24zm0 1a11 11 0 0 1 10.59 8.01 19.09 19.09 0 0 1-4.66 6.08c-.94.81-1.96 1.53-3.08 2.04-1.13.5-2.37.8-3.6.72a6.23 6.23 0 0 1-2.66-.76 20.02 20.02 0 0 0 5.68-4.58 9.97 9.97 0 0 0 2.31-4.17c.18-.79.2-1.6.04-2.4a4.42 4.42 0 0 0-1.08-2.11 4.33 4.33 0 0 0-2-1.19 5.25 5.25 0 0 0-2.33-.08A7.8 7.8 0 0 0 7.2 4.85a9.77 9.77 0 0 0-2.94 7.49 7.88 7.88 0 0 0 1.95 4.59 18 18 0 0 1-3.56.85A11 11 0 0 1 12 1zm.07 2.22c.77 0 1.55.24 2.17.7.55.42.97 1.02 1.2 1.68.23.65.3 1.37.21 2.06a7.85 7.85 0 0 1-1.7 3.76 16.22 16.22 0 0 1-6.37 4.96c-.48-.42-.9-.92-1.2-1.48a6.61 6.61 0 0 1-.75-3.87c.12-1.32.58-2.6 1.2-3.79a7.92 7.92 0 0 1 3.02-3.42c.68-.37 1.45-.6 2.22-.6zm10.83 7.3A11 11 0 0 1 3.52 19a19.8 19.8 0 0 0 3.63-1.2c.51.4 1.08.71 1.67.94a8 8 0 0 0 5.44-.04 13.3 13.3 0 0 0 4.64-2.95 20 20 0 0 0 4-5.22z"/></svg>

After

Width:  |  Height:  |  Size: 940 B

View File

@@ -0,0 +1 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>FreeNAS</title><path d="M19.598 2.707h.143c.06 0 .101.01.124.032a.107.107 0 0 1 .034.083c0 .045-.017.077-.051.097a.307.307 0 0 1-.153.029h-.098v-.241zm0 .391h.09l.214.337h.203l-.242-.356v-.008a.29.29 0 0 0 .161-.081.237.237 0 0 0 .059-.168.254.254 0 0 0-.03-.125.241.241 0 0 0-.08-.082.349.349 0 0 0-.114-.045.645.645 0 0 0-.133-.013h-.303v.879h.175v-.338m-.485-.368a.628.628 0 0 1 .348-.367.688.688 0 0 1 .277-.053.64.64 0 0 1 .625.42.735.735 0 0 1 .049.27.74.74 0 0 1-.049.271.642.642 0 0 1-.348.366.675.675 0 0 1-.277.054.646.646 0 0 1-.624-.421.712.712 0 0 1-.049-.27c0-.095.015-.185.048-.27zm.014.884a.835.835 0 0 0 .273.179.884.884 0 0 0 .338.064c.12 0 .233-.021.339-.064A.83.83 0 0 0 20.598 3a.852.852 0 0 0-.249-.613.815.815 0 0 0-.272-.179c-.105-.043-.218-.064-.339-.064s-.233.021-.338.064a.82.82 0 0 0-.454.45.838.838 0 0 0-.067.342c0 .125.021.239.067.343a.796.796 0 0 0 .181.271m-1.864 12.361a3.268 3.268 0 0 1-.931 1.215 3.203 3.203 0 0 1-2.008.695 3.199 3.199 0 0 1-2.423-1.085 1.989 1.989 0 0 1-.439-.855 2.223 2.223 0 0 1-.06-.519c.002-.854.428-1.71.845-2.362.21-.326.418-.602.575-.794l.208.254.036.046a7.499 7.499 0 0 0 1.126 1.083c.766.597 1.85 1.197 3.126 1.229.012 0 .023.003.035.004a.172.172 0 0 1 .064 0 .16.16 0 0 1 .126.189c-.061.33-.158.628-.28.9zm6.719-7.025a5.339 5.339 0 0 1-.821.905c-.752.664-1.936 1.343-3.649 1.435l-.505.926a.173.173 0 0 1-.299.008l-.581-.954c-.275.051-.984.168-1.808.168-1.376-.03-1.807-.241-2.263-.532l1.538-2.072-3.297-.764 4.136-.795c1.208-2.437 1.583-4.521 1.675-5.157-4.638.514-8.102 1.666-10.329 2.632l-.179.079-.034.014-.249-.241a9.292 9.292 0 0 0-1.459-.985 9.404 9.404 0 0 0-4.516-1.175 8.05 8.05 0 0 0-.894.043c-.491.031-.253.153-.194.203.225.184.544.573.753 1.112.211.541.354 1.27.354 2.254 0 .275-.012.579-.036.896-.195.362-.376.741-.539 1.132C.311 9.227 0 10.479 0 11.767c0 2.291.9 4.378 2.181 6.074l.173.262-1.355 2.7a.063.063 0 0 0 .021.08c.011.007.023.01.035.01a.067.067 0 0 0 .047-.02l2.117-1.863.248.24a10.1 10.1 0 0 0 6.812 2.63c4.516 0 8.342-2.953 9.652-7.032l.288-.124a6.314 6.314 0 0 0 1.132-.555c.684-.424 1.502-1.107 2.045-2.141.362-.687.604-1.534.604-2.576 0-.163-.006-.33-.018-.502"/></svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -0,0 +1 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Gentoo</title><path d="M9.94 0a7.31 7.31 0 00-1.26.116c-4.344.795-7.4 4.555-7.661 7.031-.126 1.215.53 2.125.89 2.526.977 1.085 2.924 1.914 4.175 2.601-1.81 1.543-2.64 2.296-3.457 3.154C1.403 16.712.543 18.125.54 19.138c0 .325-.053 1.365.371 2.187.16.309.613 1.338 1.98 2.109.874.494 2.119.675 3.337.501 3.772-.538 8.823-3.737 12.427-6.716 2.297-1.9 3.977-3.739 4.462-4.644.39-.731.434-2.043.207-2.866-.645-2.337-5.887-7.125-10.172-9.051A7.824 7.824 0 009.94 0zm-.008.068a7.4 7.4 0 013.344.755c3.46 1.7 9.308 6.482 9.739 8.886.534 2.972-9.931 11.017-16.297 12.272-2.47.485-4.576.618-5.537-1.99-.832-2.262.783-3.916 3.16-6.09a92.546 92.546 0 012.96-2.576c.065-.069-5.706-2.059-5.89-4.343C1.221 4.634 4.938.3 9.697.076c.08-.004.157-.007.235-.008zm-.112.52a5.647 5.647 0 00-.506.032c-2.337.245-2.785.547-4.903 2.149-.71.537-2.016 1.844-2.35 3.393-.128.59.024 1.1.448 1.458 1.36 1.144 3.639 2.072 5.509 2.97.547.263.185.74-.698 1.505-2.227 1.928-5.24 4.276-5.45 6.066-.099.842.19 1.988 1.213 2.574 1.195.685 3.676.238 5.333-.379 2.422-.902 5.602-2.892 8.127-4.848 2.625-2.034 5.067-4.617 5.188-5.038.148-.517.133-.996-.154-1.546-.448-.862-1.049-1.503-1.694-2.22-1.732-1.825-3.563-3.43-5.754-4.658C12.694 1.242 11.417.564 9.82.588zm1.075 3.623c.546 0 1.176.173 1.853.5 1.688.817 3.422 2.961-.015 4.195-.935.336-3.9-.824-3.81-2.407.09-1.57.854-2.289 1.972-2.288zm.285 1.367c-.317-.002-.575.079-.694.263-.557.861-.303 1.472.212 1.862.192-.457 2.156.043 2.148.472a.32.32 0 00.055-.032c1.704-1.282-.472-2.557-1.72-2.565z"/></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -0,0 +1 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Manjaro</title><path d="M2.182 0A2.177 2.177 0 0 0 0 2.182v19.636C0 23.027.973 24 2.182 24h4.363V6.545h8.728V0Zm15.273 0v24h4.363A2.177 2.177 0 0 0 24 21.818V2.182A2.177 2.177 0 0 0 21.818 0ZM8.727 8.727V24h6.546V8.727Z"/></svg>

After

Width:  |  Height:  |  Size: 306 B

View File

@@ -0,0 +1 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>NetBSD</title><path d="M22.686 10.681c0-.181.064-.336.193-.464a.634.634 0 0 1 .464-.193c.182 0 .336.065.465.193a.633.633 0 0 1 .192.464.635.635 0 0 1-.192.465.632.632 0 0 1-.465.193.634.634 0 0 1-.464-.193.634.634 0 0 1-.193-.465zm1.206 0a.53.53 0 0 0-.16-.388.53.53 0 0 0-.389-.16.53.53 0 0 0-.388.16.528.528 0 0 0-.161.388.53.53 0 0 0 .16.389.53.53 0 0 0 .39.161.529.529 0 0 0 .388-.161.53.53 0 0 0 .16-.389zm-.344.396-.207-.349h-.117v.349h-.114v-.808h.207c.194 0 .292.074.292.223 0 .104-.053.177-.157.22l.221.365zm-.324-.71v.27l.076.001c.075 0 .126-.01.151-.028.026-.02.04-.056.04-.11 0-.09-.059-.134-.175-.134h-.092m-3.892 3.28c0 .403.014.667.146.82.132.147.344.213.607.213 1.266 0 1.698-1.127 1.698-2.122 0-1.318-.695-2.1-2.02-2.1-.197 0-.336.036-.38.095-.044.058-.051.197-.051.424v2.67zm-1.046-2.319c0-.695-.015-.834-.352-.87l-.139-.015c-.073-.037-.073-.25.015-.257a30.521 30.521 0 0 1 1.96-.065c.6 0 1.2.059 1.706.241.958.344 1.485 1.208 1.485 2.122 0 .981-.468 1.771-1.31 2.188-.497.25-1.097.344-1.85.344-.345 0-.71-.044-.974-.044-.351 0-.724.008-1.141.022-.059-.044-.059-.22 0-.256l.226-.036c.33-.059.374-.11.374-.783v-2.59m-2.405 3.76c-.673 0-1.09-.19-1.244-.277-.139-.161-.234-.688-.234-1.186.051-.095.22-.102.278-.022.146.476.636 1.149 1.258 1.149.542 0 .79-.373.79-.74 0-.592-.555-.943-.994-1.163-.527-.263-1.098-.702-1.105-1.427 0-.827.636-1.398 1.697-1.398.242 0 .542.03.834.118.095.029.161.043.25.058.057.161.13.556.13 1.047-.036.087-.219.095-.285.022-.124-.374-.439-.908-.965-.908-.483 0-.747.315-.747.68 0 .337.3.645.666.835l.483.256c.454.242 1.032.666 1.032 1.471 0 .9-.74 1.486-1.844 1.486m-4.2-1.354c0 .57.072.93.643.93.542 0 .827-.418.827-1.01 0-.637-.366-1.084-1.068-1.084-.403 0-.403.007-.403.3v.864zm0-1.69c0 .19.007.204.387.204.63 0 .863-.402.863-.841 0-.637-.395-.952-.9-.952-.343 0-.35.06-.35.381zm-1.01-.71c0-.74-.015-.82-.322-.857l-.198-.03c-.066-.036-.08-.255.03-.263.555-.036 1.09-.065 1.821-.065.703 0 1.171.08 1.493.27.314.19.505.498.505.93 0 .615-.52.856-.747.915-.073.014-.146.044-.146.08 0 .022.037.044.103.059.578.124 1.068.504 1.075 1.214.007.673-.395 1.069-.856 1.23-.461.16-1.01.183-1.456.183-.263 0-.541-.03-.754-.03-.358 0-.717.008-1.134.022-.058-.044-.058-.234 0-.256l.213-.044c.329-.065.373-.117.373-.775v-2.584M9.038 12.44c-.095 0-.102.007-.102.168v1.097c0 .41 0 .864.512.864.102 0 .22-.051.307-.11.073.022.117.103.102.19-.204.22-.6.425-1.053.425-.607 0-.82-.351-.82-.834v-1.632c0-.154-.007-.168-.139-.168H7.62c-.08-.03-.103-.176-.044-.227.226-.08.431-.213.607-.33.132-.095.315-.248.541-.57.051-.03.183-.022.22.036v.549c0 .139.007.146.139.146h.651c.037.03.059.074.059.14 0 .08-.022.211-.095.256h-.66m-2.627.475c.103 0 .22-.015.3-.066.037-.022.051-.095.051-.168 0-.241-.139-.402-.388-.402-.307 0-.57.292-.57.526 0 .103.102.11.336.11zm-.483.322c-.168 0-.183.015-.183.132 0 .549.351 1.083 1.032 1.083.205 0 .483-.044.68-.38.08-.015.19.043.19.168-.3.622-.84.834-1.28.834-.988 0-1.522-.695-1.522-1.493 0-.922.666-1.64 1.58-1.64.762 0 1.171.491 1.171 1.055 0 .139-.036.241-.263.241H5.927m-1.255.49c0 .476 0 .937.014 1.179-.05.087-.256.168-.431.168-.008 0-.25-.373-.593-.798l-1.69-2.093c-.417-.527-.666-.826-.798-.936-.036.073-.036.197-.036.468v1.42c0 .593.029 1.141.11 1.339.065.154.234.198.424.234l.205.03c.058.058.044.212 0 .255a26.585 26.585 0 0 0-.98-.022c-.271 0-.542.008-.82.022-.044-.043-.059-.197 0-.256l.124-.022c.198-.044.337-.087.403-.241.073-.198.102-.746.102-1.34v-1.858c0-.402 0-.52-.051-.622-.051-.124-.161-.198-.417-.249l-.205-.029c-.051-.059-.044-.234.03-.256.343.014.709.022 1.009.022.249 0 .46-.008.615-.022.073.33.548.885 1.207 1.668l.614.725c.33.38.535.636.703.805.03-.073.03-.198.03-.33v-1.01c0-.592-.03-1.141-.11-1.339-.067-.153-.227-.197-.425-.234l-.198-.029c-.058-.059-.043-.212 0-.256.396.014.68.022.98.022.272 0 .535-.008.82-.022.044.044.059.197 0 .256l-.131.022c-.19.044-.33.088-.396.242-.08.197-.11.746-.11 1.339v1.749M21.537 3.59c-2.848-1.367-5.425-.715-8.306.148-2.902.868-5.482 1.337-8.381.154l.79 1.41.87 1.557.79 1.41c2.309.652 4.22-.194 6.271-1.22 2.463-1.23 4.688-2.337 7.502-1.696-2.378-1.19-4.534-.895-7.02-.22 2.434-1.24 4.726-2.204 7.484-1.543M13.16 20.478l-2.272-4.385H9.91l2.283 4.826s.23.455.724.203c.493-.25.245-.644.245-.644M4.634 4.025s-.068-.159-.26-.053c-.16.089-.077.253-.077.253l3.004 6.351h.728L4.634 4.025"/></svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -0,0 +1 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>openSUSE</title><path d="M10.724 0a12 12 0 0 0-9.448 4.623c1.464.391 2.5.727 2.81.832.005-.19.037-1.893.037-1.893s.004-.04.025-.06c.026-.026.065-.018.065-.018.385.056 8.602 1.274 12.066 3.292.427.25.638.517.902.786.958.99 2.223 5.108 2.359 5.957.005.033-.036.07-.054.083a5.177 5.177 0 0 1-.313.228c-.82.55-2.708 1.872-5.13 1.656-2.176-.193-5.018-1.44-8.445-3.699.336.79.668 1.58 1 2.371.497.258 5.287 2.7 7.651 2.651 1.904-.04 3.941-.968 4.756-1.458 0 0 .179-.108.257-.048.085.066.061.167.041.27-.05.234-.164.66-.242.863l-.065.165c-.093.25-.183.482-.356.625-.48.436-1.246.784-2.446 1.305-1.855.812-4.865 1.328-7.66 1.31-1.001-.022-1.968-.133-2.817-.232-1.743-.197-3.161-.357-4.026.269A12 12 0 0 0 10.724 24a12 12 0 0 0 12-12 12 12 0 0 0-12-12zM13.4 6.963a3.503 3.503 0 0 0-2.521.942 3.498 3.498 0 0 0-1.114 2.449 3.528 3.528 0 0 0 3.39 3.64 3.48 3.48 0 0 0 2.524-.946 3.504 3.504 0 0 0 1.114-2.446 3.527 3.527 0 0 0-3.393-3.64zm-.03 1.035a2.458 2.458 0 0 1 2.368 2.539 2.43 2.43 0 0 1-.774 1.706 2.456 2.456 0 0 1-1.762.659 2.461 2.461 0 0 1-2.364-2.542c.02-.655.3-1.26.777-1.707a2.419 2.419 0 0 1 1.756-.655zm.402 1.23c-.602 0-1.087.325-1.087.727 0 .4.485.725 1.087.725.6 0 1.088-.326 1.088-.725 0-.402-.487-.726-1.088-.726Z"/></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1 @@
./parrotsecurity.svg

View File

@@ -0,0 +1 @@
./parrotsecurity.svg

View File

@@ -0,0 +1 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Parrot Security</title><path d="M12 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0Zm6.267 2.784L13.03 5.54l8.05-.179-8.05 3.333-2.154 2.688 5.007 9.038-1.536-1.605 1.645 3.456-4.937-5.527-6.268-6.28L2.77 12.11l.7-3.442 4.018-.261.823-4.06Z"/></svg>

After

Width:  |  Height:  |  Size: 351 B

View File

@@ -0,0 +1 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>pfSense</title><path d="M2.013 0C.898 0 0 .929 0 2.044v17.775L3.252 8.27h3.282L6.1 9.785h.063c.186-.217.433-.403.742-.62.31-.216.62-.402.96-.588.342-.186.713-.31 1.116-.433.402-.124.805-.155 1.208-.155.867 0 1.579.154 2.198.433.62.279 1.084.712 1.455 1.239.31.464.5 1.019.593 1.669.006.06.027.135.027.189.062.712-.031 1.518-.28 2.385a8.571 8.571 0 0 1-1.02 2.322 9.885 9.885 0 0 1-1.58 1.95 8.125 8.125 0 0 1-2.044 1.364 5.536 5.536 0 0 1-2.354.495 5.655 5.655 0 0 1-1.982-.34c-.588-.217-.99-.62-1.238-1.177h-.062L2.353 24h19.603A2.042 2.042 0 0 0 24 21.956V4.706c-.093-.03-.186-.06-.248-.092a2.771 2.771 0 0 0-.557-.062c-.557 0-1.022.124-1.394.372-.34.248-.65.743-.867 1.518l-.526 1.826h2.013l.495 1.58-1.3 1.27h-2.014l-2.446 8.67h-3.53l2.446-8.67h-1.455l.805-2.85h1.425l.557-2.044c.185-.619.403-1.238.681-1.795a4.996 4.996 0 0 1 1.053-1.487c.433-.434.99-.775 1.641-1.022.65-.248 1.487-.372 2.447-.372.248 0 .464 0 .712.031A2.082 2.082 0 0 0 21.988 0zm6.565 11.118c-.898 0-1.672.278-2.323.805-.65.526-1.083 1.239-1.331 2.106-.248.867-.217 1.579.155 2.105.31.557.929.805 1.858.805.898 0 1.672-.278 2.322-.805.65-.526 1.115-1.238 1.363-2.105.247-.867.185-1.58-.155-2.106-.34-.527-.991-.805-1.89-.805Z"/></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1 @@
proxmox.svg

View File

@@ -0,0 +1 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>ReactOS</title><path d="M20.21 24c-1.148-.007-2.477-.334-3.89-.915-2.823-1.163-6.04-3.372-9.036-6.367C4.289 13.723 2.075 10.505.913 7.68-.25 4.857-.383 2.36.988.989 2.358-.38 4.855-.248 7.679.915c.306.125.617.265.932.415-.331.113-.658.24-.974.383l-.141-.058C4.832.558 2.698.519 1.607 1.609.517 2.7.557 4.83 1.653 7.494c1.097 2.663 3.235 5.793 6.147 8.704 2.91 2.911 6.044 5.05 8.708 6.147 2.664 1.097 4.79 1.136 5.88.045 1.091-1.09 1.056-3.22-.041-5.884-.108-.263-.23-.531-.358-.803.134-.317.25-.642.354-.973.282.54.53 1.07.744 1.589 1.163 2.823 1.292 5.32-.079 6.691-.685.685-1.651.997-2.799.99zM3.79 24c-1.148.008-2.117-.305-2.802-.99-1.37-1.37-1.238-3.868-.075-6.691.235-.572.517-1.16.836-1.76.098.333.212.66.34.978a17.67 17.67 0 00-.436.969C.556 19.169.521 21.3 1.611 22.39c1.091 1.091 3.221 1.051 5.885-.045.922-.38 3.021-1.69 4.026-2.308.216.162.433.32.649.474-1.157.733-3.415 2.13-4.492 2.574-1.412.581-2.74.907-3.888.915zm9.753-4.458c-.214-.14-.429-.282-.645-.433a34.547 34.547 0 003.302-2.911c2.912-2.911 5.05-6.04 6.147-8.704 1.097-2.664 1.132-4.794.042-5.885-1.091-1.09-3.217-1.055-5.88.042l-.072.029a10.726 10.726 0 00-.99-.379c.295-.14.587-.272.874-.39 2.824-1.163 5.321-1.292 6.691.078s1.238 3.864.075 6.688c-1.162 2.823-3.376 6.046-6.37 9.04a35.747 35.747 0 01-3.174 2.825zm1.95 1.156c-.325-.17-1.798-1.073-2.135-1.273 1.002-.806 2.423-1.97 3.396-2.944 1.718-1.718 3.981-4.787 5.162-6.555-.008.111-.093 2.49-.105 2.6a9.802 9.802 0 01-6.318 8.172zm-6.928-.034c-3.407-1.308-6.043-4.71-6.287-8.198-.01-.151-.06-.399-.054-.984.007-.602.056-1.423.159-1.283 1.036 1.42 3.976 5.455 5.352 6.83.973.973 1.927 1.624 2.929 2.43a112.45 112.45 0 01-2.1 1.205zm3.43-2.208a33.27 33.27 0 01-3.443-3.01c-2.54-2.54-4.462-5.254-5.568-7.582 1.45-3.597 4.973-6.138 9.087-6.138 4.051 0 7.53 2.465 9.02 5.976-1.093 2.363-3.045 5.145-5.643 7.743a33.161 33.161 0 01-3.452 3.011z"/></svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -0,0 +1 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Slackware</title><path d="M12.924 1.228c-.584-.01-1.251 0-1.485.027-2.46.282-4.138 1.3-4.753 2.891-.218.552-.274 1.002-.243 1.772.048 1.21.419 2.004 1.262 2.742 1.225 1.06 2.98 1.508 5.998 1.508 2.737 0 3.71.413 3.916 1.675.313 1.867-1.57 3.07-4.414 2.827-1.878-.16-3.496-.912-4.223-1.967a7.772 7.772 0 01-.355-.62c-.382-.76-.64-.978-1.176-.978-.43.005-.732.165-.918.494l-.133.24v4.03l.137.296c.165.344.4.546.744.63.35.09.794-.036 1.42-.402l.5-.29.826.185c1.82.403 2.75.523 4.065.523 1.103.005 1.548-.046 2.455-.285 1.124-.297 1.974-.785 2.717-1.57.8-.844 1.15-1.853 1.097-3.147-.069-1.628-.695-2.698-2-3.414-.96-.525-2.292-.79-4.377-.88-2.042-.086-2.794-.155-3.515-.32-.51-.12-.785-.25-1.076-.515-.653-.589-.59-1.755.136-2.482.642-.637 1.511-.928 2.774-.928 1.432.005 2.393.27 3.412.955.185.127.721.62 1.193 1.092.886.902 1.135 1.082 1.506 1.082.244 0 .59-.163.732-.344.26-.329.303-.63.303-2.2 0-1.66-.043-1.91-.377-2.282-.387-.425-.848-.42-1.75.031l-.59.297-.63-.17c-1.496-.392-2.038-.477-3.178-.504zM0 13.775v9h24v-1H1v-8z"/></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Solus</title><path d="M7.453 0c-.18.587-.369 1.167-.565 1.75A11.638 11.638 0 0 0 0 12.364a11.638 11.638 0 0 0 .516 3.403l-.339.598L0 16.73l.279.143a3.448 3.448 0 0 0 .741.222A11.638 11.638 0 0 0 2 18.868c4.034.343 8.55.512 12.446-.056 3.192-.463 5.94-1.423 7.735-3.117.252-.233.474-.474.674-.722.019-.038.037-.053.06-.076.011 0 .026-.037.038-.052.015 0 .03-.038.041-.057.008 0 .015-.038.023-.038.33-.444.587-.892.801-1.31l.181-.365-.365-.365a5.936 5.936 0 0 0-.361-.35A11.638 11.638 0 0 0 11.635.722a11.638 11.638 0 0 0-3.211.463C7.96.508 7.596.041 7.453 0zm.365 1.637C9.06 3.82 10.13 5.06 11.454 7.457c.132 1.524.67 9.45.727 10.181-.392-.037-2.485-.24-5.104-.515-1.43-.147-2.899-.316-4.092-.49l-1.9-.447c2.149-3.787 5.551-9.727 6.737-14.548zm4.543 6.18s4.991 3.927 7.092 8.73c-2.56 1.26-4.916 1.098-6.361 1.09 1.023-2.634 1.023-6.21-.73-9.82zm3.456 2.184a45.14 45.14 0 0 1 2.91.907c1.768.629 3.417 1.49 4.365 2.364a6.956 6.956 0 0 1-2.91 2.91c.151-1.495-.39-2.933-1.456-4.002-.787-.787-1.822-1.453-2.91-2.183zm6.707 6.478c-2.352 1.667-5.126 2.68-7.965 3.112a41.026 41.026 0 0 1-3.715.34h-.323a53.48 53.48 0 0 1-3.727 0 85.763 85.763 0 0 1-4.178-.23h-.003c2.555 3.255 6.993 4.893 11.092 4.102a11.367 11.367 0 0 0 4.498-1.852 11.638 11.638 0 0 0 .007 0c.312-.214.614-.444.903-.685a11.638 11.638 0 0 0 .038-.037 11.555 11.555 0 0 0 3.376-4.762zM2.511 19.584a11.638 11.638 0 0 0 .023.038c-.008 0-.015-.038-.023-.038z"/></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>TrueNAS</title><path d="M24 10.049v5.114l-10.949 6.324v-5.114L24 10.049zm-24 0v5.114l10.956 6.324v-5.114L0 10.049zm12.004-.605l-4.433 2.559 4.433 2.559 4.429-2.559-4.429-2.559zm10.952-1.207l-9.905-5.723v5.118l5.473 3.164 4.432-2.559zm-12-.605V2.513L1.044 8.236l4.432 2.555 5.48-3.159z"/></svg>

After

Width:  |  Height:  |  Size: 371 B

View File

@@ -0,0 +1 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Zorin</title><path d="M4 18.944L5.995 22.4h12.01L20 18.944H4zM24 12l-2.013 3.488H9.216l12.771-6.976L24 12zM0 12l2.013-3.488h12.771L2.013 15.488 0 12zm4-6.944L5.995 1.6h12.01L20 5.056H4z"/></svg>

After

Width:  |  Height:  |  Size: 272 B

View File

@@ -76,9 +76,10 @@ fun CreateImgDialog(
val fullFileName = if (fileName.isNotBlank()) "$fileName.img" else ""
val fileExists = fullFileName.isNotEmpty() && existingFiles.contains(fullFileName)
val hasInvalidChars = fileName.any { !it.isLetterOrDigit() && it !in "-_. ()[]+," }
val isValidInput = fileName.isNotBlank() &&
!fileName.contains("/") &&
!hasInvalidChars &&
sizeValue.toLongOrNull()?.let { it > 0 } == true &&
!fileExists
@@ -135,11 +136,11 @@ fun CreateImgDialog(
// File name input
OutlinedTextField(
value = fileName,
onValueChange = { fileName = it.replace("/", "") },
onValueChange = { fileName = it },
label = { Text("File Name") },
suffix = { Text(".img") },
singleLine = true,
isError = fileExists,
isError = fileExists || hasInvalidChars,
modifier = Modifier.fillMaxWidth()
)
@@ -153,6 +154,16 @@ fun CreateImgDialog(
)
}
// Show error if invalid characters
if (hasInvalidChars) {
Spacer(modifier = Modifier.height(8.dp))
Text(
text = "Invalid file name",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.error
)
}
Spacer(modifier = Modifier.height(16.dp))
// Size input

View File

@@ -134,7 +134,7 @@ fun FileBrowser(
// Parent directory item
if (canNavigateUp) {
item {
FileItem(
FileItemCard(
name = "..",
size = "",
isDirectory = true,
@@ -145,7 +145,7 @@ fun FileBrowser(
}
items(files) { file ->
FileItem(
FileItemCard(
name = file.name,
size = file.formattedSize,
isIso = file.name.lowercase().endsWith(".iso"),
@@ -164,7 +164,7 @@ fun FileBrowser(
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun FileItem(
fun FileItemCard(
name: String,
size: String,
isDirectory: Boolean = false,

View File

@@ -170,7 +170,8 @@ private fun RenameDialog(
val nameWithoutExtension = currentName.removeSuffix(extension)
var newName by remember { mutableStateOf(nameWithoutExtension) }
val isValid = newName.isNotBlank() && !newName.contains("/")
val hasInvalidChars = newName.any { !it.isLetterOrDigit() && it !in "-_. ()[]+," }
val isValid = newName.isNotBlank() && !hasInvalidChars
AlertDialog(
onDismissRequest = onDismiss,
@@ -179,12 +180,22 @@ private fun RenameDialog(
Column {
OutlinedTextField(
value = newName,
onValueChange = { newName = it.replace("/", "") },
onValueChange = { newName = it },
label = { Text("File Name") },
suffix = { Text(extension) },
singleLine = true,
isError = hasInvalidChars,
modifier = Modifier.fillMaxWidth()
)
if (hasInvalidChars) {
Spacer(modifier = Modifier.height(8.dp))
Text(
text = "Invalid file name",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.error
)
}
}
},
confirmButton = {

View File

@@ -6,6 +6,7 @@
package sh.sar.isodroid.ui.components
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@@ -42,9 +43,6 @@ import coil.decode.SvgDecoder
import coil.request.ImageRequest
import sh.sar.isodroid.data.MountStatus
import sh.sar.isodroid.data.MountType
import sh.sar.isodroid.ui.theme.ErrorRed
import sh.sar.isodroid.ui.theme.MountedGreen
import sh.sar.isodroid.ui.theme.UnmountedGray
import java.io.File
@Composable
@@ -52,7 +50,9 @@ fun StatusCard(
mountStatus: MountStatus,
rootAvailable: Boolean?,
deviceSupported: Boolean?,
modifier: Modifier = Modifier
rootDenied: Boolean = false,
modifier: Modifier = Modifier,
onRequestRoot: (() -> Unit)? = null
) {
val context = LocalContext.current
@@ -73,7 +73,15 @@ fun StatusCard(
val isImg = fileName?.lowercase()?.endsWith(".img") == true
Card(
modifier = modifier.fillMaxWidth(),
modifier = modifier
.fillMaxWidth()
.then(
if (showRootError && onRequestRoot != null) {
Modifier.clickable(onClick = onRequestRoot)
} else {
Modifier
}
),
colors = CardDefaults.cardColors(
containerColor = when {
hasError -> MaterialTheme.colorScheme.errorContainer
@@ -101,9 +109,9 @@ fun StatusCard(
},
contentDescription = null,
tint = when {
hasError -> ErrorRed
mountStatus.mounted -> MountedGreen
else -> UnmountedGray
hasError -> MaterialTheme.colorScheme.onErrorContainer
mountStatus.mounted -> MaterialTheme.colorScheme.onPrimaryContainer
else -> MaterialTheme.colorScheme.onSurfaceVariant
},
modifier = Modifier.size(32.dp)
)
@@ -121,7 +129,7 @@ fun StatusCard(
)
if (showRootError) {
Text(
text = "Grant root access to use ISODroid",
text = if (rootDenied) "Grant root access to use ISO Droid" else "Tap to grant root access",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
@@ -190,20 +198,20 @@ fun StatusCard(
text = fileName,
style = MaterialTheme.typography.bodyMedium,
fontWeight = FontWeight.Medium,
color = MaterialTheme.colorScheme.onSurface
color = MaterialTheme.colorScheme.onPrimaryContainer
)
Spacer(modifier = Modifier.height(4.dp))
Row {
Text(
text = if (mountStatus.type == MountType.CDROM) "CD-ROM" else "Mass Storage",
style = MaterialTheme.typography.labelSmall,
color = MaterialTheme.colorScheme.primary
color = MaterialTheme.colorScheme.onPrimaryContainer.copy(alpha = 0.8f)
)
Spacer(modifier = Modifier.width(8.dp))
Text(
text = if (mountStatus.readOnly) "Read-Only" else "Read-Write",
style = MaterialTheme.typography.labelSmall,
color = MaterialTheme.colorScheme.secondary
color = MaterialTheme.colorScheme.onPrimaryContainer.copy(alpha = 0.8f)
)
}
}

View File

@@ -50,6 +50,7 @@ import org.json.JSONArray
data class OsDownload(
val name: String,
val category: String,
val subcategory: String?,
val description: String,
val icon: String?,
val url: String
@@ -67,6 +68,7 @@ private fun loadOsDownloads(context: Context): List<OsDownload> {
OsDownload(
name = obj.getString("name"),
category = obj.getString("category"),
subcategory = if (obj.isNull("subcategory")) null else obj.optString("subcategory", null),
description = obj.optString("description", ""),
icon = if (obj.isNull("icon")) null else obj.optString("icon", null),
url = obj.getString("url")
@@ -88,11 +90,9 @@ fun DownloadsScreen(
val context = LocalContext.current
val downloads = remember { loadOsDownloads(context) }
// Group by category and maintain order: Linux, BSD, Windows, Recovery
val categoryOrder = listOf("Linux", "BSD", "Windows", "Recovery")
// Group by category alphabetically
val groupedDownloads = remember(downloads) {
downloads.groupBy { it.category }
.toSortedMap(compareBy { categoryOrder.indexOf(it).takeIf { i -> i >= 0 } ?: Int.MAX_VALUE })
downloads.groupBy { it.category }.toSortedMap()
}
fun openUrl(url: String) {
@@ -114,7 +114,8 @@ fun DownloadsScreen(
},
colors = TopAppBarDefaults.topAppBarColors(
containerColor = MaterialTheme.colorScheme.primaryContainer,
titleContentColor = MaterialTheme.colorScheme.onPrimaryContainer
titleContentColor = MaterialTheme.colorScheme.onPrimaryContainer,
navigationIconContentColor = MaterialTheme.colorScheme.onPrimaryContainer
)
)
}
@@ -166,22 +167,71 @@ private fun DownloadCategory(
)
) {
Column {
downloads.forEachIndexed { index, os ->
DownloadItem(
os = os,
onClick = { onItemClick(os) }
)
if (index < downloads.lastIndex) {
HorizontalDivider(
modifier = Modifier.padding(horizontal = 16.dp),
color = MaterialTheme.colorScheme.outline.copy(alpha = 0.3f)
// Check if any items have subcategories
val hasSubcategories = downloads.any { it.subcategory != null }
if (hasSubcategories) {
// Group by subcategory alphabetically
val groupedBySubcategory = downloads
.groupBy { it.subcategory ?: "" }
.toSortedMap()
var isFirstGroup = true
groupedBySubcategory.forEach { (subcategory, osList) ->
if (subcategory.isNotEmpty()) {
// Subcategory header
if (!isFirstGroup) {
HorizontalDivider(
modifier = Modifier.padding(horizontal = 16.dp),
color = MaterialTheme.colorScheme.outline.copy(alpha = 0.3f)
)
}
SubcategoryHeader(subcategory)
}
isFirstGroup = false
osList.sortedBy { it.name.lowercase() }.forEachIndexed { index, os ->
DownloadItem(
os = os,
onClick = { onItemClick(os) }
)
if (index < osList.lastIndex) {
HorizontalDivider(
modifier = Modifier.padding(horizontal = 16.dp),
color = MaterialTheme.colorScheme.outline.copy(alpha = 0.15f)
)
}
}
}
} else {
// No subcategories, show flat list
downloads.forEachIndexed { index, os ->
DownloadItem(
os = os,
onClick = { onItemClick(os) }
)
if (index < downloads.lastIndex) {
HorizontalDivider(
modifier = Modifier.padding(horizontal = 16.dp),
color = MaterialTheme.colorScheme.outline.copy(alpha = 0.3f)
)
}
}
}
}
}
}
@Composable
private fun SubcategoryHeader(title: String) {
Text(
text = title,
style = MaterialTheme.typography.labelMedium,
color = MaterialTheme.colorScheme.primary,
modifier = Modifier.padding(horizontal = 16.dp, vertical = 12.dp)
)
}
@Composable
private fun DownloadItem(
os: OsDownload,

View File

@@ -5,18 +5,28 @@
package sh.sar.isodroid.ui.screens
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.pulltorefresh.PullToRefreshContainer
import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Album
import androidx.compose.material.icons.filled.Download
import androidx.compose.material.icons.filled.Eject
import androidx.compose.material.icons.filled.Refresh
import androidx.compose.material.icons.filled.Settings
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExtendedFloatingActionButton
@@ -42,10 +52,9 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.launch
import sh.sar.isodroid.data.IsoFile
import sh.sar.isodroid.data.MountOptions
import sh.sar.isodroid.ui.components.CreateImgDialog
import sh.sar.isodroid.ui.components.FileContextMenu
import sh.sar.isodroid.ui.components.FileBrowser
import sh.sar.isodroid.ui.components.FileItemCard
import sh.sar.isodroid.ui.components.MountDialog
import sh.sar.isodroid.ui.components.StatusCard
import sh.sar.isodroid.viewmodel.MainViewModel
@@ -66,6 +75,22 @@ fun MainScreen(
var showCreateImgDialog by remember { mutableStateOf(false) }
var contextMenuFile by remember { mutableStateOf<IsoFile?>(null) }
val pullToRefreshState = rememberPullToRefreshState()
// Handle pull-to-refresh
if (pullToRefreshState.isRefreshing) {
LaunchedEffect(true) {
viewModel.refresh()
}
}
// Stop refreshing when loading completes
LaunchedEffect(uiState.isLoading) {
if (!uiState.isLoading) {
pullToRefreshState.endRefresh()
}
}
// Show error messages
LaunchedEffect(uiState.errorMessage) {
uiState.errorMessage?.let { message ->
@@ -92,7 +117,8 @@ fun MainScreen(
title = { Text("ISO Droid") },
colors = TopAppBarDefaults.topAppBarColors(
containerColor = MaterialTheme.colorScheme.primaryContainer,
titleContentColor = MaterialTheme.colorScheme.onPrimaryContainer
titleContentColor = MaterialTheme.colorScheme.onPrimaryContainer,
actionIconContentColor = MaterialTheme.colorScheme.onPrimaryContainer
),
actions = {
IconButton(
@@ -104,12 +130,6 @@ fun MainScreen(
contentDescription = "Create IMG"
)
}
IconButton(onClick = { viewModel.refresh() }) {
Icon(
imageVector = Icons.Default.Refresh,
contentDescription = "Refresh"
)
}
IconButton(onClick = onNavigateToDownloads) {
Icon(
imageVector = Icons.Default.Download,
@@ -147,43 +167,118 @@ fun MainScreen(
}
}
) { paddingValues ->
Column(
val hapticFeedback = LocalHapticFeedback.current
Box(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues)
.padding(16.dp)
.nestedScroll(pullToRefreshState.nestedScrollConnection)
) {
StatusCard(
mountStatus = uiState.mountStatus,
rootAvailable = uiState.hasRoot,
deviceSupported = uiState.isSupported
)
Spacer(modifier = Modifier.height(16.dp))
if (uiState.isLoading) {
if (uiState.isLoading && !pullToRefreshState.isRefreshing) {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
CircularProgressIndicator()
}
} else if (uiState.hasRoot == true && uiState.isSupported == true) {
FileBrowser(
files = uiState.isoFiles,
currentPath = uiState.currentPath,
onFileClick = { file ->
selectedFile = file
showMountDialog = true
},
onFileLongClick = { file ->
contextMenuFile = file
},
onNavigateUp = { viewModel.navigateUp() },
canNavigateUp = viewModel.canNavigateUp(),
modifier = Modifier.weight(1f)
)
} else {
LazyColumn(
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(16.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
// Status card
item {
StatusCard(
mountStatus = uiState.mountStatus,
rootAvailable = uiState.hasRoot,
deviceSupported = uiState.isSupported,
rootDenied = uiState.rootDenied,
onRequestRoot = { viewModel.requestRootAccess() }
)
}
// File browser content
if (uiState.hasRoot == true && uiState.isSupported == true) {
if (uiState.isoFiles.isEmpty()) {
// Empty state
item {
Column(
modifier = Modifier
.fillParentMaxSize()
.padding(32.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Icon(
imageVector = Icons.Default.Album,
contentDescription = null,
modifier = Modifier.size(64.dp),
tint = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.5f)
)
Spacer(modifier = Modifier.height(16.dp))
Text(
text = "No ISO/IMG files found",
style = MaterialTheme.typography.bodyLarge,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
Spacer(modifier = Modifier.height(8.dp))
Text(
text = "Place ISO or IMG files in:\n${uiState.currentPath}",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.7f),
textAlign = androidx.compose.ui.text.style.TextAlign.Center
)
Spacer(modifier = Modifier.height(16.dp))
Text(
text = "Tap + to create an empty IMG file\nChange directory in Settings",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.5f),
textAlign = androidx.compose.ui.text.style.TextAlign.Center
)
}
}
} else {
// Parent directory navigation
if (viewModel.canNavigateUp()) {
item {
FileItemCard(
name = "..",
size = "",
isDirectory = true,
onClick = { viewModel.navigateUp() },
onLongClick = null
)
}
}
// File list
items(uiState.isoFiles) { file ->
FileItemCard(
name = file.name,
size = file.formattedSize,
isIso = file.name.lowercase().endsWith(".iso"),
isImg = file.name.lowercase().endsWith(".img"),
onClick = {
selectedFile = file
showMountDialog = true
},
onLongClick = {
hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress)
contextMenuFile = file
}
)
}
}
}
}
}
PullToRefreshContainer(
state = pullToRefreshState,
modifier = Modifier.align(Alignment.TopCenter)
)
}
}

View File

@@ -158,7 +158,8 @@ fun SettingsScreen(
},
colors = TopAppBarDefaults.topAppBarColors(
containerColor = MaterialTheme.colorScheme.primaryContainer,
titleContentColor = MaterialTheme.colorScheme.onPrimaryContainer
titleContentColor = MaterialTheme.colorScheme.onPrimaryContainer,
navigationIconContentColor = MaterialTheme.colorScheme.onPrimaryContainer
)
)
}
@@ -172,11 +173,20 @@ fun SettingsScreen(
// Storage section
SectionHeader(title = "Storage")
val hasRootForStorage = uiState.hasRoot ?: false
SettingsItem(
icon = Icons.Default.Folder,
title = "ISO Directory",
subtitle = uiState.isoDirectory,
onClick = { showPathDialog = true }
enabled = hasRootForStorage,
disabledHint = if (!uiState.rootDenied) "Tap to grant root access" else null,
onClick = {
if (hasRootForStorage) {
showPathDialog = true
} else {
viewModel.requestRootAccess()
}
}
)
HorizontalDivider(modifier = Modifier.padding(horizontal = 16.dp))
@@ -489,6 +499,7 @@ private fun DirectoryBrowserDialog(
scope.launch {
val trimmedName = name.trim()
if (trimmedName.isEmpty()) return@launch
val newPath = "$currentPath/$trimmedName"
RootManager.executeCommand("mkdir -p \"$newPath\"")
// Create marker file to indicate this folder was created by the app
@@ -716,6 +727,9 @@ private fun DirectoryBrowserDialog(
// Create folder dialog
if (showCreateFolderDialog) {
val hasInvalidChars = newFolderName.any { !it.isLetterOrDigit() && it !in "-_. ()[]+," }
val isValidInput = newFolderName.isNotBlank() && !hasInvalidChars
AlertDialog(
onDismissRequest = {
showCreateFolderDialog = false
@@ -723,24 +737,36 @@ private fun DirectoryBrowserDialog(
},
title = { Text("Create Directory") },
text = {
OutlinedTextField(
value = newFolderName,
onValueChange = { newFolderName = it },
label = { Text("Directory name") },
singleLine = true,
modifier = Modifier.fillMaxWidth()
)
Column {
OutlinedTextField(
value = newFolderName,
onValueChange = { newFolderName = it },
label = { Text("Directory name") },
singleLine = true,
isError = hasInvalidChars,
modifier = Modifier.fillMaxWidth()
)
if (hasInvalidChars) {
Spacer(modifier = Modifier.height(8.dp))
Text(
text = "Invalid directory name",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.error
)
}
}
},
confirmButton = {
TextButton(
onClick = {
if (newFolderName.isNotBlank()) {
if (isValidInput) {
createFolder(newFolderName)
showCreateFolderDialog = false
newFolderName = ""
}
},
enabled = newFolderName.isNotBlank()
enabled = isValidInput
) {
Text("Create")
}
@@ -798,8 +824,12 @@ private fun SettingsItem(
icon: ImageVector,
title: String,
subtitle: String,
enabled: Boolean = true,
disabledHint: String? = null,
onClick: () -> Unit
) {
val contentAlpha = if (enabled) 1f else 0.38f
Row(
modifier = Modifier
.fillMaxWidth()
@@ -810,19 +840,28 @@ private fun SettingsItem(
Icon(
imageVector = icon,
contentDescription = null,
tint = MaterialTheme.colorScheme.onSurfaceVariant
tint = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = contentAlpha)
)
Spacer(modifier = Modifier.width(16.dp))
Column(modifier = Modifier.weight(1f)) {
Text(
text = title,
style = MaterialTheme.typography.bodyLarge
style = MaterialTheme.typography.bodyLarge,
color = MaterialTheme.colorScheme.onSurface.copy(alpha = contentAlpha)
)
Text(
text = subtitle,
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = contentAlpha)
)
if (!enabled && disabledHint != null) {
Spacer(modifier = Modifier.height(4.dp))
Text(
text = disabledHint,
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.error.copy(alpha = 0.7f)
)
}
}
}
}

View File

@@ -0,0 +1 @@
* Fix app name displaying as "ISO Drive" instead of "ISO Droid"

View File

@@ -0,0 +1 @@
* Fix default ISO directory path to /sdcard/isodroid

View File

@@ -0,0 +1,6 @@
* Directory browser for changing ISO directory in settings
* Create new directories from the directory browser
* Delete directories created by the app (long press)
* Shows ISO/IMG files with OS icons in directory browser
* Empty state on home screen now shows current path and helpful hints
* Version number now read dynamically from app config

View File

@@ -0,0 +1,7 @@
* More OS download links
* Sub-categories for OS downloads (Desktop, Server, etc.)
* Replaced refresh button with pull-to-refresh gesture
* Disable ISO directory change when root access is not available
* Status card now shows "Tap to request root" and updates when granted
* Improved monet/dynamic theme support for icons and text colors
* Fixed: Sanitize user input for folder creation, IMG file creation, and file renaming