mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-10-21 01:57:41 +02:00
fix(scheduler): pin the force-color variant when selecting the AMS slot (#2650)
Follow-up to 0f203ce: force color match now picks the right AMS slot, not
just the right printer. The slot mapper cleared tray_info_idx when applying
an override, so on a printer holding two same-colour PLA spools of different
variants (Basic GFA00 / Matte GFA01 / Silk GFA06) it could map to the wrong
one. It now keeps the variant for force_color_match overrides (both the 3MF
and no-3MF fallback paths) so the matcher pins the matching tray, and falls
back to type+colour when that variant isn't loaded. A manual filament swap
(a preference override) still clears the idx so it matches the swapped-in
spool rather than the old one.
The printer-card queue-compatibility hint applies the same variant rule.
This commit is contained in:
+1
-1
@@ -5,7 +5,7 @@ All notable changes to Bambuddy will be documented in this file.
|
||||
## [1.2.6b1] - Unreleased
|
||||
|
||||
### Fixed
|
||||
- **Force color match dispatched a print onto the wrong PLA variant — Matte jobs went to Basic and Silk printers alike (#2650, reporter @MartinNYHC)** — With **Force color match** on, a job sliced for **White PLA Matte** was dispatched to every printer that had *any* white PLA loaded — the ones holding White PLA **Basic** and White PLA **Silk+** included — so a matte model came out glossy on the wrong machine. **Root cause.** Bambu's MQTT status reports every PLA sub-variant as `tray_type == "PLA"`; the Basic/Matte/Silk distinction is carried only in `tray_info_idx` (`GFA00` = Basic, `GFA01` = Matte, `GFA06` = Silk, …), which the 3MF's `slice_info.config` also records per filament. Two places dropped it: the Virtual-Printer queue built each force override as `{slot_id, type, color, force_color_match}` without the parsed `tray_info_idx`, and the scheduler's eligibility check (`_get_missing_force_color_slots`) compared loaded trays on `(type, colour)` only — so `(PLA, #FFFFFF)` matched Basic, Matte and Silk indiscriminately and all three printers looked eligible. **Fix.** The force override now carries the 3MF's `tray_info_idx`, and a slot counts as satisfied only when a loaded tray matches type **and** colour **and** the variant — identical `tray_info_idx`, *or* either side lacks one. A blank idx on either side (custom/third-party spools report none, and older 3MFs carry none) falls back to the historical type+colour behaviour, so those setups are unaffected. A job sliced for GFA01 now goes only to a printer with GFA01 loaded; Basic/Silk report a mismatch. Covered by scheduler tests (Matte requirement rejects Basic/Silk, accepts Matte, blank loaded idx falls back, requirement without an idx is unchanged) and a Virtual-Printer test asserting the override carries `tray_info_idx`.
|
||||
- **Force color match dispatched a print onto the wrong PLA variant — Matte jobs went to Basic and Silk printers alike, and the wrong AMS slot on a printer holding two same-colour variants (#2650, reporter @MartinNYHC)** — With **Force color match** on, a job sliced for **White PLA Matte** was dispatched to every printer that had *any* white PLA loaded — the ones holding White PLA **Basic** and White PLA **Silk+** included — so a matte model came out glossy on the wrong machine. **Root cause.** Bambu's MQTT status reports every PLA sub-variant as `tray_type == "PLA"`; the Basic/Matte/Silk distinction is carried only in `tray_info_idx` (`GFA00` = Basic, `GFA01` = Matte, `GFA06` = Silk, …), which the 3MF's `slice_info.config` also records per filament. Three places dropped it: the Virtual-Printer queue built each force override as `{slot_id, type, color, force_color_match}` without the parsed `tray_info_idx`; the scheduler's eligibility check (`_get_missing_force_color_slots`) compared loaded trays on `(type, colour)` only — so `(PLA, #FFFFFF)` matched Basic, Matte and Silk indiscriminately and all three printers looked eligible; and the AMS slot mapper cleared `tray_info_idx` when applying the override, so even on the correct printer it could pick a different-variant tray of the same colour. **Fix.** The force override now carries the 3MF's `tray_info_idx`; a slot counts as satisfied only when a loaded tray matches type **and** colour **and** the variant (identical `tray_info_idx`, *or* either side lacks one); and the slot mapper now keeps the variant for force-colour overrides so it pins the matching tray. A blank idx on either side (custom/third-party spools report none, and older 3MFs carry none) falls back to the historical type+colour behaviour, so those setups are unaffected, and a manual filament *swap* (a preference override) still clears the idx so it matches the swapped-in spool rather than the old one. A job sliced for GFA01 now goes only to a printer with GFA01 loaded, and lands on that printer's GFA01 tray. The printer-card queue-compatibility hint (which printers show a pending job as runnable) now applies the same variant rule. Covered by scheduler tests (Matte requirement rejects Basic/Silk, accepts Matte, blank loaded idx falls back, requirement without an idx unchanged; the mapper pins the GFA01 tray over a same-colour GFA00 on both the 3MF and no-3MF paths; a preference swap still matches by colour), a Virtual-Printer test asserting the override carries `tray_info_idx`, and frontend tests for the variant-aware queue hint (rejects other variants, accepts the match, blank-idx and no-variant-data fall back).
|
||||
|
||||
|
||||
## [1.2.5] - 2026-07-24
|
||||
|
||||
@@ -1388,9 +1388,18 @@ class PrintScheduler:
|
||||
override = override_map[req["slot_id"]]
|
||||
req["type"] = override["type"]
|
||||
req["color"] = override["color"]
|
||||
# Clear tray_info_idx so matching uses type+color instead of
|
||||
# the original 3MF's tray_info_idx (which would match the old filament)
|
||||
req["tray_info_idx"] = ""
|
||||
# A manual/preference override SWAPS the slot's filament, so the
|
||||
# 3MF's original tray_info_idx now points at the old spool and must
|
||||
# be cleared — matching then falls back to type+colour. A
|
||||
# force_color_match override is not a swap: it carries the 3MF's
|
||||
# intended variant (Basic GFA00 / Matte GFA01 / Silk GFA06), so keep
|
||||
# it here too, letting the matcher pin the correct variant slot on a
|
||||
# printer holding two same-colour spools of different variants (#2650).
|
||||
# If that variant isn't loaded the matcher falls back to type+colour,
|
||||
# so an eligible printer never fails to map.
|
||||
req["tray_info_idx"] = (
|
||||
override.get("tray_info_idx", "") if override.get("force_color_match") else ""
|
||||
)
|
||||
logger.debug(
|
||||
"Queue item %s: Override slot %d -> %s %s",
|
||||
item.id,
|
||||
@@ -1454,7 +1463,11 @@ class PrintScheduler:
|
||||
"slot_id": o["slot_id"],
|
||||
"type": o.get("type", ""),
|
||||
"color": o.get("color", ""),
|
||||
"tray_info_idx": "",
|
||||
# These are all force_color_match overrides, so the idx (when the
|
||||
# 3MF carried one) is the intended variant, not a stale swap —
|
||||
# keep it so the matcher pins the right variant slot, falling back
|
||||
# to type+colour when it isn't loaded (#2650).
|
||||
"tray_info_idx": o.get("tray_info_idx", ""),
|
||||
}
|
||||
for o in force_overrides
|
||||
]
|
||||
|
||||
@@ -124,6 +124,27 @@ class TestBuildOverrideDirectMapping:
|
||||
# Should match by colour (#CBC6B8 ≈ CBC6B8FF after strip), not by tray_info_idx.
|
||||
assert result == [0]
|
||||
|
||||
def test_direct_mapping_pins_variant_when_override_carries_idx(self, scheduler):
|
||||
"""#2650: the no-3MF fallback honours a force override's own tray_info_idx,
|
||||
so two same-colour PLA variants map to the intended slot rather than the
|
||||
first same-colour tray."""
|
||||
status = self._status(
|
||||
ams=[
|
||||
{
|
||||
"id": 0,
|
||||
"tray": [
|
||||
{"id": 0, "tray_type": "PLA", "tray_color": "FFFFFFFF", "tray_info_idx": "GFA00"},
|
||||
{"id": 1, "tray_type": "PLA", "tray_color": "FFFFFFFF", "tray_info_idx": "GFA01"},
|
||||
],
|
||||
}
|
||||
]
|
||||
)
|
||||
overrides = [
|
||||
{"slot_id": 1, "type": "PLA", "color": "#FFFFFF", "tray_info_idx": "GFA01", "force_color_match": True}
|
||||
]
|
||||
result = scheduler._build_override_direct_mapping(overrides, status)
|
||||
assert result == [1] # global_tray_id 1 = GFA01 (Matte), not GFA00 (Basic) at 0
|
||||
|
||||
|
||||
class TestComputeAmsMappingFallback:
|
||||
"""Integration tests for the force-color fallback inside
|
||||
@@ -242,6 +263,84 @@ class TestComputeAmsMappingFallback:
|
||||
|
||||
assert result is None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@patch("backend.app.services.print_scheduler.printer_manager")
|
||||
async def test_force_color_override_pins_the_matching_variant_slot(self, mock_pm, scheduler):
|
||||
"""#2650 slot selection: with two same-colour PLA spools of different
|
||||
variants loaded, applying a force_color_match override must map to the
|
||||
tray whose tray_info_idx matches the 3MF (Matte GFA01), not the first
|
||||
same-colour tray (Basic GFA00)."""
|
||||
mock_pm.get_status.return_value = MagicMock(
|
||||
raw_data={
|
||||
"ams": [
|
||||
{
|
||||
"id": 0,
|
||||
"tray": [
|
||||
{"id": 0, "tray_type": "PLA", "tray_color": "FFFFFFFF", "tray_info_idx": "GFA00"},
|
||||
{"id": 1, "tray_type": "PLA", "tray_color": "FFFFFFFF", "tray_info_idx": "GFA01"},
|
||||
],
|
||||
}
|
||||
]
|
||||
},
|
||||
ams_filament_backup=None,
|
||||
)
|
||||
item = self._make_item(
|
||||
filament_overrides_json=(
|
||||
'[{"slot_id": 1, "type": "PLA", "color": "#FFFFFF", '
|
||||
'"tray_info_idx": "GFA01", "force_color_match": true}]'
|
||||
)
|
||||
)
|
||||
filament_reqs = [{"slot_id": 1, "type": "PLA", "color": "#FFFFFF", "tray_info_idx": "GFA01"}]
|
||||
db = AsyncMock()
|
||||
|
||||
with (
|
||||
patch.object(scheduler, "_get_filament_requirements", return_value=filament_reqs),
|
||||
patch.object(scheduler, "_get_bool_setting", new=AsyncMock(return_value=False)),
|
||||
):
|
||||
result = await scheduler._compute_ams_mapping_for_printer(db, 5, item)
|
||||
|
||||
assert result == [1] # global_tray_id 1 = GFA01 (Matte), not GFA00 (Basic) at 0
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@patch("backend.app.services.print_scheduler.printer_manager")
|
||||
async def test_preference_override_still_clears_idx_so_a_swap_matches_by_colour(self, mock_pm, scheduler):
|
||||
"""A non-force (preference) override is a filament SWAP: its slot must
|
||||
match by the new type+colour, never by a stale 3MF variant that would pin
|
||||
the old spool. Only force_color_match overrides keep their idx — anything
|
||||
else is cleared, exactly as before #2650."""
|
||||
mock_pm.get_status.return_value = MagicMock(
|
||||
raw_data={
|
||||
"ams": [
|
||||
{
|
||||
"id": 0,
|
||||
"tray": [
|
||||
# The 3MF's original variant (blue GFA01) and the swapped-to red.
|
||||
{"id": 0, "tray_type": "PLA", "tray_color": "0000FFFF", "tray_info_idx": "GFA01"},
|
||||
{"id": 1, "tray_type": "PLA", "tray_color": "FF0000FF", "tray_info_idx": "GFA00"},
|
||||
],
|
||||
}
|
||||
]
|
||||
},
|
||||
ams_filament_backup=None,
|
||||
)
|
||||
# 3MF wants blue GFA01; the user swaps this slot to red via a preference
|
||||
# override that (defensively) also carries the stale GFA01 idx.
|
||||
item = self._make_item(
|
||||
filament_overrides_json='[{"slot_id": 1, "type": "PLA", "color": "#FF0000", "tray_info_idx": "GFA01"}]'
|
||||
)
|
||||
filament_reqs = [{"slot_id": 1, "type": "PLA", "color": "#0000FF", "tray_info_idx": "GFA01"}]
|
||||
db = AsyncMock()
|
||||
|
||||
with (
|
||||
patch.object(scheduler, "_get_filament_requirements", return_value=filament_reqs),
|
||||
patch.object(scheduler, "_get_bool_setting", new=AsyncMock(return_value=False)),
|
||||
):
|
||||
result = await scheduler._compute_ams_mapping_for_printer(db, 5, item)
|
||||
|
||||
# idx cleared → matches the swapped-to red spool (global 1), not the stale
|
||||
# GFA01 blue (global 0) that a preserved idx would have pinned.
|
||||
assert result == [1]
|
||||
|
||||
|
||||
class TestGetMissingForceColorSlotsVariant:
|
||||
"""force_color_match must distinguish Bambu PLA variants that share a base
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { getPrinterImage, isGcodeCompatible } from '../../utils/printer';
|
||||
import { getPrinterImage, isGcodeCompatible, filterCompatibleQueueItems } from '../../utils/printer';
|
||||
import type { PrintQueueItem } from '../../api/client';
|
||||
|
||||
describe('getPrinterImage', () => {
|
||||
describe('X2D (#988)', () => {
|
||||
@@ -136,3 +137,42 @@ describe('isGcodeCompatible', () => {
|
||||
expect(isGcodeCompatible('H2D Pro', 'H2DPRO')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('filterCompatibleQueueItems — force-color PLA variant (#2650)', () => {
|
||||
const makeItem = (
|
||||
overrides: Array<{ slot_id: number; type: string; color: string; tray_info_idx?: string; force_color_match?: boolean }>,
|
||||
): PrintQueueItem => ({ id: 1, filament_overrides: overrides } as unknown as PrintQueueItem);
|
||||
|
||||
// A job sliced for White PLA Matte (GFA01).
|
||||
const matteJob = makeItem([
|
||||
{ slot_id: 1, type: 'PLA', color: '#FFFFFF', tray_info_idx: 'GFA01', force_color_match: true },
|
||||
]);
|
||||
const loadedTypes = new Set(['PLA']);
|
||||
const loaded = new Set(['PLA:ffffff']);
|
||||
|
||||
it('rejects a printer loaded only with other white PLA variants (Basic/Silk)', () => {
|
||||
const variants = new Set(['PLA:ffffff:GFA00', 'PLA:ffffff:GFA06']);
|
||||
expect(filterCompatibleQueueItems([matteJob], loadedTypes, loaded, variants)).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('accepts a printer loaded with the matching variant (Matte GFA01)', () => {
|
||||
const variants = new Set(['PLA:ffffff:GFA00', 'PLA:ffffff:GFA01']);
|
||||
expect(filterCompatibleQueueItems([matteJob], loadedTypes, loaded, variants)).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('accepts a same-colour spool that reports no tray_info_idx (custom/third-party)', () => {
|
||||
const variants = new Set(['PLA:ffffff:']);
|
||||
expect(filterCompatibleQueueItems([matteJob], loadedTypes, loaded, variants)).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('falls back to type+colour when no variant data is supplied', () => {
|
||||
// loadedVariants omitted → the hint is never stricter than the data it has.
|
||||
expect(filterCompatibleQueueItems([matteJob], loadedTypes, loaded)).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('an override without a tray_info_idx keeps the old type+colour behaviour', () => {
|
||||
const noIdxJob = makeItem([{ slot_id: 1, type: 'PLA', color: '#FFFFFF', force_color_match: true }]);
|
||||
const variants = new Set(['PLA:ffffff:GFA06']);
|
||||
expect(filterCompatibleQueueItems([noIdxJob], loadedTypes, loaded, variants)).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2192,7 +2192,7 @@ export interface PrintQueueItem {
|
||||
// PrintModal's deficit warning was acknowledged.
|
||||
skip_filament_check: boolean;
|
||||
ams_mapping: number[] | null; // AMS slot mapping for multi-color prints
|
||||
filament_overrides: Array<{ slot_id: number; type: string; color: string; color_name?: string; force_color_match?: boolean }> | null; // Filament overrides for model-based assignment
|
||||
filament_overrides: Array<{ slot_id: number; type: string; color: string; color_name?: string; tray_info_idx?: string; force_color_match?: boolean }> | null; // Filament overrides for model-based assignment
|
||||
plate_id: number | null; // Plate ID for multi-plate 3MF files
|
||||
// Print options
|
||||
bed_levelling: CalibrationMode;
|
||||
|
||||
@@ -11,10 +11,11 @@ interface PrinterQueueWidgetProps {
|
||||
printerModel?: string | null;
|
||||
loadedFilamentTypes?: Set<string>;
|
||||
loadedFilaments?: Set<string>; // "TYPE:rrggbb" pairs for filament override color matching
|
||||
loadedVariants?: Set<string>; // "TYPE:rrggbb:idx" triples for PLA sub-variant matching (#2650)
|
||||
variant?: 'card' | 'panelExtension';
|
||||
}
|
||||
|
||||
export function PrinterQueueWidget({ printerId, printerModel, loadedFilamentTypes, loadedFilaments, variant = 'card' }: PrinterQueueWidgetProps) {
|
||||
export function PrinterQueueWidget({ printerId, printerModel, loadedFilamentTypes, loadedFilaments, loadedVariants, variant = 'card' }: PrinterQueueWidgetProps) {
|
||||
const { t } = useTranslation();
|
||||
const { data: queue } = useQuery({
|
||||
queryKey: ['queue', printerId, 'pending', printerModel],
|
||||
@@ -23,7 +24,7 @@ export function PrinterQueueWidget({ printerId, printerModel, loadedFilamentType
|
||||
});
|
||||
|
||||
// Filter queue to items this printer can actually print (filament type + color check)
|
||||
const compatibleQueue = queue ? filterCompatibleQueueItems(queue, loadedFilamentTypes, loadedFilaments) : undefined;
|
||||
const compatibleQueue = queue ? filterCompatibleQueueItems(queue, loadedFilamentTypes, loadedFilaments, loadedVariants) : undefined;
|
||||
const totalPending = compatibleQueue?.length || 0;
|
||||
|
||||
if (totalPending === 0) {
|
||||
|
||||
@@ -1990,6 +1990,30 @@ function PrinterCard({
|
||||
return filaments;
|
||||
}, [status?.ams, status?.vt_tray]);
|
||||
|
||||
// Collect loaded type+color+tray_info_idx triples for variant-aware force-color
|
||||
// matching. Format: "TYPE:rrggbb:idx" (idx "" for custom/third-party spools) —
|
||||
// distinguishes Bambu PLA sub-variants that share a base type+colour (#2650).
|
||||
const loadedVariants = useMemo(() => {
|
||||
const variants = new Set<string>();
|
||||
if (status?.ams) {
|
||||
for (const ams of status.ams) {
|
||||
for (const tray of ams.tray || []) {
|
||||
if (tray.tray_type && tray.tray_color) {
|
||||
const color = tray.tray_color.replace('#', '').toLowerCase().slice(0, 6);
|
||||
variants.add(`${tray.tray_type.toUpperCase()}:${color}:${tray.tray_info_idx || ''}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const vt of status?.vt_tray ?? []) {
|
||||
if (vt.tray_type && vt.tray_color) {
|
||||
const color = vt.tray_color.replace('#', '').toLowerCase().slice(0, 6);
|
||||
variants.add(`${vt.tray_type.toUpperCase()}:${color}:${vt.tray_info_idx || ''}`);
|
||||
}
|
||||
}
|
||||
return variants;
|
||||
}, [status?.ams, status?.vt_tray]);
|
||||
|
||||
// Fetch cloud filament info for tooltips (name includes color, also has K value)
|
||||
const { data: filamentInfo } = useQuery({
|
||||
queryKey: ['filamentInfo', trayInfoIds],
|
||||
@@ -2163,8 +2187,8 @@ function PrinterCard({
|
||||
// An empty Set means no filaments are loaded — jobs requiring specific types are incompatible.
|
||||
const queueCount = useMemo(() => {
|
||||
if (!queueItems?.length) return 0;
|
||||
return filterCompatibleQueueItems(queueItems, loadedFilamentTypes, loadedFilaments).length;
|
||||
}, [queueItems, loadedFilamentTypes, loadedFilaments]);
|
||||
return filterCompatibleQueueItems(queueItems, loadedFilamentTypes, loadedFilaments, loadedVariants).length;
|
||||
}, [queueItems, loadedFilamentTypes, loadedFilaments, loadedVariants]);
|
||||
|
||||
// Fetch currently printing queue item to show who started it (Issue #206)
|
||||
const { data: printingQueueItems } = useQuery({
|
||||
@@ -3740,6 +3764,7 @@ function PrinterCard({
|
||||
printerModel={printer.model}
|
||||
loadedFilamentTypes={loadedFilamentTypes}
|
||||
loadedFilaments={loadedFilaments}
|
||||
loadedVariants={loadedVariants}
|
||||
variant="panelExtension"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -56,12 +56,19 @@ import type { PrintQueueItem } from '../api/client';
|
||||
* @param items - Array of queue items to filter
|
||||
* @param loadedFilamentTypes - Set of loaded filament types (e.g., "PLA", "PETG")
|
||||
* @param loadedFilaments - Set of loaded filament type+color pairs (e.g., "PLA:ffffff", "PETG:ff0000")
|
||||
* @param loadedVariants - Set of loaded type+color+tray_info_idx triples
|
||||
* (e.g., "PLA:ffffff:GFA01"; the idx is "" for custom/third-party spools). Used to
|
||||
* distinguish Bambu PLA sub-variants (Basic GFA00 / Matte GFA01 / Silk GFA06) that
|
||||
* share a base type+colour, mirroring the backend _get_missing_force_color_slots (#2650).
|
||||
* When omitted, force matching falls back to type+colour so the hint is never stricter
|
||||
* than the data available.
|
||||
* @returns Array of compatible queue items
|
||||
*/
|
||||
export function filterCompatibleQueueItems(
|
||||
items: PrintQueueItem[],
|
||||
loadedFilamentTypes?: Set<string>,
|
||||
loadedFilaments?: Set<string>
|
||||
loadedFilaments?: Set<string>,
|
||||
loadedVariants?: Set<string>
|
||||
): PrintQueueItem[] {
|
||||
return items.filter(item => {
|
||||
// Type check: all required filament types must be loaded
|
||||
@@ -78,12 +85,20 @@ export function filterCompatibleQueueItems(
|
||||
const forceOverrides = item.filament_overrides.filter(o => o.force_color_match === true);
|
||||
const prefOverrides = item.filament_overrides.filter(o => o.force_color_match !== true);
|
||||
|
||||
// All force-matched slots must have exact type+color on this printer
|
||||
// All force-matched slots must have an exact type+color match — and, when the
|
||||
// override carries a tray_info_idx, the same variant too (a loaded tray with a
|
||||
// blank idx still satisfies it, matching the backend's type+colour fallback).
|
||||
if (forceOverrides.length > 0) {
|
||||
const allForceMatch = forceOverrides.every(o => {
|
||||
const oType = (o.type || '').toUpperCase();
|
||||
const oColor = (o.color || '').replace('#', '').toLowerCase().slice(0, 6);
|
||||
return loadedFilaments.has(`${oType}:${oColor}`);
|
||||
const oIdx = o.tray_info_idx || '';
|
||||
// No variant on the override, or no variant data supplied → type+colour only.
|
||||
if (!oIdx || loadedVariants === undefined) {
|
||||
return loadedFilaments.has(`${oType}:${oColor}`);
|
||||
}
|
||||
// Variant-specific: same idx, or a same-colour tray that reports no idx.
|
||||
return loadedVariants.has(`${oType}:${oColor}:${oIdx}`) || loadedVariants.has(`${oType}:${oColor}:`);
|
||||
});
|
||||
if (!allForceMatch) return false;
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
+1
-1
@@ -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-ByvLwk1T.js"></script>
|
||||
<script type="module" crossorigin src="/assets/index-Bx0t8q7D.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-kl51qImb.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
Reference in New Issue
Block a user