Misc Spoolbuddy frontend fixes

This commit is contained in:
maziggy
2026-03-22 11:34:21 +01:00
parent 750accb17d
commit d6e883652d
8 changed files with 330 additions and 194 deletions
@@ -1,4 +1,5 @@
import type { AMSUnit, AMSTray } from '../../api/client';
import { getFillBarColor } from '../../utils/amsHelpers';
function trayColorToCSS(color: string | null): string {
if (!color) return '#808080';
@@ -140,16 +141,18 @@ interface SpoolSlotProps {
slotIndex: number;
isActive: boolean;
fillOverride?: number | null;
spoolmanFill?: number | null;
onClick?: () => void;
}
function SpoolSlot({ tray, slotIndex, isActive, fillOverride, onClick }: SpoolSlotProps) {
function SpoolSlot({ tray, slotIndex, isActive, fillOverride, spoolmanFill, onClick }: SpoolSlotProps) {
const isEmpty = isTrayEmpty(tray);
const color = trayColorToCSS(tray.tray_color);
const amsFill = tray.remain !== null && tray.remain !== undefined && tray.remain >= 0 ? tray.remain : null;
// If inventory says 0% but AMS reports positive remain, prefer AMS (#676)
const resolvedOverride = (fillOverride === 0 && amsFill !== null && amsFill > 0) ? null : fillOverride;
const effectiveFill = resolvedOverride ?? amsFill;
// Fill level fallback chain: Spoolman → Inventory → AMS remain
const effectiveFill = spoolmanFill ?? resolvedOverride ?? amsFill;
return (
<div
@@ -188,7 +191,7 @@ function SpoolSlot({ tray, slotIndex, isActive, fillOverride, onClick }: SpoolSl
className="h-full rounded-full transition-all"
style={{
width: `${effectiveFill}%`,
backgroundColor: effectiveFill > 50 ? '#22c55e' : effectiveFill > 20 ? '#f59e0b' : '#ef4444',
backgroundColor: getFillBarColor(effectiveFill),
}}
/>
</div>
@@ -215,9 +218,10 @@ interface AmsUnitCardProps {
nozzleSide?: 'L' | 'R' | null;
thresholds?: AmsThresholds;
fillOverrides?: Record<string, number>;
spoolmanFillOverrides?: Record<string, number>;
}
export function AmsUnitCard({ unit, activeSlot, onConfigureSlot, isDualNozzle, nozzleSide, thresholds, fillOverrides }: AmsUnitCardProps) {
export function AmsUnitCard({ unit, activeSlot, onConfigureSlot, isDualNozzle, nozzleSide, thresholds, fillOverrides, spoolmanFillOverrides }: AmsUnitCardProps) {
const trays = unit.tray || [];
const isHt = unit.is_ams_ht;
const slotCount = isHt ? 1 : 4;
@@ -275,6 +279,7 @@ export function AmsUnitCard({ unit, activeSlot, onConfigureSlot, isDualNozzle, n
slotIndex={i}
isActive={activeSlot === i}
fillOverride={fillOverrides?.[`${unit.id}-${i}`] ?? null}
spoolmanFill={spoolmanFillOverrides?.[`${unit.id}-${i}`] ?? null}
onClick={onConfigureSlot ? () => onConfigureSlot(unit.id, i, isTrayEmpty(tray) ? null : tray) : undefined}
/>
);
@@ -4,6 +4,8 @@ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { X, Loader2, CheckCircle, XCircle, Layers } from 'lucide-react';
import { api, type InventorySpool, type PrinterStatus, type AMSTray } from '../../api/client';
import { AmsUnitCard, NozzleBadge } from './AmsUnitCard';
import type { AmsThresholds } from './AmsUnitCard';
import { getFillBarColor } from '../../utils/amsHelpers';
function getAmsName(id: number): string {
if (id <= 3) return `AMS ${String.fromCharCode(65 + id)}`;
@@ -62,6 +64,41 @@ export function AssignToAmsModal({ isOpen, onClose, spool, printerId }: AssignTo
enabled: isOpen && printerId !== null,
});
const { data: settings } = useQuery({
queryKey: ['settings'],
queryFn: () => api.getSettings(),
enabled: isOpen,
staleTime: 5 * 60 * 1000,
});
const { data: assignments } = useQuery({
queryKey: ['spool-assignments', printerId],
queryFn: () => api.getAssignments(printerId!),
enabled: isOpen && printerId !== null,
staleTime: 30 * 1000,
});
// Build fill-level override map from inventory assignments
const fillOverrides = useMemo(() => {
const map: Record<string, number> = {};
if (!assignments) return map;
for (const a of assignments) {
const sp = a.spool;
if (sp && sp.label_weight > 0 && sp.weight_used != null) {
const fill = Math.round(Math.max(0, sp.label_weight - sp.weight_used) / sp.label_weight * 100);
map[`${a.ams_id}-${a.tray_id}`] = fill;
}
}
return map;
}, [assignments]);
const amsThresholds: AmsThresholds | undefined = settings ? {
humidityGood: Number(settings.ams_humidity_good) || 40,
humidityFair: Number(settings.ams_humidity_fair) || 60,
tempGood: Number(settings.ams_temp_good) || 28,
tempFair: Number(settings.ams_temp_fair) || 35,
} : undefined;
const isConnected = status?.connected ?? false;
const amsUnits = useMemo(() => status?.ams ?? [], [status?.ams]);
const regularAms = useMemo(() => amsUnits.filter(u => !u.is_ams_ht), [amsUnits]);
@@ -130,6 +167,7 @@ export function AssignToAmsModal({ isOpen, onClose, spool, printerId }: AssignTo
const items: {
key: string; label: string; amsId: number; trayId: number;
tray: AMSTray; isEmpty: boolean; nozzleSide: 'L' | 'R' | null;
effectiveFill: number | null;
}[] = [];
for (const unit of htAms) {
@@ -138,28 +176,37 @@ export function AssignToAmsModal({ isOpen, onClose, spool, printerId }: AssignTo
tray_id_name: null, tray_info_idx: null, remain: -1, k: null,
cali_idx: null, tag_uid: null, tray_uuid: null, nozzle_temp_min: null, nozzle_temp_max: null,
};
const invFill = fillOverrides[`${unit.id}-0`] ?? null;
const amsFill = tray.remain != null && tray.remain >= 0 ? tray.remain : null;
const resolvedInvFill = (invFill === 0 && amsFill !== null && amsFill > 0) ? null : invFill;
items.push({
key: `ht-${unit.id}`, label: getAmsName(unit.id),
amsId: unit.id, trayId: 0, tray, isEmpty: isTrayEmpty(tray),
nozzleSide: getNozzleSide(unit.id),
effectiveFill: resolvedInvFill ?? amsFill,
});
}
for (const extTray of vtTrays) {
const extTrayId = extTray.id ?? 254;
const extSlotTrayId = extTrayId - 254;
const extInvFill = fillOverrides[`255-${extSlotTrayId}`] ?? null;
const extAmsFill = extTray.remain != null && extTray.remain >= 0 ? extTray.remain : null;
const extResolvedInvFill = (extInvFill === 0 && extAmsFill !== null && extAmsFill > 0) ? null : extInvFill;
items.push({
key: `ext-${extTrayId}`,
label: isDualNozzle
? (extTrayId === 254 ? t('printers.extL', 'Ext-L') : t('printers.extR', 'Ext-R'))
: t('printers.ext', 'Ext'),
amsId: 255, trayId: extTrayId - 254, tray: extTray,
amsId: 255, trayId: extSlotTrayId, tray: extTray,
isEmpty: isTrayEmpty(extTray),
nozzleSide: isDualNozzle ? (extTrayId === 254 ? 'L' : 'R') : null,
effectiveFill: extResolvedInvFill ?? extAmsFill,
});
}
return items;
}, [htAms, vtTrays, isDualNozzle, t, getNozzleSide]);
}, [htAms, vtTrays, isDualNozzle, t, getNozzleSide, fillOverrides]);
if (!isOpen) return null;
@@ -236,6 +283,8 @@ export function AssignToAmsModal({ isOpen, onClose, spool, printerId }: AssignTo
onConfigureSlot={(_amsId, trayId) => handleSlotClick(unit.id, trayId)}
isDualNozzle={isDualNozzle}
nozzleSide={getNozzleSide(unit.id)}
thresholds={amsThresholds}
fillOverrides={fillOverrides}
/>
))}
</div>
@@ -244,7 +293,7 @@ export function AssignToAmsModal({ isOpen, onClose, spool, printerId }: AssignTo
{/* Single-slot items (HT + External) */}
{singleSlots.length > 0 && (
<div className="flex gap-2 shrink-0">
{singleSlots.map(({ key, label, amsId, trayId, tray, isEmpty, nozzleSide }) => {
{singleSlots.map(({ key, label, amsId, trayId, tray, isEmpty, nozzleSide, effectiveFill }) => {
const color = trayColorToCSS(tray.tray_color);
return (
<div
@@ -278,13 +327,13 @@ export function AssignToAmsModal({ isOpen, onClose, spool, printerId }: AssignTo
{isEmpty ? 'Empty' : tray.tray_type || '?'}
</div>
</div>
{!isEmpty && tray.remain != null && tray.remain >= 0 && (
{!isEmpty && effectiveFill != null && effectiveFill >= 0 && (
<div className="w-1.5 h-8 bg-bambu-dark-tertiary rounded-full overflow-hidden shrink-0 flex flex-col-reverse">
<div
className="w-full rounded-full"
style={{
height: `${tray.remain}%`,
backgroundColor: tray.remain > 50 ? '#22c55e' : tray.remain > 20 ? '#f59e0b' : '#ef4444',
height: `${effectiveFill}%`,
backgroundColor: getFillBarColor(effectiveFill),
}}
/>
</div>
+1 -38
View File
@@ -72,7 +72,7 @@ import { SkipObjectsModal, SkipObjectsIcon } from '../components/SkipObjectsModa
import { FileUploadModal } from '../components/FileUploadModal';
import { PrintModal } from '../components/PrintModal';
import { PrinterInfoModal } from '../components/PrinterInfoModal';
import { getGlobalTrayId } from '../utils/amsHelpers';
import { getGlobalTrayId, getFillBarColor, getSpoolmanFillLevel, getFallbackSpoolTag } from '../utils/amsHelpers';
import { getPrinterImage, getWifiStrength, filterCompatibleQueueItems } from '../utils/printer';
import { FilamentSlotCircle } from '../components/FilamentSlotCircle';
import { hexToColorName, parseFilamentColor, isLightColor } from '../utils/colors';
@@ -999,23 +999,6 @@ function getAmsLabel(amsId: number | string, trayCount: number): string {
return isHt ? `HT-${letter}` : `AMS-${letter}`;
}
// Get fill bar color based on spool fill level
function getFillBarColor(fillLevel: number): string {
if (fillLevel > 50) return '#00ae42'; // Green - good
if (fillLevel >= 15) return '#f59e0b'; // Amber - warning (<= 50%)
return '#ef4444'; // Red - critical (< 15%)
}
// Calculate fill level from Spoolman weight data (used as fallback when AMS reports 0%)
function getSpoolmanFillLevel(
linkedSpool: LinkedSpoolInfo | undefined
): number | null {
if (!linkedSpool?.remaining_weight || !linkedSpool?.filament_weight
|| linkedSpool.filament_weight <= 0) return null;
return Math.min(100, Math.round(
(linkedSpool.remaining_weight / linkedSpool.filament_weight) * 100
));
}
/**
* Check if a tray contains a Bambu Lab spool (RFID-tagged).
@@ -1042,26 +1025,6 @@ function isBambuLabSpool(tray: {
return false;
}
function toFixedHex(value: number, width: number): string {
const safe = Number.isFinite(value) ? Math.max(0, Math.trunc(value)) : 0;
return safe.toString(16).toUpperCase().padStart(width, '0').slice(-width);
}
// 32-bit FNV-1a hash -> 8-char hex (stable for alphanumeric serials)
function hashSerialToHex32(serial: string): string {
const input = (serial || '').trim().toUpperCase();
let hash = 0x811c9dc5;
for (let i = 0; i < input.length; i++) {
hash ^= input.charCodeAt(i);
hash = Math.imul(hash, 0x01000193);
}
return (hash >>> 0).toString(16).toUpperCase().padStart(8, '0');
}
function getFallbackSpoolTag(printerSerial: string, amsId: number, trayId: number): string {
// 16-char stable hex tag for slots without RFID identifiers
return `${hashSerialToHex32(printerSerial)}${toFixedHex(amsId, 4)}${toFixedHex(trayId, 4)}`;
}
function CoverImage({ url, printName }: { url: string | null; printName?: string }) {
const { t } = useTranslation();
@@ -6,6 +6,7 @@ import { Layers } from 'lucide-react';
import type { SpoolBuddyOutletContext } from '../../components/spoolbuddy/SpoolBuddyLayout';
import { api } from '../../api/client';
import type { PrinterStatus, AMSTray } from '../../api/client';
import { getGlobalTrayId, getFillBarColor, getSpoolmanFillLevel, getFallbackSpoolTag } from '../../utils/amsHelpers';
import { AmsUnitCard, HumidityIndicator, TemperatureIndicator, NozzleBadge } from '../../components/spoolbuddy/AmsUnitCard';
import type { AmsThresholds } from '../../components/spoolbuddy/AmsUnitCard';
import { ConfigureAmsSlotModal } from '../../components/ConfigureAmsSlotModal';
@@ -70,6 +71,23 @@ export function SpoolBuddyAmsPage() {
staleTime: 5 * 60 * 1000,
});
// Fetch Spoolman status to enable fill-level chain
const { data: spoolmanStatus } = useQuery({
queryKey: ['spoolman-status'],
queryFn: api.getSpoolmanStatus,
staleTime: 60 * 1000,
});
const spoolmanEnabled = spoolmanStatus?.enabled && spoolmanStatus?.connected;
// Fetch linked spools map (tag -> spool info) for Spoolman fill levels
const { data: linkedSpoolsData } = useQuery({
queryKey: ['linked-spools'],
queryFn: api.getLinkedSpools,
enabled: !!spoolmanEnabled,
staleTime: 30 * 1000,
});
const linkedSpools = linkedSpoolsData?.linked;
const { data: assignments } = useQuery({
queryKey: ['spool-assignments', selectedPrinterId],
queryFn: () => api.getAssignments(selectedPrinterId!),
@@ -92,11 +110,58 @@ export function SpoolBuddyAmsPage() {
return map;
}, [assignments]);
// Look up Spoolman fill level for a given tray
const printerSerial = printer?.serial_number ?? '';
const getSpoolmanFillForSlot = useCallback((amsId: number, trayId: number, tray: AMSTray | null): number | null => {
if (!linkedSpools || !printerSerial) return null;
const tag = (tray?.tray_uuid || tray?.tag_uid || getFallbackSpoolTag(printerSerial, amsId, trayId))?.toUpperCase();
const linkedSpool = tag ? linkedSpools[tag] : undefined;
return getSpoolmanFillLevel(linkedSpool);
}, [linkedSpools, printerSerial]);
const isConnected = status?.connected ?? false;
const amsUnits = useMemo(() => status?.ams ?? [], [status?.ams]);
// Cache AMS data to prevent it disappearing on idle/offline printers
const cachedAmsData = useRef<PrinterStatus['ams']>([]);
useEffect(() => {
if (status?.ams && status.ams.length > 0) {
cachedAmsData.current = status.ams;
}
}, [status?.ams]);
const amsUnits = useMemo(() => {
const live = status?.ams;
return (live && live.length > 0) ? live : (cachedAmsData.current ?? []);
}, [status?.ams]);
const regularAms = useMemo(() => amsUnits.filter(u => !u.is_ams_ht), [amsUnits]);
const htAms = useMemo(() => amsUnits.filter(u => u.is_ams_ht), [amsUnits]);
const trayNow = status?.tray_now ?? 255;
// Build Spoolman fill-level override map for regular AMS cards
const spoolmanFillOverrides = useMemo(() => {
const map: Record<string, number> = {};
if (!linkedSpools || !printerSerial) return map;
for (const unit of regularAms) {
for (let i = 0; i < (unit.tray?.length ?? 0); i++) {
const tray = unit.tray![i];
const fill = getSpoolmanFillForSlot(unit.id, i, isTrayEmpty(tray) ? null : tray);
if (fill !== null) map[`${unit.id}-${i}`] = fill;
}
}
return map;
}, [linkedSpools, printerSerial, regularAms, getSpoolmanFillForSlot]);
// Cache tray_now to prevent flickering when undefined values come in
// Valid tray IDs: 0-253 for AMS, 254 for external spool
// tray_now=255 means "no tray loaded" (Bambu protocol sentinel) — never active
const cachedTrayNow = useRef<number | undefined>(undefined);
const currentTrayNow = status?.tray_now;
if (currentTrayNow !== undefined && currentTrayNow !== 255) {
cachedTrayNow.current = currentTrayNow;
} else if (currentTrayNow === 255) {
cachedTrayNow.current = undefined;
}
const effectiveTrayNow = (currentTrayNow !== undefined && currentTrayNow !== 255)
? currentTrayNow
: cachedTrayNow.current;
const isDualNozzle = printer?.nozzle_count === 2 || status?.temperatures?.nozzle_2 !== undefined;
const vtTrays = useMemo(() => [...(status?.vt_tray ?? [])].sort((a, b) => (a.id ?? 254) - (b.id ?? 254)), [status?.vt_tray]);
@@ -141,17 +206,17 @@ export function SpoolBuddyAmsPage() {
} | null>(null);
const getActiveSlotForAms = useCallback((amsId: number): number | null => {
if (trayNow === 255 || trayNow === 254) return null;
if (effectiveTrayNow === undefined) return null;
if (amsId <= 3) {
const activeAmsId = Math.floor(trayNow / 4);
if (activeAmsId === amsId) return trayNow % 4;
const activeAmsId = Math.floor(effectiveTrayNow / 4);
if (activeAmsId === amsId) return effectiveTrayNow % 4;
}
if (amsId >= 128 && amsId <= 135) {
const htIndex = amsId - 128;
if (trayNow === 16 + htIndex) return 0;
// AMS-HT: global tray ID equals the AMS unit ID itself (128, 129, ...)
if (effectiveTrayNow === getGlobalTrayId(amsId, 0, false)) return 0;
}
return null;
}, [trayNow]);
}, [effectiveTrayNow]);
const handleAmsSlotClick = useCallback((amsId: number, trayId: number, tray: AMSTray | null) => {
const globalTrayId = amsId >= 128 ? (amsId - 128) * 4 + trayId + 64 : amsId * 4 + trayId;
@@ -226,6 +291,8 @@ export function SpoolBuddyAmsPage() {
tray_id_name: null, tray_info_idx: null, remain: -1, k: null,
cali_idx: null, tag_uid: null, tray_uuid: null, nozzle_temp_min: null, nozzle_temp_max: null,
};
// Fill level fallback chain: Spoolman → Inventory → AMS remain
const spoolmanFill = getSpoolmanFillForSlot(unit.id, 0, isTrayEmpty(tray) ? null : tray);
const invFill = fillOverrides[`${unit.id}-0`] ?? null;
const amsFill = tray.remain != null && tray.remain >= 0 ? tray.remain : null;
// If inventory says 0% but AMS reports positive remain, prefer AMS (#676)
@@ -239,20 +306,23 @@ export function SpoolBuddyAmsPage() {
temp: unit.temp,
humidity: unit.humidity,
nozzleSide: getNozzleSide(unit.id),
effectiveFill: resolvedInvFill ?? amsFill,
effectiveFill: spoolmanFill ?? resolvedInvFill ?? amsFill,
onClick: () => handleAmsSlotClick(unit.id, 0, isTrayEmpty(tray) ? null : tray),
});
}
for (const extTray of vtTrays) {
const extTrayId = extTray.id ?? 254;
// tray_now=255 means "no tray loaded" (idle) — never active
const isExtActive = trayNow === 255 ? false
: isDualNozzle && trayNow === 254
? (extTrayId === 254 && status?.active_extruder === 1) ||
(extTrayId === 255 && status?.active_extruder === 0)
: trayNow === extTrayId;
// On dual-nozzle (H2C/H2D), tray_now=254 means "external spool"
// generically — use active_extruder to determine L vs R:
// extruder 1=left → Ext-L (id=254), extruder 0=right → Ext-R (id=255)
const isExtActive = isDualNozzle && effectiveTrayNow === 254
? (extTrayId === 254 && status?.active_extruder === 1) ||
(extTrayId === 255 && status?.active_extruder === 0)
: effectiveTrayNow === extTrayId;
const extSlotTrayId = extTrayId - 254;
// Fill level fallback chain: Spoolman → Inventory → AMS remain
const extSpoolmanFill = getSpoolmanFillForSlot(255, extSlotTrayId, isTrayEmpty(extTray) ? null : extTray);
const extInvFill = fillOverrides[`255-${extSlotTrayId}`] ?? null;
const extAmsFill = extTray.remain != null && extTray.remain >= 0 ? extTray.remain : null;
// If inventory says 0% but AMS reports positive remain, prefer AMS (#676)
@@ -266,13 +336,13 @@ export function SpoolBuddyAmsPage() {
isEmpty: isTrayEmpty(extTray),
isActive: isExtActive,
nozzleSide: null,
effectiveFill: extResolvedInvFill ?? extAmsFill,
effectiveFill: extSpoolmanFill ?? extResolvedInvFill ?? extAmsFill,
onClick: () => handleExtSlotClick(extTray),
});
}
return items;
}, [htAms, vtTrays, isDualNozzle, trayNow, status?.active_extruder, t, getActiveSlotForAms, getNozzleSide, handleAmsSlotClick, handleExtSlotClick, fillOverrides]);
}, [htAms, vtTrays, isDualNozzle, effectiveTrayNow, status?.active_extruder, t, getActiveSlotForAms, getNozzleSide, handleAmsSlotClick, handleExtSlotClick, fillOverrides, getSpoolmanFillForSlot]);
return (
<div className="h-full flex flex-col p-4">
@@ -312,6 +382,7 @@ export function SpoolBuddyAmsPage() {
nozzleSide={getNozzleSide(unit.id)}
thresholds={amsThresholds}
fillOverrides={fillOverrides}
spoolmanFillOverrides={spoolmanFillOverrides}
/>
))}
</div>
@@ -381,7 +452,7 @@ export function SpoolBuddyAmsPage() {
className="w-full rounded-full"
style={{
height: `${effectiveFill}%`,
backgroundColor: effectiveFill > 50 ? '#22c55e' : effectiveFill > 20 ? '#f59e0b' : '#ef4444',
backgroundColor: getFillBarColor(effectiveFill),
}}
/>
</div>
+48
View File
@@ -129,6 +129,54 @@ export function getGlobalTrayId(
return amsId * 4 + trayId;
}
/**
* Get fill bar color based on spool fill level.
* Matches PrintersPage thresholds and Bambu Lab brand green.
*/
export function getFillBarColor(fillLevel: number): string {
if (fillLevel > 50) return '#00ae42'; // Green - good
if (fillLevel >= 15) return '#f59e0b'; // Amber - warning (<= 50%)
return '#ef4444'; // Red - critical (< 15%)
}
/**
* Calculate fill level from Spoolman weight data.
* Used as the first source in the Spoolman → Inventory → AMS fill chain.
*/
export function getSpoolmanFillLevel(
linkedSpool: { remaining_weight: number | null; filament_weight: number | null } | undefined
): number | null {
if (!linkedSpool?.remaining_weight || !linkedSpool?.filament_weight
|| linkedSpool.filament_weight <= 0) return null;
return Math.min(100, Math.round(
(linkedSpool.remaining_weight / linkedSpool.filament_weight) * 100
));
}
function toFixedHex(value: number, width: number): string {
const safe = Number.isFinite(value) ? Math.max(0, Math.trunc(value)) : 0;
return safe.toString(16).toUpperCase().padStart(width, '0').slice(-width);
}
// 32-bit FNV-1a hash -> 8-char hex (stable for alphanumeric serials)
function hashSerialToHex32(serial: string): string {
const input = (serial || '').trim().toUpperCase();
let hash = 0x811c9dc5;
for (let i = 0; i < input.length; i++) {
hash ^= input.charCodeAt(i);
hash = Math.imul(hash, 0x01000193);
}
return (hash >>> 0).toString(16).toUpperCase().padStart(8, '0');
}
/**
* Generate a stable fallback spool tag for slots without RFID identifiers.
* Returns a 16-char hex string derived from the printer serial + slot position.
*/
export function getFallbackSpoolTag(printerSerial: string, amsId: number, trayId: number): string {
return `${hashSerialToHex32(printerSerial)}${toFixedHex(amsId, 4)}${toFixedHex(trayId, 4)}`;
}
/**
* Get minimum datetime for scheduling (now + 1 minute).
* Returns ISO string format for datetime-local input.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -23,8 +23,8 @@
<!-- Splash screens for iOS -->
<link rel="apple-touch-startup-image" href="/img/android-chrome-512x512.png" />
<script type="module" crossorigin src="/assets/index-DUZN_weu.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-DI9VPacx.css">
<script type="module" crossorigin src="/assets/index-D5Vkuxg1.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-CJ-drcFM.css">
</head>
<body>
<div id="root"></div>