Martin Ziegler 5 месяцев назад
Родитель
Сommit
f3ddfb88c3

+ 4 - 0
frontend/public/icons/humidity-empty.svg

@@ -0,0 +1,4 @@
+<svg width="36" height="54" viewBox="0 0 36 54" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M17.8131 0.00537678C18.4463 -0.150913 20.3648 3.14642 20.8264 3.84781C25.4187 10.816 35.3089 26.9368 35.9383 34.8694C37.4182 53.5822 11.882 61.3357 2.53721 45.3789C-1.73471 38.0791 0.016016 32.2049 3.178 25.0232C6.99221 16.3662 12.6411 7.90372 17.8131 0.00537678ZM18.3738 7.24807L17.5881 7.48441C14.4452 12.9431 10.917 18.2341 8.19369 23.9368C4.6808 31.29 1.18317 38.5479 7.69403 45.5657C17.3058 55.9228 34.9847 46.8808 31.4604 32.8681C29.2558 24.0969 22.4207 15.2913 18.3776 7.24807H18.3738Z" fill="#D0D0D0"/>
+<path d="M8 46C12 48 24 48 28 46C26 50 22 52 18 52C14 52 10 50 8 46Z" fill="#1F8FEB"/>
+</svg>

+ 4 - 0
frontend/public/icons/humidity-full.svg

@@ -0,0 +1,4 @@
+<svg width="36" height="54" viewBox="0 0 36 54" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M17.9625 4.48059L4.77216 26.3154L2.08228 40.2175L10.0224 50.8414H23.1594L33.3246 42.1693V30.2455L17.9625 4.48059Z" fill="#1F8FEB"/>
+<path d="M17.7948 0.00537678C18.4273 -0.150913 20.3438 3.14642 20.8048 3.84781C25.3921 10.816 35.2715 26.9368 35.9001 34.8694C37.3784 53.5822 11.8702 61.3357 2.53562 45.3789C-1.73163 38.0829 0.0133678 32.2087 3.1757 25.027C6.98574 16.3662 12.6284 7.90372 17.7948 0.00537678ZM18.3549 7.24807L17.57 7.48441C14.4306 12.9431 10.9063 18.2341 8.1859 23.9368C4.67686 31.29 1.18305 38.5479 7.68679 45.5657C17.2881 55.9228 34.9476 46.8808 31.4271 32.8681C29.2249 24.0969 22.3974 15.2913 18.3587 7.24807H18.3549Z" fill="#D0D0D0"/>
+</svg>

+ 4 - 0
frontend/public/icons/humidity-half.svg

@@ -0,0 +1,4 @@
+<svg width="35" height="53" viewBox="0 0 35 53" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M17.3165 0.00379674C17.932 -0.149588 19.7971 3.08645 20.2458 3.77481C24.7103 10.6135 34.3251 26.4346 34.937 34.2198C36.3757 52.5848 11.5505 60.1942 2.46584 44.534C-1.68714 37.3735 0.0148377 31.6085 3.08879 24.5603C6.79681 16.0605 12.2884 7.75907 17.3165 0.00379674ZM17.8615 7.11561L17.0977 7.34755C14.0423 12.7048 10.6124 17.8974 7.96483 23.4941C4.54975 30.7107 1.14949 37.8337 7.47908 44.721C16.8233 54.8856 34.01 46.0117 30.5838 32.2595C28.4405 23.6512 21.7957 15.0093 17.8652 7.11561H17.8615Z" fill="#D0D0D0"/>
+<path d="M5.03547 30.112C9.64453 30.4936 11.632 35.7985 16.4154 35.791C19.6339 35.7873 20.2161 33.2283 22.3853 31.6197C31.6776 24.7286 33.5835 37.4894 27.9881 44.4254C18.1878 56.5653 -1.16063 44.6013 5.03917 30.1158L5.03547 30.112Z" fill="#1F8FEB"/>
+</svg>

+ 55 - 35
frontend/src/components/control/AMSSectionDual.tsx

@@ -29,6 +29,26 @@ function isLightColor(hex: string | null): boolean {
   return luminance > 0.5;
 }
 
+// Single humidity icon that fills based on level
+// <25% = empty (dry/good)
+// <40% = half filled
+// >=40% = full (wet/bad)
+function HumidityIcon({ humidity }: { humidity: number }) {
+  const getIconSrc = (): string => {
+    if (humidity < 25) return '/icons/humidity-empty.svg';
+    if (humidity < 40) return '/icons/humidity-half.svg';
+    return '/icons/humidity-full.svg';
+  };
+
+  return (
+    <img
+      src={getIconSrc()}
+      alt=""
+      className="w-2.5 h-[14px]"
+    />
+  );
+}
+
 interface AMSPanelContentProps {
   units: AMSUnit[];
   side: 'left' | 'right';
@@ -54,20 +74,16 @@ function AMSPanelContent({
 
   return (
     <div className="flex-1 min-w-0">
-      <div className="text-center text-[11px] font-semibold text-bambu-gray uppercase mb-2">
-        {side === 'left' ? 'Left Nozzle' : 'Right Nozzle'}
-      </div>
-
       {/* AMS Tab Selectors */}
-      <div className="flex gap-1.5 mb-2.5 p-1.5 bg-bambu-dark/50 rounded-lg w-fit">
+      <div className="flex gap-1.5 mb-2.5 p-1.5 bg-white dark:bg-bambu-dark rounded-lg">
         {units.map((unit, index) => (
           <button
             key={unit.id}
             onClick={() => onSelectAms(index)}
-            className={`flex items-center p-1.5 rounded border-2 transition-colors bg-bambu-dark ${
+            className={`flex items-center p-1.5 rounded border-2 transition-colors ${
               selectedAmsIndex === index
-                ? 'border-bambu-green'
-                : 'border-transparent hover:border-bambu-gray'
+                ? 'border-bambu-green bg-bambu-dark-secondary'
+                : 'bg-bambu-dark border-transparent hover:border-bambu-gray'
             }`}
           >
             <div className="flex gap-0.5">
@@ -87,30 +103,29 @@ function AMSPanelContent({
 
       {/* AMS Content */}
       {selectedUnit && (
-        <div className="bg-bambu-dark-secondary rounded-[10px] p-2.5">
-          {/* AMS Header - Humidity & Temp */}
-          <div className="flex items-center gap-2.5 text-xs text-bambu-gray mb-2">
+        <div className="bg-white dark:bg-bambu-dark-secondary rounded-[10px] p-2.5">
+          {/* AMS Header - Humidity & Temp - Centered */}
+          <div className="flex items-center justify-center gap-4 text-xs text-bambu-gray mb-2.5">
             {selectedUnit.humidity !== null && (
-              <span className="flex items-center gap-1">
-                <img src="/icons/water.svg" alt="" className="w-3.5 icon-theme" />
+              <span className="flex items-center gap-1.5">
+                <HumidityIcon humidity={selectedUnit.humidity} />
                 {selectedUnit.humidity} %
               </span>
             )}
             {selectedUnit.temp !== null && (
-              <span className="flex items-center gap-1">
+              <span className="flex items-center gap-1.5">
                 <img src="/icons/temperature.svg" alt="" className="w-3.5 icon-theme" />
                 {selectedUnit.temp}°C
               </span>
             )}
-            <span className="text-yellow-500 text-sm">☀</span>
           </div>
 
           {/* Slot Labels */}
-          <div className="flex justify-center gap-1.5 mb-1.5">
+          <div className="flex justify-center gap-2 mb-1.5">
             {selectedUnit.tray.map((tray, index) => (
               <div
                 key={tray.id}
-                className="w-12 flex items-center justify-center gap-0.5 text-[10px] text-bambu-gray px-1.5 py-[3px] bg-bambu-dark rounded-full border border-bambu-dark-tertiary"
+                className="w-14 flex items-center justify-center gap-0.5 text-[10px] text-bambu-gray px-1.5 py-[3px] bg-bambu-dark rounded-full border border-bambu-dark-tertiary"
               >
                 {slotPrefix}{index + 1}
                 <img src="/icons/reload.svg" alt="" className="w-2.5 h-2.5 icon-theme" />
@@ -119,7 +134,7 @@ function AMSPanelContent({
           </div>
 
           {/* AMS Slots - NO wiring here */}
-          <div className="flex justify-center gap-1.5">
+          <div className="flex justify-center gap-2">
             {selectedUnit.tray.map((tray) => {
               const globalTrayId = selectedUnit.id * 4 + tray.id;
               const isSelected = selectedTray === globalTrayId;
@@ -131,7 +146,7 @@ function AMSPanelContent({
                   key={tray.id}
                   onClick={() => !isEmpty && onSelectTray(isSelected ? null : globalTrayId)}
                   disabled={isEmpty || isPrinting}
-                  className={`w-12 h-[70px] rounded-md border-2 overflow-hidden transition-all bg-bambu-dark ${
+                  className={`w-14 h-[80px] rounded-md border-2 overflow-hidden transition-all bg-bambu-dark ${
                     isSelected
                       ? 'border-[#d4a84b]'
                       : 'border-bambu-dark-tertiary hover:border-bambu-gray'
@@ -192,6 +207,11 @@ function WiringLayer({ isDualNozzle }: WiringLayerProps) {
   return (
     <div className="relative w-full" style={{ height: '120px' }}>
       {/* SVG for all wiring - single coordinate system */}
+      {/* Slots are w-14 (56px) with gap-2 (8px), 4 slots = 248px total, centered in each ~300px panel */}
+      {/* Left panel center ~150, slots start at 150 - 124 = 26 */}
+      {/* Slot centers: 26+28=54, 54+64=118, 118+64=182, 182+64=246 */}
+      {/* Right panel center ~450, slots start at 450 - 124 = 326 */}
+      {/* Slot centers: 326+28=354, 354+64=418, 418+64=482, 482+64=546 */}
       <svg
         className="absolute inset-0 w-full h-full"
         viewBox="0 0 600 120"
@@ -199,44 +219,44 @@ function WiringLayer({ isDualNozzle }: WiringLayerProps) {
       >
         {/* Left panel wiring */}
         {/* Vertical lines from 4 slots */}
-        <line x1="68" y1="0" x2="68" y2="14" stroke="#909090" strokeWidth="2" />
-        <line x1="122" y1="0" x2="122" y2="14" stroke="#909090" strokeWidth="2" />
-        <line x1="176" y1="0" x2="176" y2="14" stroke="#909090" strokeWidth="2" />
-        <line x1="230" y1="0" x2="230" y2="14" stroke="#909090" strokeWidth="2" />
+        <line x1="54" y1="0" x2="54" y2="14" stroke="#909090" strokeWidth="2" />
+        <line x1="118" y1="0" x2="118" y2="14" stroke="#909090" strokeWidth="2" />
+        <line x1="182" y1="0" x2="182" y2="14" stroke="#909090" strokeWidth="2" />
+        <line x1="246" y1="0" x2="246" y2="14" stroke="#909090" strokeWidth="2" />
 
         {/* Horizontal bar connecting left slots */}
-        <line x1="68" y1="14" x2="230" y2="14" stroke="#909090" strokeWidth="2" />
+        <line x1="54" y1="14" x2="246" y2="14" stroke="#909090" strokeWidth="2" />
 
         {/* Left hub */}
-        <rect x="135" y="8" width="28" height="14" rx="2" fill="#c0c0c0" stroke="#909090" strokeWidth="1" />
+        <rect x="136" y="8" width="28" height="14" rx="2" fill="#c0c0c0" stroke="#909090" strokeWidth="1" />
 
         {/* Vertical from left hub down */}
-        <line x1="149" y1="22" x2="149" y2="36" stroke="#909090" strokeWidth="2" />
+        <line x1="150" y1="22" x2="150" y2="36" stroke="#909090" strokeWidth="2" />
 
         {/* Horizontal from left hub toward center */}
-        <line x1="149" y1="36" x2="288" y2="36" stroke="#909090" strokeWidth="2" />
+        <line x1="150" y1="36" x2="288" y2="36" stroke="#909090" strokeWidth="2" />
 
         {/* Vertical down to left extruder inlet */}
         <line x1="288" y1="36" x2="288" y2="85" stroke="#909090" strokeWidth="2" />
 
         {/* Right panel wiring */}
         {/* Vertical lines from 4 slots */}
-        <line x1="370" y1="0" x2="370" y2="14" stroke="#909090" strokeWidth="2" />
-        <line x1="424" y1="0" x2="424" y2="14" stroke="#909090" strokeWidth="2" />
-        <line x1="478" y1="0" x2="478" y2="14" stroke="#909090" strokeWidth="2" />
-        <line x1="532" y1="0" x2="532" y2="14" stroke="#909090" strokeWidth="2" />
+        <line x1="354" y1="0" x2="354" y2="14" stroke="#909090" strokeWidth="2" />
+        <line x1="418" y1="0" x2="418" y2="14" stroke="#909090" strokeWidth="2" />
+        <line x1="482" y1="0" x2="482" y2="14" stroke="#909090" strokeWidth="2" />
+        <line x1="546" y1="0" x2="546" y2="14" stroke="#909090" strokeWidth="2" />
 
         {/* Horizontal bar connecting right slots */}
-        <line x1="370" y1="14" x2="532" y2="14" stroke="#909090" strokeWidth="2" />
+        <line x1="354" y1="14" x2="546" y2="14" stroke="#909090" strokeWidth="2" />
 
         {/* Right hub */}
-        <rect x="437" y="8" width="28" height="14" rx="2" fill="#c0c0c0" stroke="#909090" strokeWidth="1" />
+        <rect x="436" y="8" width="28" height="14" rx="2" fill="#c0c0c0" stroke="#909090" strokeWidth="1" />
 
         {/* Vertical from right hub down */}
-        <line x1="451" y1="22" x2="451" y2="36" stroke="#909090" strokeWidth="2" />
+        <line x1="450" y1="22" x2="450" y2="36" stroke="#909090" strokeWidth="2" />
 
         {/* Horizontal from right hub toward center */}
-        <line x1="312" y1="36" x2="451" y2="36" stroke="#909090" strokeWidth="2" />
+        <line x1="312" y1="36" x2="450" y2="36" stroke="#909090" strokeWidth="2" />
 
         {/* Vertical down to right extruder inlet */}
         <line x1="312" y1="36" x2="312" y2="85" stroke="#909090" strokeWidth="2" />