Browse Source

Added HMS health status; Added MQTT debug log window

Martin Ziegler 6 months ago
parent
commit
8b2841be1e

+ 77 - 0
backend/app/api/routes/printers.py

@@ -18,6 +18,7 @@ from backend.app.schemas.printer import (
     PrinterUpdate,
     PrinterResponse,
     PrinterStatus,
+    HMSErrorResponse,
 )
 from backend.app.services.printer_manager import printer_manager
 from backend.app.services.bambu_ftp import (
@@ -138,6 +139,12 @@ async def get_printer_status(printer_id: int, db: AsyncSession = Depends(get_db)
     if state.state == "RUNNING" and state.gcode_file:
         cover_url = f"/api/v1/printers/{printer_id}/cover"
 
+    # Convert HMS errors to response format
+    hms_errors = [
+        HMSErrorResponse(code=e.code, module=e.module, severity=e.severity)
+        for e in (state.hms_errors or [])
+    ]
+
     return PrinterStatus(
         id=printer_id,
         name=printer.name,
@@ -152,6 +159,7 @@ async def get_printer_status(printer_id: int, db: AsyncSession = Depends(get_db)
         total_layers=state.total_layers,
         temperatures=state.temperatures,
         cover_url=cover_url,
+        hms_errors=hms_errors,
     )
 
 
@@ -410,3 +418,72 @@ async def get_printer_storage(
     storage_info = await get_storage_info_async(printer.ip_address, printer.access_code)
 
     return storage_info or {"used_bytes": None, "free_bytes": None}
+
+
+# ============================================
+# MQTT Debug Logging Endpoints
+# ============================================
+
+@router.post("/{printer_id}/logging/enable")
+async def enable_mqtt_logging(printer_id: int, db: AsyncSession = Depends(get_db)):
+    """Enable MQTT message logging for a printer."""
+    result = await db.execute(select(Printer).where(Printer.id == printer_id))
+    printer = result.scalar_one_or_none()
+    if not printer:
+        raise HTTPException(404, "Printer not found")
+
+    success = printer_manager.enable_logging(printer_id, True)
+    if not success:
+        raise HTTPException(400, "Printer not connected")
+
+    return {"logging_enabled": True}
+
+
+@router.post("/{printer_id}/logging/disable")
+async def disable_mqtt_logging(printer_id: int, db: AsyncSession = Depends(get_db)):
+    """Disable MQTT message logging for a printer."""
+    result = await db.execute(select(Printer).where(Printer.id == printer_id))
+    printer = result.scalar_one_or_none()
+    if not printer:
+        raise HTTPException(404, "Printer not found")
+
+    success = printer_manager.enable_logging(printer_id, False)
+    if not success:
+        raise HTTPException(400, "Printer not connected")
+
+    return {"logging_enabled": False}
+
+
+@router.get("/{printer_id}/logging")
+async def get_mqtt_logs(printer_id: int, db: AsyncSession = Depends(get_db)):
+    """Get MQTT message logs for a printer."""
+    result = await db.execute(select(Printer).where(Printer.id == printer_id))
+    printer = result.scalar_one_or_none()
+    if not printer:
+        raise HTTPException(404, "Printer not found")
+
+    logs = printer_manager.get_logs(printer_id)
+    return {
+        "logging_enabled": printer_manager.is_logging_enabled(printer_id),
+        "logs": [
+            {
+                "timestamp": log.timestamp,
+                "topic": log.topic,
+                "direction": log.direction,
+                "payload": log.payload,
+            }
+            for log in logs
+        ],
+    }
+
+
+@router.delete("/{printer_id}/logging")
+async def clear_mqtt_logs(printer_id: int, db: AsyncSession = Depends(get_db)):
+    """Clear MQTT message logs for a printer."""
+    result = await db.execute(select(Printer).where(Printer.id == printer_id))
+    printer = result.scalar_one_or_none()
+    if not printer:
+        raise HTTPException(404, "Printer not found")
+
+    printer_manager.clear_logs(printer_id)
+    return {"status": "cleared"}

+ 0 - 1
backend/app/api/routes/settings.py

@@ -1,4 +1,3 @@
-import json
 from fastapi import APIRouter, Depends
 from sqlalchemy.ext.asyncio import AsyncSession
 from sqlalchemy import select

+ 7 - 0
backend/app/schemas/printer.py

@@ -34,6 +34,12 @@ class PrinterResponse(PrinterBase):
         from_attributes = True
 
 
+class HMSErrorResponse(BaseModel):
+    code: str
+    module: int
+    severity: int  # 1=fatal, 2=serious, 3=common, 4=info
+
+
 class PrinterStatus(BaseModel):
     id: int
     name: str
@@ -48,3 +54,4 @@ class PrinterStatus(BaseModel):
     total_layers: int | None = None
     temperatures: dict | None = None
     cover_url: str | None = None
+    hms_errors: list[HMSErrorResponse] = []

+ 84 - 0
backend/app/services/bambu_mqtt.py

@@ -1,12 +1,32 @@
 import json
 import ssl
 import asyncio
+from collections import deque
+from datetime import datetime
 from typing import Callable
 from dataclasses import dataclass, field
 
 import paho.mqtt.client as mqtt
 
 
+@dataclass
+class MQTTLogEntry:
+    """Log entry for MQTT message debugging."""
+    timestamp: str
+    topic: str
+    direction: str  # "in" or "out"
+    payload: dict
+
+
+@dataclass
+class HMSError:
+    """Health Management System error from printer."""
+    code: str
+    module: int
+    severity: int  # 1=fatal, 2=serious, 3=common, 4=info
+    message: str = ""
+
+
 @dataclass
 class PrinterState:
     connected: bool = False
@@ -21,6 +41,7 @@ class PrinterState:
     raw_data: dict = field(default_factory=dict)
     gcode_file: str | None = None
     subtask_id: str | None = None
+    hms_errors: list = field(default_factory=list)  # List of HMSError
 
 
 class BambuMQTTClient:
@@ -49,6 +70,8 @@ class BambuMQTTClient:
         self._loop: asyncio.AbstractEventLoop | None = None
         self._previous_gcode_state: str | None = None
         self._previous_gcode_file: str | None = None
+        self._message_log: deque[MQTTLogEntry] = deque(maxlen=100)
+        self._logging_enabled: bool = False
 
     @property
     def topic_subscribe(self) -> str:
@@ -75,6 +98,14 @@ class BambuMQTTClient:
     def _on_message(self, client, userdata, msg):
         try:
             payload = json.loads(msg.payload.decode())
+            # Log message if logging is enabled
+            if self._logging_enabled:
+                self._message_log.append(MQTTLogEntry(
+                    timestamp=datetime.now().isoformat(),
+                    topic=msg.topic,
+                    direction="in",
+                    payload=payload,
+                ))
             self._process_message(payload)
         except json.JSONDecodeError:
             pass
@@ -131,6 +162,32 @@ class BambuMQTTClient:
         if temps:
             self.state.temperatures = temps
 
+        # Parse HMS (Health Management System) errors
+        if "hms" in data:
+            hms_list = data["hms"]
+            self.state.hms_errors = []
+            if isinstance(hms_list, list):
+                for hms in hms_list:
+                    if isinstance(hms, dict):
+                        # HMS format: {"attr": code, "code": full_code}
+                        # The code is a hex string, severity is in bits
+                        code = hms.get("code", hms.get("attr", "0"))
+                        if isinstance(code, int):
+                            code = hex(code)
+                        # Parse severity from code (typically last 4 bits indicate level)
+                        try:
+                            code_int = int(str(code).replace("0x", ""), 16) if code else 0
+                            severity = (code_int >> 16) & 0xF  # Extract severity bits
+                            module = (code_int >> 24) & 0xFF  # Extract module bits
+                        except (ValueError, TypeError):
+                            severity = 3
+                            module = 0
+                        self.state.hms_errors.append(HMSError(
+                            code=str(code),
+                            module=module,
+                            severity=severity if severity > 0 else 3,
+                        ))
+
         self.state.raw_data = data
 
         # Detect print start (state changes TO RUNNING with a file)
@@ -236,4 +293,31 @@ class BambuMQTTClient:
     def send_command(self, command: dict):
         """Send a command to the printer."""
         if self._client and self.state.connected:
+            # Log outgoing message if logging is enabled
+            if self._logging_enabled:
+                self._message_log.append(MQTTLogEntry(
+                    timestamp=datetime.now().isoformat(),
+                    topic=self.topic_publish,
+                    direction="out",
+                    payload=command,
+                ))
             self._client.publish(self.topic_publish, json.dumps(command))
+
+    def enable_logging(self, enabled: bool = True):
+        """Enable or disable MQTT message logging."""
+        self._logging_enabled = enabled
+        if not enabled:
+            self._message_log.clear()
+
+    def get_logs(self) -> list[MQTTLogEntry]:
+        """Get all logged MQTT messages."""
+        return list(self._message_log)
+
+    def clear_logs(self):
+        """Clear the message log."""
+        self._message_log.clear()
+
+    @property
+    def logging_enabled(self) -> bool:
+        """Check if logging is enabled."""
+        return self._logging_enabled

+ 31 - 1
backend/app/services/printer_manager.py

@@ -6,7 +6,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
 from sqlalchemy import select
 
 from backend.app.models.printer import Printer
-from backend.app.services.bambu_mqtt import BambuMQTTClient, PrinterState
+from backend.app.services.bambu_mqtt import BambuMQTTClient, PrinterState, MQTTLogEntry
 from backend.app.services.bambu_ftp import BambuFTPClient
 
 
@@ -118,6 +118,32 @@ class PrinterManager:
             return self._clients[printer_id].start_print(filename)
         return False
 
+    def enable_logging(self, printer_id: int, enabled: bool = True) -> bool:
+        """Enable or disable MQTT logging for a printer."""
+        if printer_id in self._clients:
+            self._clients[printer_id].enable_logging(enabled)
+            return True
+        return False
+
+    def get_logs(self, printer_id: int) -> list[MQTTLogEntry]:
+        """Get MQTT logs for a printer."""
+        if printer_id in self._clients:
+            return self._clients[printer_id].get_logs()
+        return []
+
+    def clear_logs(self, printer_id: int) -> bool:
+        """Clear MQTT logs for a printer."""
+        if printer_id in self._clients:
+            self._clients[printer_id].clear_logs()
+            return True
+        return False
+
+    def is_logging_enabled(self, printer_id: int) -> bool:
+        """Check if logging is enabled for a printer."""
+        if printer_id in self._clients:
+            return self._clients[printer_id].logging_enabled
+        return False
+
     async def test_connection(
         self,
         ip_address: str,
@@ -159,6 +185,10 @@ def printer_state_to_dict(state: PrinterState, printer_id: int | None = None) ->
         "layer_num": state.layer_num,
         "total_layers": state.total_layers,
         "temperatures": state.temperatures,
+        "hms_errors": [
+            {"code": e.code, "module": e.module, "severity": e.severity}
+            for e in (state.hms_errors or [])
+        ],
     }
     # Add cover URL if there's an active print and printer_id is provided
     if printer_id and state.state == "RUNNING" and state.gcode_file:

+ 37 - 0
frontend/src/api/client.ts

@@ -34,6 +34,12 @@ export interface Printer {
   updated_at: string;
 }
 
+export interface HMSError {
+  code: string;
+  module: number;
+  severity: number;  // 1=fatal, 2=serious, 3=common, 4=info
+}
+
 export interface PrinterStatus {
   id: number;
   name: string;
@@ -54,6 +60,7 @@ export interface PrinterStatus {
     chamber?: number;
   } | null;
   cover_url: string | null;
+  hms_errors: HMSError[];
 }
 
 export interface PrinterCreate {
@@ -235,6 +242,19 @@ export interface SmartPlugTestResult {
   device_name: string | null;
 }
 
+// MQTT Logging types
+export interface MQTTLogEntry {
+  timestamp: string;
+  topic: string;
+  direction: 'in' | 'out';
+  payload: Record<string, unknown>;
+}
+
+export interface MQTTLogsResponse {
+  logging_enabled: boolean;
+  logs: MQTTLogEntry[];
+}
+
 // API functions
 export const api = {
   // Printers
@@ -263,6 +283,22 @@ export const api = {
       method: 'POST',
     }),
 
+  // MQTT Debug Logging
+  enableMQTTLogging: (printerId: number) =>
+    request<{ logging_enabled: boolean }>(`/printers/${printerId}/logging/enable`, {
+      method: 'POST',
+    }),
+  disableMQTTLogging: (printerId: number) =>
+    request<{ logging_enabled: boolean }>(`/printers/${printerId}/logging/disable`, {
+      method: 'POST',
+    }),
+  getMQTTLogs: (printerId: number) =>
+    request<MQTTLogsResponse>(`/printers/${printerId}/logging`),
+  clearMQTTLogs: (printerId: number) =>
+    request<{ status: string }>(`/printers/${printerId}/logging`, {
+      method: 'DELETE',
+    }),
+
   // Printer File Manager
   getPrinterFiles: (printerId: number, path = '/') =>
     request<{
@@ -507,4 +543,5 @@ export const api = {
       method: 'POST',
       body: JSON.stringify({ ip_address, username, password }),
     }),
+
 };

+ 229 - 0
frontend/src/components/MQTTDebugModal.tsx

@@ -0,0 +1,229 @@
+import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
+import { X, Play, Square, Trash2, RefreshCw, ArrowDown, ArrowUp } from 'lucide-react';
+import { api, type MQTTLogEntry } from '../api/client';
+import { Button } from './Button';
+import { useState, useEffect, useRef } from 'react';
+
+interface MQTTDebugModalProps {
+  printerId: number;
+  printerName: string;
+  onClose: () => void;
+}
+
+export function MQTTDebugModal({ printerId, printerName, onClose }: MQTTDebugModalProps) {
+  const queryClient = useQueryClient();
+  const [autoScroll, setAutoScroll] = useState(true);
+  const [expandedLogs, setExpandedLogs] = useState<Set<number>>(new Set());
+  const logContainerRef = useRef<HTMLDivElement>(null);
+
+  const { data, isLoading, refetch } = useQuery({
+    queryKey: ['mqtt-logs', printerId],
+    queryFn: () => api.getMQTTLogs(printerId),
+    refetchInterval: 1000, // Poll every second when logging is enabled
+  });
+
+  const enableMutation = useMutation({
+    mutationFn: () => api.enableMQTTLogging(printerId),
+    onSuccess: () => {
+      queryClient.invalidateQueries({ queryKey: ['mqtt-logs', printerId] });
+    },
+  });
+
+  const disableMutation = useMutation({
+    mutationFn: () => api.disableMQTTLogging(printerId),
+    onSuccess: () => {
+      queryClient.invalidateQueries({ queryKey: ['mqtt-logs', printerId] });
+    },
+  });
+
+  const clearMutation = useMutation({
+    mutationFn: () => api.clearMQTTLogs(printerId),
+    onSuccess: () => {
+      queryClient.invalidateQueries({ queryKey: ['mqtt-logs', printerId] });
+    },
+  });
+
+  // Auto-scroll to bottom when new logs arrive
+  useEffect(() => {
+    if (autoScroll && logContainerRef.current) {
+      logContainerRef.current.scrollTop = logContainerRef.current.scrollHeight;
+    }
+  }, [data?.logs, autoScroll]);
+
+  const toggleExpand = (index: number) => {
+    setExpandedLogs((prev) => {
+      const newSet = new Set(prev);
+      if (newSet.has(index)) {
+        newSet.delete(index);
+      } else {
+        newSet.add(index);
+      }
+      return newSet;
+    });
+  };
+
+  const formatTimestamp = (timestamp: string) => {
+    const date = new Date(timestamp);
+    return date.toLocaleTimeString('en-US', { hour12: false, fractionalSecondDigits: 3 });
+  };
+
+  const formatPayload = (payload: Record<string, unknown>, expanded: boolean) => {
+    const json = JSON.stringify(payload, null, expanded ? 2 : 0);
+    if (!expanded && json.length > 100) {
+      return json.substring(0, 100) + '...';
+    }
+    return json;
+  };
+
+  const loggingEnabled = data?.logging_enabled ?? false;
+  const logs = data?.logs ?? [];
+
+  return (
+    <div className="fixed inset-0 bg-black/70 flex items-center justify-center z-50 p-4">
+      <div className="bg-bambu-dark-secondary rounded-lg max-w-4xl w-full max-h-[85vh] flex flex-col">
+        {/* Header */}
+        <div className="flex items-center justify-between p-4 border-b border-bambu-dark-tertiary">
+          <div>
+            <h2 className="text-lg font-semibold text-white">MQTT Debug Log</h2>
+            <p className="text-sm text-bambu-gray">{printerName}</p>
+          </div>
+          <button
+            onClick={onClose}
+            className="text-bambu-gray hover:text-white transition-colors"
+          >
+            <X className="w-5 h-5" />
+          </button>
+        </div>
+
+        {/* Controls */}
+        <div className="flex items-center gap-2 p-4 border-b border-bambu-dark-tertiary">
+          {loggingEnabled ? (
+            <Button
+              size="sm"
+              variant="secondary"
+              onClick={() => disableMutation.mutate()}
+              disabled={disableMutation.isPending}
+            >
+              <Square className="w-4 h-4" />
+              Stop
+            </Button>
+          ) : (
+            <Button
+              size="sm"
+              onClick={() => enableMutation.mutate()}
+              disabled={enableMutation.isPending}
+            >
+              <Play className="w-4 h-4" />
+              Start Logging
+            </Button>
+          )}
+          <Button
+            size="sm"
+            variant="secondary"
+            onClick={() => clearMutation.mutate()}
+            disabled={clearMutation.isPending || logs.length === 0}
+          >
+            <Trash2 className="w-4 h-4" />
+            Clear
+          </Button>
+          <Button
+            size="sm"
+            variant="secondary"
+            onClick={() => refetch()}
+            disabled={isLoading}
+          >
+            <RefreshCw className={`w-4 h-4 ${isLoading ? 'animate-spin' : ''}`} />
+          </Button>
+          <div className="flex-1" />
+          <label className="flex items-center gap-2 text-sm text-bambu-gray cursor-pointer">
+            <input
+              type="checkbox"
+              checked={autoScroll}
+              onChange={(e) => setAutoScroll(e.target.checked)}
+              className="rounded border-bambu-dark-tertiary"
+            />
+            Auto-scroll
+          </label>
+          <span className="text-sm text-bambu-gray">
+            {logs.length} message{logs.length !== 1 ? 's' : ''}
+          </span>
+        </div>
+
+        {/* Log Content */}
+        <div
+          ref={logContainerRef}
+          className="flex-1 overflow-auto p-4 font-mono text-xs bg-bambu-dark min-h-[400px]"
+        >
+          {logs.length === 0 ? (
+            <div className="flex flex-col items-center justify-center h-full text-bambu-gray">
+              <p className="mb-2">No messages logged yet</p>
+              {!loggingEnabled && (
+                <p className="text-sm">Click "Start Logging" to begin capturing MQTT messages</p>
+              )}
+            </div>
+          ) : (
+            <div className="space-y-1">
+              {logs.map((log: MQTTLogEntry, index: number) => {
+                const isExpanded = expandedLogs.has(index);
+                const isIncoming = log.direction === 'in';
+
+                return (
+                  <div
+                    key={index}
+                    className={`p-2 rounded cursor-pointer hover:bg-bambu-dark-secondary transition-colors ${
+                      isExpanded ? 'bg-bambu-dark-secondary' : ''
+                    }`}
+                    onClick={() => toggleExpand(index)}
+                  >
+                    <div className="flex items-start gap-2">
+                      <span className="text-bambu-gray shrink-0">
+                        {formatTimestamp(log.timestamp)}
+                      </span>
+                      <span
+                        className={`shrink-0 ${
+                          isIncoming ? 'text-blue-400' : 'text-green-400'
+                        }`}
+                        title={isIncoming ? 'Incoming' : 'Outgoing'}
+                      >
+                        {isIncoming ? (
+                          <ArrowDown className="w-3 h-3" />
+                        ) : (
+                          <ArrowUp className="w-3 h-3" />
+                        )}
+                      </span>
+                      <span className="text-purple-400 shrink-0">{log.topic}</span>
+                    </div>
+                    <pre
+                      className={`mt-1 text-white/80 overflow-x-auto ${
+                        isExpanded ? 'whitespace-pre-wrap' : 'truncate'
+                      }`}
+                    >
+                      {formatPayload(log.payload, isExpanded)}
+                    </pre>
+                  </div>
+                );
+              })}
+            </div>
+          )}
+        </div>
+
+        {/* Footer */}
+        <div className="flex items-center justify-between p-4 border-t border-bambu-dark-tertiary">
+          <div className="text-sm text-bambu-gray">
+            {loggingEnabled ? (
+              <span className="flex items-center gap-2">
+                <span className="w-2 h-2 bg-green-500 rounded-full animate-pulse" />
+                Logging active - messages will auto-refresh
+              </span>
+            ) : (
+              <span>Logging stopped</span>
+            )}
+          </div>
+          <Button variant="secondary" onClick={onClose}>
+            Close
+          </Button>
+        </div>
+      </div>
+    </div>
+  );
+}

+ 45 - 0
frontend/src/pages/PrintersPage.tsx

@@ -11,6 +11,8 @@ import {
   RefreshCw,
   Box,
   HardDrive,
+  AlertTriangle,
+  Terminal,
 } from 'lucide-react';
 import { api } from '../api/client';
 import type { Printer, PrinterCreate } from '../api/client';
@@ -18,6 +20,7 @@ import { Card, CardContent } from '../components/Card';
 import { Button } from '../components/Button';
 import { ConfirmModal } from '../components/ConfirmModal';
 import { FileManagerModal } from '../components/FileManagerModal';
+import { MQTTDebugModal } from '../components/MQTTDebugModal';
 
 function formatTime(seconds: number): string {
   const hours = Math.floor(seconds / 3600);
@@ -79,6 +82,7 @@ function PrinterCard({ printer, hideIfDisconnected }: { printer: Printer; hideIf
   const [showMenu, setShowMenu] = useState(false);
   const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
   const [showFileManager, setShowFileManager] = useState(false);
+  const [showMQTTDebug, setShowMQTTDebug] = useState(false);
 
   const { data: status } = useQuery({
     queryKey: ['printerStatus', printer.id],
@@ -131,6 +135,28 @@ function PrinterCard({ printer, hideIfDisconnected }: { printer: Printer; hideIf
               )}
               {status?.connected ? 'Connected' : 'Offline'}
             </span>
+            {/* HMS Status Indicator */}
+            {status?.connected && (
+              <span
+                className={`flex items-center gap-1 px-2 py-1 rounded-full text-xs ${
+                  status.hms_errors && status.hms_errors.length > 0
+                    ? status.hms_errors.some(e => e.severity <= 2)
+                      ? 'bg-red-500/20 text-red-400'
+                      : 'bg-orange-500/20 text-orange-400'
+                    : 'bg-bambu-green/20 text-bambu-green'
+                }`}
+                title={
+                  status.hms_errors && status.hms_errors.length > 0
+                    ? `${status.hms_errors.length} HMS error(s)`
+                    : 'No HMS errors'
+                }
+              >
+                <AlertTriangle className="w-3 h-3" />
+                {status.hms_errors && status.hms_errors.length > 0
+                  ? status.hms_errors.length
+                  : 'OK'}
+              </span>
+            )}
             <div className="relative">
               <Button
                 variant="ghost"
@@ -151,6 +177,16 @@ function PrinterCard({ printer, hideIfDisconnected }: { printer: Printer; hideIf
                     <RefreshCw className="w-4 h-4" />
                     Reconnect
                   </button>
+                  <button
+                    className="w-full px-4 py-2 text-left text-sm hover:bg-bambu-dark-tertiary flex items-center gap-2"
+                    onClick={() => {
+                      setShowMQTTDebug(true);
+                      setShowMenu(false);
+                    }}
+                  >
+                    <Terminal className="w-4 h-4" />
+                    MQTT Debug
+                  </button>
                   <button
                     className="w-full px-4 py-2 text-left text-sm text-red-400 hover:bg-bambu-dark-tertiary flex items-center gap-2"
                     onClick={() => {
@@ -289,6 +325,15 @@ function PrinterCard({ printer, hideIfDisconnected }: { printer: Printer; hideIf
           onClose={() => setShowFileManager(false)}
         />
       )}
+
+      {/* MQTT Debug Modal */}
+      {showMQTTDebug && (
+        <MQTTDebugModal
+          printerId={printer.id}
+          printerName={printer.name}
+          onClose={() => setShowMQTTDebug(false)}
+        />
+      )}
     </Card>
   );
 }

File diff suppressed because it is too large
+ 0 - 0
static/assets/index-BPRATuOd.js


File diff suppressed because it is too large
+ 0 - 0
static/assets/index-DUX4pLTn.css


File diff suppressed because it is too large
+ 0 - 0
static/assets/index-DwtaaDcN.css


+ 2 - 2
static/index.html

@@ -7,8 +7,8 @@
     <link rel="icon" type="image/png" sizes="32x32" href="/img/favicon-32x32.png" />
     <link rel="icon" type="image/png" sizes="16x16" href="/img/favicon-16x16.png" />
     <link rel="apple-touch-icon" sizes="180x180" href="/img/apple-touch-icon.png" />
-    <script type="module" crossorigin src="/assets/index-DZsA8zAw.js"></script>
-    <link rel="stylesheet" crossorigin href="/assets/index-DwtaaDcN.css">
+    <script type="module" crossorigin src="/assets/index-BPRATuOd.js"></script>
+    <link rel="stylesheet" crossorigin href="/assets/index-DUX4pLTn.css">
   </head>
   <body>
     <div id="root"></div>

Some files were not shown because too many files changed in this diff