feat(#1115): add nozzle icon to dual-nozzle status card

The dual-nozzle active-extruder card was the only tile in the printer
  status row without a theme icon, making the row look uneven on H2D /
  H2S / H2C. Adds a schematic nozzle icon (filament body + heater block
  + tip) matching the SVG @m4rtini2 contributed, sized and coloured to
  match the adjacent Nozzle/Bed/Chamber temperature cards.
This commit is contained in:
maziggy
2026-04-24 18:11:16 +02:00
parent 7f11618e1e
commit 23ef821851
4 changed files with 73 additions and 49 deletions
+1
View File
@@ -16,6 +16,7 @@ All notable changes to Bambuddy will be documented in this file.
**Test coverage** — 46 unit tests for `services/makerworld.py` (header shape, API base, `get_design`/`get_design_instances`/`get_profile`, `get_profile_download` 200/401/403/404/no-token, `download_3mf` SSRF rejection of 4 hostile hosts, S3 path delegation, CDN path with minimal headers, size-cap, `_download_s3_urllib` happy/redirect/size/network paths, `fetch_thumbnail` with `follow_redirects=False`); 19 route tests (`/resolve`, `/import` with folder autocreation + explicit folder + dedupe + filename basename + profile_id response, `/recent-imports` with empty-list / ordering / pydantic shape / limit clamping, `_canonical_url` unit); 12 frontend tests (button labels, slicer-name interpolation, URL-change detection, inline post-import actions, Recent imports rendering, DOMPurify `<script>` strip).
### Changed
- **Nozzle icon on the dual-nozzle status card** ([#1115](https://github.com/maziggy/bambuddy/issues/1115)) — the dual-nozzle active-extruder card on the printer status bar was the only card in that row without a theme icon (the Nozzle/Bed/Chamber temperature cards all carry a thermometer icon), which left the row looking visually uneven on H2D / H2S / H2C. Adds a small schematic nozzle icon (filament body + heater block + tip) above the L/R diameter labels, styled in amber-400 to match the card's active-extruder accent. SVG design contributed by @m4rtini2.
- **Settings page: permission-gated instead of admin-only** — the Settings sidebar entry has always been visible to any user holding `settings:read`, but the route guard required admin role, so a non-admin with `settings:read` would see the entry, click it, and get silently redirected back to the dashboard. The route guard now matches the sidebar: any user with `settings:read` can open the page, and the individual tabs / cards continue to enforce their own per-feature permissions (`users:read`, `groups:update`, `oidc:*`, etc. — many of them admin-only, some not). Group editor routes moved to permission-based guards too (`groups:create` for `/groups/new`, `groups:update` for `/groups/:id/edit`), so permission delegation works end-to-end. Admins retain full access since admins implicitly hold every permission.
### Fixed
+24 -1
View File
@@ -595,6 +595,28 @@ function ThermometerFull({ className }: { className?: string }) {
);
}
// Nozzle icon - schematic hot-end view (filament body + heater block + tip).
// Added for visual parity with the thermometer icons on the dual-nozzle card
// that previously had no icon at all (#1115, design by @m4rtini2).
function NozzleIcon({ className }: { className?: string }) {
return (
<svg
className={className}
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
>
<rect x="9.2" y="3.4" width="5.6" height="8.1" />
<rect x="6" y="11.5" width="12.1" height="3.7" />
<path d="M 7.3 15.2 L 12.1 19.6 L 16.7 15.2" />
</svg>
);
}
// Heater thermometer icon - filled when heating, outline when off
interface HeaterThermometerProps {
className?: string;
@@ -2882,7 +2904,8 @@ function PrinterCard({
filamentInfo={filamentInfo}
>
<div className="text-center px-3 py-1.5 bg-bambu-dark rounded-lg h-full flex flex-col justify-center items-center cursor-default" title={t('printers.activeNozzle', { nozzle: activeNozzle === 'L' ? t('common.left') : t('common.right') })}>
<div className="flex items-center gap-2 mb-1">
<NozzleIcon className="w-3.5 h-3.5 mb-0.5 text-amber-400" />
<div className="flex items-center gap-2">
<span className={`text-[11px] font-bold ${activeNozzle === 'L' ? 'text-amber-400' : 'text-gray-500'}`}>
L{leftNozzleSlot?.nozzle_diameter ? ` ${leftNozzleSlot.nozzle_diameter}` : ''}
</span>
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -26,7 +26,7 @@
<!-- Splash screens for iOS -->
<link rel="apple-touch-startup-image" href="/img/android-chrome-512x512.png" />
<script type="module" crossorigin src="/assets/index-C8M6qM08.js"></script>
<script type="module" crossorigin src="/assets/index-Dm505pTR.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-CgXLDG6B.css">
</head>
<body>