mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-10-17 19:36:28 +02:00
Merge branch 'dev' into feature/ukrainian-localization
This commit is contained in:
@@ -332,6 +332,138 @@ describe('SliceModal', () => {
|
||||
expect(screen.queryByLabelText(/Use the file's built-in settings/)).toBeNull();
|
||||
});
|
||||
|
||||
// #2622: a MakerWorld 3MF designed for another printer carries the author's
|
||||
// own process tweaks. BambuStudio records which keys deviate from the stock
|
||||
// preset inside the file, so a cross-printer re-slice can carry them instead
|
||||
// of losing them to the picked process profile.
|
||||
const designedFor = {
|
||||
file_id: 100,
|
||||
filename: 'Designed.3mf',
|
||||
plates: [],
|
||||
is_multi_plate: false,
|
||||
embedded_printer: 'Bambu Lab A1 0.4 nozzle',
|
||||
embedded_process: '0.20mm Standard @BBL A1',
|
||||
design_overrides: [
|
||||
{ key: 'wall_loops', value: '5', printer_coupled: false },
|
||||
{ key: 'sparse_infill_density', value: '100%', printer_coupled: false },
|
||||
{ key: 'outer_wall_speed', value: '200', printer_coupled: true },
|
||||
],
|
||||
};
|
||||
|
||||
async function openDesignSection() {
|
||||
const user = userEvent.setup();
|
||||
await user.click(await screen.findByText(/Keep the designer's settings/));
|
||||
return user;
|
||||
}
|
||||
|
||||
it("carries the design's printer-independent settings by default (#2622)", async () => {
|
||||
mockApi.sliceLibraryFile.mockResolvedValue({
|
||||
job_id: 42,
|
||||
status: 'pending',
|
||||
status_url: '/api/v1/slice-jobs/42',
|
||||
});
|
||||
mockApi.getLibraryFilePlates.mockResolvedValue(designedFor);
|
||||
|
||||
renderWithTracker({
|
||||
source: { kind: 'libraryFile', id: 100, filename: 'Designed.3mf' },
|
||||
onClose: vi.fn(),
|
||||
});
|
||||
|
||||
// Two of three pre-selected: the speed key is machine-coupled.
|
||||
expect(await screen.findByText('2 of 3 selected')).toBeInTheDocument();
|
||||
|
||||
const user = userEvent.setup();
|
||||
await user.click(screen.getByRole('button', { name: /^Slice$/ }));
|
||||
|
||||
await waitFor(() => expect(mockApi.sliceLibraryFile).toHaveBeenCalled());
|
||||
const payload = mockApi.sliceLibraryFile.mock.calls[0][1] as { design_overrides?: string[] };
|
||||
expect([...(payload.design_overrides ?? [])].sort()).toEqual(['sparse_infill_density', 'wall_loops']);
|
||||
});
|
||||
|
||||
it('lists every changed setting with its value and flags the machine-coupled ones (#2622)', async () => {
|
||||
mockApi.getLibraryFilePlates.mockResolvedValue(designedFor);
|
||||
|
||||
renderWithTracker({
|
||||
source: { kind: 'libraryFile', id: 100, filename: 'Designed.3mf' },
|
||||
onClose: vi.fn(),
|
||||
});
|
||||
|
||||
await openDesignSection();
|
||||
|
||||
expect(screen.getByText('wall_loops')).toBeInTheDocument();
|
||||
expect(screen.getByText('5')).toBeInTheDocument();
|
||||
expect(screen.getByText('sparse_infill_density')).toBeInTheDocument();
|
||||
expect(screen.getByText('100%')).toBeInTheDocument();
|
||||
// The risky one is listed too — visible, explained, just not pre-ticked.
|
||||
expect(screen.getByText('outer_wall_speed')).toBeInTheDocument();
|
||||
expect(screen.getByText('printer-specific')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('lets the user opt a machine-coupled setting in and a safe one out (#2622)', async () => {
|
||||
mockApi.sliceLibraryFile.mockResolvedValue({
|
||||
job_id: 42,
|
||||
status: 'pending',
|
||||
status_url: '/api/v1/slice-jobs/42',
|
||||
});
|
||||
mockApi.getLibraryFilePlates.mockResolvedValue(designedFor);
|
||||
|
||||
renderWithTracker({
|
||||
source: { kind: 'libraryFile', id: 100, filename: 'Designed.3mf' },
|
||||
onClose: vi.fn(),
|
||||
});
|
||||
|
||||
const user = await openDesignSection();
|
||||
const boxes = screen.getAllByRole('checkbox') as HTMLInputElement[];
|
||||
const byKey = (key: string) =>
|
||||
boxes.find((b) => b.closest('label')?.textContent?.includes(key)) as HTMLInputElement;
|
||||
|
||||
await user.click(byKey('outer_wall_speed'));
|
||||
await user.click(byKey('wall_loops'));
|
||||
|
||||
await user.click(screen.getByRole('button', { name: /^Slice$/ }));
|
||||
|
||||
await waitFor(() => expect(mockApi.sliceLibraryFile).toHaveBeenCalled());
|
||||
const payload = mockApi.sliceLibraryFile.mock.calls[0][1] as { design_overrides?: string[] };
|
||||
expect([...(payload.design_overrides ?? [])].sort()).toEqual(['outer_wall_speed', 'sparse_infill_density']);
|
||||
});
|
||||
|
||||
it('omits design_overrides entirely when the user unticks everything (#2622)', async () => {
|
||||
mockApi.sliceLibraryFile.mockResolvedValue({
|
||||
job_id: 42,
|
||||
status: 'pending',
|
||||
status_url: '/api/v1/slice-jobs/42',
|
||||
});
|
||||
mockApi.getLibraryFilePlates.mockResolvedValue(designedFor);
|
||||
|
||||
renderWithTracker({
|
||||
source: { kind: 'libraryFile', id: 100, filename: 'Designed.3mf' },
|
||||
onClose: vi.fn(),
|
||||
});
|
||||
|
||||
const user = await openDesignSection();
|
||||
const boxes = screen.getAllByRole('checkbox') as HTMLInputElement[];
|
||||
for (const box of boxes) {
|
||||
if (box.checked) await user.click(box);
|
||||
}
|
||||
|
||||
await user.click(screen.getByRole('button', { name: /^Slice$/ }));
|
||||
|
||||
await waitFor(() => expect(mockApi.sliceLibraryFile).toHaveBeenCalled());
|
||||
expect(mockApi.sliceLibraryFile.mock.calls[0][1]).not.toHaveProperty('design_overrides');
|
||||
});
|
||||
|
||||
it('hides the section for a file that changes nothing (#2622)', async () => {
|
||||
mockApi.getLibraryFilePlates.mockResolvedValue({ ...designedFor, design_overrides: [] });
|
||||
|
||||
renderWithTracker({
|
||||
source: { kind: 'libraryFile', id: 100, filename: 'Designed.3mf' },
|
||||
onClose: vi.fn(),
|
||||
});
|
||||
|
||||
await waitFor(() => expect(screen.getByRole('button', { name: /^Slice$/ })).toBeEnabled());
|
||||
expect(screen.queryByText(/Keep the designer's 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({
|
||||
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
import { useSliceJobTracker } from '../contexts/SliceJobTrackerContext';
|
||||
import { useToast } from '../contexts/ToastContext';
|
||||
import { PlatePickerModal } from './PlatePickerModal';
|
||||
import type { PlateFilament } from '../types/plates';
|
||||
import type { DesignOverride, PlateFilament } from '../types/plates';
|
||||
import {
|
||||
presetCompatibility,
|
||||
buildCompatibilityIndex,
|
||||
@@ -186,6 +186,16 @@ function formatElapsed(seconds: number): string {
|
||||
return `${h}h ${remM}m`;
|
||||
}
|
||||
|
||||
// Render a slicer parameter value for the design-settings list. Bambu's process
|
||||
// schema stores everything as strings or arrays of strings, so this only has to
|
||||
// flatten arrays and keep scalars readable — no unit or type interpretation,
|
||||
// which would rot against every slicer release.
|
||||
function formatDesignValue(value: unknown): string {
|
||||
if (Array.isArray(value)) return value.map((v) => String(v)).join(', ');
|
||||
if (value == null) return '';
|
||||
return String(value);
|
||||
}
|
||||
|
||||
export function SliceModal({ source, onClose }: SliceModalProps) {
|
||||
const { t } = useTranslation();
|
||||
const { trackJob } = useSliceJobTracker();
|
||||
@@ -227,6 +237,15 @@ export function SliceModal({ source, onClose }: SliceModalProps) {
|
||||
// see canUseEmbedded below.
|
||||
const [useEmbedded, setUseEmbedded] = useState(false);
|
||||
|
||||
// #2622: process settings the designer changed away from the stock preset,
|
||||
// carried onto the picked process profile so a cross-printer re-slice keeps
|
||||
// the model's intended wall count / infill / first layer instead of losing
|
||||
// them to --load-settings. Keys the file flags as machine-coupled (speeds,
|
||||
// accelerations, prime-tower geometry) are listed but start unticked — those
|
||||
// were tuned for the designer's printer and can be plain wrong on another.
|
||||
const [designKeys, setDesignKeys] = useState<Set<string>>(new Set());
|
||||
const [designExpanded, setDesignExpanded] = 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({
|
||||
@@ -385,6 +404,10 @@ export function SliceModal({ source, onClose }: SliceModalProps) {
|
||||
// plates query resolves before the presets query (the latter is gated on
|
||||
// it), so these are known by the time the pre-pick effects run.
|
||||
const embeddedPrinter = platesQuery.data?.embedded_printer ?? null;
|
||||
const designOverrides = useMemo<DesignOverride[]>(
|
||||
() => platesQuery.data?.design_overrides ?? [],
|
||||
[platesQuery.data],
|
||||
);
|
||||
const embeddedProcess = platesQuery.data?.embedded_process ?? null;
|
||||
|
||||
// "Slice as designed" is offered only when the source carries embedded
|
||||
@@ -405,6 +428,12 @@ export function SliceModal({ source, onClose }: SliceModalProps) {
|
||||
if (!canUseEmbedded) setUseEmbedded(false);
|
||||
}, [canUseEmbedded]);
|
||||
|
||||
// Pre-tick the printer-independent design settings once the source's list
|
||||
// arrives. Machine-coupled keys stay off until the user opts in explicitly.
|
||||
useEffect(() => {
|
||||
setDesignKeys(new Set(designOverrides.filter((o) => !o.printer_coupled).map((o) => o.key)));
|
||||
}, [designOverrides]);
|
||||
|
||||
// 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.
|
||||
@@ -505,6 +534,10 @@ export function SliceModal({ source, onClose }: SliceModalProps) {
|
||||
// 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 } : {}),
|
||||
// Carried design settings are patched onto the resolved process JSON,
|
||||
// which the embedded-settings path never sends — so they are mutually
|
||||
// exclusive by construction (#2622).
|
||||
...(!useEmbedded && designKeys.size > 0 ? { design_overrides: [...designKeys] } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -775,6 +808,68 @@ export function SliceModal({ source, onClose }: SliceModalProps) {
|
||||
selectedPrinterName={selectedPrinterName}
|
||||
compatIndex={compatIndex}
|
||||
/>
|
||||
{/* Designer's process tweaks (#2622). BambuStudio records which
|
||||
keys deviate from the stock preset in the 3MF itself, so a
|
||||
re-slice for another printer can carry them instead of
|
||||
flattening them under --load-settings. Hidden entirely when
|
||||
the source lists none, and disabled in embedded mode where
|
||||
the process JSON these patch is never sent. */}
|
||||
{designOverrides.length > 0 && (
|
||||
<div className="rounded-lg border border-bambu-dark-tertiary bg-bambu-dark/40 p-3">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setDesignExpanded((v) => !v)}
|
||||
className="flex w-full items-center justify-between gap-2 text-left"
|
||||
>
|
||||
<span className="text-sm text-white">
|
||||
{t('slice.designSettings')}
|
||||
<span className="block text-xs text-bambu-gray/70">
|
||||
{t('slice.designSettingsHint', { count: designOverrides.length })}
|
||||
</span>
|
||||
</span>
|
||||
<span className="shrink-0 text-xs text-bambu-gray">
|
||||
{t('slice.designSettingsSelected', { selected: designKeys.size, total: designOverrides.length })}
|
||||
</span>
|
||||
</button>
|
||||
{designExpanded && (
|
||||
<div className="mt-3 space-y-1.5 border-t border-bambu-dark-tertiary pt-3">
|
||||
{designOverrides.map((o) => (
|
||||
<label
|
||||
key={o.key}
|
||||
className={`flex items-start gap-2 text-xs ${useEmbedded ? 'opacity-50' : 'cursor-pointer'}`}
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={designKeys.has(o.key)}
|
||||
disabled={isEnqueuing || useEmbedded}
|
||||
onChange={(e) => {
|
||||
setDesignKeys((prev) => {
|
||||
const next = new Set(prev);
|
||||
if (e.target.checked) next.add(o.key);
|
||||
else next.delete(o.key);
|
||||
return next;
|
||||
});
|
||||
}}
|
||||
className="mt-0.5 shrink-0 cursor-pointer"
|
||||
/>
|
||||
<span className="min-w-0 flex-1">
|
||||
<span className="font-mono text-bambu-gray">{o.key}</span>
|
||||
<span className="ml-1.5 break-all text-white">{formatDesignValue(o.value)}</span>
|
||||
{o.printer_coupled && (
|
||||
<span
|
||||
className="ml-1.5 rounded bg-amber-100 px-1 py-0.5 text-[10px] text-amber-700 dark:bg-amber-500/20 dark:text-amber-400"
|
||||
title={t('slice.designSettingsPrinterCoupledHint')}
|
||||
>
|
||||
{t('slice.designSettingsPrinterCoupled')}
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{/* Bed-type override (#1337). Always visible, always enabled.
|
||||
The backend patches curr_bed_type on the resolved process
|
||||
JSON before forwarding to the sidecar. */}
|
||||
|
||||
@@ -4072,6 +4072,11 @@ export default {
|
||||
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.',
|
||||
designSettings: 'Einstellungen des Erstellers behalten',
|
||||
designSettingsHint: 'Diese Datei ändert {{count}} Druckeinstellung(en) gegenüber dem Standardprofil.',
|
||||
designSettingsSelected: '{{selected}} von {{total}} ausgewählt',
|
||||
designSettingsPrinterCoupled: 'druckerspezifisch',
|
||||
designSettingsPrinterCoupledHint: 'Auf den Drucker abgestimmt, für den die Datei erstellt wurde — auf deinem kann der Wert falsch oder unzulässig sein.',
|
||||
enqueuing: 'Slice-Auftrag wird übermittelt…',
|
||||
queued: 'In Warteschlange…',
|
||||
failed: 'Slicen fehlgeschlagen. Logs des Slicer-Sidecars prüfen.',
|
||||
|
||||
@@ -4106,6 +4106,11 @@ export default {
|
||||
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.",
|
||||
designSettings: 'Keep the designer\'s settings',
|
||||
designSettingsHint: 'This file changes {{count}} print setting(s) from the stock profile.',
|
||||
designSettingsSelected: '{{selected}} of {{total}} selected',
|
||||
designSettingsPrinterCoupled: 'printer-specific',
|
||||
designSettingsPrinterCoupledHint: 'Tuned for the printer this file was designed for — may be wrong or out of range on yours.',
|
||||
enqueuing: 'Submitting slice job…',
|
||||
queued: 'Queued…',
|
||||
failed: 'Slicing failed. Check the slicer sidecar logs.',
|
||||
|
||||
@@ -4075,6 +4075,11 @@ export default {
|
||||
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.',
|
||||
designSettings: 'Mantener los ajustes del diseñador',
|
||||
designSettingsHint: 'Este archivo cambia {{count}} ajuste(s) de impresión respecto al perfil estándar.',
|
||||
designSettingsSelected: '{{selected}} de {{total}} seleccionados',
|
||||
designSettingsPrinterCoupled: 'específico de la impresora',
|
||||
designSettingsPrinterCoupledHint: 'Ajustado para la impresora para la que se diseñó el archivo: puede ser incorrecto o quedar fuera de rango en la tuya.',
|
||||
enqueuing: 'Enviando el trabajo de laminado…',
|
||||
queued: 'En cola…',
|
||||
failed: 'Error al laminar. Consulte los registros del contenedor auxiliar del laminador.',
|
||||
|
||||
@@ -4061,6 +4061,11 @@ export default {
|
||||
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.",
|
||||
designSettings: 'Conserver les réglages du concepteur',
|
||||
designSettingsHint: 'Ce fichier modifie {{count}} réglage(s) d\'impression par rapport au profil standard.',
|
||||
designSettingsSelected: '{{selected}} sur {{total}} sélectionnés',
|
||||
designSettingsPrinterCoupled: 'spécifique à l\'imprimante',
|
||||
designSettingsPrinterCoupledHint: 'Réglé pour l\'imprimante visée par le fichier — peut être incorrect ou hors plage sur la vôtre.',
|
||||
enqueuing: 'Envoi du travail de découpage…',
|
||||
queued: 'En file d\'attente…',
|
||||
failed: 'Échec du découpage. Vérifiez les journaux du sidecar.',
|
||||
|
||||
@@ -4060,6 +4060,11 @@ export default {
|
||||
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.',
|
||||
designSettings: 'Mantieni le impostazioni del progettista',
|
||||
designSettingsHint: 'Questo file modifica {{count}} impostazione/i di stampa rispetto al profilo standard.',
|
||||
designSettingsSelected: '{{selected}} di {{total}} selezionate',
|
||||
designSettingsPrinterCoupled: 'specifico della stampante',
|
||||
designSettingsPrinterCoupledHint: 'Tarato per la stampante per cui è stato progettato il file: sulla tua può essere errato o fuori intervallo.',
|
||||
enqueuing: 'Invio lavoro di slicing…',
|
||||
queued: 'In coda…',
|
||||
failed: 'Slicing fallito. Controlla i log del sidecar.',
|
||||
|
||||
@@ -4072,6 +4072,11 @@ export default {
|
||||
allPresetsRequired: 'すべてのプリセットを選択する必要があります',
|
||||
useEmbedded: 'ファイルに埋め込まれた設定を使用',
|
||||
useEmbeddedHint: '上のプロファイルではなく、設計者が設定したとおり(ウォール、インフィル、フィラメント)にスライスします。お使いのプリンターがファイルと一致するため利用できます。',
|
||||
designSettings: '設計者の設定を保持',
|
||||
designSettingsHint: 'このファイルは標準プロファイルから {{count}} 個の印刷設定を変更しています。',
|
||||
designSettingsSelected: '{{total}} 個中 {{selected}} 個を選択',
|
||||
designSettingsPrinterCoupled: 'プリンター固有',
|
||||
designSettingsPrinterCoupledHint: 'このファイルが対象とするプリンター向けに調整された値です。お使いのプリンターでは不適切または範囲外になる場合があります。',
|
||||
enqueuing: 'スライスジョブを送信中…',
|
||||
queued: '待機中…',
|
||||
failed: 'スライスに失敗。サイドカーのログを確認してください。',
|
||||
|
||||
@@ -3861,6 +3861,11 @@ export default {
|
||||
allPresetsRequired: '모든 프리셋을 선택해야 합니다',
|
||||
useEmbedded: '파일에 포함된 설정 사용',
|
||||
useEmbeddedHint: '위 프로필 대신 디자이너가 설정한 대로(벽, 내부 채움, 필라멘트) 슬라이싱합니다. 프린터가 파일과 일치하여 사용할 수 있습니다.',
|
||||
designSettings: '디자이너 설정 유지',
|
||||
designSettingsHint: '이 파일은 기본 프로파일에서 {{count}}개의 출력 설정을 변경합니다.',
|
||||
designSettingsSelected: '{{total}}개 중 {{selected}}개 선택됨',
|
||||
designSettingsPrinterCoupled: '프린터 전용',
|
||||
designSettingsPrinterCoupledHint: '이 파일이 대상으로 한 프린터에 맞춘 값입니다. 사용 중인 프린터에서는 잘못되었거나 범위를 벗어날 수 있습니다.',
|
||||
enqueuing: '슬라이싱 작업 제출 중…',
|
||||
queued: '대기 중…',
|
||||
failed: '슬라이싱 실패. 슬라이서 사이드카 로그를 확인하세요.',
|
||||
|
||||
@@ -4060,6 +4060,11 @@ export default {
|
||||
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.',
|
||||
designSettings: 'Manter as configurações do designer',
|
||||
designSettingsHint: 'Este arquivo altera {{count}} configuração(ões) de impressão em relação ao perfil padrão.',
|
||||
designSettingsSelected: '{{selected}} de {{total}} selecionadas',
|
||||
designSettingsPrinterCoupled: 'específico da impressora',
|
||||
designSettingsPrinterCoupledHint: 'Ajustado para a impressora para a qual o arquivo foi projetado — pode estar incorreto ou fora de faixa na sua.',
|
||||
enqueuing: 'Enviando trabalho de fatiamento…',
|
||||
queued: 'Na fila…',
|
||||
failed: 'Falha ao fatiar. Verifique os logs do sidecar.',
|
||||
|
||||
@@ -3857,6 +3857,11 @@ export default {
|
||||
allPresetsRequired: "Необходимо выбрать все профили",
|
||||
useEmbedded: "Использовать встроенные настройки файла",
|
||||
useEmbeddedHint: "Нарезать так, как задумал автор модели (стенки, заполнение, филамент), вместо профилей выше. Доступно, потому что ваш принтер совпадает с указанным в файле.",
|
||||
designSettings: "Сохранить настройки автора",
|
||||
designSettingsHint: "Этот файл меняет {{count}} настроек печати по сравнению со стандартным профилем.",
|
||||
designSettingsSelected: "Выбрано {{selected}} из {{total}}",
|
||||
designSettingsPrinterCoupled: "зависит от принтера",
|
||||
designSettingsPrinterCoupledHint: "Значение подобрано для принтера, под который создан файл, — на вашем оно может быть неверным или вне допустимого диапазона.",
|
||||
enqueuing: "Отправка задания на нарезку…",
|
||||
queued: "В очереди…",
|
||||
failed: "Ошибка нарезки. Проверьте журналы вспомогательного сервиса слайсера.",
|
||||
|
||||
@@ -4062,6 +4062,11 @@ export default {
|
||||
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.',
|
||||
designSettings: 'Tasarımcının ayarlarını koru',
|
||||
designSettingsHint: 'Bu dosya standart profile göre {{count}} baskı ayarını değiştiriyor.',
|
||||
designSettingsSelected: '{{total}} ayardan {{selected}} tanesi seçili',
|
||||
designSettingsPrinterCoupled: 'yazıcıya özel',
|
||||
designSettingsPrinterCoupledHint: 'Dosyanın tasarlandığı yazıcıya göre ayarlanmış — sizinkinde yanlış veya aralık dışı olabilir.',
|
||||
enqueuing: 'Dilimleme işi gönderiliyor…',
|
||||
queued: 'Kuyrukta…',
|
||||
failed: 'Dilimleme başarısız. Dilimleyici yardımcı bileşen günlüklerini kontrol edin.',
|
||||
|
||||
@@ -4060,6 +4060,11 @@ export default {
|
||||
allPresetsRequired: '必须选择所有预设',
|
||||
useEmbedded: '使用文件的内置设置',
|
||||
useEmbeddedHint: '按设计者的设置(壁、填充、耗材)切片,而非上方的配置文件。因您的打印机与文件匹配而可用。',
|
||||
designSettings: '保留设计者的设置',
|
||||
designSettingsHint: '此文件相对标准配置修改了 {{count}} 项打印设置。',
|
||||
designSettingsSelected: '已选择 {{selected}} / {{total}}',
|
||||
designSettingsPrinterCoupled: '与打印机相关',
|
||||
designSettingsPrinterCoupledHint: '该值是为此文件面向的打印机调校的,在你的打印机上可能不正确或超出范围。',
|
||||
enqueuing: '提交切片任务中…',
|
||||
queued: '已排队…',
|
||||
failed: '切片失败。请检查切片器 sidecar 日志。',
|
||||
|
||||
@@ -4060,6 +4060,11 @@ export default {
|
||||
allPresetsRequired: '必須選擇所有預設',
|
||||
useEmbedded: '使用檔案的內建設定',
|
||||
useEmbeddedHint: '依設計者的設定(外牆、填充、耗材)切片,而非上方的設定檔。因您的印表機與檔案相符而可用。',
|
||||
designSettings: '保留設計者的設定',
|
||||
designSettingsHint: '此檔案相對標準設定檔修改了 {{count}} 項列印設定。',
|
||||
designSettingsSelected: '已選擇 {{selected}} / {{total}}',
|
||||
designSettingsPrinterCoupled: '與印表機相關',
|
||||
designSettingsPrinterCoupledHint: '此值是為該檔案面向的印表機調校的,在你的印表機上可能不正確或超出範圍。',
|
||||
enqueuing: '提交切片任務中…',
|
||||
queued: '已排隊…',
|
||||
failed: '切片失敗。請檢查切片器 sidecar 日誌。',
|
||||
|
||||
@@ -38,6 +38,21 @@ export interface PlateMetadata {
|
||||
interface EmbeddedPresets {
|
||||
embedded_printer?: string | null;
|
||||
embedded_process?: string | null;
|
||||
// Process settings the designer changed away from the stock preset, read
|
||||
// from the 3MF's own `different_settings_to_system` (#2622). Offered in the
|
||||
// SliceModal so a re-slice for another printer can carry them instead of
|
||||
// losing them to the picked process profile. Empty for STL, OrcaSlicer
|
||||
// files, and older exports that predate the field.
|
||||
design_overrides?: DesignOverride[];
|
||||
}
|
||||
|
||||
// One process setting the designer deviated on. `printer_coupled` marks the
|
||||
// values that only make sense on the machine they were tuned for (speeds,
|
||||
// accelerations, prime-tower geometry) — offered, but never pre-selected.
|
||||
export interface DesignOverride {
|
||||
key: string;
|
||||
value: unknown;
|
||||
printer_coupled: boolean;
|
||||
}
|
||||
|
||||
export interface ArchivePlatesResponse extends EmbeddedPresets {
|
||||
|
||||
Reference in New Issue
Block a user