diff --git a/THIRD-PARTY-NOTICES.txt b/THIRD-PARTY-NOTICES.txt index c5715f22..288d1670 100644 --- a/THIRD-PARTY-NOTICES.txt +++ b/THIRD-PARTY-NOTICES.txt @@ -18,6 +18,7 @@ VENDORED THIRD-PARTY SOURCE (inside first-party crates) Vulkan-Headers (vendored, crates/pyrowave-sys) — https://github.com/KhronosGroup/Vulkan-Headers Font Awesome Free brand icons (vendored, assets/os-icons) — https://fontawesome.com Simple Icons (vendored, assets/os-icons) — https://simpleicons.org + Bazzite logo (vendored, assets/os-icons) — https://github.com/ublue-os/bazzite ---------------------------------------------------------------------------- MANIFEST (crate version — SPDX license — source) @@ -2930,6 +2931,25 @@ IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +---------------------------------------------------------------------------- +The following license (bazzite.txt) applies to: Bazzite logo (vendored, assets/os-icons) +---------------------------------------------------------------------------- +Bazzite — the `bazzite` mark in assets/os-icons/ is derived from the Bazzite logo in +the Bazzite source repository (repo_content/Bazzite.svg). + +Copyright (c) Universal Blue (https://github.com/ublue-os/bazzite) + +Licensed under the Apache License, Version 2.0, +https://www.apache.org/licenses/LICENSE-2.0. + +Modifications: the logo's "b" letterform was lifted out of the surrounding badge, the +gradient and decorative overlays were dropped, and the path was translated and scaled +into a 24x24 box with a monochrome fill (fill="currentColor"). + +Brand icons are trademarks of their respective owners and are used for identification +purposes only; their use does not imply endorsement. + + ---------------------------------------------------------------------------- The following license (LICENSE) applies to: bindgen 0.72.1 ---------------------------------------------------------------------------- @@ -6283,8 +6303,8 @@ the following restrictions: ---------------------------------------------------------------------------- The following license (font-awesome-brands.txt) applies to: Font Awesome Free brand icons (vendored, assets/os-icons) ---------------------------------------------------------------------------- -Font Awesome Free — brand icons (windows, apple, linux, steam, ubuntu, fedora, -opensuse in assets/os-icons/) are from Font Awesome Free. +Font Awesome Free — brand icons (apple, linux, steam, ubuntu, fedora, opensuse in +assets/os-icons/) are from Font Awesome Free. Copyright (c) Fonticons, Inc. (https://fontawesome.com) @@ -12997,8 +13017,9 @@ SOFTWARE. ---------------------------------------------------------------------------- The following license (simple-icons.txt) applies to: Simple Icons (vendored, assets/os-icons) ---------------------------------------------------------------------------- -Simple Icons — brand icons (arch, nixos, debian in assets/os-icons/) are from -Simple Icons (https://simpleicons.org, https://github.com/simple-icons/simple-icons). +Simple Icons — brand icons (arch, nixos, debian, cachyos, nobara in assets/os-icons/) +are from Simple Icons +(https://simpleicons.org, https://github.com/simple-icons/simple-icons). The Simple Icons SVG path data is released under CC0 1.0 Universal (public domain dedication), https://creativecommons.org/publicdomain/zero/1.0/ — no attribution diff --git a/assets/os-icons/LICENSES/bazzite.txt b/assets/os-icons/LICENSES/bazzite.txt new file mode 100644 index 00000000..05fd1a8b --- /dev/null +++ b/assets/os-icons/LICENSES/bazzite.txt @@ -0,0 +1,14 @@ +Bazzite — the `bazzite` mark in assets/os-icons/ is derived from the Bazzite logo in +the Bazzite source repository (repo_content/Bazzite.svg). + +Copyright (c) Universal Blue (https://github.com/ublue-os/bazzite) + +Licensed under the Apache License, Version 2.0, +https://www.apache.org/licenses/LICENSE-2.0. + +Modifications: the logo's "b" letterform was lifted out of the surrounding badge, the +gradient and decorative overlays were dropped, and the path was translated and scaled +into a 24x24 box with a monochrome fill (fill="currentColor"). + +Brand icons are trademarks of their respective owners and are used for identification +purposes only; their use does not imply endorsement. diff --git a/assets/os-icons/LICENSES/font-awesome-brands.txt b/assets/os-icons/LICENSES/font-awesome-brands.txt index 5159bcf0..4092ec9d 100644 --- a/assets/os-icons/LICENSES/font-awesome-brands.txt +++ b/assets/os-icons/LICENSES/font-awesome-brands.txt @@ -1,5 +1,5 @@ -Font Awesome Free — brand icons (windows, apple, linux, steam, ubuntu, fedora, -opensuse in assets/os-icons/) are from Font Awesome Free. +Font Awesome Free — brand icons (apple, linux, steam, ubuntu, fedora, opensuse in +assets/os-icons/) are from Font Awesome Free. Copyright (c) Fonticons, Inc. (https://fontawesome.com) diff --git a/assets/os-icons/LICENSES/simple-icons.txt b/assets/os-icons/LICENSES/simple-icons.txt index 4b1cea40..71591e22 100644 --- a/assets/os-icons/LICENSES/simple-icons.txt +++ b/assets/os-icons/LICENSES/simple-icons.txt @@ -1,5 +1,6 @@ -Simple Icons — brand icons (arch, nixos, debian in assets/os-icons/) are from -Simple Icons (https://simpleicons.org, https://github.com/simple-icons/simple-icons). +Simple Icons — brand icons (arch, nixos, debian, cachyos, nobara in assets/os-icons/) +are from Simple Icons +(https://simpleicons.org, https://github.com/simple-icons/simple-icons). The Simple Icons SVG path data is released under CC0 1.0 Universal (public domain dedication), https://creativecommons.org/publicdomain/zero/1.0/ — no attribution diff --git a/assets/os-icons/README.md b/assets/os-icons/README.md index f1e34bd5..e118f08b 100644 --- a/assets/os-icons/README.md +++ b/assets/os-icons/README.md @@ -7,7 +7,7 @@ Android `ImageVector`s). One file per **icon token** of the host's OS-identity c | token | mark | source | |---|---|---| -| `windows` | Windows | Font Awesome Free brands (CC BY 4.0) | +| `windows` | Windows (the current four-pane mark, no perspective skew) | own geometry | | `apple` | Apple (also `macos` via alias) | Font Awesome Free brands (CC BY 4.0) | | `linux` | Tux | Font Awesome Free brands (CC BY 4.0) | | `steam` | Steam (also `steamos` via alias) | Font Awesome Free brands (CC BY 4.0) | @@ -17,14 +17,36 @@ Android `ImageVector`s). One file per **icon token** of the host's OS-identity c | `arch` | Arch Linux | Simple Icons (CC0 1.0) | | `nixos` | NixOS | Simple Icons (CC0 1.0) | | `debian` | Debian | Simple Icons (CC0 1.0) | +| `bazzite` | Bazzite | ublue-os/bazzite (Apache-2.0) | +| `cachyos` | CachyOS | Simple Icons (CC0 1.0) | +| `nobara` | Nobara | Simple Icons (CC0 1.0, slug `nobaralinux`) | -Distros with no file here (Bazzite, CachyOS, Nobara, Pop!_OS, Mint, …) are intentional: -the host advertises the full chain (`linux/fedora/bazzite`), and clients walk it -most-specific-first, so they degrade to the family mark and finally to Tux. +The last three are **distro leaves, not families**: a chain walks most-specific-first, so +`linux/fedora/bazzite` would otherwise draw the Fedora mark. They earn their own art because +"a Bazzite box" and "a Fedora box" are different machines to the person reading the card, and +they are what this project's hosts actually run. Every other distro with no file here (Pop!_OS, +Mint, …) still degrades to its family's mark and finally to Tux — that fallback is the design, +not a gap. -All files are monochrome (`fill="currentColor"`), original per-icon viewBoxes preserved. -Licensing: attribution notices live in `LICENSES/` and are folded into -`THIRD-PARTY-NOTICES.txt` by `scripts/gen-third-party-notices.py`. The marks are -trademarks of their respective owners; they are used here nominatively — to *identify* -the operating system a host runs, the standard practice in this ecosystem — and imply no -affiliation or endorsement. +Windows is the one mark drawn here rather than sourced: every icon set that ships a "Windows" +brand glyph still carries the **Windows 8/10 flag with the perspective skew**, which reads as +dated next to the flat four-pane mark Microsoft has used since Windows 11. Four equal squares +(11.377 + 1.246 gap) is the current proportion. + +All files are monochrome (`fill="currentColor"`), original per-icon viewBoxes preserved. Because +those viewBoxes are not all square, a client must letterbox rather than stretch — see the aspect +note in `clients/android/.../components/OsIcons.kt`. + +## Regenerating the per-client derivatives + +`bash scripts/gen-os-icons.sh [token ...]` turns a master into the three baked forms (GTK +symbolic SVG, Windows PNG, Apple template PDF) and prints the path data for the three clients +that inline it (web console, Decky plugin, Android). Adding a **new** token also means adding it +to each client's shipped-token list — the script prints that checklist too. + +## Licensing + +Attribution notices live in `LICENSES/` and are folded into `THIRD-PARTY-NOTICES.txt` by +`scripts/gen-third-party-notices.py`. The marks are trademarks of their respective owners; they +are used here nominatively — to *identify* the operating system a host runs, the standard +practice in this ecosystem — and imply no affiliation or endorsement. diff --git a/assets/os-icons/bazzite.svg b/assets/os-icons/bazzite.svg new file mode 100644 index 00000000..167605e4 --- /dev/null +++ b/assets/os-icons/bazzite.svg @@ -0,0 +1,2 @@ + + diff --git a/assets/os-icons/cachyos.svg b/assets/os-icons/cachyos.svg new file mode 100644 index 00000000..ad6c25b4 --- /dev/null +++ b/assets/os-icons/cachyos.svg @@ -0,0 +1,2 @@ + + diff --git a/assets/os-icons/nobara.svg b/assets/os-icons/nobara.svg new file mode 100644 index 00000000..2ccda4fc --- /dev/null +++ b/assets/os-icons/nobara.svg @@ -0,0 +1,2 @@ + + diff --git a/assets/os-icons/windows.svg b/assets/os-icons/windows.svg index 3899dc4e..37832cea 100644 --- a/assets/os-icons/windows.svg +++ b/assets/os-icons/windows.svg @@ -1,2 +1,2 @@ - - + + diff --git a/clients/android/app/src/main/kotlin/io/unom/punktfunk/components/OsIcons.kt b/clients/android/app/src/main/kotlin/io/unom/punktfunk/components/OsIcons.kt index 6b81c824..c65aa238 100644 --- a/clients/android/app/src/main/kotlin/io/unom/punktfunk/components/OsIcons.kt +++ b/clients/android/app/src/main/kotlin/io/unom/punktfunk/components/OsIcons.kt @@ -6,6 +6,7 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.PathParser import androidx.compose.ui.unit.dp import io.unom.punktfunk.kit.discovery.osIconTokens +import kotlin.math.max /** * The host card's OS marks, resolved from the host's OS-identity chain (mDNS `os` TXT, @@ -14,18 +15,19 @@ import io.unom.punktfunk.kit.discovery.osIconTokens * ship, so an unknown distro degrades to its family's mark and finally to Tux; null means * "no icon", rendering the card exactly as before the field existed. * - * Path data is vendored from the assets/os-icons masters (Font Awesome Free brands - * CC BY 4.0 + Simple Icons CC0 — provenance in that directory's README); Material ships - * no brand icons. Hand-kept as raw SVG path strings (one line each) rather than - * transcribed ImageVector DSL — [PathParser] builds the vector once, then it's cached. + * Path data is vendored from the assets/os-icons masters (per-mark provenance and licensing + * in that directory's README; `bash scripts/gen-os-icons.sh ` prints a master's + * viewport + path ready to paste); Material ships no brand icons. Hand-kept as raw SVG path + * strings (one line each) rather than transcribed ImageVector DSL — [PathParser] builds the + * vector once, then it's cached. */ private class OsGlyph(val viewportWidth: Float, val viewportHeight: Float, val d: String) private val GLYPHS: Map = mapOf( "windows" to OsGlyph( - viewportWidth = 448f, - viewportHeight = 512f, - d = "M0 93.7l183.6-25.3v177.4H0V93.7zm0 324.6l183.6 25.3V268.4H0v149.9zm203.8 28L448 480V268.4H203.8v177.9zm0-380.6v180.1H448V32L203.8 65.7z", + viewportWidth = 24f, + viewportHeight = 24f, + d = "M0 0h11.377v11.377H0zm12.623 0H24v11.377H12.623zM0 12.623h11.377V24H0zm12.623 0H24V24H12.623z", ), "apple" to OsGlyph( viewportWidth = 384f, @@ -72,8 +74,28 @@ private val GLYPHS: Map = mapOf( viewportHeight = 512f, d = "M471.08 102.66s-.3 18.3-.3 20.3c-9.1-3-74.4-24.1-135.7-26.3-51.9-1.8-122.8-4.3-223 57.3-19.4 12.4-73.9 46.1-99.6 109.7C7 277-.12 307 7 335.06a111 111 0 0 0 16.5 35.7c17.4 25 46.6 41.6 78.1 44.4 44.4 3.9 78.1-16 90-53.3 8.2-25.8 0-63.6-31.5-82.9-25.6-15.7-53.3-12.1-69.2-1.6-13.9 9.2-21.8 23.5-21.6 39.2.3 27.8 24.3 42.6 41.5 42.6a49 49 0 0 0 15.8-2.7c6.5-1.8 13.3-6.5 13.3-14.9 0-12.1-11.6-14.8-16.8-13.9-2.9.5-4.5 2-11.8 2.4-2-.2-12-3.1-12-14V316c.2-12.3 13.2-18 25.5-16.9 32.3 2.8 47.7 40.7 28.5 65.7-18.3 23.7-76.6 23.2-99.7-20.4-26-49.2 12.7-111.2 87-98.4 33.2 5.7 83.6 35.5 102.4 104.3h45.9c-5.7-17.6-8.9-68.3 42.7-68.3 56.7 0 63.9 39.9 79.8 68.3H460c-12.8-18.3-21.7-38.7-18.9-55.8 5.6-33.8 39.7-18.4 82.4-17.4 66.5.4 102.1-27 103.1-28 3.7-3.1 6.5-15.8 7-17.7 1.3-5.1-3.2-2.4-3.2-2.4-8.7 5.2-30.5 15.2-50.9 15.6-25.3.5-76.2-25.4-81.6-28.2-.3-.4.1 1.2-11-25.5 88.4 58.3 118.3 40.5 145.2 21.7.8-.6 4.3-2.9 3.6-5.7-13.8-48.1-22.4-62.7-34.5-69.6-37-21.6-125-34.7-129.2-35.3.1-.1-.9-.3-.9.7zm60.4 72.8a37.54 37.54 0 0 1 38.9-36.3c33.4 1.2 48.8 42.3 24.4 65.2-24.2 22.7-64.4 4.6-63.3-28.9zm38.6-25.3a26.27 26.27 0 1 0 25.4 27.2 26.19 26.19 0 0 0-25.4-27.2zm4.3 28.8c-15.4 0-15.4-15.6 0-15.6s15.4 15.64 0 15.64z", ), + // The gaming distros get their own mark rather than their family's: "a Bazzite box" and + // "a Fedora box" are different machines to the person reading the card. + "bazzite" to OsGlyph( + viewportWidth = 24f, + viewportHeight = 24f, + d = "M7.178 0h3.589v7.178h7.524c3.153 0 5.709 2.556 5.709 5.709 0 6.138-4.976 11.113-11.113 11.113-3.153 0-5.709-2.556-5.709-5.709V10.766H0v-3.589h7.178zm3.589 10.766v7.524c0 1.171.949 2.12 2.12 2.12 4.156 0 7.524-3.369 7.524-7.524 0-1.171-.949-2.12-2.12-2.12z", + ), + "cachyos" to OsGlyph( + viewportWidth = 24f, + viewportHeight = 24f, + d = "M5.301 2.646 0 11.771l5.541 9.583h11.486l2.904-5.017H8.102l-2.56-4.429L8.067 7.54h6.063l2.83-4.893ZM20.058 4.12a.748.748 0 0 0 0 1.496.748.748 0 0 0 0-1.496m-1.983 4.303a1.45 1.45 0 0 0 0 2.9 1.45 1.45 0 0 0 0-2.9m4.02 3.98a1.904 1.904 0 0 0 0 3.809 1.904 1.904 0 0 0 0-3.81", + ), + "nobara" to OsGlyph( + viewportWidth = 24f, + viewportHeight = 24f, + d = "M23.808 11.808v8.281a3.542 3.542 0 0 1-3.542 3.527h-.46a3.543 3.543 0 0 1-3.083-3.513v-7.282l3.543-1.013-3.66-1.045a4.724 4.724 0 0 0-9.33 1.045v2.362a2.362 2.362 0 0 0 2.362 2.362 3.543 3.543 0 0 1 3.543 3.542V24a3.539 3.539 0 0 0-3.542-3.542 3.537 3.537 0 0 0-3.063 1.76 3.54 3.54 0 0 1-2.382 1.398h-.46A3.542 3.542 0 0 1 .192 20.09V3.543a3.542 3.542 0 0 1 6.323-2.194A11.756 11.756 0 0 1 12 0c6.521 0 11.808 5.287 11.808 11.808zm-9.446 0A2.359 2.359 0 0 1 12 14.17a2.362 2.362 0 1 1 2.362-2.362z", + ), ) +/** Longest edge of a built mark, in dp — the box callers size the [Icon] to. */ +private const val GLYPH_DP = 24f + private val built = mutableMapOf() /** The mark for a chain, or null (no icon). Vectors build lazily and cache per token. */ @@ -82,11 +104,18 @@ fun resolveOsIcon(chain: String): ImageVector? = GLYPHS[token]?.let { glyph -> built.getOrPut(token) { glyph.build(token) } } } -private fun OsGlyph.build(token: String): ImageVector = - ImageVector.Builder( +private fun OsGlyph.build(token: String): ImageVector { + // The intrinsic size has to carry the VIEWPORT'S ASPECT RATIO, not a fixed square: + // a VectorPainter maps the viewport onto the default size with independent x and y + // scales, so declaring a 448x512 mark as 24x24 dp stretches it horizontally — which is + // exactly how Tux and the Apple mark used to come out on a phone. Scaling the longest + // edge to GLYPH_DP instead keeps the ratio, and Icon() paints with ContentScale.Fit, so + // the mark letterboxes inside whatever box the caller sized us to. + val longest = max(viewportWidth, viewportHeight) + return ImageVector.Builder( name = "OsIcon.$token", - defaultWidth = 24.dp, - defaultHeight = 24.dp, + defaultWidth = (GLYPH_DP * viewportWidth / longest).dp, + defaultHeight = (GLYPH_DP * viewportHeight / longest).dp, viewportWidth = viewportWidth, viewportHeight = viewportHeight, ).apply { @@ -96,3 +125,4 @@ private fun OsGlyph.build(token: String): ImageVector = fill = SolidColor(Color.Black), ) }.build() +} diff --git a/clients/android/app/src/test/kotlin/io/unom/punktfunk/OsIconsTest.kt b/clients/android/app/src/test/kotlin/io/unom/punktfunk/OsIconsTest.kt new file mode 100644 index 00000000..51f81446 --- /dev/null +++ b/clients/android/app/src/test/kotlin/io/unom/punktfunk/OsIconsTest.kt @@ -0,0 +1,72 @@ +package io.unom.punktfunk + +import io.unom.punktfunk.components.resolveOsIcon +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertNull +import org.junit.Test + +/** + * Pure JVM test of the host card's OS marks (`components/OsIcons.kt`). Run: + * `./gradlew :app:testDebugUnitTest`. + * + * The aspect assertions are the point: a [androidx.compose.ui.graphics.vector.VectorPainter] maps + * the viewport onto the vector's default size with independent x and y scales, so a mark whose + * default size does not carry its viewport's ratio renders STRETCHED — silently, with no crash and + * no warning. That is exactly how Tux and the Apple mark used to look on a phone. + */ +class OsIconsTest { + /** `ImageVector.name` is "OsIcon." — the only handle on which mark got resolved. */ + private fun markOf(chain: String) = resolveOsIcon(chain)?.name?.removePrefix("OsIcon.") + + @Test + fun defaultSizeCarriesTheViewportAspect() { + for (chain in listOf("windows", "linux", "opensuse", "steam", "apple", "bazzite")) { + val v = resolveOsIcon(chain) + assertNotNull("no mark for $chain", v) + v!! + assertEquals( + "$chain default size must keep the viewport ratio", + v.viewportWidth / v.viewportHeight, + v.defaultWidth.value / v.defaultHeight.value, + 0.001f, + ) + assertEquals( + "$chain longest edge must be the 24dp box", + 24f, + maxOf(v.defaultWidth.value, v.defaultHeight.value), + 0.001f, + ) + } + } + + @Test + fun tallMarkIsNarrowerThanItsBox() { + // Tux is 448x512, so a correct build is 21x24dp — 24x24 would be the stretched bug. + val tux = resolveOsIcon("linux")!! + assertEquals(21f, tux.defaultWidth.value, 0.001f) + assertEquals(24f, tux.defaultHeight.value, 0.001f) + } + + @Test + fun gamingDistrosResolveToTheirOwnMark() { + // The whole reason these three ship art: without it they'd draw their family's mark. + assertEquals("bazzite", markOf("linux/fedora/bazzite")) + assertEquals("cachyos", markOf("linux/arch/cachyos")) + assertEquals("nobara", markOf("linux/rhel/nobara")) + } + + @Test + fun unknownDistroStillDegradesThroughItsFamily() { + assertEquals("fedora", markOf("linux/fedora/somethingnew")) + assertEquals("linux", markOf("linux/frontier/chimera")) + assertEquals("steam", markOf("linux/arch/steamos")) // brand alias + assertEquals("apple", markOf("macos")) + } + + @Test + fun noChainMeansNoMark() { + assertNull(resolveOsIcon("")) + assertNull(resolveOsIcon("!!!")) + } +} diff --git a/clients/apple/Package.swift b/clients/apple/Package.swift index 24655d13..9379f13b 100644 --- a/clients/apple/Package.swift +++ b/clients/apple/Package.swift @@ -34,9 +34,10 @@ let package = Package( // Geist (SIL OFL 1.1) — the brand typeface, shared with punktfunk-website. // Registered with Core Text at first use; see BrandFont.swift. .copy("Resources/Fonts"), - // The host cards' OS marks (template vector imagesets derived from the - // assets/os-icons masters — FA brands CC BY 4.0 + Simple Icons CC0, see that - // README). `.process` compiles the catalog; loaded via OsIcon.swift. + // The host cards' OS marks (template vector imagesets generated from the + // assets/os-icons masters by scripts/gen-os-icons.sh — per-mark provenance and + // licensing in that README). `.process` compiles the catalog; loaded via + // OsIcon.swift. .process("Resources/OsIcons.xcassets"), ], linkerSettings: [ diff --git a/clients/apple/Sources/PunktfunkKit/OsIcon.swift b/clients/apple/Sources/PunktfunkKit/OsIcon.swift index b75df6eb..70bc65c9 100644 --- a/clients/apple/Sources/PunktfunkKit/OsIcon.swift +++ b/clients/apple/Sources/PunktfunkKit/OsIcon.swift @@ -1,17 +1,19 @@ // The host cards' OS marks: template vector imagesets in Resources/OsIcons.xcassets -// (derived from the repo's assets/os-icons masters — Font Awesome Free brands CC BY 4.0 + -// Simple Icons CC0; provenance in that directory's README), resolved from the host's +// (generated from the repo's assets/os-icons masters by scripts/gen-os-icons.sh — +// per-mark provenance and licensing in that directory's README), resolved from the host's // OS-identity chain via PunktfunkShared's `osIconTokens` walk. Template rendering means // they tint with `foregroundStyle` like an SF Symbol. import PunktfunkShared import SwiftUI -/// The icon tokens this client ships art for. A distro without its own mark (Bazzite, -/// CachyOS, ...) degrades to its family's and finally to Tux via the chain walk. +/// The icon tokens this client ships art for: the families a chain can land on, plus the +/// gaming distros that earn their own mark because "a Bazzite box" and "a Fedora box" are +/// different machines to the person reading the card. A distro with no mark of its own +/// still degrades to its family's and finally to Tux via the chain walk. private let osIconTokensShipped: Set = [ "windows", "apple", "linux", "steam", "ubuntu", "fedora", "arch", "debian", "nixos", - "opensuse", + "opensuse", "bazzite", "cachyos", "nobara", ] /// The mark for an OS-identity chain (`linux/fedora/bazzite`, ...), or nil — no view at diff --git a/clients/apple/Sources/PunktfunkKit/Resources/OsIcons.xcassets/os-bazzite.imageset/Contents.json b/clients/apple/Sources/PunktfunkKit/Resources/OsIcons.xcassets/os-bazzite.imageset/Contents.json new file mode 100644 index 00000000..13de051b --- /dev/null +++ b/clients/apple/Sources/PunktfunkKit/Resources/OsIcons.xcassets/os-bazzite.imageset/Contents.json @@ -0,0 +1,10 @@ +{ + "images" : [ + { "filename" : "bazzite.pdf", "idiom" : "universal" } + ], + "info" : { "author" : "xcode", "version" : 1 }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/clients/apple/Sources/PunktfunkKit/Resources/OsIcons.xcassets/os-bazzite.imageset/bazzite.pdf b/clients/apple/Sources/PunktfunkKit/Resources/OsIcons.xcassets/os-bazzite.imageset/bazzite.pdf new file mode 100644 index 00000000..d6b499f9 Binary files /dev/null and b/clients/apple/Sources/PunktfunkKit/Resources/OsIcons.xcassets/os-bazzite.imageset/bazzite.pdf differ diff --git a/clients/apple/Sources/PunktfunkKit/Resources/OsIcons.xcassets/os-cachyos.imageset/Contents.json b/clients/apple/Sources/PunktfunkKit/Resources/OsIcons.xcassets/os-cachyos.imageset/Contents.json new file mode 100644 index 00000000..c5444f3f --- /dev/null +++ b/clients/apple/Sources/PunktfunkKit/Resources/OsIcons.xcassets/os-cachyos.imageset/Contents.json @@ -0,0 +1,10 @@ +{ + "images" : [ + { "filename" : "cachyos.pdf", "idiom" : "universal" } + ], + "info" : { "author" : "xcode", "version" : 1 }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/clients/apple/Sources/PunktfunkKit/Resources/OsIcons.xcassets/os-cachyos.imageset/cachyos.pdf b/clients/apple/Sources/PunktfunkKit/Resources/OsIcons.xcassets/os-cachyos.imageset/cachyos.pdf new file mode 100644 index 00000000..56ee16fb Binary files /dev/null and b/clients/apple/Sources/PunktfunkKit/Resources/OsIcons.xcassets/os-cachyos.imageset/cachyos.pdf differ diff --git a/clients/apple/Sources/PunktfunkKit/Resources/OsIcons.xcassets/os-nobara.imageset/Contents.json b/clients/apple/Sources/PunktfunkKit/Resources/OsIcons.xcassets/os-nobara.imageset/Contents.json new file mode 100644 index 00000000..7efd91c5 --- /dev/null +++ b/clients/apple/Sources/PunktfunkKit/Resources/OsIcons.xcassets/os-nobara.imageset/Contents.json @@ -0,0 +1,10 @@ +{ + "images" : [ + { "filename" : "nobara.pdf", "idiom" : "universal" } + ], + "info" : { "author" : "xcode", "version" : 1 }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/clients/apple/Sources/PunktfunkKit/Resources/OsIcons.xcassets/os-nobara.imageset/nobara.pdf b/clients/apple/Sources/PunktfunkKit/Resources/OsIcons.xcassets/os-nobara.imageset/nobara.pdf new file mode 100644 index 00000000..ee677315 Binary files /dev/null and b/clients/apple/Sources/PunktfunkKit/Resources/OsIcons.xcassets/os-nobara.imageset/nobara.pdf differ diff --git a/clients/apple/Sources/PunktfunkKit/Resources/OsIcons.xcassets/os-windows.imageset/windows.pdf b/clients/apple/Sources/PunktfunkKit/Resources/OsIcons.xcassets/os-windows.imageset/windows.pdf index 5511f150..2a6b14e0 100644 Binary files a/clients/apple/Sources/PunktfunkKit/Resources/OsIcons.xcassets/os-windows.imageset/windows.pdf and b/clients/apple/Sources/PunktfunkKit/Resources/OsIcons.xcassets/os-windows.imageset/windows.pdf differ diff --git a/clients/decky/src/os-icon.tsx b/clients/decky/src/os-icon.tsx index 9ad34452..1a151e45 100644 --- a/clients/decky/src/os-icon.tsx +++ b/clients/decky/src/os-icon.tsx @@ -1,40 +1,82 @@ // The host row's OS mark, resolved from the host's OS-identity chain (mDNS `os` TXT / // `--list-hosts` `os`, e.g. "linux/fedora/bazzite"): walk the chain most-specific-first and -// take the first token react-icons has a brand mark for, so an unknown distro degrades to its -// family's mark and finally to Tux. Mirrors pf-client-core's `os_icon_tokens` (aliases -// macos→apple, steamos→steam); null when the chain is absent or entirely unknown — the row -// then renders exactly as it did before the field existed. +// take the first token we ship a mark for, so an unknown distro degrades to its family's +// mark and finally to Tux. Mirrors pf-client-core's `os_icon_tokens` (aliases macos→apple, +// steamos→steam); null when the chain is absent or entirely unknown — the row then renders +// exactly as it did before the field existed. +// +// Path data is transcribed from the assets/os-icons masters rather than pulled from +// react-icons: that package's Font Awesome 5 brand set still carries the perspective-skewed +// Windows flag, and it has no Bazzite or CachyOS mark at all. `bash scripts/gen-os-icons.sh +// ` prints a master's viewBox + path ready to paste here. import { FC } from "react"; -import { - FaApple, - FaFedora, - FaLinux, - FaSteam, - FaSuse, - FaUbuntu, - FaWindows, -} from "react-icons/fa"; -import { SiArchlinux, SiDebian, SiNixos } from "react-icons/si"; -import { IconType } from "react-icons"; -const OS_ICONS: Record = { - windows: FaWindows, - apple: FaApple, - macos: FaApple, - linux: FaLinux, - steam: FaSteam, - steamos: FaSteam, - ubuntu: FaUbuntu, - fedora: FaFedora, - opensuse: FaSuse, - arch: SiArchlinux, - debian: SiDebian, - nixos: SiNixos, +/** One monochrome brand mark: original per-icon viewBox, drawn in currentColor. */ +const OS_ICONS: Record = { + windows: { + viewBox: "0 0 24 24", + d: "M0 0h11.377v11.377H0zm12.623 0H24v11.377H12.623zM0 12.623h11.377V24H0zm12.623 0H24V24H12.623z", + }, + apple: { + viewBox: "0 0 384 512", + d: "M318.7 268.7c-.2-36.7 16.4-64.4 50-84.8-18.8-26.9-47.2-41.7-84.7-44.6-35.5-2.8-74.3 20.7-88.5 20.7-15 0-49.4-19.7-76.4-19.7C63.3 141.2 4 184.8 4 273.5q0 39.3 14.4 81.2c12.8 36.7 59 126.7 107.2 125.2 25.2-.6 43-17.9 75.8-17.9 31.8 0 48.3 17.9 76.4 17.9 48.6-.7 90.4-82.5 102.6-119.3-65.2-30.7-61.7-90-61.7-91.9zm-56.6-164.2c27.3-32.4 24.8-61.9 24-72.5-24.1 1.4-52 16.4-67.9 34.9-17.5 19.8-27.8 44.3-25.6 71.9 26.1 2 49.9-11.4 69.5-34.3z", + }, + linux: { + viewBox: "0 0 448 512", + d: "M220.8 123.3c1 .5 1.8 1.7 3 1.7 1.1 0 2.8-.4 2.9-1.5.2-1.4-1.9-2.3-3.2-2.9-1.7-.7-3.9-1-5.5-.1-.4.2-.8.7-.6 1.1.3 1.3 2.3 1.1 3.4 1.7zm-21.9 1.7c1.2 0 2-1.2 3-1.7 1.1-.6 3.1-.4 3.5-1.6.2-.4-.2-.9-.6-1.1-1.6-.9-3.8-.6-5.5.1-1.3.6-3.4 1.5-3.2 2.9.1 1 1.8 1.5 2.8 1.4zM420 403.8c-3.6-4-5.3-11.6-7.2-19.7-1.8-8.1-3.9-16.8-10.5-22.4-1.3-1.1-2.6-2.1-4-2.9-1.3-.8-2.7-1.5-4.1-2 9.2-27.3 5.6-54.5-3.7-79.1-11.4-30.1-31.3-56.4-46.5-74.4-17.1-21.5-33.7-41.9-33.4-72C311.1 85.4 315.7.1 234.8 0 132.4-.2 158 103.4 156.9 135.2c-1.7 23.4-6.4 41.8-22.5 64.7-18.9 22.5-45.5 58.8-58.1 96.7-6 17.9-8.8 36.1-6.2 53.3-6.5 5.8-11.4 14.7-16.6 20.2-4.2 4.3-10.3 5.9-17 8.3s-14 6-18.5 14.5c-2.1 3.9-2.8 8.1-2.8 12.4 0 3.9.6 7.9 1.2 11.8 1.2 8.1 2.5 15.7.8 20.8-5.2 14.4-5.9 24.4-2.2 31.7 3.8 7.3 11.4 10.5 20.1 12.3 17.3 3.6 40.8 2.7 59.3 12.5 19.8 10.4 39.9 14.1 55.9 10.4 11.6-2.6 21.1-9.6 25.9-20.2 12.5-.1 26.3-5.4 48.3-6.6 14.9-1.2 33.6 5.3 55.1 4.1.6 2.3 1.4 4.6 2.5 6.7v.1c8.3 16.7 23.8 24.3 40.3 23 16.6-1.3 34.1-11 48.3-27.9 13.6-16.4 36-23.2 50.9-32.2 7.4-4.5 13.4-10.1 13.9-18.3.4-8.2-4.4-17.3-15.5-29.7zM223.7 87.3c9.8-22.2 34.2-21.8 44-.4 6.5 14.2 3.6 30.9-4.3 40.4-1.6-.8-5.9-2.6-12.6-4.9 1.1-1.2 3.1-2.7 3.9-4.6 4.8-11.8-.2-27-9.1-27.3-7.3-.5-13.9 10.8-11.8 23-4.1-2-9.4-3.5-13-4.4-1-6.9-.3-14.6 2.9-21.8zM183 75.8c10.1 0 20.8 14.2 19.1 33.5-3.5 1-7.1 2.5-10.2 4.6 1.2-8.9-3.3-20.1-9.6-19.6-8.4.7-9.8 21.2-1.8 28.1 1 .8 1.9-.2-5.9 5.5-15.6-14.6-10.5-52.1 8.4-52.1zm-13.6 60.7c6.2-4.6 13.6-10 14.1-10.5 4.7-4.4 13.5-14.2 27.9-14.2 7.1 0 15.6 2.3 25.9 8.9 6.3 4.1 11.3 4.4 22.6 9.3 8.4 3.5 13.7 9.7 10.5 18.2-2.6 7.1-11 14.4-22.7 18.1-11.1 3.6-19.8 16-38.2 14.9-3.9-.2-7-1-9.6-2.1-8-3.5-12.2-10.4-20-15-8.6-4.8-13.2-10.4-14.7-15.3-1.4-4.9 0-9 4.2-12.3zm3.3 334c-2.7 35.1-43.9 34.4-75.3 18-29.9-15.8-68.6-6.5-76.5-21.9-2.4-4.7-2.4-12.7 2.6-26.4v-.2c2.4-7.6.6-16-.6-23.9-1.2-7.8-1.8-15 .9-20 3.5-6.7 8.5-9.1 14.8-11.3 10.3-3.7 11.8-3.4 19.6-9.9 5.5-5.7 9.5-12.9 14.3-18 5.1-5.5 10-8.1 17.7-6.9 8.1 1.2 15.1 6.8 21.9 16l19.6 35.6c9.5 19.9 43.1 48.4 41 68.9zm-1.4-25.9c-4.1-6.6-9.6-13.6-14.4-19.6 7.1 0 14.2-2.2 16.7-8.9 2.3-6.2 0-14.9-7.4-24.9-13.5-18.2-38.3-32.5-38.3-32.5-13.5-8.4-21.1-18.7-24.6-29.9s-3-23.3-.3-35.2c5.2-22.9 18.6-45.2 27.2-59.2 2.3-1.7.8 3.2-8.7 20.8-8.5 16.1-24.4 53.3-2.6 82.4.6-20.7 5.5-41.8 13.8-61.5 12-27.4 37.3-74.9 39.3-112.7 1.1.8 4.6 3.2 6.2 4.1 4.6 2.7 8.1 6.7 12.6 10.3 12.4 10 28.5 9.2 42.4 1.2 6.2-3.5 11.2-7.5 15.9-9 9.9-3.1 17.8-8.6 22.3-15 7.7 30.4 25.7 74.3 37.2 95.7 6.1 11.4 18.3 35.5 23.6 64.6 3.3-.1 7 .4 10.9 1.4 13.8-35.7-11.7-74.2-23.3-84.9-4.7-4.6-4.9-6.6-2.6-6.5 12.6 11.2 29.2 33.7 35.2 59 2.8 11.6 3.3 23.7.4 35.7 16.4 6.8 35.9 17.9 30.7 34.8-2.2-.1-3.2 0-4.2 0 3.2-10.1-3.9-17.6-22.8-26.1-19.6-8.6-36-8.6-38.3 12.5-12.1 4.2-18.3 14.7-21.4 27.3-2.8 11.2-3.6 24.7-4.4 39.9-.5 7.7-3.6 18-6.8 29-32.1 22.9-76.7 32.9-114.3 7.2zm257.4-11.5c-.9 16.8-41.2 19.9-63.2 46.5-13.2 15.7-29.4 24.4-43.6 25.5s-26.5-4.8-33.7-19.3c-4.7-11.1-2.4-23.1 1.1-36.3 3.7-14.2 9.2-28.8 9.9-40.6.8-15.2 1.7-28.5 4.2-38.7 2.6-10.3 6.6-17.2 13.7-21.1.3-.2.7-.3 1-.5.8 13.2 7.3 26.6 18.8 29.5 12.6 3.3 30.7-7.5 38.4-16.3 9-.3 15.7-.9 22.6 5.1 9.9 8.5 7.1 30.3 17.1 41.6 10.6 11.6 14 19.5 13.7 24.6zM173.3 148.7c2 1.9 4.7 4.5 8 7.1 6.6 5.2 15.8 10.6 27.3 10.6 11.6 0 22.5-5.9 31.8-10.8 4.9-2.6 10.9-7 14.8-10.4s5.9-6.3 3.1-6.6-2.6 2.6-6 5.1c-4.4 3.2-9.7 7.4-13.9 9.8-7.4 4.2-19.5 10.2-29.9 10.2s-18.7-4.8-24.9-9.7c-3.1-2.5-5.7-5-7.7-6.9-1.5-1.4-1.9-4.6-4.3-4.9-1.4-.1-1.8 3.7 1.7 6.5z", + }, + steam: { + viewBox: "0 0 496 512", + d: "M496 256c0 137-111.2 248-248.4 248-113.8 0-209.6-76.3-239-180.4l95.2 39.3c6.4 32.1 34.9 56.4 68.9 56.4 39.2 0 71.9-32.4 70.2-73.5l84.5-60.2c52.1 1.3 95.8-40.9 95.8-93.5 0-51.6-42-93.5-93.7-93.5s-93.7 42-93.7 93.5v1.2L176.6 279c-15.5-.9-30.7 3.4-43.5 12.1L0 236.1C10.2 108.4 117.1 8 247.6 8 384.8 8 496 119 496 256zM155.7 384.3l-30.5-12.6a52.79 52.79 0 0 0 27.2 25.8c26.9 11.2 57.8-1.6 69-28.4 5.4-13 5.5-27.3.1-40.3-5.4-13-15.5-23.2-28.5-28.6-12.9-5.4-26.7-5.2-38.9-.6l31.5 13c19.8 8.2 29.2 30.9 20.9 50.7-8.3 19.9-31 29.2-50.8 21zm173.8-129.9c-34.4 0-62.4-28-62.4-62.3s28-62.3 62.4-62.3 62.4 28 62.4 62.3-27.9 62.3-62.4 62.3zm.1-15.6c25.9 0 46.9-21 46.9-46.8 0-25.9-21-46.8-46.9-46.8s-46.9 21-46.9 46.8c.1 25.8 21.1 46.8 46.9 46.8z", + }, + ubuntu: { + viewBox: "0 0 496 512", + d: "M248 8C111 8 0 119 0 256s111 248 248 248 248-111 248-248S385 8 248 8zm52.7 93c8.8-15.2 28.3-20.5 43.5-11.7 15.3 8.8 20.5 28.3 11.7 43.6-8.8 15.2-28.3 20.5-43.5 11.7-15.3-8.9-20.5-28.4-11.7-43.6zM87.4 287.9c-17.6 0-31.9-14.3-31.9-31.9 0-17.6 14.3-31.9 31.9-31.9 17.6 0 31.9 14.3 31.9 31.9 0 17.6-14.3 31.9-31.9 31.9zm28.1 3.1c22.3-17.9 22.4-51.9 0-69.9 8.6-32.8 29.1-60.7 56.5-79.1l23.7 39.6c-51.5 36.3-51.5 112.5 0 148.8L172 370c-27.4-18.3-47.8-46.3-56.5-79zm228.7 131.7c-15.3 8.8-34.7 3.6-43.5-11.7-8.8-15.3-3.6-34.8 11.7-43.6 15.2-8.8 34.7-3.6 43.5 11.7 8.8 15.3 3.6 34.8-11.7 43.6zm.3-69.5c-26.7-10.3-56.1 6.6-60.5 35-5.2 1.4-48.9 14.3-96.7-9.4l22.5-40.3c57 26.5 123.4-11.7 128.9-74.4l46.1.7c-2.3 34.5-17.3 65.5-40.3 88.4zm-5.9-105.3c-5.4-62-71.3-101.2-128.9-74.4l-22.5-40.3c47.9-23.7 91.5-10.8 96.7-9.4 4.4 28.3 33.8 45.3 60.5 35 23.1 22.9 38 53.9 40.2 88.5l-46 .6z", + }, + fedora: { + viewBox: "0 0 448 512", + d: "M225 32C101.3 31.7.8 131.7.4 255.4L0 425.7a53.6 53.6 0 0 0 53.6 53.9l170.2.4c123.7.3 224.3-99.7 224.6-223.4S348.7 32.3 225 32zm169.8 157.2L333 126.6c2.3-4.7 3.8-9.2 3.8-14.3v-1.6l55.2 56.1a101 101 0 0 1 2.8 22.4zM331 94.3a106.06 106.06 0 0 1 58.5 63.8l-54.3-54.6a26.48 26.48 0 0 0-4.2-9.2zM118.1 247.2a49.66 49.66 0 0 0-7.7 11.4l-8.5-8.5a85.78 85.78 0 0 1 16.2-2.9zM97 251.4l11.8 11.9-.9 8a34.74 34.74 0 0 0 2.4 12.5l-27-27.2a80.6 80.6 0 0 1 13.7-5.2zm-18.2 7.4l38.2 38.4a53.17 53.17 0 0 0-14.1 4.7L67.6 266a107 107 0 0 1 11.2-7.2zm-15.2 9.8l35.3 35.5a67.25 67.25 0 0 0-10.5 8.5L53.5 278a64.33 64.33 0 0 1 10.1-9.4zm-13.3 12.3l34.9 35a56.84 56.84 0 0 0-7.7 11.4l-35.8-35.9c2.8-3.8 5.7-7.2 8.6-10.5zm-11 14.3l36.4 36.6a48.29 48.29 0 0 0-3.6 15.2l-39.5-39.8a99.81 99.81 0 0 1 6.7-12zm-8.8 16.3l41.3 41.8a63.47 63.47 0 0 0 6.7 26.2L25.8 326c1.4-4.9 2.9-9.6 4.7-14.5zm-7.9 43l61.9 62.2a31.24 31.24 0 0 0-3.6 14.3v1.1l-55.4-55.7a88.27 88.27 0 0 1-2.9-21.9zm5.3 30.7l54.3 54.6a28.44 28.44 0 0 0 4.2 9.2 106.32 106.32 0 0 1-58.5-63.8zm-5.3-37a80.69 80.69 0 0 1 2.1-17l72.2 72.5a37.59 37.59 0 0 0-9.9 8.7zm253.3-51.8l-42.6-.1-.1 56c-.2 69.3-64.4 115.8-125.7 102.9-5.7 0-19.9-8.7-19.9-24.2a24.89 24.89 0 0 1 24.5-24.6c6.3 0 6.3 1.6 15.7 1.6a55.91 55.91 0 0 0 56.1-55.9l.1-47c0-4.5-4.5-9-8.9-9l-33.6-.1c-32.6-.1-32.5-49.4.1-49.3l42.6.1.1-56a105.18 105.18 0 0 1 105.6-105 86.35 86.35 0 0 1 20.2 2.3c11.2 1.8 19.9 11.9 19.9 24 0 15.5-14.9 27.8-30.3 23.9-27.4-5.9-65.9 14.4-66 54.9l-.1 47a8.94 8.94 0 0 0 8.9 9l33.6.1c32.5.2 32.4 49.5-.2 49.4zm23.5-.3a35.58 35.58 0 0 0 7.6-11.4l8.5 8.5a102 102 0 0 1-16.1 2.9zm21-4.2L308.6 280l.9-8.1a34.74 34.74 0 0 0-2.4-12.5l27 27.2a74.89 74.89 0 0 1-13.7 5.3zm18-7.4l-38-38.4c4.9-1.1 9.6-2.4 13.7-4.7l36.2 35.9c-3.8 2.5-7.9 5-11.9 7.2zm15.5-9.8l-35.3-35.5a61.06 61.06 0 0 0 10.5-8.5l34.9 35a124.56 124.56 0 0 1-10.1 9zm13.2-12.3l-34.9-35a63.18 63.18 0 0 0 7.7-11.4l35.8 35.9a130.28 130.28 0 0 1-8.6 10.5zm11-14.3l-36.4-36.6a48.29 48.29 0 0 0 3.6-15.2l39.5 39.8a87.72 87.72 0 0 1-6.7 12zm13.5-30.9a140.63 140.63 0 0 1-4.7 14.3L345.6 190a58.19 58.19 0 0 0-7.1-26.2zm1-5.6l-71.9-72.1a32 32 0 0 0 9.9-9.2l64.3 64.7a90.93 90.93 0 0 1-2.3 16.6z", + }, + arch: { + viewBox: "0 0 24 24", + d: "M11.39.605C10.376 3.092 9.764 4.72 8.635 7.132c.693.734 1.543 1.589 2.923 2.554-1.484-.61-2.496-1.224-3.252-1.86C6.86 10.842 4.596 15.138 0 23.395c3.612-2.085 6.412-3.37 9.021-3.862a6.61 6.61 0 01-.171-1.547l.003-.115c.058-2.315 1.261-4.095 2.687-3.973 1.426.12 2.534 2.096 2.478 4.409a6.52 6.52 0 01-.146 1.243c2.58.505 5.352 1.787 8.914 3.844-.702-1.293-1.33-2.459-1.929-3.57-.943-.73-1.926-1.682-3.933-2.713 1.38.359 2.367.772 3.137 1.234-6.09-11.334-6.582-12.84-8.67-17.74zM22.898 21.36v-.623h-.234v-.084h.562v.084h-.234v.623h.331v-.707h.142l.167.5.034.107a2.26 2.26 0 01.038-.114l.17-.493H24v.707h-.091v-.593l-.206.593h-.084l-.205-.602v.602h-.091", + }, + debian: { + viewBox: "0 0 24 24", + d: "M13.88 12.685c-.4 0 .08.2.601.28.14-.1.27-.22.39-.33a3.001 3.001 0 01-.99.05m2.14-.53c.23-.33.4-.69.47-1.06-.06.27-.2.5-.33.73-.75.47-.07-.27 0-.56-.8 1.01-.11.6-.14.89m.781-2.05c.05-.721-.14-.501-.2-.221.07.04.13.5.2.22M12.38.31c.2.04.45.07.42.12.23-.05.28-.1-.43-.12m.43.12l-.15.03.14-.01V.43m6.633 9.944c.02.64-.2.95-.38 1.5l-.35.181c-.28.54.03.35-.17.78-.44.39-1.34 1.22-1.62 1.301-.201 0 .14-.25.19-.34-.591.4-.481.6-1.371.85l-.03-.06c-2.221 1.04-5.303-1.02-5.253-3.842-.03.17-.07.13-.12.2a3.551 3.552 0 012.001-3.501 3.361 3.362 0 013.732.48 3.341 3.342 0 00-2.721-1.3c-1.18.01-2.281.76-2.651 1.57-.6.38-.67 1.47-.93 1.661-.361 2.601.66 3.722 2.38 5.042.27.19.08.21.12.35a4.702 4.702 0 01-1.53-1.16c.23.33.47.66.8.91-.55-.18-1.27-1.3-1.48-1.35.93 1.66 3.78 2.921 5.261 2.3a6.203 6.203 0 01-2.33-.28c-.33-.16-.77-.51-.7-.57a5.802 5.803 0 005.902-.84c.44-.35.93-.94 1.07-.95-.2.32.04.16-.12.44.44-.72-.2-.3.46-1.24l.24.33c-.09-.6.74-1.321.66-2.262.19-.3.2.3 0 .97.29-.74.08-.85.15-1.46.08.2.18.42.23.63-.18-.7.2-1.2.28-1.6-.09-.05-.28.3-.32-.53 0-.37.1-.2.14-.28-.08-.05-.26-.32-.38-.861.08-.13.22.33.34.34-.08-.42-.2-.75-.2-1.08-.34-.68-.12.1-.4-.3-.34-1.091.3-.25.34-.74.54.77.84 1.96.981 2.46-.1-.6-.28-1.2-.49-1.76.16.07-.26-1.241.21-.37A7.823 7.824 0 0017.702 1.6c.18.17.42.39.33.42-.75-.45-.62-.48-.73-.67-.61-.25-.65.02-1.06 0C15.082.73 14.862.8 13.8.4l.05.23c-.77-.25-.9.1-1.73 0-.05-.04.27-.14.53-.18-.741.1-.701-.14-1.431.03.17-.13.36-.21.55-.32-.6.04-1.44.35-1.18.07C9.6.68 7.847 1.3 6.867 2.22L6.838 2c-.45.54-1.96 1.611-2.08 2.311l-.131.03c-.23.4-.38.85-.57 1.261-.3.52-.45.2-.4.28-.6 1.22-.9 2.251-1.16 3.102.18.27 0 1.65.07 2.76-.3 5.463 3.84 10.776 8.363 12.006.67.23 1.65.23 2.49.25-.99-.28-1.12-.15-2.08-.49-.7-.32-.85-.7-1.34-1.13l.2.35c-.971-.34-.57-.42-1.361-.67l.21-.27c-.31-.03-.83-.53-.97-.81l-.34.01c-.41-.501-.63-.871-.61-1.161l-.111.2c-.13-.21-1.52-1.901-.8-1.511-.13-.12-.31-.2-.5-.55l.14-.17c-.35-.44-.64-1.02-.62-1.2.2.24.32.3.45.33-.88-2.172-.93-.12-1.601-2.202l.15-.02c-.1-.16-.18-.34-.26-.51l.06-.6c-.63-.74-.18-3.102-.09-4.402.07-.54.53-1.1.88-1.981l-.21-.04c.4-.71 2.341-2.872 3.241-2.761.43-.55-.09 0-.18-.14.96-.991 1.26-.7 1.901-.88.7-.401-.6.16-.27-.151 1.2-.3.85-.7 2.421-.85.16.1-.39.14-.52.26 1-.49 3.151-.37 4.562.27 1.63.77 3.461 3.011 3.531 5.132l.08.02c-.04.85.13 1.821-.17 2.711l.2-.42M9.54 13.236l-.05.28c.26.35.47.73.8 1.01-.24-.47-.42-.66-.75-1.3m.62-.02c-.14-.15-.22-.34-.31-.52.08.32.26.6.43.88l-.12-.36m10.945-2.382l-.07.15c-.1.76-.34 1.511-.69 2.212.4-.73.65-1.541.75-2.362M12.45.12c.27-.1.66-.05.95-.12-.37.03-.74.05-1.1.1l.15.02M3.006 5.142c.07.57-.43.8.11.42.3-.66-.11-.18-.1-.42m-.64 2.661c.12-.39.15-.62.2-.84-.35.44-.17.53-.2.83", + }, + nixos: { + viewBox: "0 0 24 24", + d: "M7.352 1.592l-1.364.002L5.32 2.75l1.557 2.713-3.137-.008-1.32 2.34H14.11l-1.353-2.332-3.192-.006-2.214-3.865zm6.175 0l-2.687.025 5.846 10.127 1.341-2.34-1.59-2.765 2.24-3.85-.683-1.182h-1.336l-1.57 2.705-1.56-2.72zm6.887 4.195l-5.846 10.125 2.696-.008 1.601-2.76 4.453.016.682-1.183-.666-1.157-3.13-.008L21.778 8.1l-1.365-2.313zM9.432 8.086l-2.696.008-1.601 2.76-4.453-.016L0 12.02l.666 1.157 3.13.008-1.575 2.71 1.365 2.315L9.432 8.086zM7.33 12.25l-.006.01-.002-.004-1.342 2.34 1.59 2.765-2.24 3.85.684 1.182H7.35l.004-.006h.001l1.567-2.698 1.558 2.72 2.688-.026-.004-.006h.01L7.33 12.25zm2.55 3.93l1.354 2.332 3.192.006 2.215 3.865 1.363-.002.668-1.156-1.557-2.713 3.137.008 1.32-2.34H9.881Z", + }, + opensuse: { + viewBox: "0 0 640 512", + d: "M471.08 102.66s-.3 18.3-.3 20.3c-9.1-3-74.4-24.1-135.7-26.3-51.9-1.8-122.8-4.3-223 57.3-19.4 12.4-73.9 46.1-99.6 109.7C7 277-.12 307 7 335.06a111 111 0 0 0 16.5 35.7c17.4 25 46.6 41.6 78.1 44.4 44.4 3.9 78.1-16 90-53.3 8.2-25.8 0-63.6-31.5-82.9-25.6-15.7-53.3-12.1-69.2-1.6-13.9 9.2-21.8 23.5-21.6 39.2.3 27.8 24.3 42.6 41.5 42.6a49 49 0 0 0 15.8-2.7c6.5-1.8 13.3-6.5 13.3-14.9 0-12.1-11.6-14.8-16.8-13.9-2.9.5-4.5 2-11.8 2.4-2-.2-12-3.1-12-14V316c.2-12.3 13.2-18 25.5-16.9 32.3 2.8 47.7 40.7 28.5 65.7-18.3 23.7-76.6 23.2-99.7-20.4-26-49.2 12.7-111.2 87-98.4 33.2 5.7 83.6 35.5 102.4 104.3h45.9c-5.7-17.6-8.9-68.3 42.7-68.3 56.7 0 63.9 39.9 79.8 68.3H460c-12.8-18.3-21.7-38.7-18.9-55.8 5.6-33.8 39.7-18.4 82.4-17.4 66.5.4 102.1-27 103.1-28 3.7-3.1 6.5-15.8 7-17.7 1.3-5.1-3.2-2.4-3.2-2.4-8.7 5.2-30.5 15.2-50.9 15.6-25.3.5-76.2-25.4-81.6-28.2-.3-.4.1 1.2-11-25.5 88.4 58.3 118.3 40.5 145.2 21.7.8-.6 4.3-2.9 3.6-5.7-13.8-48.1-22.4-62.7-34.5-69.6-37-21.6-125-34.7-129.2-35.3.1-.1-.9-.3-.9.7zm60.4 72.8a37.54 37.54 0 0 1 38.9-36.3c33.4 1.2 48.8 42.3 24.4 65.2-24.2 22.7-64.4 4.6-63.3-28.9zm38.6-25.3a26.27 26.27 0 1 0 25.4 27.2 26.19 26.19 0 0 0-25.4-27.2zm4.3 28.8c-15.4 0-15.4-15.6 0-15.6s15.4 15.64 0 15.64z", + }, + // The gaming distros get their own mark rather than their family's: on a Deck, "a Bazzite + // box" and "a Fedora box" are different machines to the person reading the row. + bazzite: { + viewBox: "0 0 24 24", + d: "M7.178 0h3.589v7.178h7.524c3.153 0 5.709 2.556 5.709 5.709 0 6.138-4.976 11.113-11.113 11.113-3.153 0-5.709-2.556-5.709-5.709V10.766H0v-3.589h7.178zm3.589 10.766v7.524c0 1.171.949 2.12 2.12 2.12 4.156 0 7.524-3.369 7.524-7.524 0-1.171-.949-2.12-2.12-2.12z", + }, + cachyos: { + viewBox: "0 0 24 24", + d: "M5.301 2.646 0 11.771l5.541 9.583h11.486l2.904-5.017H8.102l-2.56-4.429L8.067 7.54h6.063l2.83-4.893ZM20.058 4.12a.748.748 0 0 0 0 1.496.748.748 0 0 0 0-1.496m-1.983 4.303a1.45 1.45 0 0 0 0 2.9 1.45 1.45 0 0 0 0-2.9m4.02 3.98a1.904 1.904 0 0 0 0 3.809 1.904 1.904 0 0 0 0-3.81", + }, + nobara: { + viewBox: "0 0 24 24", + d: "M23.808 11.808v8.281a3.542 3.542 0 0 1-3.542 3.527h-.46a3.543 3.543 0 0 1-3.083-3.513v-7.282l3.543-1.013-3.66-1.045a4.724 4.724 0 0 0-9.33 1.045v2.362a2.362 2.362 0 0 0 2.362 2.362 3.543 3.543 0 0 1 3.543 3.542V24a3.539 3.539 0 0 0-3.542-3.542 3.537 3.537 0 0 0-3.063 1.76 3.54 3.54 0 0 1-2.382 1.398h-.46A3.542 3.542 0 0 1 .192 20.09V3.543a3.542 3.542 0 0 1 6.323-2.194A11.756 11.756 0 0 1 12 0c6.521 0 11.808 5.287 11.808 11.808zm-9.446 0A2.359 2.359 0 0 1 12 14.17a2.362 2.362 0 1 1 2.362-2.362z", + }, }; -export function resolveOsIcon(os: string | undefined): IconType | null { - for (const token of (os ?? "").toLowerCase().split("/").reverse()) { - const icon = OS_ICONS[token]; +/** `macos` renders the apple mark, `steamos` the steam mark — same aliases as every client. */ +const ALIASES: Record = { macos: "apple", steamos: "steam" }; + +export function resolveOsIcon( + os: string | undefined, +): { viewBox: string; d: string } | null { + for (const raw of (os ?? "").toLowerCase().split("/").reverse()) { + const icon = OS_ICONS[ALIASES[raw] ?? raw]; if (icon) { return icon; } @@ -44,6 +86,20 @@ export function resolveOsIcon(os: string | undefined): IconType | null { /** The mark itself, or nothing — sized/colored by the surrounding text like the lock glyph. */ export const OsMark: FC<{ os?: string }> = ({ os }) => { - const Icon = resolveOsIcon(os); - return Icon ? : null; + const icon = resolveOsIcon(os); + if (!icon) return null; + // 1em square with the viewBox letterboxed inside it: the same box react-icons drew, so a + // non-square mark keeps its aspect ratio instead of being stretched to fill. + return ( + + + + ); }; diff --git a/clients/linux/data/icons/scalable/actions/pf-os-bazzite-symbolic.svg b/clients/linux/data/icons/scalable/actions/pf-os-bazzite-symbolic.svg new file mode 100644 index 00000000..5294d57d --- /dev/null +++ b/clients/linux/data/icons/scalable/actions/pf-os-bazzite-symbolic.svg @@ -0,0 +1,2 @@ + + diff --git a/clients/linux/data/icons/scalable/actions/pf-os-cachyos-symbolic.svg b/clients/linux/data/icons/scalable/actions/pf-os-cachyos-symbolic.svg new file mode 100644 index 00000000..b2d14159 --- /dev/null +++ b/clients/linux/data/icons/scalable/actions/pf-os-cachyos-symbolic.svg @@ -0,0 +1,2 @@ + + diff --git a/clients/linux/data/icons/scalable/actions/pf-os-nobara-symbolic.svg b/clients/linux/data/icons/scalable/actions/pf-os-nobara-symbolic.svg new file mode 100644 index 00000000..2d751dee --- /dev/null +++ b/clients/linux/data/icons/scalable/actions/pf-os-nobara-symbolic.svg @@ -0,0 +1,2 @@ + + diff --git a/clients/linux/data/icons/scalable/actions/pf-os-windows-symbolic.svg b/clients/linux/data/icons/scalable/actions/pf-os-windows-symbolic.svg index f7f972bf..f8b2d1e1 100644 --- a/clients/linux/data/icons/scalable/actions/pf-os-windows-symbolic.svg +++ b/clients/linux/data/icons/scalable/actions/pf-os-windows-symbolic.svg @@ -1,2 +1,2 @@ - - + + diff --git a/clients/linux/data/resources.gresource.xml b/clients/linux/data/resources.gresource.xml index 660e71e4..ff0944dd 100644 --- a/clients/linux/data/resources.gresource.xml +++ b/clients/linux/data/resources.gresource.xml @@ -14,5 +14,8 @@ icons/scalable/actions/pf-os-debian-symbolic.svg icons/scalable/actions/pf-os-nixos-symbolic.svg icons/scalable/actions/pf-os-opensuse-symbolic.svg + icons/scalable/actions/pf-os-bazzite-symbolic.svg + icons/scalable/actions/pf-os-cachyos-symbolic.svg + icons/scalable/actions/pf-os-nobara-symbolic.svg diff --git a/clients/linux/src/ui_hosts.rs b/clients/linux/src/ui_hosts.rs index ac538172..b1da11fd 100644 --- a/clients/linux/src/ui_hosts.rs +++ b/clients/linux/src/ui_hosts.rs @@ -583,10 +583,13 @@ const PROBE_INTERVAL: std::time::Duration = std::time::Duration::from_secs(12); /// at a glance is the whole reason the chip exists. No colour set keeps the neutral pill, so /// the palette stays opt-in. /// The OS-icon tokens this shell ships symbolic art for (`data/icons/.../pf-os--symbolic.svg`, -/// embedded via gresource). Chains walk most-specific-first, so a distro without its own mark -/// (Bazzite, CachyOS, ...) lands on its family's and finally on plain Tux. +/// embedded via gresource): the families a chain can land on, plus the gaming distros that earn +/// their own mark because "a Bazzite box" and "a Fedora box" are different machines to the person +/// reading the card. Chains walk most-specific-first, so a distro without a mark of its own still +/// lands on its family's and finally on plain Tux. const OS_ICON_TOKENS: &[&str] = &[ - "windows", "apple", "linux", "steam", "ubuntu", "fedora", "arch", "debian", "nixos", "opensuse", + "windows", "apple", "linux", "steam", "ubuntu", "fedora", "arch", "debian", "nixos", + "opensuse", "bazzite", "cachyos", "nobara", ]; /// The card's OS glyph for an advertised chain, or `None` (no widget) when the host doesn't diff --git a/clients/windows/assets/os/bazzite.png b/clients/windows/assets/os/bazzite.png new file mode 100644 index 00000000..bc9f2f83 Binary files /dev/null and b/clients/windows/assets/os/bazzite.png differ diff --git a/clients/windows/assets/os/cachyos.png b/clients/windows/assets/os/cachyos.png new file mode 100644 index 00000000..163bb893 Binary files /dev/null and b/clients/windows/assets/os/cachyos.png differ diff --git a/clients/windows/assets/os/nobara.png b/clients/windows/assets/os/nobara.png new file mode 100644 index 00000000..88b70942 Binary files /dev/null and b/clients/windows/assets/os/nobara.png differ diff --git a/clients/windows/assets/os/windows.png b/clients/windows/assets/os/windows.png index 3da00a82..8a661e62 100644 Binary files a/clients/windows/assets/os/windows.png and b/clients/windows/assets/os/windows.png differ diff --git a/clients/windows/src/app/os_icons.rs b/clients/windows/src/app/os_icons.rs index 9a3390ff..139dabcb 100644 --- a/clients/windows/src/app/os_icons.rs +++ b/clients/windows/src/app/os_icons.rs @@ -8,8 +8,10 @@ use std::path::PathBuf; use std::sync::OnceLock; -/// Embedded PNG per icon token, most-generic set the chain walk can land on. A distro -/// without its own mark (Bazzite, CachyOS, ...) degrades to its family's and finally Tux. +/// Embedded PNG per icon token: the families the chain walk can land on, plus the gaming +/// distros that earn their own mark because "a Bazzite box" and "a Fedora box" are +/// different machines to the person reading the tile. A distro with no mark of its own +/// still degrades to its family's and finally to Tux. const ICONS: &[(&str, &[u8])] = &[ ("windows", include_bytes!("../../assets/os/windows.png")), ("apple", include_bytes!("../../assets/os/apple.png")), @@ -21,6 +23,9 @@ const ICONS: &[(&str, &[u8])] = &[ ("debian", include_bytes!("../../assets/os/debian.png")), ("nixos", include_bytes!("../../assets/os/nixos.png")), ("opensuse", include_bytes!("../../assets/os/opensuse.png")), + ("bazzite", include_bytes!("../../assets/os/bazzite.png")), + ("cachyos", include_bytes!("../../assets/os/cachyos.png")), + ("nobara", include_bytes!("../../assets/os/nobara.png")), ]; fn dir() -> Option { diff --git a/crates/punktfunk-host/src/osinfo.rs b/crates/punktfunk-host/src/osinfo.rs index 67ef876a..50f7e688 100644 --- a/crates/punktfunk-host/src/osinfo.rs +++ b/crates/punktfunk-host/src/osinfo.rs @@ -183,6 +183,19 @@ mod tests { assert_eq!(chain, "linux/arch/cachyos"); } + #[test] + fn nobara_takes_rhel_the_first_recognized_ancestor() { + // Verified against a real Nobara 44 box: it declares `ID_LIKE="rhel centos fedora"`, + // so the family token is *rhel*, not the fedora one might expect. Clients ship a + // `nobara` mark, so the leaf matches before the family ever gets a say — but a + // RHEL-family distro we have no art for lands on plain Tux, not on Fedora. + let (chain, pretty) = parsed( + "NAME=\"Nobara Linux\"\nID=nobara\nID_LIKE=\"rhel centos fedora\"\nPRETTY_NAME=\"Nobara Linux 44 (KDE Plasma Desktop Edition)\"\n", + ); + assert_eq!(chain, "linux/rhel/nobara"); + assert_eq!(pretty, "Nobara Linux 44 (KDE Plasma Desktop Edition)"); + } + #[test] fn nixos_without_id_like_is_two_segments() { let (chain, _) = parsed("ID=nixos\nPRETTY_NAME=\"NixOS 25.05 (Warbler)\"\n"); diff --git a/scripts/gen-os-icons.sh b/scripts/gen-os-icons.sh new file mode 100755 index 00000000..ea6ffeba --- /dev/null +++ b/scripts/gen-os-icons.sh @@ -0,0 +1,97 @@ +#!/usr/bin/env bash +# Derive the per-client host-OS-icon assets from the assets/os-icons masters. +# +# The masters are monochrome `fill="currentColor"` SVGs, one per icon token of the host's +# OS-identity chain (see assets/os-icons/README.md). Three clients need a baked derivative +# because they cannot consume the master directly: +# +# GTK shell symbolic SVG, black fill -> clients/linux/data/icons/scalable/actions/ +# Windows shell PNG, h=32, mid-grey -> clients/windows/assets/os/ +# Apple clients vector PDF, black fill -> clients/apple/.../OsIcons.xcassets/ +# +# The web console, the Decky plugin and the Android client transcribe the master's path +# data inline instead — those are hand-kept, and this script prints them at the end so a +# new token can be pasted straight in. +# +# Idempotent. Usage: bash scripts/gen-os-icons.sh [token ...] (default: every master) +set -euo pipefail + +cd "$(dirname "$0")/.." + +MASTERS=assets/os-icons +GTK=clients/linux/data/icons/scalable/actions +WIN=clients/windows/assets/os +APPLE=clients/apple/Sources/PunktfunkKit/Resources/OsIcons.xcassets + +# The Windows shell has no vector element and no theme-aware tint, so its PNGs are baked in +# one mid-grey that stays legible on both the light and the dark WinUI theme. +WIN_GREY='#8A8F98' +WIN_HEIGHT=32 + +log() { printf '\033[1;36m==>\033[0m %s\n' "$*"; } + +command -v rsvg-convert >/dev/null 2>&1 || { + echo "rsvg-convert not found (brew install librsvg / apt install librsvg2-bin)" >&2 + exit 1 +} + +tokens=("$@") +if [ ${#tokens[@]} -eq 0 ]; then + for f in "$MASTERS"/*.svg; do tokens+=("$(basename "$f" .svg)"); done +fi + +tmp=$(mktemp -d) +trap 'rm -rf "$tmp"' EXIT + +for t in "${tokens[@]}"; do + src="$MASTERS/$t.svg" + [ -f "$src" ] || { echo "no master for token '$t' ($src)" >&2; exit 1; } + log "$t" + + # GTK: the master with the fill resolved to black — Adwaita recolors a `-symbolic` icon + # from the fill it finds, so the value only has to be a real colour, not the final one. + sed 's/currentColor/#000000/' "$src" > "$GTK/pf-os-$t-symbolic.svg" + + # Windows: same black-to-grey substitution, rasterized at a fixed height so every mark + # shares an optical size and keeps its own aspect ratio. + sed "s/currentColor/$WIN_GREY/" "$src" > "$tmp/$t.grey.svg" + rsvg-convert -h "$WIN_HEIGHT" -f png -o "$WIN/$t.png" "$tmp/$t.grey.svg" + + # Apple: a vector PDF at the master's natural size, in a template imageset — SwiftUI + # tints it from foregroundStyle, so the baked colour is irrelevant. + sed 's/currentColor/#000000/' "$src" > "$tmp/$t.black.svg" + mkdir -p "$APPLE/os-$t.imageset" + rsvg-convert -f pdf -o "$APPLE/os-$t.imageset/$t.pdf" "$tmp/$t.black.svg" + cat > "$APPLE/os-$t.imageset/Contents.json" <]* d="([^"]+)"', svg).group(1) +w, h = box.split()[2:] +print(f'\n {sys.argv[2]}: viewBox "{box}" (viewport {w} x {h})\n {d}') +PY +done + +echo +log "Remember: a NEW token also has to be added to each client's shipped-token list —" +log " clients/linux/src/ui_hosts.rs, clients/linux/data/resources.gresource.xml," +log " clients/windows/src/app/os_icons.rs, clients/apple/.../PunktfunkKit/OsIcon.swift," +log " plus the three inline registries above." diff --git a/scripts/gen-third-party-notices.py b/scripts/gen-third-party-notices.py index 53a5fa4a..4f09e6f3 100755 --- a/scripts/gen-third-party-notices.py +++ b/scripts/gen-third-party-notices.py @@ -68,14 +68,17 @@ VENDORED_TREES = [ ("Vulkan-Headers (vendored, crates/pyrowave-sys)", "crates/pyrowave-sys/vendor/pyrowave/Granite/third_party/khronos/vulkan-headers/LICENSE.md", "https://github.com/KhronosGroup/Vulkan-Headers"), - # OS brand marks for the host-card OS icon (assets/os-icons/, CC BY 4.0 / CC0 — - # see assets/os-icons/README.md; clients embed per-platform derivatives). + # OS brand marks for the host-card OS icon (assets/os-icons/, CC BY 4.0 / CC0 / + # Apache-2.0 — see assets/os-icons/README.md; clients embed per-platform derivatives). ("Font Awesome Free brand icons (vendored, assets/os-icons)", "assets/os-icons/LICENSES/font-awesome-brands.txt", "https://fontawesome.com"), ("Simple Icons (vendored, assets/os-icons)", "assets/os-icons/LICENSES/simple-icons.txt", "https://simpleicons.org"), + ("Bazzite logo (vendored, assets/os-icons)", + "assets/os-icons/LICENSES/bazzite.txt", + "https://github.com/ublue-os/bazzite"), ] diff --git a/web/src/components/os-icon.tsx b/web/src/components/os-icon.tsx index 79161a54..b0c2e09e 100644 --- a/web/src/components/os-icon.tsx +++ b/web/src/components/os-icon.tsx @@ -1,15 +1,16 @@ -// Host OS icon: the web-console derivative of the assets/os-icons masters (Font Awesome -// Free brands CC BY 4.0 + Simple Icons CC0 — provenance in assets/os-icons/README.md; -// lucide deliberately ships no brand marks). Follows the curated-registry pattern of -// api/plugins.ts: a fixed token set, resolved by walking the host's OS-identity chain. +// Host OS icon: the web-console derivative of the assets/os-icons masters (per-mark +// provenance and licensing in assets/os-icons/README.md; lucide deliberately ships no +// brand marks). Follows the curated-registry pattern of api/plugins.ts: a fixed token set, +// resolved by walking the host's OS-identity chain. Path data is transcribed from the +// masters — `bash scripts/gen-os-icons.sh ` prints it ready to paste. import { Monitor } from "lucide-react"; import type { FC } from "react"; /** One monochrome brand mark: original per-icon viewBox, drawn in currentColor. */ const OS_ICONS: Record = { windows: { - viewBox: "0 0 448 512", - d: "M0 93.7l183.6-25.3v177.4H0V93.7zm0 324.6l183.6 25.3V268.4H0v149.9zm203.8 28L448 480V268.4H203.8v177.9zm0-380.6v180.1H448V32L203.8 65.7z", + viewBox: "0 0 24 24", + d: "M0 0h11.377v11.377H0zm12.623 0H24v11.377H12.623zM0 12.623h11.377V24H0zm12.623 0H24V24H12.623z", }, apple: { viewBox: "0 0 384 512", @@ -47,6 +48,21 @@ const OS_ICONS: Record = { viewBox: "0 0 640 512", d: "M471.08 102.66s-.3 18.3-.3 20.3c-9.1-3-74.4-24.1-135.7-26.3-51.9-1.8-122.8-4.3-223 57.3-19.4 12.4-73.9 46.1-99.6 109.7C7 277-.12 307 7 335.06a111 111 0 0 0 16.5 35.7c17.4 25 46.6 41.6 78.1 44.4 44.4 3.9 78.1-16 90-53.3 8.2-25.8 0-63.6-31.5-82.9-25.6-15.7-53.3-12.1-69.2-1.6-13.9 9.2-21.8 23.5-21.6 39.2.3 27.8 24.3 42.6 41.5 42.6a49 49 0 0 0 15.8-2.7c6.5-1.8 13.3-6.5 13.3-14.9 0-12.1-11.6-14.8-16.8-13.9-2.9.5-4.5 2-11.8 2.4-2-.2-12-3.1-12-14V316c.2-12.3 13.2-18 25.5-16.9 32.3 2.8 47.7 40.7 28.5 65.7-18.3 23.7-76.6 23.2-99.7-20.4-26-49.2 12.7-111.2 87-98.4 33.2 5.7 83.6 35.5 102.4 104.3h45.9c-5.7-17.6-8.9-68.3 42.7-68.3 56.7 0 63.9 39.9 79.8 68.3H460c-12.8-18.3-21.7-38.7-18.9-55.8 5.6-33.8 39.7-18.4 82.4-17.4 66.5.4 102.1-27 103.1-28 3.7-3.1 6.5-15.8 7-17.7 1.3-5.1-3.2-2.4-3.2-2.4-8.7 5.2-30.5 15.2-50.9 15.6-25.3.5-76.2-25.4-81.6-28.2-.3-.4.1 1.2-11-25.5 88.4 58.3 118.3 40.5 145.2 21.7.8-.6 4.3-2.9 3.6-5.7-13.8-48.1-22.4-62.7-34.5-69.6-37-21.6-125-34.7-129.2-35.3.1-.1-.9-.3-.9.7zm60.4 72.8a37.54 37.54 0 0 1 38.9-36.3c33.4 1.2 48.8 42.3 24.4 65.2-24.2 22.7-64.4 4.6-63.3-28.9zm38.6-25.3a26.27 26.27 0 1 0 25.4 27.2 26.19 26.19 0 0 0-25.4-27.2zm4.3 28.8c-15.4 0-15.4-15.6 0-15.6s15.4 15.64 0 15.64z", }, + // The gaming distros get their own mark rather than their family's: "a Bazzite box" and + // "a Fedora box" are different machines to the person reading the card, and these three + // are what this project's hosts actually run. + bazzite: { + viewBox: "0 0 24 24", + d: "M7.178 0h3.589v7.178h7.524c3.153 0 5.709 2.556 5.709 5.709 0 6.138-4.976 11.113-11.113 11.113-3.153 0-5.709-2.556-5.709-5.709V10.766H0v-3.589h7.178zm3.589 10.766v7.524c0 1.171.949 2.12 2.12 2.12 4.156 0 7.524-3.369 7.524-7.524 0-1.171-.949-2.12-2.12-2.12z", + }, + cachyos: { + viewBox: "0 0 24 24", + d: "M5.301 2.646 0 11.771l5.541 9.583h11.486l2.904-5.017H8.102l-2.56-4.429L8.067 7.54h6.063l2.83-4.893ZM20.058 4.12a.748.748 0 0 0 0 1.496.748.748 0 0 0 0-1.496m-1.983 4.303a1.45 1.45 0 0 0 0 2.9 1.45 1.45 0 0 0 0-2.9m4.02 3.98a1.904 1.904 0 0 0 0 3.809 1.904 1.904 0 0 0 0-3.81", + }, + nobara: { + viewBox: "0 0 24 24", + d: "M23.808 11.808v8.281a3.542 3.542 0 0 1-3.542 3.527h-.46a3.543 3.543 0 0 1-3.083-3.513v-7.282l3.543-1.013-3.66-1.045a4.724 4.724 0 0 0-9.33 1.045v2.362a2.362 2.362 0 0 0 2.362 2.362 3.543 3.543 0 0 1 3.543 3.542V24a3.539 3.539 0 0 0-3.542-3.542 3.537 3.537 0 0 0-3.063 1.76 3.54 3.54 0 0 1-2.382 1.398h-.46A3.542 3.542 0 0 1 .192 20.09V3.543a3.542 3.542 0 0 1 6.323-2.194A11.756 11.756 0 0 1 12 0c6.521 0 11.808 5.287 11.808 11.808zm-9.446 0A2.359 2.359 0 0 1 12 14.17a2.362 2.362 0 1 1 2.362-2.362z", + }, }; /** `macos` renders the apple mark, `steamos` the steam mark — same aliases as every client. */ diff --git a/web/src/stories/Host.stories.tsx b/web/src/stories/Host.stories.tsx index 54ed4551..7ae75e7d 100644 --- a/web/src/stories/Host.stories.tsx +++ b/web/src/stories/Host.stories.tsx @@ -33,6 +33,18 @@ export const WindowsHost: Story = { }, }; +/** A gaming distro wears its OWN mark, not its family's: `cachyos` is resolved before the + * `arch` it descends from, which is the whole point of shipping art for the leaf. */ +export const CachyOsHost: Story = { + args: { + host: { + data: { ...hostInfo, os: "linux/arch/cachyos", os_name: "CachyOS Linux" }, + isLoading: false, + error: null, + }, + }, +}; + /** An unrecognized distro chain walks up to its family mark — here neither `chimera` nor * `frontier` have art, so the icon degrades all the way to generic Tux. */ export const UnknownDistro: Story = {