mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-09-30 03:01:21 +02:00
fix: display friendly legacy printer model names in stream overlay (#3134)
This commit is contained in:
@@ -150,6 +150,17 @@ describe('StreamOverlayPage', () => {
|
||||
if (query === '?show=model') expect(screen.queryByText(/X1 Carbon/)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it.each([
|
||||
['BL-P001', 'X1C'],
|
||||
['O1D', 'H2D'],
|
||||
['H2D', 'H2D'],
|
||||
['Future printer', 'Future printer'],
|
||||
])('displays the saved model %s as %s', async (model, displayName) => {
|
||||
server.use(http.get('/api/v1/printers/:id', () => HttpResponse.json({ ...mockPrinter, model })));
|
||||
renderOverlayPage(1, '?show=model');
|
||||
expect(await screen.findByText(displayName)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it.each([null, ''])('omits a missing model without adding a separator (%s)', async (model) => {
|
||||
server.use(http.get('/api/v1/printers/:id', () => HttpResponse.json({ ...mockPrinter, model })));
|
||||
renderOverlayPage(1, '?show=printer,model');
|
||||
@@ -157,7 +168,7 @@ describe('StreamOverlayPage', () => {
|
||||
expect(screen.queryByText(/·/)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it.each([['H2D', 'Workshop · H2D'], [null, 'Workshop'], ['', 'Workshop']] as const)('reads the model (%s) from the OBS token feed without requesting printer details', async (model, identity) => {
|
||||
it.each([['H2D', 'H2D'], ['BL-P001', 'X1C'], ['O1D', 'H2D'], [null, ''], ['', '']] as const)('reads the model %s from the OBS token feed without requesting printer details', async (model, displayName) => {
|
||||
let printerHit = false;
|
||||
server.use(
|
||||
http.get('/api/v1/printers/:id/overlay-status', () => HttpResponse.json({
|
||||
@@ -170,7 +181,7 @@ describe('StreamOverlayPage', () => {
|
||||
}),
|
||||
);
|
||||
renderOverlayPage(1, '?token=obs-tok&show=printer,model');
|
||||
expect(await screen.findByText(identity)).toBeInTheDocument();
|
||||
expect(await screen.findByText(displayName ? `Workshop · ${displayName}` : 'Workshop')).toBeInTheDocument();
|
||||
expect(printerHit).toBe(false);
|
||||
if (!model) expect(screen.queryByText(/·/)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useState, useEffect, useLayoutEffect, useMemo, useRef, useCallback } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { compareFwVersions } from '../utils/firmwareVersion';
|
||||
import { mapModelCode } from '../utils/printerModel';
|
||||
import { formatPrintName } from '../utils/printName';
|
||||
import { isBedSlinger } from '../utils/bedSlinger';
|
||||
import { computePopoverPosition, type PopoverPosition } from '../utils/popoverPosition';
|
||||
@@ -1747,51 +1748,6 @@ const MODELS_WITH_EXHAUST_LABEL: ReadonlySet<string> = new Set([
|
||||
'X2D',
|
||||
]);
|
||||
|
||||
// Map SSDP model codes to display names
|
||||
function mapModelCode(ssdpModel: string | null): string {
|
||||
if (!ssdpModel) return '';
|
||||
const modelMap: Record<string, string> = {
|
||||
// H2 Series
|
||||
'O1D': 'H2D',
|
||||
'O1E': 'H2D Pro',
|
||||
'O2D': 'H2D Pro',
|
||||
'O1C': 'H2C',
|
||||
'O1C2': 'H2C',
|
||||
'O1S': 'H2S',
|
||||
// X1 Series
|
||||
'BL-P001': 'X1C',
|
||||
'BL-P002': 'X1',
|
||||
'BL-P003': 'X1E',
|
||||
// X2 Series
|
||||
'N6': 'X2D',
|
||||
// A2 Series
|
||||
'N9': 'A2L',
|
||||
// P Series
|
||||
'C11': 'P1S',
|
||||
'C12': 'P1P',
|
||||
'C13': 'P2S',
|
||||
// A1 Series
|
||||
'N2S': 'A1',
|
||||
'N1': 'A1 Mini',
|
||||
// Direct matches
|
||||
'X1C': 'X1C',
|
||||
'X1': 'X1',
|
||||
'X1E': 'X1E',
|
||||
'X2D': 'X2D',
|
||||
'P1S': 'P1S',
|
||||
'P1P': 'P1P',
|
||||
'P2S': 'P2S',
|
||||
'A1': 'A1',
|
||||
'A1 Mini': 'A1 Mini',
|
||||
'A2L': 'A2L',
|
||||
'H2D': 'H2D',
|
||||
'H2D Pro': 'H2D Pro',
|
||||
'H2C': 'H2C',
|
||||
'H2S': 'H2S',
|
||||
};
|
||||
return modelMap[ssdpModel] || ssdpModel;
|
||||
}
|
||||
|
||||
// ─── AMS Name Hover Card ──────────────────────────────────────────────────────
|
||||
// Wraps the AMS label (e.g. "AMS-A") and shows a popup with:
|
||||
// • User-defined friendly name (editable, protected by printers:update)
|
||||
|
||||
@@ -5,6 +5,7 @@ import { useTranslation } from 'react-i18next';
|
||||
import { Layers, Clock, Timer, Printer, Flame, Square, Box } from 'lucide-react';
|
||||
import { api, ApiError, withStreamToken } from '../api/client';
|
||||
import { formatDuration, formatETA, type TimeFormat } from '../utils/date';
|
||||
import { mapModelCode } from '../utils/printerModel';
|
||||
|
||||
type TFunction = (key: string, options?: Record<string, unknown>) => string;
|
||||
|
||||
@@ -217,7 +218,7 @@ export function StreamOverlayPage() {
|
||||
);
|
||||
const printerIdentity = [
|
||||
config.showPrinter ? printer?.name : null,
|
||||
config.showModel ? printer?.model : null,
|
||||
config.showModel ? mapModelCode(printer?.model ?? null) : null,
|
||||
].filter(Boolean).join(' · ');
|
||||
const status = kiosk ? overlay : statusData;
|
||||
const timeFormat: TimeFormat = (kiosk ? overlay?.time_format : settings?.time_format) || 'system';
|
||||
|
||||
@@ -8,6 +8,7 @@ import { api } from '../../api/client';
|
||||
import type { PrinterStatus, AMSTray, SpoolAssignment } from '../../api/client';
|
||||
import { getGlobalTrayId, getFillBarColor, getSpoolmanFillLevel, getFallbackSpoolTag, formatSlotLabel, isBambuLabSpool, resolveSlotNozzleDiameter } from '../../utils/amsHelpers';
|
||||
import { getSwatchStyle, resolveSpoolColorName } from '../../utils/colors';
|
||||
import { mapModelCode } from '../../utils/printerModel';
|
||||
import { spoolSwatchStyle } from '../../components/spoolbuddy/spoolPaint';
|
||||
|
||||
/**
|
||||
@@ -39,21 +40,6 @@ function getAmsName(amsId: number): string {
|
||||
return `AMS ${amsId}`;
|
||||
}
|
||||
|
||||
function mapModelCode(ssdpModel: string | null): string {
|
||||
if (!ssdpModel) return '';
|
||||
const modelMap: Record<string, string> = {
|
||||
'O1D': 'H2D', 'O1E': 'H2D Pro', 'O2D': 'H2D Pro', 'O1C': 'H2C', 'O1C2': 'H2C', 'O1S': 'H2S',
|
||||
'BL-P001': 'X1C', 'BL-P002': 'X1', 'BL-P003': 'X1E',
|
||||
'N6': 'X2D',
|
||||
'N9': 'A2L',
|
||||
'C11': 'P1S', 'C12': 'P1P', 'C13': 'P2S',
|
||||
'N2S': 'A1', 'N1': 'A1 Mini',
|
||||
'X1C': 'X1C', 'X1': 'X1', 'X1E': 'X1E', 'X2D': 'X2D', 'P1S': 'P1S', 'P1P': 'P1P', 'P2S': 'P2S',
|
||||
'A1': 'A1', 'A1 Mini': 'A1 Mini', 'A2L': 'A2L', 'H2D': 'H2D', 'H2D Pro': 'H2D Pro', 'H2C': 'H2C', 'H2S': 'H2S',
|
||||
};
|
||||
return modelMap[ssdpModel] || ssdpModel;
|
||||
}
|
||||
|
||||
function isTrayEmpty(tray: AMSTray): boolean {
|
||||
return !tray.tray_type || tray.tray_type === '';
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
// Map SSDP model codes to display names
|
||||
export function mapModelCode(ssdpModel: string | null): string {
|
||||
if (!ssdpModel) return '';
|
||||
const modelMap: Record<string, string> = {
|
||||
// H2 Series
|
||||
'O1D': 'H2D',
|
||||
'O1E': 'H2D Pro',
|
||||
'O2D': 'H2D Pro',
|
||||
'O1C': 'H2C',
|
||||
'O1C2': 'H2C',
|
||||
'O1S': 'H2S',
|
||||
// X1 Series
|
||||
'BL-P001': 'X1C',
|
||||
'BL-P002': 'X1',
|
||||
'BL-P003': 'X1E',
|
||||
// X2 Series
|
||||
'N6': 'X2D',
|
||||
// A2 Series
|
||||
'N9': 'A2L',
|
||||
// P Series
|
||||
'C11': 'P1S',
|
||||
'C12': 'P1P',
|
||||
'C13': 'P2S',
|
||||
// A1 Series
|
||||
'N2S': 'A1',
|
||||
'N1': 'A1 Mini',
|
||||
// Direct matches
|
||||
'X1C': 'X1C',
|
||||
'X1': 'X1',
|
||||
'X1E': 'X1E',
|
||||
'X2D': 'X2D',
|
||||
'P1S': 'P1S',
|
||||
'P1P': 'P1P',
|
||||
'P2S': 'P2S',
|
||||
'A1': 'A1',
|
||||
'A1 Mini': 'A1 Mini',
|
||||
'A2L': 'A2L',
|
||||
'H2D': 'H2D',
|
||||
'H2D Pro': 'H2D Pro',
|
||||
'H2C': 'H2C',
|
||||
'H2S': 'H2S',
|
||||
};
|
||||
return modelMap[ssdpModel] || ssdpModel;
|
||||
}
|
||||
Reference in New Issue
Block a user