mirror of
https://github.com/TautvydasDerzinskas/Thingport.git
synced 2026-10-19 05:10:01 -05:00
fix: file labels in 3d preview
This commit is contained in:
1 parent
ef14899fe7
commit
1fa98a830e
6 files changed
+61
-51
No files matched your search
@@ -63,8 +63,8 @@
|
||||
"importing": "Importing…",
|
||||
"importFailed": "Import failed",
|
||||
"uploadFailed": "Failed to upload: {{files}}",
|
||||
"multiplateImportFailedWithMessage": "Multi-plate import ({{message}})",
|
||||
"multiplateImportFailed": "Multi-plate import",
|
||||
"multiplateImportFailedWithMessage": "Multi-file import ({{message}})",
|
||||
"multiplateImportFailed": "Multi-file import",
|
||||
"imported": "Imported \"{{name}}\"",
|
||||
"profileAdded": "Added a print profile to \"{{name}}\"",
|
||||
"alreadyInLibrary": "\"{{name}}\" is already in your library",
|
||||
@@ -110,8 +110,8 @@
|
||||
"prompt": "How should these files be imported?",
|
||||
"separateTitle": "Import as separate prints",
|
||||
"separateDesc": "Each file becomes its own print with a single plate.",
|
||||
"multiplateTitle": "Import as one multi-plate print",
|
||||
"multiplateDesc": "All files become plates of a single new print, in the order selected.",
|
||||
"multiplateTitle": "Import as one model with several files",
|
||||
"multiplateDesc": "All files are added to a single new model, in the order selected.",
|
||||
"importing": "Importing…",
|
||||
"close": "Close",
|
||||
"importError": "Import failed"
|
||||
@@ -595,26 +595,19 @@
|
||||
"addPlatesFailed": "Failed to add plate(s).",
|
||||
"addPlateFailed": "Failed to add plate.",
|
||||
"removePlateFailed": "Failed to remove plate.",
|
||||
"multiplateImportFailed": "Multi-plate import",
|
||||
"multiplateImportFailedWithMessage": "Multi-plate import ({{message}})"
|
||||
"multiplateImportFailed": "Multi-file import",
|
||||
"multiplateImportFailedWithMessage": "Multi-file import ({{message}})"
|
||||
},
|
||||
"confirm": {
|
||||
"deleteMultiple": "Delete {{count}} selected items? This cannot be undone.",
|
||||
"deleteSingle": "Delete \"{{name}}\"? This cannot be undone.",
|
||||
"removeFile": "Remove {{filename}} from this model?",
|
||||
"clearPrepared": "Clear the attached prepared print from this model?",
|
||||
"removePlate": "Remove plate \"{{filename}}\"?"
|
||||
"clearPrepared": "Clear the attached prepared print from this model?"
|
||||
},
|
||||
"card": {
|
||||
"select": "Select",
|
||||
"plateCountBadge_one": "{{count}} plate",
|
||||
"plateCountBadge_few": "{{count}} plates",
|
||||
"plateCountBadge_other": "{{count}} plates",
|
||||
"addingPlates": "Adding plate(s)...",
|
||||
"dropToAddPlate": "Drop to add as plate",
|
||||
"doubleClickPreview": "Double-click to open large preview",
|
||||
"downloadPlaceholder": "Download...",
|
||||
"downloadAllPlatesZip": "Download all plates (zip)",
|
||||
"downloadFile": "Download file",
|
||||
"downloadSelected": "Download all selected",
|
||||
"downloadTag": "Download tag: {{tag}}",
|
||||
@@ -659,11 +652,6 @@
|
||||
"minutes": "{{count}}m"
|
||||
},
|
||||
"previewModal": {
|
||||
"plateCount_one": "{{count}} plate",
|
||||
"plateCount_few": "{{count}} plates",
|
||||
"plateCount_other": "{{count}} plates",
|
||||
"removePlateTooltip": "Remove plate",
|
||||
"addPlateTooltip": "Add plate",
|
||||
"addingPlate": "Adding...",
|
||||
"addPlate": "Add plate",
|
||||
"openPlateInSlicer": "Open plate in {{slicer}}",
|
||||
@@ -1118,6 +1106,8 @@
|
||||
"preview3d": "3D Preview",
|
||||
"description": "Description",
|
||||
"noDescription": "No description yet.",
|
||||
"fileLabel": "File {{n}}",
|
||||
"fileMeta": "File {{n}} · {{type}}",
|
||||
"plateLabel": "Plate {{n}}",
|
||||
"internalPlatesDivider": "{{count}} plates in this file",
|
||||
"internalPlateObjectCount_one": "{{count}} object",
|
||||
@@ -1158,8 +1148,8 @@
|
||||
"openInSlicer": "Open in {{slicer}}",
|
||||
"openInSlicerGeneric": "Open in Slicer",
|
||||
"downloadPickerTitle": "Download",
|
||||
"downloadAllZip": "Download all {{count}} plates (.zip)",
|
||||
"orDownloadOne": "or download one plate",
|
||||
"downloadAllZip": "Download all {{count}} files (.zip)",
|
||||
"orDownloadOne": "or download one file",
|
||||
"tags": "Tags",
|
||||
"noTags": "No tags yet.",
|
||||
"previousImage": "Previous image",
|
||||
|
||||
@@ -63,8 +63,8 @@
|
||||
"importing": "Importuojama…",
|
||||
"importFailed": "Importuoti nepavyko",
|
||||
"uploadFailed": "Nepavyko įkelti: {{files}}",
|
||||
"multiplateImportFailedWithMessage": "Kelių plokščių importas ({{message}})",
|
||||
"multiplateImportFailed": "Kelių plokščių importas",
|
||||
"multiplateImportFailedWithMessage": "Kelių failų importas ({{message}})",
|
||||
"multiplateImportFailed": "Kelių failų importas",
|
||||
"imported": "Importuota „{{name}}“",
|
||||
"profileAdded": "Pridėtas spausdinimo profilis prie „{{name}}“",
|
||||
"alreadyInLibrary": "„{{name}}“ jau yra jūsų bibliotekoje",
|
||||
@@ -110,8 +110,8 @@
|
||||
"prompt": "Kaip turėtų būti importuojami šie failai?",
|
||||
"separateTitle": "Importuoti kaip atskirus modelius",
|
||||
"separateDesc": "Kiekvienas failas taps atskiru modeliu su viena plokšte.",
|
||||
"multiplateTitle": "Importuoti kaip vieną kelių plokščių modelį",
|
||||
"multiplateDesc": "Visi failai taps vieno naujo modelio plokštėmis pasirinkta tvarka.",
|
||||
"multiplateTitle": "Importuoti kaip vieną modelį su keliais failais",
|
||||
"multiplateDesc": "Visi failai bus pridėti prie vieno naujo modelio pasirinkta tvarka.",
|
||||
"importing": "Importuojama…",
|
||||
"close": "Uždaryti",
|
||||
"importError": "Importuoti nepavyko"
|
||||
@@ -596,26 +596,19 @@
|
||||
"addPlatesFailed": "Nepavyko pridėti plokštės (-ių).",
|
||||
"addPlateFailed": "Nepavyko pridėti plokštės.",
|
||||
"removePlateFailed": "Nepavyko pašalinti plokštės.",
|
||||
"multiplateImportFailed": "Kelių plokščių importas",
|
||||
"multiplateImportFailedWithMessage": "Kelių plokščių importas ({{message}})"
|
||||
"multiplateImportFailed": "Kelių failų importas",
|
||||
"multiplateImportFailedWithMessage": "Kelių failų importas ({{message}})"
|
||||
},
|
||||
"confirm": {
|
||||
"deleteMultiple": "Ištrinti {{count}} pasirinktus elementus? Šio veiksmo anuliuoti negalima.",
|
||||
"deleteSingle": "Ištrinti „{{name}}“? Šio veiksmo anuliuoti negalima.",
|
||||
"removeFile": "Pašalinti {{filename}} iš šio modelio?",
|
||||
"clearPrepared": "Išvalyti prie modelio pridėtą paruoštą spaudinį?",
|
||||
"removePlate": "Pašalinti plokštę „{{filename}}“?"
|
||||
"clearPrepared": "Išvalyti prie modelio pridėtą paruoštą spaudinį?"
|
||||
},
|
||||
"card": {
|
||||
"select": "Pasirinkti",
|
||||
"plateCountBadge_one": "{{count}} plokštė",
|
||||
"plateCountBadge_few": "{{count}} plokštės",
|
||||
"plateCountBadge_other": "{{count}} plokščių",
|
||||
"addingPlates": "Pridedamos plokštės...",
|
||||
"dropToAddPlate": "Vilkite čia, kad pridėtumėte kaip plokštę",
|
||||
"doubleClickPreview": "Dukart spustelėkite, kad atidarytumėte didelį peržiūros langą",
|
||||
"downloadPlaceholder": "Atsisiųsti...",
|
||||
"downloadAllPlatesZip": "Atsisiųsti visas plokštes (zip)",
|
||||
"downloadFile": "Atsisiųsti failą",
|
||||
"downloadSelected": "Atsisiųsti visus pasirinktus",
|
||||
"downloadTag": "Atsisiųsti žymą: {{tag}}",
|
||||
@@ -660,11 +653,6 @@
|
||||
"minutes": "{{count}} min."
|
||||
},
|
||||
"previewModal": {
|
||||
"plateCount_one": "{{count}} plokštė",
|
||||
"plateCount_few": "{{count}} plokštės",
|
||||
"plateCount_other": "{{count}} plokščių",
|
||||
"removePlateTooltip": "Pašalinti plokštę",
|
||||
"addPlateTooltip": "Pridėti plokštę",
|
||||
"addingPlate": "Pridedama...",
|
||||
"addPlate": "Pridėti plokštę",
|
||||
"openPlateInSlicer": "Atidaryti plokštę su {{slicer}}",
|
||||
@@ -1119,6 +1107,8 @@
|
||||
"preview3d": "3D peržiūra",
|
||||
"description": "Aprašymas",
|
||||
"noDescription": "Aprašymo dar nėra.",
|
||||
"fileLabel": "Failas {{n}}",
|
||||
"fileMeta": "Failas {{n}} · {{type}}",
|
||||
"plateLabel": "Plokštė {{n}}",
|
||||
"internalPlatesDivider": "{{count}} plokščių šiame faile",
|
||||
"internalPlateObjectCount_one": "{{count}} objektas",
|
||||
@@ -1160,8 +1150,8 @@
|
||||
"openInSlicer": "Atverti su {{slicer}}",
|
||||
"openInSlicerGeneric": "Atverti pjaustyklėje",
|
||||
"downloadPickerTitle": "Atsisiųsti",
|
||||
"downloadAllZip": "Atsisiųsti visas {{count}} plokštes (.zip)",
|
||||
"orDownloadOne": "arba atsisiųsti vieną plokštę",
|
||||
"downloadAllZip": "Atsisiųsti visus failus ({{count}}, .zip)",
|
||||
"orDownloadOne": "arba atsisiųsti vieną failą",
|
||||
"tags": "Žymos",
|
||||
"noTags": "Kol kas nėra žymų.",
|
||||
"previousImage": "Ankstesnis paveikslėlis",
|
||||
|
||||
@@ -14,6 +14,7 @@ import CircularProgress from "@mui/material/CircularProgress";
|
||||
import DownloadIcon from "@mui/icons-material/Download";
|
||||
import type { Plate } from "../../api/prints";
|
||||
import PlateThumbnail from "../../components/media/PlateThumbnail";
|
||||
import { fileRowText } from "./fileRowText";
|
||||
|
||||
type Props = {
|
||||
open: boolean;
|
||||
@@ -24,7 +25,7 @@ type Props = {
|
||||
downloadPlate: (plate: Plate) => void;
|
||||
};
|
||||
|
||||
/** The "which plate?" picker shown when a multi-plate model's download is triggered -- see
|
||||
/** The "which file?" picker shown when a multi-file model's download is triggered -- see
|
||||
* useDownloadPrint. Shared by ModelActionsMenu's "Download" menu item and the detail page's
|
||||
* "Download model files" button. */
|
||||
export default function DownloadPickerDialog({ open, onClose, downloading, sortedPlates, downloadAllZip, downloadPlate }: Props) {
|
||||
@@ -45,13 +46,19 @@ export default function DownloadPickerDialog({ open, onClose, downloading, sorte
|
||||
<Divider>{t("models:detail.orDownloadOne")}</Divider>
|
||||
<List disablePadding>
|
||||
{sortedPlates.map((plate, idx) => (
|
||||
<ListItemButton key={plate.id} onClick={() => downloadPlate(plate)} disabled={downloading} sx={{ borderRadius: 1 }}>
|
||||
<ListItemButton
|
||||
key={plate.id}
|
||||
onClick={() => downloadPlate(plate)}
|
||||
disabled={downloading}
|
||||
title={plate.filename}
|
||||
sx={{ borderRadius: 1 }}
|
||||
>
|
||||
<ListItemIcon sx={{ minWidth: 60 }}>
|
||||
<PlateThumbnail plate={plate} size={48} />
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
primary={t("models:detail.plateLabel", { n: idx + 1 })}
|
||||
secondary={plate.filename}
|
||||
{...fileRowText(t, plate.filename, idx)}
|
||||
primaryTypographyProps={{ noWrap: true }}
|
||||
secondaryTypographyProps={{ noWrap: true }}
|
||||
/>
|
||||
</ListItemButton>
|
||||
|
||||
@@ -19,6 +19,7 @@ import PlateThumbnail from "../../components/media/PlateThumbnail";
|
||||
import ModelViewer, { type CameraView, type ModelViewerHandle, type RenderStyle } from "../../components/media/ModelViewer";
|
||||
import type { PlateSummary } from "../../utils/bambuThreeMf";
|
||||
import PreviewToolbar, { DEFAULT_PREVIEW_COLOR } from "./PreviewToolbar";
|
||||
import { fileRowText } from "./fileRowText";
|
||||
|
||||
// A neutral, theme-independent canvas -- this is a fixed "product shot" style preview, not part
|
||||
// of the app's light/dark chrome, so it stays the same regardless of the viewer's theme.
|
||||
@@ -66,8 +67,8 @@ export default function Model3DPreviewModal({ print, onClose }: Props) {
|
||||
viewerRef.current?.setCameraView(view);
|
||||
};
|
||||
|
||||
// A lone file that is itself a multi-plate project would otherwise show one "Plate 1" file row
|
||||
// above its real plates -- redundant, and reads as if it were one of them.
|
||||
// A lone file that is itself a multi-plate project would otherwise show its one file row above
|
||||
// its real plates -- redundant, since there's no other file to switch to.
|
||||
const showFileRows = sortedPlates.length > 1 || internalPlates.length === 0;
|
||||
|
||||
const handlePlatesDetected = (plates: PlateSummary[], getThumbnail: (index: number) => Promise<string | null>) => {
|
||||
@@ -131,19 +132,22 @@ export default function Model3DPreviewModal({ print, onClose }: Props) {
|
||||
{showFileRows && (
|
||||
<List disablePadding>
|
||||
{sortedPlates.map((plate, idx) => (
|
||||
// Named by file, not "Plate N": each row is a separate file (an STL, or e.g. one
|
||||
// MakerWorld print profile's 3MF) -- "plate" is kept for the build plates inside a
|
||||
// 3MF, listed below the divider.
|
||||
<ListItemButton
|
||||
key={plate.id}
|
||||
selected={plate.id === activePlate?.id}
|
||||
onClick={() => selectPlate(plate.id)}
|
||||
title={plate.filename}
|
||||
sx={{ borderRadius: 1, mb: 0.5 }}
|
||||
>
|
||||
<ListItemIcon sx={{ minWidth: 40 }}>
|
||||
<PlateThumbnail plate={plate} />
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
primary={t("models:detail.plateLabel", { n: idx + 1 })}
|
||||
secondary={plate.filename}
|
||||
primaryTypographyProps={{ variant: "body2" }}
|
||||
{...fileRowText(t, plate.filename, idx)}
|
||||
primaryTypographyProps={{ variant: "body2", noWrap: true }}
|
||||
secondaryTypographyProps={{ variant: "caption", noWrap: true }}
|
||||
/>
|
||||
</ListItemButton>
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import type { TFunction } from "i18next";
|
||||
import { extOf, stemOf } from "../../utils/fileExtensions";
|
||||
|
||||
/** How one of a model's files is labeled in a list (3D preview, download picker): its name, with
|
||||
* its position and type as the secondary line -- e.g. "Body" / "File 2 · STL". */
|
||||
export function fileRowText(t: TFunction, filename: string, index: number): { primary: string; secondary: string } {
|
||||
const type = extOf(filename).toUpperCase();
|
||||
return {
|
||||
primary: stemOf(filename),
|
||||
secondary: type ? t("models:detail.fileMeta", { n: index + 1, type }) : t("models:detail.fileLabel", { n: index + 1 }),
|
||||
};
|
||||
}
|
||||
@@ -3,3 +3,10 @@ export function extOf(name: string): string {
|
||||
const match = /\.([^.]+)$/.exec(name || "");
|
||||
return (match?.[1] || "").toLowerCase();
|
||||
}
|
||||
|
||||
/** The filename without its extension, e.g. "Body" for "Body.stl" -- a file's display name where
|
||||
* the type is shown separately. A dotfile-style name with nothing before the dot is kept whole. */
|
||||
export function stemOf(name: string): string {
|
||||
const stem = (name || "").replace(/\.[^.]+$/, "");
|
||||
return stem || name || "";
|
||||
}
|
||||
Reference in new issue
Block a user