Quellcode durchsuchen

feat(slicer): add "slice as designed" mode honouring a 3MF's embedded settings (#2611)

Server-side slicing always applied the picked printer/process/filament
triplet via --load-settings, which overrides the designer's embedded
project_settings.config — so a MakerWorld model set up for 5 walls came
out at the picked profile's default 2. That override is correct for
re-slicing a design onto your own printer/AMS, but there was no way to
slice a file the way its author configured it.

SliceModal now offers a "Use the file's built-in settings" checkbox when
the source 3MF carries embedded settings AND the picked printer matches
the design's target model. It routes to the existing embedded-settings
slice path (previously only a crash fallback), so walls/infill/filament
come from the file. Ticking it locks all four preset dropdowns — printer
included, since it's unused on this path and changing it would drop the
match and hide the toggle. The printer-match gate stops embedded settings
being honoured across models (wrong bed); there is no cross-printer
re-targeting on this path.

- schema: use_embedded_settings on SliceRequest
- route: embedded_mode branch; crash-fallback guarded against re-running
- frontend: gated checkbox locking all four dropdowns, resets on mismatch
- 2 i18n keys across all 11 locales
- tests: backend (flag skips triplet / ignored for STL) + frontend
  (toggle offered on match, locks dropdowns + sends flag / hidden on mismatch)
maziggy vor 1 Monat
Ursprung
Commit
c469aa3407

Datei-Diff unterdrückt, da er zu groß ist
+ 0 - 0
CHANGELOG.md


+ 27 - 2
backend/app/api/routes/library.py

@@ -3528,6 +3528,12 @@ async def _run_slicer_with_fallback(
         presets["process"] = _patch_process_support_settings(presets["process"], primary_bytes)
 
     used_embedded_settings = False
+    # "Slice as designed" (#2611): honour the file's embedded
+    # project_settings.config instead of the picked profile triplet. Only
+    # meaningful for a 3MF that actually carries embedded settings; the UI
+    # gates the toggle on the picked printer matching the design's target,
+    # so this path never re-targets across printer models.
+    embedded_mode = bool(request.use_embedded_settings and is_3mf)
     service = SlicerApiService(api_url)
 
     # #1493: cross-nozzle-class re-slice (single <-> dual). Without
@@ -3606,7 +3612,22 @@ async def _run_slicer_with_fallback(
 
     try:
         try:
-            if use_cross_class_slice_all:
+            if embedded_mode:
+                # No --load-settings: feed the CLI the file's own
+                # project_settings.config untouched so the designer's tweaks
+                # (walls, infill, etc.) drive the slice. primary_bytes is
+                # already sentinel-sanitised above, the same bytes the
+                # crash-fallback uses. The resolved presets go unused here.
+                result = await service.slice_without_profiles(
+                    model_bytes=primary_bytes,
+                    model_filename=model_filename,
+                    plate=request.plate,
+                    export_3mf=request.export_3mf,
+                    request_id=progress_request_id,
+                    on_progress=progress_callback,
+                )
+                used_embedded_settings = True
+            elif use_cross_class_slice_all:
                 from backend.app.services.slicer_3mf_convert import (
                     count_plates_in_3mf,
                     merge_plate_3mfs,
@@ -3708,7 +3729,11 @@ async def _run_slicer_with_fallback(
                 # (e.g. re-slicing an H2D model for an X1C: the object is off
                 # the smaller bed). Surface the slicer's reason instead.
                 raise HTTPException(status_code=400, detail=rejection) from exc
-            if not is_3mf:
+            if not is_3mf or embedded_mode:
+                # embedded_mode already sliced with the file's own settings —
+                # there is nothing to fall back TO, so surface the server
+                # error (the outer handler turns it into a 502) instead of
+                # re-running the same embedded slice.
                 raise
             logger.warning(
                 "Slicer CLI failed on the --load-settings path for %s (%s); retrying with embedded settings",

+ 15 - 0
backend/app/schemas/slicer.py

@@ -82,6 +82,21 @@ class SliceRequest(BaseModel):
         default=False,
         description="If true, request a 3MF response with embedded G-code instead of raw G-code.",
     )
+    use_embedded_settings: bool = Field(
+        default=False,
+        description=(
+            "3MF only. Slice using the file's embedded "
+            "``Metadata/project_settings.config`` (the designer's own tweaks — wall "
+            "count, infill, etc.) instead of the picked printer/process/filament "
+            "triplet. This is the 'slice as designed' path: no ``--load-settings`` "
+            "override, so a MakerWorld author's settings survive. Ignored for STL / "
+            "plain-model 3MF (no embedded profile to honour). The preset refs are "
+            "still required by the validator but go unused on this path. Only makes "
+            "sense when the picked printer matches the design's target model — the "
+            "UI gates the toggle on that; there is no cross-printer re-targeting here "
+            "(that is exactly what the profile path is for)."
+        ),
+    )
     bed_type: str | None = Field(
         default=None,
         max_length=64,

+ 97 - 0
backend/tests/integration/test_library_slice_api.py

@@ -534,6 +534,103 @@ class TestSliceLibraryFile:
         assert "Metadata/cut_information.xml" in names
         assert "3D/3dmodel.model" in names
 
+    @pytest.mark.asyncio
+    @pytest.mark.integration
+    async def test_use_embedded_settings_skips_profile_triplet(
+        self, async_client: AsyncClient, db_session, slice_test_setup
+    ):
+        # "Slice as designed" (#2611): with use_embedded_settings the 3MF is
+        # sliced on its own project_settings.config — no --load-settings — so
+        # the sidecar request carries ONLY the model file, never the
+        # printer/process/filament profile parts. Succeeds on the first call
+        # (no crash-fallback), and the result is flagged used_embedded_settings.
+        src_3mf_path = slice_test_setup["tmp_path"] / "library" / "files" / "designed.3mf"
+        src_3mf_path.write_bytes(_make_3mf_with_settings({"wall_loops": "5"}))
+        threemf = LibraryFile(
+            filename="designed.3mf",
+            file_path=str(src_3mf_path.relative_to(slice_test_setup["tmp_path"])),
+            file_type="3mf",
+            file_size=src_3mf_path.stat().st_size,
+        )
+        db_session.add(threemf)
+        await db_session.commit()
+        await db_session.refresh(threemf)
+
+        captured: dict = {}
+        call_count = {"n": 0}
+
+        def handler(request: httpx.Request) -> httpx.Response:
+            call_count["n"] += 1
+            captured["body"] = request.content
+            return httpx.Response(
+                status_code=200,
+                content=b"PK\x03\x04 fake-3mf",
+                headers={
+                    "x-print-time-seconds": "100",
+                    "x-filament-used-g": "1.0",
+                    "x-filament-used-mm": "100",
+                },
+            )
+
+        _install_mock_sidecar(handler)
+        response = await async_client.post(
+            f"/api/v1/library/files/{threemf.id}/slice",
+            json={
+                "printer_preset_id": slice_test_setup["printer_id"],
+                "process_preset_id": slice_test_setup["process_id"],
+                "filament_preset_id": slice_test_setup["filament_id"],
+                "use_embedded_settings": True,
+            },
+        )
+        assert response.status_code == 202
+        final = await _wait_for_job(async_client, response.json()["job_id"])
+        assert final["status"] == "completed", final
+        assert final["result"]["used_embedded_settings"] is True
+        assert call_count["n"] == 1  # embedded path taken directly, no fallback retry
+
+        # The multipart body must NOT carry any profile part — that is the
+        # whole point of the mode. Their presence would mean --load-settings
+        # ran and overrode the designer's embedded settings.
+        body = captured["body"]
+        assert b"printerProfile" not in body
+        assert b"presetProfile" not in body
+        assert b"filamentProfile" not in body
+
+    @pytest.mark.asyncio
+    @pytest.mark.integration
+    async def test_use_embedded_settings_ignored_for_stl(self, async_client: AsyncClient, slice_test_setup):
+        # An STL has no embedded project settings to honour, so the flag is a
+        # no-op: the normal profile path runs and the triplet is forwarded.
+        captured: dict = {}
+
+        def handler(request: httpx.Request) -> httpx.Response:
+            captured["body"] = request.content
+            return httpx.Response(
+                status_code=200,
+                content=b"PK\x03\x04 fake-3mf",
+                headers={
+                    "x-print-time-seconds": "1",
+                    "x-filament-used-g": "0",
+                    "x-filament-used-mm": "0",
+                },
+            )
+
+        _install_mock_sidecar(handler)
+        response = await async_client.post(
+            f"/api/v1/library/files/{slice_test_setup['src_file_id']}/slice",
+            json={
+                "printer_preset_id": slice_test_setup["printer_id"],
+                "process_preset_id": slice_test_setup["process_id"],
+                "filament_preset_id": slice_test_setup["filament_id"],
+                "use_embedded_settings": True,
+            },
+        )
+        assert response.status_code == 202
+        final = await _wait_for_job(async_client, response.json()["job_id"])
+        assert final["status"] == "completed", final
+        assert final["result"]["used_embedded_settings"] is False
+        assert b"printerProfile" in captured["body"]  # profile path still ran
+
 
 # ---------------------------------------------------------------------------
 # GET /slice-jobs/{id}

+ 73 - 0
frontend/src/__tests__/components/SliceModal.test.tsx

@@ -259,6 +259,79 @@ describe('SliceModal', () => {
     await waitFor(() => expect(onClose).toHaveBeenCalled());
   });
 
+  it('offers "use the file\'s built-in settings" when the printer matches the design, and sends the flag (#2611)', async () => {
+    const onClose = vi.fn();
+    mockApi.sliceLibraryFile.mockResolvedValue({
+      job_id: 42,
+      status: 'pending',
+      status_url: '/api/v1/slice-jobs/42',
+    });
+    // A project 3MF whose embedded printer matches a listed preset — the
+    // printer pre-pick lands on it, so selectedPrinterName === embedded and
+    // the "slice as designed" toggle is offered.
+    mockApi.getLibraryFilePlates.mockResolvedValue({
+      file_id: 100,
+      filename: 'Designed.3mf',
+      plates: [],
+      is_multi_plate: false,
+      embedded_printer: 'Bambu Lab X1 Carbon 0.4 nozzle',
+      embedded_process: '0.20mm Standard',
+    });
+
+    renderWithTracker({
+      source: { kind: 'libraryFile', id: 100, filename: 'Designed.3mf' },
+      onClose,
+    });
+
+    const user = userEvent.setup();
+    const toggle = (await screen.findByLabelText(
+      /Use the file's built-in settings/,
+    )) as HTMLInputElement;
+    expect(toggle.checked).toBe(false);
+
+    // All preset dropdowns are live until the toggle is on, then bypassed —
+    // the printer included, so changing it can't silently drop the mode.
+    const printerSelect = presetSelects()[0];
+    const processSelect = presetSelects()[1];
+    expect(printerSelect.disabled).toBe(false);
+    expect(processSelect.disabled).toBe(false);
+    await user.click(toggle);
+    expect(printerSelect.disabled).toBe(true);
+    expect(processSelect.disabled).toBe(true);
+
+    await user.click(screen.getByRole('button', { name: /^Slice$/ }));
+    await waitFor(() => {
+      expect(mockApi.sliceLibraryFile).toHaveBeenCalledWith(
+        100,
+        expect.objectContaining({ use_embedded_settings: true }),
+      );
+    });
+    await waitFor(() => expect(onClose).toHaveBeenCalled());
+  });
+
+  it('hides the embedded-settings toggle when the picked printer differs from the design (#2611)', async () => {
+    // Embedded target is a model with no matching preset in the listing, so
+    // the printer pre-pick falls back to the local default (Imported X1C),
+    // which does not match — honouring embedded settings would risk the
+    // wrong bed, so the toggle stays hidden.
+    mockApi.getLibraryFilePlates.mockResolvedValue({
+      file_id: 100,
+      filename: 'Designed.3mf',
+      plates: [],
+      is_multi_plate: false,
+      embedded_printer: 'Bambu Lab P1S 0.4 nozzle',
+      embedded_process: '0.20mm Standard',
+    });
+
+    renderWithTracker({
+      source: { kind: 'libraryFile', id: 100, filename: 'Designed.3mf' },
+      onClose: vi.fn(),
+    });
+
+    await waitFor(() => expect(screen.getByText('Imported X1C 0.4')).toBeDefined());
+    expect(screen.queryByLabelText(/Use the file's built-in settings/)).toBeNull();
+  });
+
   it('includes bed_type in the request when the user picks a non-auto plate (#1337)', async () => {
     const onClose = vi.fn();
     mockApi.sliceLibraryFile.mockResolvedValue({

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

@@ -1543,6 +1543,11 @@ export interface SliceRequest {
   // "Textured PEI Plate", "Smooth PEI Plate", "Cool Plate (SuperTack)",
   // "Supertack Plate".
   bed_type?: string | null;
+  // "Slice as designed" (#2611). 3MF only: slice using the file's embedded
+  // project_settings.config (the designer's own wall count, infill, etc.)
+  // instead of the picked profile triplet. The preset refs above are still
+  // required by the backend validator but go unused on this path.
+  use_embedded_settings?: boolean;
 }
 
 // GET /api/v1/slicer/presets — unified listing across cloud / local / standard.

+ 61 - 4
frontend/src/components/SliceModal.tsx

@@ -220,6 +220,13 @@ export function SliceModal({ source, onClose }: SliceModalProps) {
   // user had no way to switch plates without cloning the preset.
   const [bedType, setBedType] = useState<string | null>(null);
 
+  // "Slice as designed" (#2611). When on, the backend honours the source
+  // 3MF's embedded project_settings.config (the designer's own wall count,
+  // infill, etc.) instead of the picked process/filament profiles. Only
+  // offered when the picked printer matches the design's target model —
+  // see canUseEmbedded below.
+  const [useEmbedded, setUseEmbedded] = useState(false);
+
   // Slicer Pipelines (#1425) — apply a saved preset bundle to all four slots
   // with one pick, or save the current selection as a new pipeline.
   const pipelinesQuery = useQuery({
@@ -380,6 +387,24 @@ export function SliceModal({ source, onClose }: SliceModalProps) {
   const embeddedPrinter = platesQuery.data?.embedded_printer ?? null;
   const embeddedProcess = platesQuery.data?.embedded_process ?? null;
 
+  // "Slice as designed" is offered only when the source carries embedded
+  // settings (a real project 3MF, not an STL) AND the picked printer matches
+  // the design's target model. The match gate is load-bearing: honouring
+  // embedded settings for a different model would place the model on the
+  // wrong bed. Names come from the same preset namespace, so a normalised
+  // (strip "# " prefix, case-fold) equality is enough.
+  const canUseEmbedded = useMemo<boolean>(() => {
+    if (!embeddedPrinter || !embeddedProcess || !selectedPrinterName) return false;
+    const norm = (s: string) => s.replace(/^#\s*/, '').trim().toLowerCase();
+    return norm(selectedPrinterName) === norm(embeddedPrinter);
+  }, [embeddedPrinter, embeddedProcess, selectedPrinterName]);
+
+  // Drop back to profile slicing whenever the toggle stops being offered
+  // (e.g. the user switches to a printer that doesn't match the design).
+  useEffect(() => {
+    if (!canUseEmbedded) setUseEmbedded(false);
+  }, [canUseEmbedded]);
+
   // Printer pre-pick: defaults to the printer the 3MF was prepared for when
   // that preset is available, else the first listed printer. Runs once when
   // presets first arrive; later re-renders preserve any manual choice.
@@ -476,6 +501,10 @@ export function SliceModal({ source, onClose }: SliceModalProps) {
       filament_presets: filamentPresets as PresetRef[],
       ...(plate != null ? { plate } : {}),
       ...(bedType != null ? { bed_type: bedType } : {}),
+      // The preset refs above are still sent (the backend validator requires
+      // them) but go unused when this flag is set — the slicer falls back on
+      // the file's embedded project_settings.config instead.
+      ...(useEmbedded && canUseEmbedded ? { use_embedded_settings: true } : {}),
     };
   }
 
@@ -710,25 +739,53 @@ export function SliceModal({ source, onClose }: SliceModalProps) {
                 data={presetsQuery.data}
                 value={printerPreset}
                 onChange={setPrinterPreset}
-                disabled={isEnqueuing}
+                // Locked in embedded mode too: the picked printer is unused on
+                // the embedded-settings path, and changing it away from the
+                // design's target would drop canUseEmbedded and yank the toggle
+                // out from under the user (#2611).
+                disabled={isEnqueuing || useEmbedded}
               />
+              {/* "Slice as designed" (#2611): honour the file's embedded
+                  settings instead of the picked process/filament. Offered
+                  only when the picked printer matches the design's target. */}
+              {canUseEmbedded && (
+                <label className="flex items-start gap-2 text-sm text-bambu-gray cursor-pointer select-none">
+                  <input
+                    type="checkbox"
+                    checked={useEmbedded}
+                    onChange={(e) => setUseEmbedded(e.target.checked)}
+                    disabled={isEnqueuing}
+                    className="mt-0.5 cursor-pointer"
+                  />
+                  <span>
+                    {t('slice.useEmbedded')}
+                    <span className="block text-xs text-bambu-gray/70">
+                      {t('slice.useEmbeddedHint')}
+                    </span>
+                  </span>
+                </label>
+              )}
               <PresetDropdown
                 label={t('slice.process')}
                 slot="process"
                 data={presetsQuery.data}
                 value={processPreset}
                 onChange={setProcessPreset}
-                disabled={isEnqueuing}
+                disabled={isEnqueuing || useEmbedded}
                 selectedPrinterName={selectedPrinterName}
                 compatIndex={compatIndex}
               />
               {/* Bed-type override (#1337). Always visible, always enabled.
                   The backend patches curr_bed_type on the resolved process
                   JSON before forwarding to the sidecar. */}
+              {/* Bed-type patches curr_bed_type onto the resolved process
+                  JSON, which the embedded-settings path never sends — so it
+                  has no effect there and is disabled to avoid implying it
+                  does. */}
               <BedTypeDropdown
                 value={bedType}
                 onChange={setBedType}
-                disabled={isEnqueuing}
+                disabled={isEnqueuing || useEmbedded}
               />
               {/* Filament reqs may need a server-side preview-slice for
                   unsliced project files (single-pass, then cached). Show a
@@ -775,7 +832,7 @@ export function SliceModal({ source, onClose }: SliceModalProps) {
                           return next;
                         })
                       }
-                      disabled={isEnqueuing || !isUsed}
+                      disabled={isEnqueuing || !isUsed || useEmbedded}
                       swatchColor={filamentSlots.length > 1 ? slot.color : undefined}
                       selectedPrinterName={selectedPrinterName}
                       compatIndex={compatIndex}

+ 2 - 0
frontend/src/i18n/locales/de.ts

@@ -4032,6 +4032,8 @@ export default {
     refreshPresets: 'Aktualisieren',
     refreshPresetsTitle: 'Profile neu laden — die aktuellen Cloud- und Bundle-Listen abrufen (nach dem Löschen eines Profils in Bambu Studio oder Bambu Handy verwenden)',
     allPresetsRequired: 'Alle Profile müssen ausgewählt sein',
+    useEmbedded: 'Eingebettete Einstellungen der Datei verwenden',
+    useEmbeddedHint: 'So slicen, wie der Ersteller es angelegt hat (Wände, Füllung, Filament), statt mit den obigen Profilen. Verfügbar, weil dein Drucker zur Datei passt.',
     enqueuing: 'Slice-Auftrag wird übermittelt…',
     queued: 'In Warteschlange…',
     failed: 'Slicen fehlgeschlagen. Logs des Slicer-Sidecars prüfen.',

+ 2 - 0
frontend/src/i18n/locales/en.ts

@@ -4066,6 +4066,8 @@ export default {
     refreshPresets: 'Refresh',
     refreshPresetsTitle: 'Refresh presets — fetch the latest cloud and bundled listings (use after deleting a preset in Bambu Studio or Bambu Handy)',
     allPresetsRequired: 'All presets must be selected',
+    useEmbedded: "Use the file's built-in settings",
+    useEmbeddedHint: "Slice it the way the designer set it up (walls, infill, filament) instead of the profiles above. Offered because your printer matches the file's.",
     enqueuing: 'Submitting slice job…',
     queued: 'Queued…',
     failed: 'Slicing failed. Check the slicer sidecar logs.',

+ 2 - 0
frontend/src/i18n/locales/es.ts

@@ -4035,6 +4035,8 @@ export default {
     refreshPresets: 'Actualizar',
     refreshPresetsTitle: 'Actualizar preajustes — recuperar los listados más recientes de la nube y los paquetes (úselo tras eliminar un preajuste en Bambu Studio o Bambu Handy)',
     allPresetsRequired: 'Deben seleccionarse todos los preajustes',
+    useEmbedded: 'Usar la configuración incorporada del archivo',
+    useEmbeddedHint: 'Laminar tal como lo configuró el diseñador (perímetros, relleno, filamento) en lugar de los perfiles de arriba. Disponible porque tu impresora coincide con la del archivo.',
     enqueuing: 'Enviando el trabajo de laminado…',
     queued: 'En cola…',
     failed: 'Error al laminar. Consulte los registros del contenedor auxiliar del laminador.',

+ 2 - 0
frontend/src/i18n/locales/fr.ts

@@ -4021,6 +4021,8 @@ export default {
     refreshPresets: 'Actualiser',
     refreshPresetsTitle: 'Actualiser les préréglages — récupérer les dernières listes Cloud et bundle (à utiliser après avoir supprimé un préréglage dans Bambu Studio ou Bambu Handy)',
     allPresetsRequired: 'Tous les préréglages doivent être sélectionnés',
+    useEmbedded: 'Utiliser les réglages intégrés du fichier',
+    useEmbeddedHint: "Slicer tel que le concepteur l'a configuré (parois, remplissage, filament) au lieu des profils ci-dessus. Proposé car votre imprimante correspond à celle du fichier.",
     enqueuing: 'Envoi du travail de découpage…',
     queued: 'En file d\'attente…',
     failed: 'Échec du découpage. Vérifiez les journaux du sidecar.',

+ 2 - 0
frontend/src/i18n/locales/it.ts

@@ -4020,6 +4020,8 @@ export default {
     refreshPresets: 'Aggiorna',
     refreshPresetsTitle: 'Aggiorna i preset — recupera gli elenchi più recenti dal cloud e dai bundle (da usare dopo aver eliminato un preset in Bambu Studio o Bambu Handy)',
     allPresetsRequired: 'Tutti i preset devono essere selezionati',
+    useEmbedded: 'Usa le impostazioni integrate del file',
+    useEmbeddedHint: 'Slicia come impostato dal designer (pareti, riempimento, filamento) invece dei profili sopra. Disponibile perché la tua stampante corrisponde a quella del file.',
     enqueuing: 'Invio lavoro di slicing…',
     queued: 'In coda…',
     failed: 'Slicing fallito. Controlla i log del sidecar.',

+ 2 - 0
frontend/src/i18n/locales/ja.ts

@@ -4032,6 +4032,8 @@ export default {
     refreshPresets: '再読み込み',
     refreshPresetsTitle: 'プリセットを再取得 — クラウドとバンドルの最新リストを取得します(Bambu Studio または Bambu Handy でプリセットを削除した後にお使いください)',
     allPresetsRequired: 'すべてのプリセットを選択する必要があります',
+    useEmbedded: 'ファイルに埋め込まれた設定を使用',
+    useEmbeddedHint: '上のプロファイルではなく、設計者が設定したとおり(ウォール、インフィル、フィラメント)にスライスします。お使いのプリンターがファイルと一致するため利用できます。',
     enqueuing: 'スライスジョブを送信中…',
     queued: '待機中…',
     failed: 'スライスに失敗。サイドカーのログを確認してください。',

+ 2 - 0
frontend/src/i18n/locales/ko.ts

@@ -3821,6 +3821,8 @@ export default {
     refreshPresets: '새로 고침',
     refreshPresetsTitle: '프리셋 새로 고침 — 최신 클라우드 및 번들 목록 가져오기 (Bambu Studio 또는 Bambu Handy에서 프리셋 삭제 후 사용)',
     allPresetsRequired: '모든 프리셋을 선택해야 합니다',
+    useEmbedded: '파일에 포함된 설정 사용',
+    useEmbeddedHint: '위 프로필 대신 디자이너가 설정한 대로(벽, 내부 채움, 필라멘트) 슬라이싱합니다. 프린터가 파일과 일치하여 사용할 수 있습니다.',
     enqueuing: '슬라이싱 작업 제출 중…',
     queued: '대기 중…',
     failed: '슬라이싱 실패. 슬라이서 사이드카 로그를 확인하세요.',

+ 2 - 0
frontend/src/i18n/locales/pt-BR.ts

@@ -4020,6 +4020,8 @@ export default {
     refreshPresets: 'Atualizar',
     refreshPresetsTitle: 'Atualizar predefinições — buscar as listagens mais recentes da nuvem e dos pacotes (use após excluir uma predefinição no Bambu Studio ou Bambu Handy)',
     allPresetsRequired: 'Todas as predefinições devem ser selecionadas',
+    useEmbedded: 'Usar as configurações incorporadas do arquivo',
+    useEmbeddedHint: 'Fatiar como o designer configurou (paredes, preenchimento, filamento) em vez dos perfis acima. Disponível porque sua impressora corresponde à do arquivo.',
     enqueuing: 'Enviando trabalho de fatiamento…',
     queued: 'Na fila…',
     failed: 'Falha ao fatiar. Verifique os logs do sidecar.',

+ 2 - 0
frontend/src/i18n/locales/tr.ts

@@ -4022,6 +4022,8 @@ export default {
     refreshPresets: 'Yenile',
     refreshPresetsTitle: "Ön ayarları yenile — en güncel bulut ve paketli listeleri getir (Bambu Studio veya Bambu Handy'de bir ön ayar sildikten sonra kullanın)",
     allPresetsRequired: 'Tüm ön ayarlar seçilmelidir',
+    useEmbedded: 'Dosyanın yerleşik ayarlarını kullan',
+    useEmbeddedHint: 'Yukarıdaki profiller yerine tasarımcının ayarladığı gibi (duvarlar, dolgu, filament) dilimle. Yazıcınız dosyayla eşleştiği için sunuluyor.',
     enqueuing: 'Dilimleme işi gönderiliyor…',
     queued: 'Kuyrukta…',
     failed: 'Dilimleme başarısız. Dilimleyici yardımcı bileşen günlüklerini kontrol edin.',

+ 2 - 0
frontend/src/i18n/locales/zh-CN.ts

@@ -4020,6 +4020,8 @@ export default {
     refreshPresets: '刷新',
     refreshPresetsTitle: '刷新预设 — 获取最新的云端和打包配置列表(在 Bambu Studio 或 Bambu Handy 中删除预设后使用)',
     allPresetsRequired: '必须选择所有预设',
+    useEmbedded: '使用文件的内置设置',
+    useEmbeddedHint: '按设计者的设置(壁、填充、耗材)切片,而非上方的配置文件。因您的打印机与文件匹配而可用。',
     enqueuing: '提交切片任务中…',
     queued: '已排队…',
     failed: '切片失败。请检查切片器 sidecar 日志。',

+ 2 - 0
frontend/src/i18n/locales/zh-TW.ts

@@ -4020,6 +4020,8 @@ export default {
     refreshPresets: '重新整理',
     refreshPresetsTitle: '重新整理預設 — 擷取最新的雲端與打包設定清單(在 Bambu Studio 或 Bambu Handy 中刪除預設後使用)',
     allPresetsRequired: '必須選擇所有預設',
+    useEmbedded: '使用檔案的內建設定',
+    useEmbeddedHint: '依設計者的設定(外牆、填充、耗材)切片,而非上方的設定檔。因您的印表機與檔案相符而可用。',
     enqueuing: '提交切片任務中…',
     queued: '已排隊…',
     failed: '切片失敗。請檢查切片器 sidecar 日誌。',

Datei-Diff unterdrückt, da er zu groß ist
+ 0 - 0
static/assets/index-G-CyGmAG.js


+ 1 - 1
static/index.html

@@ -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-Bvvb3PBX.js"></script>
+    <script type="module" crossorigin src="/assets/index-G-CyGmAG.js"></script>
     <link rel="stylesheet" crossorigin href="/assets/index-CZwzTgpo.css">
   </head>
   <body>

Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.