feat(dedicated): official OS logos + collapsible distro families
Two changes to the OS picker that should be felt together: 1. Swapped my hand-authored SVGs for official brand marks pulled from simple-icons (CC0). These are the actual path geometries used in each distro's brand kit, just colored to read on dark navy. AlmaLinux uses their accent orange (#FA9001) instead of the brand navy (#0E1F3D) so it's visible on our dark background. Affected: ubuntu, debian, almalinux, rocky, fedora, freebsd, proxmox. windows.svg and no-os.svg unchanged (geometric / generic). 2. Each distro family is now a collapsible accordion. The family containing the current selection auto-expands on mount; everything else collapses to a one-line row showing logo + family name + option count + chevron. Header gets a primary-color "selected" chip + tinted border when its family contains the active choice. "Expand all" / "Collapse all" toggle in the title row for power users; collapseAll() keeps the active selection's family open. Net effect: the picker is ~1/4 of its previous height when only one family is in use, and the official logos replace my approximations (AlmaLinux flame mark is now correct, FreeBSD daemon is correct, Proxmox four-square crown is correct, etc.). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@@ -1,8 +1 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" role="img" aria-label="AlmaLinux">
|
<svg fill="#FA9001" 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>
|
||||||
<circle cx="32" cy="32" r="32" fill="#0E1F3D"/>
|
|
||||||
<g transform="translate(32 32)">
|
|
||||||
<circle r="14" fill="none" stroke="#FA9001" stroke-width="3"/>
|
|
||||||
<path d="M-9 -2 Q 0 -14, 9 -2 Q 0 4, -9 -2 Z" fill="#FA9001"/>
|
|
||||||
<path d="M-7 5 Q 0 -2, 7 5 Q 0 12, -7 5 Z" fill="#FFD200"/>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 392 B After Width: | Height: | Size: 3.0 KiB |
@@ -1,7 +1 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" role="img" aria-label="Debian">
|
<svg fill="#D70751" role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Debian</title><path 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"/></svg>
|
||||||
<circle cx="32" cy="32" r="32" fill="#fff"/>
|
|
||||||
<g transform="translate(32 32)">
|
|
||||||
<path d="M -2 -16 C -10 -16, -16 -10, -16 -2 C -16 6, -10 12, -2 12 C 4 12, 8 9, 9 5 C 6 8, 2 9, -2 8 C -8 7, -12 2, -12 -3 C -12 -8, -8 -13, -2 -13 C 3 -13, 7 -10, 8 -6 C 8 -12, 4 -16, -2 -16 Z" fill="#D70751"/>
|
|
||||||
<circle cx="9" cy="-6" r="1.4" fill="#D70751"/>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 457 B After Width: | Height: | Size: 2.8 KiB |
@@ -1,7 +1 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" role="img" aria-label="Fedora">
|
<svg fill="#51A2DA" role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Fedora</title><path d="M12.001 0C5.376 0 .008 5.369.004 11.992H.002v9.287h.002A2.726 2.726 0 0 0 2.73 24h9.275c6.626-.004 11.993-5.372 11.993-11.997C23.998 5.375 18.628 0 12 0zm2.431 4.94c2.015 0 3.917 1.543 3.917 3.671 0 .197.001.395-.03.619a1.002 1.002 0 0 1-1.137.893 1.002 1.002 0 0 1-.842-1.175 2.61 2.61 0 0 0 .013-.337c0-1.207-.987-1.672-1.92-1.672-.934 0-1.775.784-1.777 1.672.016 1.027 0 2.046 0 3.07l1.732-.012c1.352-.028 1.368 2.009.016 1.998l-1.748.013c-.004.826.006.677.002 1.093 0 0 .015 1.01-.016 1.776-.209 2.25-2.124 4.046-4.424 4.046-2.438 0-4.448-1.993-4.448-4.437.073-2.515 2.078-4.492 4.603-4.469l1.409-.01v1.996l-1.409.013h-.007c-1.388.04-2.577.984-2.6 2.47a2.438 2.438 0 0 0 2.452 2.439c1.356 0 2.441-.987 2.441-2.437l-.001-7.557c0-.14.005-.252.02-.407.23-1.848 1.883-3.256 3.754-3.256z"/></svg>
|
||||||
<circle cx="32" cy="32" r="32" fill="#294172"/>
|
|
||||||
<g transform="translate(32 32)">
|
|
||||||
<path d="M -2 -14 L 10 -14 L 10 -8 L 4 -8 L 4 -3 L 9 -3 L 9 3 L 4 3 L 4 14 L -2 14 L -2 -8 C -2 -12, -1 -14, 2 -14 Z" fill="#fff"/>
|
|
||||||
<circle cx="-6" cy="9" r="5" fill="none" stroke="#fff" stroke-width="2.5"/>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 407 B After Width: | Height: | Size: 911 B |
@@ -1,10 +1 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" role="img" aria-label="FreeBSD">
|
<svg fill="#AB2B28" role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>FreeBSD</title><path d="M23.682 2.406c-.001-.149-.097-.187-.24-.189h-.25v.659h.108v-.282h.102l.17.282h.122l-.184-.29c.102-.012.175-.065.172-.18zm-.382.096v-.193h.13c.06-.002.145.011.143.089.005.09-.08.107-.153.103h-.12zM21.851 1.49c1.172 1.171-2.077 6.319-2.626 6.869-.549.548-1.944.044-3.115-1.128-1.172-1.171-1.676-2.566-1.127-3.115.549-.55 5.697-3.798 6.868-2.626zM1.652 6.61C.626 4.818-.544 2.215.276 1.395c.81-.81 3.355.319 5.144 1.334A11.003 11.003 0 0 0 1.652 6.61zm18.95.418a10.584 10.584 0 0 1 1.368 5.218c0 5.874-4.762 10.636-10.637 10.636C5.459 22.882.697 18.12.697 12.246.697 6.371 5.459 1.61 11.333 1.61c1.771 0 3.441.433 4.909 1.199-.361.201-.69.398-.969.574-.428-.077-.778-.017-.998.202-.402.402-.269 1.245.263 2.2.273.539.701 1.124 1.25 1.674.103.104.208.202.315.297 1.519 1.446 3.205 2.111 3.829 1.486.267-.267.297-.728.132-1.287.167-.27.35-.584.538-.927zm2.814-5.088c-.322 0-.584.266-.584.595s.261.595.584.595c.323 0 .584-.266.584-.595s-.261-.595-.584-.595zm0 1.087c-.252 0-.457-.22-.457-.492s.204-.492.457-.492c.252 0 .457.22.457.492s-.204.492-.457.492z"/></svg>
|
||||||
<circle cx="32" cy="32" r="32" fill="#AB2B28"/>
|
|
||||||
<g transform="translate(32 32)">
|
|
||||||
<circle r="15" fill="#fff"/>
|
|
||||||
<circle cx="-5" cy="-3" r="2.5" fill="#AB2B28"/>
|
|
||||||
<circle cx="5" cy="-3" r="2.5" fill="#AB2B28"/>
|
|
||||||
<path d="M -7 4 Q 0 11, 7 4" fill="none" stroke="#AB2B28" stroke-width="2.5" stroke-linecap="round"/>
|
|
||||||
<path d="M -12 -10 L -8 -6 M 12 -10 L 8 -6" stroke="#AB2B28" stroke-width="3" stroke-linecap="round"/>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 543 B After Width: | Height: | Size: 1.1 KiB |
@@ -1,11 +1 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" role="img" aria-label="Proxmox VE">
|
<svg fill="#E57000" role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Proxmox</title><path d="M4.928 1.825c-1.09.553-1.09.64-.07 1.78 5.655 6.295 7.004 7.782 7.107 7.782.139.017 7.971-8.542 8.058-8.801.034-.07-.208-.312-.519-.536-.415-.312-.864-.433-1.712-.467-1.59-.104-2.144.242-4.115 2.455-.899 1.003-1.66 1.833-1.66 1.833-.017 0-.76-.813-1.642-1.798S8.473 2.1 8.127 1.91c-.796-.45-2.421-.484-3.2-.086zM1.297 4.367C.45 4.695 0 5.007 0 5.248c0 .121 1.331 1.678 2.94 3.459 1.625 1.78 2.939 3.268 2.939 3.302 0 .035-1.331 1.522-2.94 3.303C1.314 17.11.017 18.683.035 18.822c.086.467 1.504 1.055 2.541 1.055 1.678-.018 2.058-.312 5.603-4.202 1.78-1.954 3.233-3.614 3.233-3.666 0-.069-1.435-1.694-3.199-3.63-2.3-2.508-3.423-3.632-3.96-3.874-.812-.398-2.126-.467-2.956-.138zm18.467.12c-.502.26-1.764 1.505-3.943 3.891-1.763 1.937-3.199 3.562-3.199 3.631 0 .07 1.453 1.712 3.234 3.666 3.544 3.89 3.925 4.184 5.602 4.202 1.038 0 2.455-.588 2.542-1.055.017-.156-1.28-1.712-2.905-3.493-1.608-1.78-2.94-3.285-2.94-3.32 0-.034 1.332-1.539 2.94-3.32C22.72 6.91 24.017 5.352 24 5.214c-.087-.45-1.366-.968-2.473-1.038-.795-.034-1.21.035-1.763.312zM7.954 16.973c-2.144 2.369-3.908 4.374-3.943 4.46-.034.07.208.312.52.537.414.311.864.432 1.711.467 1.574.103 2.161-.26 4.15-2.508.864-.968 1.608-1.78 1.625-1.78s.761.812 1.643 1.798c2.023 2.248 2.559 2.576 4.132 2.49.848-.035 1.297-.156 1.712-.467.311-.225.553-.467.519-.536-.087-.26-7.92-8.819-8.058-8.801-.069 0-1.867 1.954-4.011 4.34z"/></svg>
|
||||||
<circle cx="32" cy="32" r="32" fill="#E57000"/>
|
|
||||||
<g transform="translate(32 32)">
|
|
||||||
<rect x="-13" y="-13" width="26" height="26" rx="2" fill="#fff"/>
|
|
||||||
<rect x="-9" y="-9" width="6" height="6" fill="#E57000"/>
|
|
||||||
<rect x="3" y="-9" width="6" height="6" fill="#E57000"/>
|
|
||||||
<rect x="-9" y="3" width="6" height="6" fill="#E57000"/>
|
|
||||||
<rect x="3" y="3" width="6" height="6" fill="#E57000"/>
|
|
||||||
<rect x="-3" y="-3" width="6" height="6" fill="#E57000"/>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 571 B After Width: | Height: | Size: 1.5 KiB |
@@ -1,9 +1 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" role="img" aria-label="Rocky Linux">
|
<svg fill="#10B981" role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Rocky Linux</title><path d="M23.332 15.957c.433-1.239.668-2.57.668-3.957 0-6.627-5.373-12-12-12S0 5.373 0 12c0 3.28 1.315 6.251 3.447 8.417L15.62 8.245l3.005 3.005zm-2.192 3.819l-5.52-5.52L6.975 22.9c1.528.706 3.23 1.1 5.025 1.1 3.661 0 6.94-1.64 9.14-4.224z"/></svg>
|
||||||
<circle cx="32" cy="32" r="32" fill="#10B981"/>
|
|
||||||
<g>
|
|
||||||
<path d="M14 46 L 26 30 L 32 38 L 42 22 L 50 33 L 50 46 Z" fill="#fff"/>
|
|
||||||
<path d="M26 30 L 32 38 L 28.5 38 Z" fill="#0F766E" opacity="0.35"/>
|
|
||||||
<path d="M42 22 L 50 33 L 45 33 Z" fill="#0F766E" opacity="0.35"/>
|
|
||||||
<circle cx="44" cy="20" r="3" fill="#FDE68A"/>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 439 B After Width: | Height: | Size: 360 B |
@@ -1,7 +1 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" role="img" aria-label="Ubuntu">
|
<svg fill="#E95420" role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Ubuntu</title><path d="M17.61.455a3.41 3.41 0 0 0-3.41 3.41 3.41 3.41 0 0 0 3.41 3.41 3.41 3.41 0 0 0 3.41-3.41 3.41 3.41 0 0 0-3.41-3.41zM12.92.8C8.923.777 5.137 2.941 3.148 6.451a4.5 4.5 0 0 1 .26-.007 4.92 4.92 0 0 1 2.585.737A8.316 8.316 0 0 1 12.688 3.6 4.944 4.944 0 0 1 13.723.834 11.008 11.008 0 0 0 12.92.8zm9.226 4.994a4.915 4.915 0 0 1-1.918 2.246 8.36 8.36 0 0 1-.273 8.303 4.89 4.89 0 0 1 1.632 2.54 11.156 11.156 0 0 0 .559-13.089zM3.41 7.932A3.41 3.41 0 0 0 0 11.342a3.41 3.41 0 0 0 3.41 3.409 3.41 3.41 0 0 0 3.41-3.41 3.41 3.41 0 0 0-3.41-3.41zm2.027 7.866a4.908 4.908 0 0 1-2.915.358 11.1 11.1 0 0 0 7.991 6.698 11.234 11.234 0 0 0 2.422.249 4.879 4.879 0 0 1-.999-2.85 8.484 8.484 0 0 1-.836-.136 8.304 8.304 0 0 1-5.663-4.32zm11.405.928a3.41 3.41 0 0 0-3.41 3.41 3.41 3.41 0 0 0 3.41 3.41 3.41 3.41 0 0 0 3.41-3.41 3.41 3.41 0 0 0-3.41-3.41z"/></svg>
|
||||||
<circle cx="32" cy="32" r="32" fill="#E95420"/>
|
|
||||||
<circle cx="32" cy="9.5" r="4.5" fill="#fff"/>
|
|
||||||
<circle cx="51.5" cy="42" r="4.5" fill="#fff"/>
|
|
||||||
<circle cx="12.5" cy="42" r="4.5" fill="#fff"/>
|
|
||||||
<circle cx="32" cy="32" r="9.5" fill="none" stroke="#fff" stroke-width="3"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 377 B After Width: | Height: | Size: 963 B |
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed } from 'vue'
|
import { computed, ref, watch } from 'vue'
|
||||||
import { shortGroupLabel, type DedicatedConfigGroup, type DedicatedConfigValue, type DedicatedCycle } from '@/stores/dedicatedConfigurator'
|
import { shortGroupLabel, type DedicatedConfigGroup, type DedicatedConfigValue, type DedicatedCycle } from '@/stores/dedicatedConfigurator'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -19,14 +19,11 @@ const values = computed<DedicatedConfigValue[]>(() => props.group.options[0]?.va
|
|||||||
interface FamilyMeta {
|
interface FamilyMeta {
|
||||||
family: string
|
family: string
|
||||||
logo: string
|
logo: string
|
||||||
// Lower number = earlier in the picker. Lets us order families
|
|
||||||
// independently of the seeder's value order.
|
|
||||||
rank: number
|
rank: number
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map a value slug to its distro family + logo path. Adding a new OS
|
// slug → distro family + logo path. New OSes added in the seeder need a row
|
||||||
// in the seeder means adding one row here so the picker knows where
|
// here so the picker knows where to put them.
|
||||||
// to slot it.
|
|
||||||
function metaFor(slug: string): FamilyMeta {
|
function metaFor(slug: string): FamilyMeta {
|
||||||
if (slug.startsWith('alma')) return { family: 'AlmaLinux', logo: '/img/os/almalinux.svg', rank: 10 }
|
if (slug.startsWith('alma')) return { family: 'AlmaLinux', logo: '/img/os/almalinux.svg', rank: 10 }
|
||||||
if (slug.startsWith('rocky')) return { family: 'Rocky Linux', logo: '/img/os/rocky.svg', rank: 20 }
|
if (slug.startsWith('rocky')) return { family: 'Rocky Linux', logo: '/img/os/rocky.svg', rank: 20 }
|
||||||
@@ -42,6 +39,7 @@ function metaFor(slug: string): FamilyMeta {
|
|||||||
interface FamilyGroup {
|
interface FamilyGroup {
|
||||||
family: string
|
family: string
|
||||||
rank: number
|
rank: number
|
||||||
|
logo: string
|
||||||
values: DedicatedConfigValue[]
|
values: DedicatedConfigValue[]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,15 +48,45 @@ const familyGroups = computed<FamilyGroup[]>(() => {
|
|||||||
for (const v of values.value) {
|
for (const v of values.value) {
|
||||||
const meta = metaFor(v.value)
|
const meta = metaFor(v.value)
|
||||||
if (!map.has(meta.family)) {
|
if (!map.has(meta.family)) {
|
||||||
map.set(meta.family, { family: meta.family, rank: meta.rank, values: [] })
|
map.set(meta.family, { family: meta.family, rank: meta.rank, logo: meta.logo, values: [] })
|
||||||
}
|
}
|
||||||
map.get(meta.family)!.values.push(v)
|
map.get(meta.family)!.values.push(v)
|
||||||
}
|
}
|
||||||
return Array.from(map.values()).sort((a, b) => a.rank - b.rank)
|
return Array.from(map.values()).sort((a, b) => a.rank - b.rank)
|
||||||
})
|
})
|
||||||
|
|
||||||
function logoFor(slug: string): string {
|
// Set of family names that are currently expanded. By default we open
|
||||||
return metaFor(slug).logo
|
// only the family that contains the current selection — keeps the picker
|
||||||
|
// compact while still showing the active choice.
|
||||||
|
const openFamilies = ref<Set<string>>(new Set())
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.selected,
|
||||||
|
(slug) => {
|
||||||
|
if (!slug) return
|
||||||
|
const meta = metaFor(slug)
|
||||||
|
if (!openFamilies.value.has(meta.family)) {
|
||||||
|
const next = new Set(openFamilies.value)
|
||||||
|
next.add(meta.family)
|
||||||
|
openFamilies.value = next
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true },
|
||||||
|
)
|
||||||
|
|
||||||
|
function toggleFamily(name: string): void {
|
||||||
|
const next = new Set(openFamilies.value)
|
||||||
|
if (next.has(name)) next.delete(name)
|
||||||
|
else next.add(name)
|
||||||
|
openFamilies.value = next
|
||||||
|
}
|
||||||
|
|
||||||
|
function isFamilyOpen(name: string): boolean {
|
||||||
|
return openFamilies.value.has(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectedInFamily(fam: FamilyGroup): DedicatedConfigValue | null {
|
||||||
|
return fam.values.find(v => v.value === props.selected) ?? null
|
||||||
}
|
}
|
||||||
|
|
||||||
function priceFor(v: DedicatedConfigValue): number {
|
function priceFor(v: DedicatedConfigValue): number {
|
||||||
@@ -87,12 +115,42 @@ function priceLabel(v: DedicatedConfigValue): string {
|
|||||||
if (p === 0) return v.is_default ? 'default' : 'included'
|
if (p === 0) return v.is_default ? 'default' : 'included'
|
||||||
return `+$${p.toFixed(2)}${cycleSuffix.value}`
|
return `+$${p.toFixed(2)}${cycleSuffix.value}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function logoFor(slug: string): string {
|
||||||
|
return metaFor(slug).logo
|
||||||
|
}
|
||||||
|
|
||||||
|
function expandAll(): void {
|
||||||
|
openFamilies.value = new Set(familyGroups.value.map(f => f.family))
|
||||||
|
}
|
||||||
|
|
||||||
|
function collapseAll(): void {
|
||||||
|
// Keep the family with the selection open so the user always sees their pick.
|
||||||
|
const meta = props.selected ? metaFor(props.selected) : null
|
||||||
|
openFamilies.value = new Set(meta ? [meta.family] : [])
|
||||||
|
}
|
||||||
|
|
||||||
|
const allOpen = computed<boolean>(() => openFamilies.value.size === familyGroups.value.length)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="os-group">
|
<div class="os-group">
|
||||||
<div class="os-group__head">
|
<div class="os-group__head">
|
||||||
|
<div class="os-group__head-row">
|
||||||
<h4 class="os-group__title">{{ shortGroupLabel(group.name) }}</h4>
|
<h4 class="os-group__title">{{ shortGroupLabel(group.name) }}</h4>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="os-group__expand-toggle"
|
||||||
|
@click="allOpen ? collapseAll() : expandAll()"
|
||||||
|
>
|
||||||
|
<VIcon
|
||||||
|
:icon="allOpen ? 'tabler-fold' : 'tabler-fold-up'"
|
||||||
|
size="14"
|
||||||
|
class="me-1"
|
||||||
|
/>
|
||||||
|
{{ allOpen ? 'Collapse all' : 'Expand all' }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<p v-if="group.description" class="os-group__desc">{{ group.description }}</p>
|
<p v-if="group.description" class="os-group__desc">{{ group.description }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -101,8 +159,34 @@ function priceLabel(v: DedicatedConfigValue): string {
|
|||||||
v-for="fam in familyGroups"
|
v-for="fam in familyGroups"
|
||||||
:key="fam.family"
|
:key="fam.family"
|
||||||
class="os-family"
|
class="os-family"
|
||||||
|
:class="{
|
||||||
|
'os-family--open': isFamilyOpen(fam.family),
|
||||||
|
'os-family--has-selection': !!selectedInFamily(fam),
|
||||||
|
}"
|
||||||
>
|
>
|
||||||
<div class="os-family__heading">{{ fam.family }}</div>
|
<button
|
||||||
|
type="button"
|
||||||
|
class="os-family__header"
|
||||||
|
:aria-expanded="isFamilyOpen(fam.family)"
|
||||||
|
@click="toggleFamily(fam.family)"
|
||||||
|
>
|
||||||
|
<img :src="fam.logo" :alt="fam.family" class="os-family__logo" />
|
||||||
|
<span class="os-family__name">{{ fam.family }}</span>
|
||||||
|
<span v-if="selectedInFamily(fam)" class="os-family__selected">
|
||||||
|
<VIcon icon="tabler-circle-check-filled" size="14" color="primary" class="me-1" />
|
||||||
|
{{ selectedInFamily(fam)!.label }}
|
||||||
|
</span>
|
||||||
|
<span v-else class="os-family__count">
|
||||||
|
{{ fam.values.length }} option{{ fam.values.length === 1 ? '' : 's' }}
|
||||||
|
</span>
|
||||||
|
<VIcon
|
||||||
|
:icon="isFamilyOpen(fam.family) ? 'tabler-chevron-up' : 'tabler-chevron-down'"
|
||||||
|
size="18"
|
||||||
|
class="os-family__chevron"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div v-if="isFamilyOpen(fam.family)" class="os-family__panel">
|
||||||
<div class="os-family__grid">
|
<div class="os-family__grid">
|
||||||
<button
|
<button
|
||||||
v-for="v in fam.values"
|
v-for="v in fam.values"
|
||||||
@@ -134,6 +218,7 @@ function priceLabel(v: DedicatedConfigValue): string {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@@ -141,11 +226,38 @@ function priceLabel(v: DedicatedConfigValue): string {
|
|||||||
margin-bottom: 14px;
|
margin-bottom: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.os-group__head-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
.os-group__title {
|
.os-group__title {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
letter-spacing: -0.01em;
|
letter-spacing: -0.01em;
|
||||||
margin-bottom: 4px;
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.os-group__expand-toggle {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: rgba(var(--v-theme-on-surface), 0.6);
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid rgba(var(--v-theme-on-surface), 0.12);
|
||||||
|
padding: 4px 10px;
|
||||||
|
border-radius: 999px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: color 0.15s, border-color 0.15s, background-color 0.15s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: rgba(var(--v-theme-on-surface), 0.95);
|
||||||
|
border-color: rgba(var(--v-theme-primary), 0.45);
|
||||||
|
background: rgba(var(--v-theme-primary), 0.06);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.os-group__desc {
|
.os-group__desc {
|
||||||
@@ -157,16 +269,87 @@ function priceLabel(v: DedicatedConfigValue): string {
|
|||||||
.os-group__families {
|
.os-group__families {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 18px;
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.os-family__heading {
|
.os-family {
|
||||||
font-size: 11px;
|
border-radius: 12px;
|
||||||
|
border: 1px solid rgba(var(--v-theme-on-surface), 0.08);
|
||||||
|
background: rgba(var(--v-theme-on-surface), 0.03);
|
||||||
|
overflow: hidden;
|
||||||
|
transition: border-color 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.os-family--has-selection {
|
||||||
|
border-color: rgba(var(--v-theme-primary), 0.35);
|
||||||
|
background: rgba(var(--v-theme-primary), 0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
.os-family--open {
|
||||||
|
background: rgba(var(--v-theme-on-surface), 0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
.os-family__header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
width: 100%;
|
||||||
|
padding: 12px 14px;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(var(--v-theme-on-surface), 0.04);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.os-family__logo {
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
object-fit: contain;
|
||||||
|
flex-shrink: 0;
|
||||||
|
user-select: none;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.os-family__name {
|
||||||
|
font-size: 14px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
letter-spacing: 0.1em;
|
color: rgba(var(--v-theme-on-surface), 0.95);
|
||||||
text-transform: uppercase;
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.os-family__selected {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: rgb(var(--v-theme-primary));
|
||||||
|
padding: 4px 10px;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: rgba(var(--v-theme-primary), 0.1);
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-right: 4px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.os-family__count {
|
||||||
|
font-size: 11px;
|
||||||
|
color: rgba(var(--v-theme-on-surface), 0.5);
|
||||||
|
font-weight: 500;
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.os-family__chevron {
|
||||||
|
flex-shrink: 0;
|
||||||
color: rgba(var(--v-theme-on-surface), 0.55);
|
color: rgba(var(--v-theme-on-surface), 0.55);
|
||||||
margin-bottom: 8px;
|
}
|
||||||
|
|
||||||
|
.os-family__panel {
|
||||||
|
padding: 4px 14px 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.os-family__grid {
|
.os-family__grid {
|
||||||
@@ -181,10 +364,10 @@ function priceLabel(v: DedicatedConfigValue): string {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
padding: 18px 12px 14px;
|
padding: 16px 12px 12px;
|
||||||
border-radius: 12px;
|
border-radius: 10px;
|
||||||
border: 1px solid rgba(var(--v-theme-on-surface), 0.08);
|
border: 1px solid rgba(var(--v-theme-on-surface), 0.08);
|
||||||
background: rgba(var(--v-theme-on-surface), 0.03);
|
background: rgba(var(--v-theme-surface), 0.6);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all 0.15s ease;
|
transition: all 0.15s ease;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@@ -209,8 +392,8 @@ function priceLabel(v: DedicatedConfigValue): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.os-tile__logo {
|
.os-tile__logo {
|
||||||
width: 44px;
|
width: 40px;
|
||||||
height: 44px;
|
height: 40px;
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
|||||||