mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-09-30 03:01:21 +02:00
After the @BBL name fallback landed, IndividualGhost1905 reported that
an X2D 0.4 selection still mixed 0.2 / 0.6 / 0.8 nozzle process variants
into the main dropdown. The fallback's two extractors —
extractPrinterPresetModel and extractBblToken — both ended their regex
with `\s+[\d.]+\s*nozzle\s*$` and discarded the match, reducing
"Bambu Lab X2D 0.4 nozzle" and "0.40mm Strength @BBL X2D 0.8 nozzle"
to the same "X2D" string. Match was model-only; nozzle ignored.
Bambu's naming convention: 0.4 is the default and DROPS the suffix; 0.2
/ 0.6 / 0.8 carry an explicit "<size> nozzle" segment. So a process
preset with no suffix is implicitly 0.4 — not "any nozzle".
Have both extractors return { model, nozzle }, parsing the suffix out
instead of stripping. classifyByBambuName then requires both model AND
nozzle to compare equal; a null process nozzle counts as "0.4" per the
convention above. Differing nozzles fall into the existing "Other
printers" group — no new group label.
The bundle path was already nozzle-correct: a .bbscfg is scoped to one
printer-preset-name including its nozzle, and the bundle-side exact
match is therefore nozzle-aware. Only the @BBL name fallback needed
fixing. The `compatible_printers` tier is also unaffected (Bambu's
bundled `compatible_printers` lists include the full printer-preset
name with nozzle, so disambiguation already works).
If the selected printer preset name has no parseable nozzle (non-Bambu
/ hand-typed), the matcher degrades to model-only. Bambu printer
presets always carry one in practice; this is defensive.
9 new tests cover the matrix:
- 0.4 printer ↔ no-suffix process: match
- 0.4 printer ↔ 0.6 / 0.8 process: mismatch
- 0.6 printer ↔ 0.6 process: match
- 0.6 printer ↔ no-suffix process (=0.4): mismatch
- same rule applied to filament presets
- explicit "0.4 nozzle" suffix on process still matches 0.4 printer
- wrong-model still mismatches even when nozzles agree
- no-nozzle printer name degrades to model-only match
One existing test ("handles a trailing nozzle-size suffix on the @BBL
tag") had asserted that a 0.6-nozzle process matched a 0.4 printer —
the exact reporter complaint. Reframed: matching 0.4-suffix still
matches, 0.6-suffix now mismatches.
This commit is contained in:
@@ -25,6 +25,7 @@ All notable changes to Bambuddy will be documented in this file.
|
||||
- **PyJWT CVE-2025-45768 (PYSEC-2025-183 / GHSA-65pc-fj4g-8rjx): permanently ignored in pip-audit** — Advisory is disputed by the PyJWT maintainers, with the advisory description literally noting *"this is disputed by the Supplier because the key length is chosen by the application that uses the library."* `fix_versions=[]` on the advisory confirms no PyJWT patch exists or will exist. Bambuddy is not affected: `backend/app/core/auth.py:184` auto-generates secrets via `secrets.token_urlsafe(64)` (~86 chars of entropy, far above any sane minimum) and the file-loaded path at `:177` rejects secrets shorter than 32 chars. Added a permanent `--ignore-vuln CVE-2025-45768` to `.github/workflows/security.yml` with an inline comment citing the file:line evidence so a future maintainer reviewing the ignore list sees why it's load-bearing. Also dropped the stale `--ignore-vuln CVE-2026-4539` for Pygments — Pygments has since shipped a patched version and the ignore is no longer load-bearing (verified: `pip-audit --ignore-vuln CVE-2025-45768` alone reports clean).
|
||||
|
||||
### Fixed
|
||||
- **SliceModal: process / filament dropdowns now filter by nozzle diameter too, not just printer model (#1325 follow-up #2, reported by @IndividualGhost1905)** — With the @BBL name fallback in place, the reporter saw that an X2D 0.4 selection still mixed 0.2 / 0.6 / 0.8 nozzle process variants into the main list. The fallback's regex stripped any trailing `<size> nozzle` suffix from both sides before comparing, so `"Bambu Lab X2D 0.4 nozzle"` and `"0.40mm Strength @BBL X2D 0.8 nozzle"` both reduced to `"X2D"` and matched. The bundle path was already nozzle-correct (a `.bbscfg` is scoped to one printer-preset-name including its nozzle, so the bundle-side exact-match was nozzle-aware); only the name fallback needed fixing. **Fix**: `extractPrinterPresetModel` and `extractBblToken` now each return `{ model, nozzle }`. The nozzle is the parsed string ("0.4" / "0.6" / etc.) or `null` when the name has no suffix. `classifyByBambuName` treats a `null` process nozzle as `"0.4"` — Bambu's convention is to omit the suffix on 0.4 (the default) and include it for 0.2 / 0.6 / 0.8, exactly as the reporter described. Both `model` and `nozzle` must compare equal for a `'match'`; differing nozzles fall into the existing "Other printers" group, no new group label needed. If the selected printer preset name has no parseable nozzle (non-Bambu / hand-typed), the matcher degrades to model-only — Bambu printer presets always include nozzle in practice, so this is defensive. **Tests**: 9 new in `slicerPrinterMatch.test.ts` covering the matrix (0.4 printer vs no-suffix / 0.6 / 0.8 process; 0.6 printer vs 0.6 / no-suffix-=-0.4; explicit 0.4-suffix-on-process still matches 0.4 printer; same rule on filament presets; wrong-model dominates over matching-nozzle; no-nozzle printer name degrades to model-only); one existing test reframed (the case that previously asserted a 0.6-nozzle process matched a 0.4 printer — the exact bug — now asserts mismatch). 46 slicerPrinterMatch + 34 SliceModal tests green; frontend build clean.
|
||||
- **Timelapse now attaches to the archive after a backend restart mid-print (#1485 follow-up, reported by @pwostran)** — With the duplicate-archive fix from #1485 in place, a restart mid-print stopped creating ghosts — but the resulting archive came back without its timelapse video (only the finish snapshot was attached). Cause is a side-effect of the #1304 first-push guard: on the first MQTT push after Bambuddy starts (`_previous_gcode_state = None`), `is_new_print` is deliberately False so `on_print_start` doesn't fire — which prevents duplicate archive creation **but also** prevents the timelapse-baseline capture, since both live behind the same callback. At PRINT COMPLETE, `_scan_for_timelapse_with_retries` finds an empty `_timelapse_baselines` for the printer and falls into the "take baseline now" fallback in `main.py`. By that point the printer has already uploaded the in-flight MP4, so the snapshot includes it. Every retry then reports "N files found / no new files since baseline" and the scan gives up. The reporter's support bundle is the smoking gun — pre-reboot baseline of 7 files, post-reboot fallback baseline of 8 files (including the just-uploaded one), 4 retries all unable to see the diff. **Fix**: `bambu_mqtt.py` now fires a sibling `on_print_running_observed` callback inside the "Now tracking RUNNING state" branch when the first-push guard suppresses `on_print_start`. `main.py` wires it to a thin handler that fetches the printer row from DB and calls the existing `_capture_timelapse_baseline_at_start`. The callback only fires the first time we observe RUNNING per session (gated on the same `not self._was_running` branch the timelapse-flag restore already lives in), so a normal print start path is unaffected. The handler is also idempotent: if a baseline already exists for that printer, it returns without touching it. Safe because the printer doesn't upload the timelapse until *after* PRINT COMPLETE, so a baseline captured any time during the in-flight print is still pre-upload — no narrow window. The plumbing (`set_print_running_observed_callback` setter, in-`connect_printer` wrapper, constructor pass-through) mirrors the existing `on_print_start` / `on_print_complete` callback chain in `printer_manager.py`. **Tests**: 7 new in `TestPrintRunningObservedCallback` in `test_bambu_mqtt.py` (fires on first RUNNING after startup, doesn't double up with `on_print_start`, fires only once per session, skips on non-RUNNING / missing file / no-callback-set, payload shape mirrors `on_print_start`); 3 new in a dedicated `test_timelapse_baseline_restart_recovery.py` (handler captures the printer's existing-videos snapshot into `_timelapse_baselines`, skips when a baseline already exists, skips when the printer row was deleted between push and handler). 336 MQTT + print-start + timelapse tests green; backend ruff clean.
|
||||
- **SliceModal: process / filament dropdowns now filter for users who haven't uploaded slicer bundles (#1325 follow-up, reported by @IndividualGhost1905)** — The original #1325 fix replaced a stale hardcoded `@BBL <model>` allow-list with bundle-based compatibility: a process / filament preset was classified against the selected printer by consulting the user's uploaded Slicer Bundles (.bbscfg). That works perfectly for users who have uploaded bundles for every printer their cloud catalogue covers — and silently no-ops for everyone else: every cloud preset resolves to `'unknown'`, nothing moves into "Other printers", and the dropdown looks identical to the pre-fix state. **Fix**: restored the `@BBL <token>` name fallback as a third tier *below* the bundle path, but with the token-to-printer mapping driven by **the backend's canonical `PRINTER_MODEL_MAP`** (`backend/app/utils/printer_models.py`) instead of a duplicated frontend table. A new `GET /api/v1/slicer/printer-models` route ships the mapping unmodified; `slicerPrinterMatch.buildCompatibilityIndex` accepts it as a second arg, inverts it into a short-code → display-fragment table (`X1C` → `X1 Carbon`, `P2S` → `P2S`, `A1 Mini` → `A1 mini`, …), and `presetCompatibility` uses it only after `compatible_printers` and the bundle index have already returned `'unknown'`. The match is case- and whitespace-insensitive (`"A1 mini"`, `"A1 Mini"` and `"a1mini"` all compare equal). When the registry doesn't list a token, the matcher falls back to comparing the raw token against the printer-preset model fragment — so a brand-new "Q1" printer with `@BBL Q1`-tagged presets matches without any code change. Adding a new model only requires updating the existing backend `PRINTER_MODEL_MAP` (already the single source of truth for `is_dual_nozzle_model`, the rod-type/ethernet registries, and 3MF metadata normalisation) — no frontend table to keep in sync. **Tests**: 2 new in `test_slicer_presets.py` (`/printer-models` returns the full `PRINTER_MODEL_MAP`; the route hands back a copy, not the live module dict); the existing 25 `slicerPrinterMatch.test.ts` cases were extended to 36 covering: registry-driven X1C vs X1 Carbon match, A1 vs A1 mini disambiguation, H2D vs H2D Pro disambiguation, the previously-missing P2S / H2C / H2S / X2D, raw-token fallback for unregistered models, graceful degradation when the registry fetch hasn't resolved yet, the `compatible_printers`-wins-over-name rule, and the bundle-wins-over-name rule. 38 slicer-presets + 36 slicerPrinterMatch tests green; backend ruff clean; frontend build clean.
|
||||
- **Cloudflare-fronted Bambuddy no longer needs an `unsafe-inline` override to load (#1460 follow-up, reported by @Soopahfly)** — A Bambuddy instance behind Cloudflare logged an inline-script CSP violation on every page load: Cloudflare's bot-detection script (`/cdn-cgi/challenge-platform/scripts/jsd/main.js`) is injected into the HTML on the edge with a hash that changes per request, so it can never be allowlisted by `script-src` hash. The contributor's workaround was to relax `script-src` to `'unsafe-inline'` in their Nginx Proxy Manager — which works but defeats most of the CSP. **Fix**: the SPA CSP now stamps a fresh per-request **nonce** into `script-src` (`'self' 'nonce-<base64>'`). Per [Cloudflare's documented behaviour](https://developers.cloudflare.com/cloudflare-challenges/challenge-types/javascript-detections/#if-you-have-a-content-security-policy-csp), when a nonce is present in the CSP header Cloudflare clones the same nonce onto its injected `<script>` and the inline script passes without `'unsafe-inline'`. Bambuddy's own `index.html` has had no inline scripts since the SW registration moved to `/sw-register.js` (#1460 first PR), so no HTML body rewriting is needed — `'self'` continues to cover every script the app ships. Implemented via a 16-byte `secrets.token_urlsafe()` nonce computed per request in `security_headers_middleware`. **Separately**, `/manifest.json`, `/sw.js` and `/sw-register.js` are now registered with `@app.api_route(methods=["GET", "HEAD"])` instead of `@app.get` — a plain `curl -I https://host/manifest.json` (and several uptime scanners) HEAD-probe these routes and were getting `405 Method Not Allowed`, which surfaced in the issue as an apparent manifest-server bug. **Tests**: 3 new in `test_security_headers.py` — `'nonce-…'` is stamped into the SPA `script-src` directive while `'self'` remains and `'unsafe-inline'` does not; the nonce is fresh per request across 5 sequential calls (collision probability ~0); HEAD on `/manifest.json`, `/sw.js`, `/sw-register.js` never returns 405. 22 security-header tests green; backend ruff clean.
|
||||
|
||||
@@ -246,6 +246,8 @@ describe('presetCompatibility — @BBL name fallback (no bundles)', () => {
|
||||
});
|
||||
|
||||
it('handles a trailing nozzle-size suffix on the @BBL tag', () => {
|
||||
// An explicit "0.4 nozzle" suffix matches the 0.4 printer (Bambu's
|
||||
// convention is to omit it for 0.4, but some cloud presets write it).
|
||||
expect(
|
||||
presetCompatibility(
|
||||
{ name: '0.20mm Standard @BBL X1C 0.4 nozzle' },
|
||||
@@ -254,6 +256,11 @@ describe('presetCompatibility — @BBL name fallback (no bundles)', () => {
|
||||
idx,
|
||||
),
|
||||
).toBe('match');
|
||||
// #1325 follow-up #2 (IndividualGhost1905, 2026-05-23): a different
|
||||
// nozzle size IS a mismatch — a 0.6-nozzle process is unusable on a
|
||||
// 0.4-nozzle printer. The dedicated "nozzle filtering" describe
|
||||
// block below covers the full matrix; this case stays here as the
|
||||
// counterpart to the matching-suffix case above.
|
||||
expect(
|
||||
presetCompatibility(
|
||||
{ name: '0.20mm Standard @BBL X1C 0.6 nozzle' },
|
||||
@@ -261,7 +268,7 @@ describe('presetCompatibility — @BBL name fallback (no bundles)', () => {
|
||||
X1C,
|
||||
idx,
|
||||
),
|
||||
).toBe('match');
|
||||
).toBe('mismatch');
|
||||
});
|
||||
|
||||
it('is unknown when the preset has no @BBL tag at all (custom name, no other signal)', () => {
|
||||
@@ -321,3 +328,100 @@ describe('presetCompatibility — @BBL name fallback (no bundles)', () => {
|
||||
).toBe('mismatch'); // X1C ≠ "X1 Carbon" without the registry
|
||||
});
|
||||
});
|
||||
|
||||
// #1325 follow-up #2 (IndividualGhost1905, 2026-05-23): the @BBL name
|
||||
// fallback must also filter by nozzle diameter. Bambu ships per-nozzle
|
||||
// variants of process / filament presets — 0.2 / 0.4 / 0.6 / 0.8 —
|
||||
// and a 0.6-nozzle process is unusable on a 0.4-nozzle printer. Bambu's
|
||||
// naming convention: 0.4 is the default and DROPS the suffix; 0.2 / 0.6
|
||||
// / 0.8 carry an explicit "<size> nozzle" segment. So an empty suffix
|
||||
// means 0.4, not "any nozzle".
|
||||
describe('presetCompatibility — nozzle filtering on @BBL name fallback', () => {
|
||||
const X1C_04 = 'Bambu Lab X1 Carbon 0.4 nozzle';
|
||||
const X1C_06 = 'Bambu Lab X1 Carbon 0.6 nozzle';
|
||||
const X1C_08 = 'Bambu Lab X1 Carbon 0.8 nozzle';
|
||||
// No bundles uploaded — exercise the @BBL fallback in isolation.
|
||||
const index = buildCompatibilityIndex([], PRINTER_MODELS);
|
||||
|
||||
it('treats a no-suffix process as 0.4 (Bambu default) and matches a 0.4 printer', () => {
|
||||
expect(
|
||||
presetCompatibility({ name: '0.20mm Standard @BBL X1C' }, 'process', X1C_04, index),
|
||||
).toBe('match');
|
||||
});
|
||||
|
||||
it('flags a 0.6-nozzle process as mismatch against a 0.4 printer', () => {
|
||||
expect(
|
||||
presetCompatibility({ name: '0.30mm @BBL X1C 0.6 nozzle' }, 'process', X1C_04, index),
|
||||
).toBe('mismatch');
|
||||
});
|
||||
|
||||
it('flags an 0.8-nozzle process as mismatch against a 0.4 printer', () => {
|
||||
expect(
|
||||
presetCompatibility({ name: '0.40mm Strength @BBL X1C 0.8 nozzle' }, 'process', X1C_04, index),
|
||||
).toBe('mismatch');
|
||||
});
|
||||
|
||||
it('matches a 0.6-nozzle process against a 0.6 printer', () => {
|
||||
expect(
|
||||
presetCompatibility({ name: '0.30mm @BBL X1C 0.6 nozzle' }, 'process', X1C_06, index),
|
||||
).toBe('match');
|
||||
});
|
||||
|
||||
it('flags a no-suffix process (=0.4) as mismatch against a 0.6 printer', () => {
|
||||
expect(
|
||||
presetCompatibility({ name: '0.20mm Standard @BBL X1C' }, 'process', X1C_06, index),
|
||||
).toBe('mismatch');
|
||||
});
|
||||
|
||||
it('applies the same rule to filament presets', () => {
|
||||
// Bambu's bundled filament presets follow the same per-nozzle naming.
|
||||
expect(
|
||||
presetCompatibility(
|
||||
{ name: 'Bambu PLA Basic @BBL X1C 0.6 nozzle' },
|
||||
'filament',
|
||||
X1C_04,
|
||||
index,
|
||||
),
|
||||
).toBe('mismatch');
|
||||
expect(
|
||||
presetCompatibility({ name: 'Bambu PLA Basic @BBL X1C' }, 'filament', X1C_04, index),
|
||||
).toBe('match');
|
||||
});
|
||||
|
||||
it('keeps a 0.4 process matching a 0.4 printer when the preset DOES carry an explicit "0.4 nozzle" suffix', () => {
|
||||
// Some cloud presets write the 0.4 suffix explicitly even though
|
||||
// Bambu's bundled convention omits it. Both forms must compare equal.
|
||||
expect(
|
||||
presetCompatibility(
|
||||
{ name: '0.20mm Standard @BBL X1C 0.4 nozzle' },
|
||||
'process',
|
||||
X1C_04,
|
||||
index,
|
||||
),
|
||||
).toBe('match');
|
||||
});
|
||||
|
||||
it('still flags a wrong-MODEL process even when the nozzle matches', () => {
|
||||
// The model filter must continue to dominate over the nozzle filter:
|
||||
// a 0.4 A1 process isn't usable on an X1C 0.4 just because both are 0.4.
|
||||
expect(
|
||||
presetCompatibility({ name: '0.20mm Standard @BBL A1' }, 'process', X1C_04, index),
|
||||
).toBe('mismatch');
|
||||
});
|
||||
|
||||
it('falls back to model-only when the selected printer name has no parseable nozzle', () => {
|
||||
// Defensive degrade for non-Bambu / hand-typed names that happen to
|
||||
// match the model. Real Bambu printer presets always carry a nozzle,
|
||||
// so this path is rare; the assertion pins the intentional behaviour.
|
||||
const noNozzle = 'Bambu Lab X1 Carbon'; // no "0.4 nozzle" suffix
|
||||
expect(
|
||||
presetCompatibility({ name: '0.30mm @BBL X1C 0.6 nozzle' }, 'process', noNozzle, index),
|
||||
).toBe('match');
|
||||
});
|
||||
|
||||
it('flags 0.4 process on 0.8 printer (sanity check across the third common size)', () => {
|
||||
expect(
|
||||
presetCompatibility({ name: '0.20mm Standard @BBL X1C' }, 'process', X1C_08, index),
|
||||
).toBe('mismatch');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -121,38 +121,62 @@ function normalizeModelFragment(s: string): string {
|
||||
return s.replace(/\s+/g, '').toLowerCase();
|
||||
}
|
||||
|
||||
// Pull the model token out of a "@BBL <token> [0.4 nozzle]" suffix. The token
|
||||
// may contain a space (e.g. "A1 mini") so we strip a trailing nozzle-size
|
||||
// segment rather than splitting on the first whitespace.
|
||||
function extractBblToken(presetName: string): string | null {
|
||||
// Bambu Studio's naming convention for bundled presets: the 0.4 nozzle is
|
||||
// the default and its variants drop the nozzle suffix; 0.2 / 0.6 / 0.8
|
||||
// carry an explicit "<size> nozzle" segment. So a process with no suffix
|
||||
// is implicitly a 0.4 process — required to compare correctly against a
|
||||
// 0.4 printer preset, which DOES carry the suffix.
|
||||
const DEFAULT_NOZZLE = '0.4';
|
||||
|
||||
// Strip a trailing "<size> nozzle" segment, returning the nozzle string
|
||||
// (e.g. "0.6") or null when absent. Used by both BBL-token and printer-
|
||||
// preset extractors so the suffix is parsed identically on both sides.
|
||||
function takeNozzleSuffix(s: string): { stripped: string; nozzle: string | null } {
|
||||
const m = s.match(/^(.*?)\s+([\d.]+)\s*nozzle\s*$/i);
|
||||
if (!m) return { stripped: s.trim(), nozzle: null };
|
||||
return { stripped: m[1].trim(), nozzle: m[2] };
|
||||
}
|
||||
|
||||
// Pull the model token and nozzle out of a "@BBL <token> [<size> nozzle]"
|
||||
// suffix. The token may contain a space (e.g. "A1 mini"), so we strip a
|
||||
// trailing nozzle segment rather than splitting on the first whitespace.
|
||||
function extractBblToken(presetName: string): { token: string; nozzle: string | null } | null {
|
||||
const marker = '@BBL ';
|
||||
const idx = presetName.indexOf(marker);
|
||||
if (idx < 0) return null;
|
||||
const rest = presetName.slice(idx + marker.length).trim();
|
||||
const cleaned = rest.replace(/\s+[\d.]+\s*nozzle\s*$/i, '').trim();
|
||||
return cleaned || null;
|
||||
const { stripped, nozzle } = takeNozzleSuffix(rest);
|
||||
return stripped ? { token: stripped, nozzle } : null;
|
||||
}
|
||||
|
||||
// Pull the model fragment out of a "Bambu Lab <model> [0.4 nozzle]" printer
|
||||
// preset name. Returns null for non-Bambu printer presets — there is no
|
||||
// reliable name-based match against those.
|
||||
function extractPrinterPresetModel(printerPresetName: string): string | null {
|
||||
const m = printerPresetName.match(/^Bambu Lab\s+(.+?)(?:\s+[\d.]+\s*nozzle)?\s*$/i);
|
||||
return m ? m[1].trim() : null;
|
||||
// Pull the model fragment and nozzle out of a "Bambu Lab <model> [<size>
|
||||
// nozzle]" printer preset name. Returns null for non-Bambu printer
|
||||
// presets — there is no reliable name-based match against those.
|
||||
function extractPrinterPresetModel(printerPresetName: string): { model: string; nozzle: string | null } | null {
|
||||
const m = printerPresetName.match(/^Bambu Lab\s+(.+)$/i);
|
||||
if (!m) return null;
|
||||
const { stripped, nozzle } = takeNozzleSuffix(m[1]);
|
||||
return stripped ? { model: stripped, nozzle } : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Name-based fallback for presets BambuStudio ships with a `@BBL <model>`
|
||||
* tag (#1325 follow-up). Used only after `compatible_printers` and the
|
||||
* uploaded-bundle index have already returned `'unknown'`.
|
||||
*
|
||||
* Compares BOTH model AND nozzle. The nozzle filter is required because
|
||||
* Bambu ships per-nozzle process / filament variants (0.2 / 0.4 / 0.6 /
|
||||
* 0.8) — a 0.6-nozzle process is unusable on a 0.4-nozzle printer.
|
||||
* 0.4 is Bambu's default and its variants drop the nozzle suffix, so a
|
||||
* preset with no suffix counts as 0.4.
|
||||
*/
|
||||
function classifyByBambuName(
|
||||
presetName: string,
|
||||
selectedPrinterName: string,
|
||||
bambuModelByShortCode: Record<string, string>,
|
||||
): PrinterCompatibility {
|
||||
const token = extractBblToken(presetName);
|
||||
if (!token) return 'unknown';
|
||||
const parsed = extractBblToken(presetName);
|
||||
if (!parsed) return 'unknown';
|
||||
// If the token isn't in the table (a brand-new Bambu model whose short
|
||||
// code the backend registry hasn't added yet, or the model map hasn't
|
||||
// loaded yet), fall back to comparing the raw token. That keeps the
|
||||
@@ -160,12 +184,21 @@ function classifyByBambuName(
|
||||
// identical — e.g. "Q1" preset against "Bambu Lab Q1 0.4 nozzle" —
|
||||
// without us having to ship a code update. When they differ in form
|
||||
// (X1C vs "X1 Carbon"), the registry is what makes the match work.
|
||||
const inferredModel = bambuModelByShortCode[token] ?? token;
|
||||
const selectedModel = extractPrinterPresetModel(selectedPrinterName);
|
||||
if (!selectedModel) return 'unknown';
|
||||
return normalizeModelFragment(selectedModel) === normalizeModelFragment(inferredModel)
|
||||
? 'match'
|
||||
: 'mismatch';
|
||||
const inferredModel = bambuModelByShortCode[parsed.token] ?? parsed.token;
|
||||
const selectedParts = extractPrinterPresetModel(selectedPrinterName);
|
||||
if (!selectedParts) return 'unknown';
|
||||
if (normalizeModelFragment(selectedParts.model) !== normalizeModelFragment(inferredModel)) {
|
||||
return 'mismatch';
|
||||
}
|
||||
// Nozzle compare — only when we have a usable size from the printer
|
||||
// side. A Bambu printer preset always carries one, so this branch is
|
||||
// taken in practice; the null path is defensive degrade for hand-typed
|
||||
// or non-Bambu printer names that happened to match the model.
|
||||
if (selectedParts.nozzle !== null) {
|
||||
const presetNozzle = parsed.nozzle ?? DEFAULT_NOZZLE;
|
||||
if (presetNozzle !== selectedParts.nozzle) return 'mismatch';
|
||||
}
|
||||
return 'match';
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
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-IAFhhAbT.js"></script>
|
||||
<script type="module" crossorigin src="/assets/index-8qalZ11b.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-HqxudbTf.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
Reference in New Issue
Block a user