-
+
+
{{ $t('App.Notifications.Remind') }}
-
-
- 1H
-
-
- 1D
-
-
- 7D
-
-
-
-
- {{ $t('App.Notifications.NextReboot') }}
-
-
- {{ $t('App.Notifications.Never') }}
-
-
+
+ {{ reminder.text }}
+
+
@@ -101,6 +83,13 @@ import BaseMixin from '@/components/mixins/base'
import { Component, Mixins, Prop, Watch } from 'vue-property-decorator'
import { mdiClose, mdiLinkVariant, mdiBellOffOutline } from '@mdi/js'
import { GuiNotificationStateEntry } from '@/store/gui/notifications/types'
+import { TranslateResult } from 'vue-i18n'
+import { GuiMaintenanceStateEntry } from '@/store/gui/maintenance/types'
+
+interface ReminderOption {
+ text: string | TranslateResult
+ clickFunction: Function
+}
@Component({
components: {},
@@ -110,7 +99,8 @@ export default class NotificationMenuEntry extends Mixins(BaseMixin) {
mdiLinkVariant = mdiLinkVariant
mdiBellOffOutline = mdiBellOffOutline
- private expand = false
+ expand = false
+ showMaintenanceDetails = false
@Prop({ required: true })
declare readonly entry: GuiNotificationStateEntry
@@ -139,6 +129,49 @@ export default class NotificationMenuEntry extends Mixins(BaseMixin) {
return this.entry.id.slice(0, posFirstSlash)
}
+ get maintenanceEntry() {
+ if (this.entryType !== 'maintenance') return null
+
+ const id = this.entry.id.replace('maintenance/', '')
+ const entries = this.$store.getters['gui/maintenance/getEntries']
+
+ return entries.find((entry: GuiMaintenanceStateEntry) => entry.id === id)
+ }
+
+ get reminderTimes() {
+ let output: ReminderOption[] = [
+ {
+ text: this.$t('App.Notifications.NextReboot'),
+ clickFunction: () => this.dismiss('reboot', null),
+ },
+ { text: this.$t('App.Notifications.Never'), clickFunction: () => this.close() },
+ ]
+
+ if (['announcement', 'maintenance'].includes(this.entryType)) {
+ output = []
+ output.push({
+ text: this.$t('App.Notifications.OneHourShort'),
+ clickFunction: () => this.dismiss('time', 60 * 60),
+ })
+ output.push({
+ text: this.$t('App.Notifications.OneDayShort'),
+ clickFunction: () => this.dismiss('time', 60 * 60 * 24),
+ })
+ output.push({
+ text: this.$t('App.Notifications.OneWeekShort'),
+ clickFunction: () => this.dismiss('time', 60 * 60 * 24 * 7),
+ })
+ }
+
+ return output
+ }
+
+ xButtonAction() {
+ if (this.entryType === 'announcement') return this.close()
+
+ this.dismiss('reboot', null)
+ }
+
close() {
this.$store.dispatch('gui/notifications/close', { id: this.entry.id })
}
@@ -157,10 +190,12 @@ export default class NotificationMenuEntry extends Mixins(BaseMixin) {
diff --git a/src/components/panels/HistoryStatisticsPanel.vue b/src/components/panels/HistoryStatisticsPanel.vue
index a282fe979..20b31c4c9 100644
--- a/src/components/panels/HistoryStatisticsPanel.vue
+++ b/src/components/panels/HistoryStatisticsPanel.vue
@@ -12,15 +12,15 @@
{{ $t('History.SelectedPrinttime') }} |
- {{ formatPrintTime(selectedPrintTime) }} |
+ {{ formatPrintTime(selectedPrintTime, false) }} |
{{ $t('History.LongestPrinttime') }} |
- {{ formatPrintTime(selectedLongestPrintTime) }} |
+ {{ formatPrintTime(selectedLongestPrintTime, false) }} |
{{ $t('History.AvgPrinttime') }} |
- {{ formatPrintTime(selectedAvgPrintTime) }} |
+ {{ formatPrintTime(selectedAvgPrintTime, false) }} |
{{ $t('History.SelectedFilamentUsed') }} |
@@ -34,15 +34,15 @@
{{ $t('History.TotalPrinttime') }} |
- {{ formatPrintTime(totalPrintTime) }} |
+ {{ formatPrintTime(totalPrintTime, false) }} |
{{ $t('History.LongestPrinttime') }} |
- {{ formatPrintTime(longestPrintTime) }} |
+ {{ formatPrintTime(longestPrintTime, false) }} |
{{ $t('History.AvgPrinttime') }} |
- {{ formatPrintTime(avgPrintTime) }} |
+ {{ formatPrintTime(avgPrintTime, false) }} |
{{ $t('History.TotalFilamentUsed') }} |
@@ -57,17 +57,12 @@
-
-
+
+
-
- {{ $t('History.Chart') }}
-
-
- {{ $t('History.Table') }}
-
+ {{ $t('History.Chart') }}
+ {{ $t('History.Table') }}
@@ -88,16 +83,12 @@
-
-
+
+
-
- {{ $t('History.FilamentUsage') }}
-
-
- {{ $t('History.PrinttimeAvg') }}
-
+ {{ $t('History.FilamentUsage') }}
+ {{ $t('History.PrinttimeAvg') }}
@@ -115,12 +106,14 @@ import HistoryPrinttimeAvg from '@/components/charts/HistoryPrinttimeAvg.vue'
import HistoryAllPrintStatusChart from '@/components/charts/HistoryAllPrintStatusChart.vue'
import { ServerHistoryStateJob } from '@/store/server/history/types'
import { mdiChartAreaspline, mdiDatabaseArrowDownOutline } from '@mdi/js'
+import { formatPrintTime } from '@/plugins/helpers'
@Component({
components: { Panel, HistoryFilamentUsage, HistoryPrinttimeAvg, HistoryAllPrintStatusChart },
})
export default class HistoryStatisticsPanel extends Mixins(BaseMixin) {
mdiChartAreaspline = mdiChartAreaspline
mdiDatabaseArrowDownOutline = mdiDatabaseArrowDownOutline
+ formatPrintTime = formatPrintTime
get selectedJobs() {
return this.$store.state.gui.view.history.selectedJobs ?? []
@@ -131,9 +124,7 @@ export default class HistoryStatisticsPanel extends Mixins(BaseMixin) {
}
get totalPrintTime() {
- return 'total_print_time' in this.$store.state.server.history.job_totals
- ? this.$store.state.server.history.job_totals.total_print_time
- : 0
+ return this.$store.state.server.history.job_totals?.total_print_time ?? 0
}
get selectedPrintTime() {
@@ -147,9 +138,7 @@ export default class HistoryStatisticsPanel extends Mixins(BaseMixin) {
}
get longestPrintTime() {
- return 'longest_print' in this.$store.state.server.history.job_totals
- ? this.$store.state.server.history.job_totals.longest_print
- : 0
+ return this.$store.state.server.history.job_totals?.longest_print ?? 0
}
get selectedLongestPrintTime() {
@@ -177,9 +166,7 @@ export default class HistoryStatisticsPanel extends Mixins(BaseMixin) {
}
get totalFilamentUsed() {
- return 'total_filament_used' in this.$store.state.server.history.job_totals
- ? this.$store.state.server.history.job_totals.total_filament_used
- : 0
+ return this.$store.state.server.history.job_totals?.total_filament_used ?? 0
}
get selectedFilamentUsed() {
@@ -193,9 +180,7 @@ export default class HistoryStatisticsPanel extends Mixins(BaseMixin) {
}
get totalJobsCount() {
- return 'total_jobs' in this.$store.state.server.history.job_totals
- ? this.$store.state.server.history.job_totals.total_jobs
- : 0
+ return this.$store.state.server.history.job_totals?.total_jobs ?? 0
}
get toggleChart() {
@@ -223,25 +208,5 @@ export default class HistoryStatisticsPanel extends Mixins(BaseMixin) {
this.$socket.emit('server.history.list', { start: 0, limit: 50 }, { action: 'server/history/getHistory' })
}
-
- formatPrintTime(totalSeconds: number) {
- if (totalSeconds) {
- let output = ''
-
- const hours = Math.floor(totalSeconds / 3600)
- totalSeconds %= 3600
- if (hours) output += ' ' + hours + 'h'
-
- const minutes = Math.floor(totalSeconds / 60)
- if (minutes) output += ' ' + minutes + 'm'
-
- const seconds = totalSeconds % 60
- if (seconds) output += ' ' + seconds.toFixed(0) + 's'
-
- return output
- }
-
- return '--'
- }
}
diff --git a/src/components/panels/Machine/ConfigFilesPanel.vue b/src/components/panels/Machine/ConfigFilesPanel.vue
index 293738e96..a02bd2649 100644
--- a/src/components/panels/Machine/ConfigFilesPanel.vue
+++ b/src/components/panels/Machine/ConfigFilesPanel.vue
@@ -842,8 +842,15 @@ export default class ConfigFilesPanel extends Mixins(BaseMixin, ThemeMixin) {
}
if (this.hideBackupFiles) {
- const backupFileMatcher = /.*\/?printer-\d{8}_\d{6}\.cfg$/
- files = files.filter((file) => !file.filename.match(backupFileMatcher))
+ const klipperBackupFileMatcher = /^printer-\d{8}_\d{6}\.cfg$/
+ const crowsnestBackupFileMatcher = /^crowsnest\.conf\.\d{4}-\d{2}-\d{2}-\d{4}$/
+
+ files = files.filter(
+ (file) =>
+ !file.filename.match(klipperBackupFileMatcher) &&
+ !file.filename.match(crowsnestBackupFileMatcher) &&
+ !file.filename.endsWith('.bkp')
+ )
}
return files
diff --git a/src/components/panels/Machine/UpdatePanel/GitCommitsList.vue b/src/components/panels/Machine/UpdatePanel/GitCommitsList.vue
index 688ff0e4d..cc11e51d5 100644
--- a/src/components/panels/Machine/UpdatePanel/GitCommitsList.vue
+++ b/src/components/panels/Machine/UpdatePanel/GitCommitsList.vue
@@ -1,5 +1,5 @@
-
+
-
-
-
-
-
-
-
-
-
- {{ $t('Machine.UpdatePanel.MoreCommitsInfo') }}
-
-
- {{ $t('Machine.UpdatePanel.LinkToGithub') }}
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+ {{ $t('Machine.UpdatePanel.MoreCommitsInfo') }}
+
+
+ {{ $t('Machine.UpdatePanel.LinkToGithub') }}
+
+
+
+
+
+
+
@@ -110,6 +106,24 @@ export default class GitCommitsList extends Mixins(BaseMixin) {
return `https://github.com/${this.repo?.owner}/${this.repo?.name}/commits/${this.repo?.branch}/?after=${this.lastCommit?.sha}+0`
}
+ get overlayScrollbarsStyle() {
+ if (this.isMobile) {
+ return {
+ height: 'calc(100vh - 48px)',
+ }
+ }
+
+ return {
+ height: '400px',
+ }
+ }
+
+ get timelineClassName() {
+ if (this.isMobile) return ['groupedCommits', 'mobile']
+
+ return ['groupedCommits']
+ }
+
closeDialog() {
this.$emit('close-dialog')
}
@@ -161,4 +175,18 @@ export default class GitCommitsList extends Mixins(BaseMixin) {
}
}
}
+
+::v-deep .groupedCommits.mobile {
+ &:before {
+ left: 20px;
+ }
+
+ .v-timeline-item__body {
+ max-width: calc(100% - 41px);
+ }
+
+ .v-timeline-item__divider {
+ min-width: 41px;
+ }
+}
diff --git a/src/components/panels/Machine/UpdatePanel/GitCommitsListDayCommit.vue b/src/components/panels/Machine/UpdatePanel/GitCommitsListDayCommit.vue
index 68854fbc9..44c2a3160 100644
--- a/src/components/panels/Machine/UpdatePanel/GitCommitsListDayCommit.vue
+++ b/src/components/panels/Machine/UpdatePanel/GitCommitsListDayCommit.vue
@@ -1,6 +1,6 @@
-
+
{{ title }}
@@ -20,7 +20,7 @@
{{ commitFormatDate }}
-
+
{{ commitShortSha }}
diff --git a/src/components/panels/StatusPanel.vue b/src/components/panels/StatusPanel.vue
index 1c68082ea..ec8612bec 100644
--- a/src/components/panels/StatusPanel.vue
+++ b/src/components/panels/StatusPanel.vue
@@ -59,8 +59,10 @@
-
- {{ mdiAlertOutline }}
+
+
+ {{ mdiAlertOutline }}
+
{{ print_stats_message }}
@@ -70,10 +72,10 @@
-
-
-
- {{ mdiMessageProcessingOutline }}
+
+
+
+ {{ mdiMessageProcessingOutline }}
{{ display_message }}
diff --git a/src/components/panels/Temperature/TemperaturePanelPresets.vue b/src/components/panels/Temperature/TemperaturePanelPresets.vue
index 6f4fbdf4e..a54464573 100644
--- a/src/components/panels/Temperature/TemperaturePanelPresets.vue
+++ b/src/components/panels/Temperature/TemperaturePanelPresets.vue
@@ -25,7 +25,7 @@
-
+
{{ mdiSnowflake }}
{{ $t('Panels.TemperaturePanel.Cooldown') }}
@@ -39,10 +39,11 @@
:text="$vuetify.breakpoint.mdAndUp"
tile
color="primary"
- @click="cooldown">
+ @click="btnCoolDown">
{{ mdiSnowflake }}
{{ $t('Panels.TemperaturePanel.Cooldown') }}
+
@@ -51,13 +52,19 @@ import Component from 'vue-class-component'
import { Mixins } from 'vue-property-decorator'
import BaseMixin from '@/components/mixins/base'
import { GuiPresetsStatePreset } from '@/store/gui/presets/types'
-import { mdiFire, mdiMenuDown, mdiSnowflake } from '@mdi/js'
+import { mdiFire, mdiMenuDown, mdiSnowflake, mdiCloseThick } from '@mdi/js'
+import CoolDownDialog from '@/components/dialogs/CoolDownDialog.vue'
-@Component
+@Component({
+ components: { CoolDownDialog },
+})
export default class TemperaturePanelPresets extends Mixins(BaseMixin) {
mdiFire = mdiFire
mdiMenuDown = mdiMenuDown
mdiSnowflake = mdiSnowflake
+ mdiCloseThick = mdiCloseThick
+
+ showCoolDownDialog = false
get presets(): GuiPresetsStatePreset[] {
return this.$store.getters['gui/presets/getPresets'] ?? []
@@ -67,6 +74,10 @@ export default class TemperaturePanelPresets extends Mixins(BaseMixin) {
return this.$store.getters['gui/presets/getCooldownGcode']
}
+ get confirmOnCoolDown(): boolean {
+ return this.$store.state.gui.uiSettings.confirmOnCoolDown
+ }
+
preheat(preset: GuiPresetsStatePreset): void {
for (const [name, attributes] of Object.entries(preset.values)) {
if (attributes.bool) {
@@ -100,7 +111,17 @@ export default class TemperaturePanelPresets extends Mixins(BaseMixin) {
}
}
+ btnCoolDown(): void {
+ if (this.confirmOnCoolDown) {
+ this.showCoolDownDialog = true
+ return
+ }
+
+ this.cooldown()
+ }
+
cooldown(): void {
+ this.showCoolDownDialog = false
this.$store.dispatch('server/addEvent', { message: this.cooldownGcode, type: 'command' })
this.$socket.emit('printer.gcode.script', { script: this.cooldownGcode })
}
diff --git a/src/components/settings/SettingsUiSettingsTab.vue b/src/components/settings/SettingsUiSettingsTab.vue
index bd4ba51e2..96d4a7556 100644
--- a/src/components/settings/SettingsUiSettingsTab.vue
+++ b/src/components/settings/SettingsUiSettingsTab.vue
@@ -142,6 +142,13 @@
+
+
+
+
ADJUSTED, якщо поточний гвинт було відрегульовано. Натисніть ACCEPT, щоб продовжити без коригування.",
+ "Headline": "Гвинти столу",
+ "ScrewAccepted": "Готові гвинти",
+ "ScrewIndex": "Індекс гвинта",
+ "ScrewName": "Ім'я гвинта",
+ "ScrewOutput": "{current} з {max}"
+ },
"ConnectionDialog": {
"CannotConnectTo": "Не вдається підключитися до Moonraker ({host}).",
"CheckMoonrakerLog": "Якщо це повідомлення з’являється неодноразово, будь ласка, подивіться у файл журналу, розташований за адресою:",
@@ -123,10 +149,25 @@
"SendCode": "Надіслати код...",
"SetupConsole": "Консоль налаштування"
},
+ "DevicesDialog": {
+ "CanBusInfo": "Можна виявити лише непризначені вузли. Рекомендується мати лише один непризначений пристрій, підключений до шини can, щоб уникнути проблем зі зв’язком. Щоб отримати детальнішу інформацію, натисніть на посилання:",
+ "ClickRefresh": "Натисніть кнопку оновити, щоб знайти пристрої.",
+ "DevicePath": "Шлях пристрою",
+ "Formats": "Формати",
+ "Headline": "Пристрої",
+ "HideSystemEntries": "Приховати системні записи",
+ "LibcameraId": "Libcamera ID",
+ "NoDeviceFound": "Пристрій не знайдено. Перевірте підключення та натисніть кнопку оновити.",
+ "PathByHardware": "Фізичний шлях",
+ "PathById": "Шлях за ID",
+ "Refresh": "оновити",
+ "Resolutions": "Резолюції"
+ },
"Dialogs": {
"StartPrint": {
"Cancel": "Скасувати",
"DoYouWantToStartFilename": "Ви хочете роздрукувати {filename}?",
+ "DoYouWantToStartFilenameFilament": "Хочете почати друк {filename} з наявного філаменту?",
"Headline": "Почати друк",
"Print": "друк",
"Timelapse": "Таймлапс"
@@ -134,6 +175,7 @@
},
"Editor": {
"ConfigReference": "Довідка Конфігурації",
+ "DeviceDialog": "Пристрої",
"DontSave": "Не зберігати",
"Downloading": "Завантаження",
"FailedSave": "{filename} не вдалося завантажити!",
@@ -149,15 +191,17 @@
"EmergencyStopDialog": {
"AreYouSure": "Ви впевнені?",
"EmergencyStop": "АВАРІЙНА ЗУПИНКА",
- "No": "НІТ",
+ "No": "НІ",
"Yes": "ТАК"
},
"Files": {
+ "AddBatchToQueue": "Додати партію в чергу",
"AddToQueue": "Додати в чергу",
"AllFiles": "ВСІ",
"BedTemp": "t° Столу.",
"Cancel": "Скасувати",
"ChamberTemp": "t° Камери.",
+ "Count": "Кількість",
"Create": "Створити",
"CreateNewDirectory": "Створити нову теку",
"CurrentPath": "Директорія",
@@ -165,11 +209,13 @@
"DeleteDirectory": "Видалити теку",
"DeleteDirectoryQuestion": "Ви дійсно хочете видалити \"{name}\" теку із усім її вмістом?",
"DeleteSelectedQuestion": "Ви дійсно хочете видалити {count} обрані файли?",
+ "DeleteSingleFileQuestion": "Ви дійсно хочете видалити файл \"{name}\"?",
"Download": "Завантажити",
+ "Duplicate": "Дублікат",
+ "DuplicateFile": "Дублікат файлу",
"EditFile": "Редагувати Файл",
"Empty": "Порожньо",
"ExtruderTemp": "t° Екструдеру.",
- "Filament": "Пруток",
"FilamentName": "Назва Прутка",
"FilamentType": "Тип Прутка",
"FilamentUsage": "Використано Прутка",
@@ -181,6 +227,8 @@
"GCodeFiles": "Файли G-Code",
"GcodesRootDirectoryDoesntExists": "Жодного каталогу G-коду не знайдено. Будь ласка, перевірте опцію \"path\" in the [virtual_sdcard] розділі конфігурації Klipper.",
"HiddenFiles": "Приховані файли",
+ "InvalidNameAlreadyExists": "Ім'я вже існує, виберіть інше ім'я.",
+ "InvalidNameEmpty": "Поле не повинне бути порожнім!",
"LastEndTime": "Закінчено",
"LastFilamentUsed": "Використано прутку",
"LastModified": "Завантажено",
@@ -196,10 +244,12 @@
"PrintedFiles": "Надруковані Файли",
"PrintStart": "Почати Друк",
"PrintTime": "Час Друку",
- "RefreshCurrentDirectory": "Оновити поточну дирикторію",
+ "RefreshCurrentDirectory": "Оновити поточну директорію",
"Rename": "Перейменувати",
"RenameDirectory": "Перейменувати директорію",
"RenameFile": "Перейменувати Файл",
+ "ScanMeta": "Сканувати метадані",
+ "ScanMetaSuccess": "Успішно проскановано метадані з: {filename}",
"Search": "Пошук",
"SetupCurrentList": "Поточний список налаштування",
"Slicer": "Слайсер",
@@ -219,6 +269,7 @@
},
"GCodeViewer": {
"ClearLoadedFile": "Очистити",
+ "CNCMode": "Режим ЧПД",
"ColorMode": "Кольоровий Режим",
"Downloading": "Завантаження",
"ForceLineRendering": "Примусова лінійна Візуалізація",
@@ -229,9 +280,10 @@
"Low": "Низько",
"Max": "Максимум",
"Medium": "Середньо",
- "ReloadRequired": "Потрібно перезавантаження",
+ "ReloadRequired": "Потрібне перезавантаження",
"Rendering": "Візуалізація",
"RenderQuality": "Якість Візуалізації",
+ "ShowGCode": "Показати G-Code",
"ShowObjectSelection": "Показати Вибір Об'єктів",
"ShowToolhead": "Показати Інструмент",
"ShowTravelMoves": "Показати Вільні Переміщення",
@@ -253,8 +305,8 @@
"Max": "Max",
"Min": "Min",
"Name": "Ім'я",
- "Size": "Розмір",
- "Variance": "Розбіжність"
+ "Range": "Діапазон",
+ "Size": "Розмір"
},
"DeleteBedMeshProfile": "Видалити профіль сітки для столу",
"DoYouReallyWantToDelete": "Ви дійсно хочете видалити профіль \"{name}\"?",
@@ -262,36 +314,36 @@
"Flat": "Плоский",
"Heightmap": "Карта",
"InvalidNameAlreadyExists": "Ім'я профілю вже існує, будь ласка, виберіть інше ім'я профілю.",
+ "InvalidNameAscii": "Ім'я недійсне. Дозволяється лише символи ascii.",
"InvalidNameEmpty": "Введення не повинно бути порожнім!",
"InvalidNameReserved": "Profile 'default' зарезервовано, будь ласка, виберіть інше ім'я профілю.",
- "Later": "Згодом",
"Mesh": "Сітка",
"Name": "Ім'я",
"NoBedMeshHasBeenLoadedYet": "Жодна сітка для ліжка ще не була завантажена.",
"NoProfile": "Немає профілю",
- "Ok": "OK",
"Probed": "Зондований",
"Profiles": "Профілі",
"Remove": "видалити",
- "RemoveSaveDescription": "Профіль BED_MESH зареєстрований як видалений. Клацніть на SAVE_CONFIG, щоб видалити його з printer.cfg та перезапустіть Klipper.",
"Rename": "перейменувати",
"RenameBedMeshProfile": "Перейменуйте профіль сітки для столу",
- "SAVE_CONFIG": "SAVE_CONFIG",
"ScaleGradient": "Градієнт Масштабу",
"ScaleZMax": "Масштаб Z-Max.",
"TitleCalibrate": "Калібрування нової сітки столу",
- "TitleClear": "Очистити сітку калібровки",
+ "TitleClear": "Очистити сітку калібрування",
"TitleHomeAll": "Всі до Дому",
"Wireframe": "Каркас"
},
"History": {
"AddNote": "Додати коментар",
+ "AddToQueueSuccessful": "Файл {filename} додано до черги.",
"AllJobs": "ВСІ",
"AvgPrinttime": "Час Друку - Ø",
"Cancel": "Скасувати",
+ "Chart": "Графік",
"CreateNote": "Створити Примітку",
"Delete": "Видалити",
- "DeleteSelectedQuestion": "Ви дійсно хочете видалити {count} вибрану роботу?",
+ "DeleteSelectedQuestion": "Ви дійсно хочете видалити {count} вибране завдання?",
+ "DeleteSingleJobQuestion": "Ви справді хочете видалити завдання?",
"Details": "Деталі",
"EditNote": "Редагувати Примітку",
"Empty": "порожньо",
@@ -300,7 +352,7 @@
"EstimatedFilamentWeight": "Орієнтовна вага прутка",
"EstimatedTime": "Орієнтовний Час",
"FilamentCalc": "Калькулятор Прутка",
- "FilamentUsage": "Використаня Прутка",
+ "FilamentUsage": "Використання Прутка",
"FilamentUsed": "Використано Прутка",
"Filename": "Ім'я",
"Filesize": "Розмір",
@@ -309,22 +361,23 @@
"FirstLayerHeight": "Висота першого шару",
"HistoryFilamentUsage": "Пруток",
"HistoryPrinttimeAVG": "Друк",
- "JobDetails": "Деталі Роботи",
- "Jobs": "Робота",
+ "JobDetails": "Деталі Завдань",
+ "Jobs": "Завдання",
"LastModified": "Дата Створення",
"LayerHeight": "Висота Шару",
+ "LoadCompleteHistory": "Завантажити повну історію",
"LongestPrinttime": "Найдовший Час Друку",
"Note": "Примітка",
"ObjectHeight": "Висота Об'єкта",
"PrintDuration": "Час Друку",
- "PrintHistory": "Історія Роздруківок",
+ "PrintHistory": "Історія друку",
"PrintTime": "Час Друку",
"PrinttimeAvg": "Час Друку - Ø",
"Reprint": "Передрукувати",
- "Save": "зберігти",
+ "Save": "зберегти",
"Search": "пошук",
- "SelectedFilamentUsed": "Вибран використовуваний пруток",
- "SelectedJobs": "Вибрані Роботи",
+ "SelectedFilamentUsed": "Використано філаменту",
+ "SelectedJobs": "Вибрані завдання",
"SelectedPrinttime": "Вибраний Час Друку",
"Slicer": "Слайсер",
"SlicerVersion": "Версія Слайсера",
@@ -336,28 +389,32 @@
"completed": "Закінчені",
"error": "Помилкові",
"in_progress": "Триває",
+ "interrupted": "Перерваний",
"klippy_disconnect": "Klippy відключився",
- "klippy_shutdown": "Помилка Кліпперу",
+ "klippy_shutdown": "Помилка Klipper",
"Others": "Інші",
"server_exit": "Вихід сервера"
},
+ "Table": "Таблиця",
"TitleExportHistory": "Історія Експорту",
- "TitleRefreshHistory": "Оновити Історію",
- "TitleSettings": "Налаштування",
"TotalDuration": "Загальний Час",
- "TotalFilamentUsed": "Використано Прутка",
- "TotalJobs": "Всі Роботи",
+ "TotalFilamentUsed": "Використано філаменту",
+ "TotalJobs": "Всі завдання",
"TotalPrinttime": "Загальний Час Друку",
"TotalTime": "Загальний Час"
},
"JobQueue": {
- "AllJobs": "Всі Роботи",
+ "Cancel": "Скасувати",
+ "ChangeCount": "Змінити кількість",
+ "Count": "Кількість",
"Empty": "Порожньо",
- "JobQueue": "Черга Робіт",
- "Jobs": "Роботи",
+ "InvalidCountEmpty": "Поле не повинне бути порожнім!",
+ "InvalidCountGreaterZero": "Значення має бути більше 0!",
+ "JobQueue": "Черга завдань",
"Pause": "Пауза",
"RemoveFromQueue": "Видаліть з Черги",
- "Start": "Почати"
+ "Start": "Почати",
+ "StartPrint": "Почати завдання"
},
"Machine": {
"ConfigFilesPanel": {
@@ -373,7 +430,10 @@
"DeleteDirectory": "Видалити директорію",
"DeleteDirectoryQuestion": "Видалити директорію \"{name}\" і весь її вміст?",
"DeleteSelectedQuestion": "Видалити {count} вибрані елементи?",
+ "DeleteSingleFileQuestion": "Ви дійсно хочете видалити файл \"{name}\"?",
"Download": "Завантажити",
+ "Duplicate": "Дублікат",
+ "DuplicateFile": "Дублікат файлу",
"EditFile": "Редагувати файл",
"Empty": "Порожньо",
"Files": "Файл",
@@ -404,15 +464,21 @@
"TRIGGERED": "ЗАМКНЕНО"
},
"LogfilesPanel": {
- "Logfiles": "Журнали"
+ "Accept": "прийняти",
+ "Cancel": "скасувати",
+ "Logfiles": "Журнали",
+ "Rollover": "Журнали переходів",
+ "RolloverDescription": "Виберіть, які журнали потрібно скинути:",
+ "RolloverToastFailed": "Перехідний журнал для \"{name}\": {message}",
+ "RolloverToastSuccessful": "Журнал для \"{name}\" успішно скинуто."
},
"SystemPanel": {
"Constants": "Константи",
- "Cpu": "ЦПК",
+ "Cpu": "CPU",
"HostDetails": "Деталі хоста",
"LastStats": "Остання статистика",
"Load": "Навантаження",
- "Memory": "ОЗУ",
+ "Memory": "RAM",
"NoMoreInfos": "Більше немає Infos",
"SystemLoad": "Навантаження системи",
"Values": {
@@ -421,8 +487,8 @@
"Distro": "Дистрибутив: {name} {version_id}",
"Frequency": "Частота: {frequency}",
"Load": "Навантаження: {load}",
- "Memory": "ОЗУ: {memory}",
- "Os": "Операційка: {os}",
+ "Memory": "RAM: {memory}",
+ "Os": "Операційна Система: {os}",
"Received": "Отримано: {received}",
"Temp": "t° проц.: {temp}°C",
"TempMax": "макс: {temp}°C",
@@ -432,38 +498,68 @@
}
},
"UpdatePanel": {
+ "Abort": "Перервати",
+ "AreYouSure": "Ти впевнений?",
"CheckForUpdates": "Перевірити оновлення",
- "Commits": "Коментар",
- "CommitsAvailable": "Немає коментарів | {count} доступний коментар | {count} Доступні",
- "CommitsOnDate": "Коментар від {date}",
- "CommittedDaysAgo": "Прокоментовано {days} днів тому",
- "CommittedHoursAgo": "Прокоментовано {hours} годин тому",
- "CommittedOnDate": "Прокоментовано {date}",
- "CommittedYesterday": "Сьогоднішній комент",
+ "Close": "Закрити",
+ "CommitHistory": "Історія комітів",
+ "Commits": "Коміт",
+ "CommitsAvailable": "Немає комітів | {count} доступний коміт | {count} доступних комітів",
+ "CommitsOnDate": "Коміт від {date}",
+ "CommittedDaysAgo": "Коміт {days} днів тому",
+ "CommittedHoursAgo": "Коміт {hours} годин тому",
+ "CommittedOnDate": "Коміт {date}",
+ "CommittedYesterday": "Коміт вчорашній",
+ "ConfigChanges": "Зміни конфігурації",
+ "Corrupt": "пошкоджений",
+ "CountPackagesCanBeUpgraded": "{count} пакетів можна оновити",
"Detached": "відокремлений",
"Dirty": "брудний",
- "ERROR": "ПОМИЛКА",
+ "GenericUpdateQuestion": "Перевірте історію комітів (якщо доступна) і сторінку GitHub для цього пакету, щоб дізнатися, чи потрібні якісь ручні налаштування для цього оновлення.",
+ "GitHubPage": "Сторінка GitHub",
+ "HardRecovery": "Повне скидання",
+ "InitUpdateManager": "Менеджер оновлень ще не ініціалізовано. Це нормально, коли ви запускаєте систему вперше. Натисніть кнопку оновити, щоб ініціалізувати всі компоненти.",
"Invalid": "недійсний",
+ "IUnderstandTheRisks": "Я розумію ризики",
+ "KlipperUpdateQuestionConfig": "Це оновлення також може містити зміни в параметрах конфігурації, які потрібно буде змінити у файлі printer.cfg, подробиці дивіться в журналі змін.",
+ "KlipperUpdateQuestionFirmware": "Це призведе до оновлення програмного забезпечення Klipper хоста. Плати керування (MCU), на яких працює мікропрограмне забезпечення Klipper, можливо, потребуватиме перепрошивки за допомогою перекомпільованого мікропрограмного забезпечення, перш ніж принтер можна буде знову ввести в експлуатацію.",
+ "LinkToGithub": "Посилання на GitHub",
+ "MoonrakerUpdateQuestion": "Це оновить Moonraker API. Для продовження використання принтера можуть знадобитися зміни у файлі moonraker.conf.",
+ "MoreCommitsInfo": "Тут відображено максимум 30 комітів. Щоб переглянути всі коміти, перейдіть за посиланням:",
"OSPackages": "ОС-Пакети",
- "PackagesCanBeUpgraded": "пакети можна оновити",
+ "SoftRecovery": "Часткове відновлення",
+ "StartUpdate": "Почати оновлення",
"System": "Система",
+ "ThesePackagesCanBeUpgrade": "Ці системні пакети можна оновити:",
"Unknown": "невідомо",
"Update": "оновити",
- "UpdateAll": "Оновіть усі компоненти",
+ "UpdateAll": "Оновити всі",
"UpdateManager": "Менеджер Оновлень",
+ "UpdateWarning": "Попередження про оновлення: {name}",
"Upgrade": "оновити",
- "UpToDate": "актуальний"
+ "UpgradeableSystemPackages": "Системні пакети що можна оновити",
+ "UpToDate": "актуальний",
+ "WebClientUpdateQuestion": "У деяких випадках оновлення веб-клієнт має зміни, які можуть спричинити несумісність. Для отримання додаткової інформації див. примітки до релізу."
}
},
+ "ManualProbe": {
+ "Abort": "перервати",
+ "Accept": "прийняти",
+ "Advanced": "Просунутий",
+ "Headline": "Проба вручну"
+ },
"Panels": {
"ExtruderControlPanel": {
"Allowed": "Допущений",
+ "CleanNozzle": "Очистити сопло",
"EstimatedExtrusion": "Розрахункова екструзія:",
"Extrude": "Видавити",
+ "ExtruderControl": "Керування екструдером",
"ExtruderTempTooLow": " t° екструдера. <",
"ExtrusionFactor": "Коефіцієнт ЕКСТРУЗІЇ",
"ExtrusionFeedrate": "Екструзійна подача",
- "FilamentLength": "Продавити на",
+ "FilamentLength": "Довжина філаменту",
+ "FirmwareRetraction": "Програмний ретракт",
"FirmwareRetractionSettings": {
"RetractLength": "Довжина Ретракту",
"RetractSpeed": "Швидкість Ретракту",
@@ -472,41 +568,50 @@
},
"Headline": "Екструдер",
"LoadFilament": "Завантажити Пруток",
+ "PressureAdvance": "Pressure Advance",
"PressureAdvanceSettings": {
"Advance": "Pressure Advance",
"Extruder": "Екструдер",
"SmoothTime": "Плавний Час"
},
- "Requested": "Запитуваний",
+ "PurgeFilament": "Purge філаменту",
+ "Requested": "Заданий",
"Retract": "Ретракт",
"TooLargeExtrusion": "Екструзія занадто велика!",
- "UnloadFilament": "Вигрузка Прутка"
+ "Tools": "Інструменти",
+ "UnloadFilament": "Вивантаження філаменту"
},
"FarmPrinterPanel": {
- "ReconnectToPrinter": "Переподключення",
+ "ReconnectToPrinter": "Перепідключення",
"SwitchToPrinter": "Перемкнутися на Принтер",
"WebcamOff": "ВИМК"
},
"KlippyStatePanel": {
+ "CheckKlippyAndUdsAddress": "Будь ласка, перевірте, чи працює служба Klipper і чи правильно налаштовано klippy_uds_address у файлі moonraker.conf.",
"FirmwareRestart": "Перезапуск Прошивки",
- "KlipperCheck": "Будь ласка, перевірте, чи працює служба Klipper та налаштована UDS (UNIX домен).",
"MoonrakerCannotConnect": "Moonraker не може підключитися до Klipper!",
- "Restart": "Перезапустити"
+ "PowerOn": "Увімкнено",
+ "PrinterSwitchedOff": "Принтер вимкнений",
+ "PrinterSwitchedOffDescription": "Принтер наразі вимкнено, і Klipper не може підключитися. Щоб увімкнути принтер, натисніть кнопку нижче:",
+ "Restart": "Перезапустити",
+ "ServiceReports": "{service} повідомляє"
},
"MachineSettingsPanel": {
"Headline": "Машина",
"MotionSettings": {
"Acceleration": "Прискорення",
- "MaxAccelToDecel": "Max Accel. to Decel.",
+ "MaxAccelToDecel": "Макс. прискорення до сповільнення.",
+ "MinimumCruiseRatio": "Мін. коефіцієнт руху",
"SquareCornerVelocity": "Квадратна кутова швидкість",
"Velocity": "Швидкість"
}
},
"MacrosPanel": {
"Headline": "Макрос",
- "Send": "надсилати"
+ "Send": "відправити"
},
"MiniconsolePanel": {
+ "Autoscroll": "Автопрокручування",
"Headline": "Консоль",
"HideTemperatures": "Сховати температуру",
"HideTimelapse": "Сховати таймлапс",
@@ -521,26 +626,57 @@
},
"MiscellaneousPanel": {
"Headline": "Різне",
+ "Light": {
+ "Blue": "блакитний",
+ "Green": "зелений",
+ "Red": "червоний",
+ "White": "білий"
+ },
"RunoutSensor": {
"Detected": "виявлено",
- "Disabled": "Не активно",
"Empty": "Порожньо"
}
},
"PowerControlPanel": {
"Error": "Помилка",
"Off": "ВИМК",
- "On": "ВВІМК",
+ "On": "УВІМК",
"PowerControl": "Управління Живленням"
},
+ "SpoolmanPanel": {
+ "Cancel": "Скасувати",
+ "ChangeSpool": "Зміна шпулі",
+ "DaysAgo": "{days} днів тому",
+ "EjectSpool": "Прибрати шпулю",
+ "EjectSpoolQuestion": "Ви впевнені, що хочете прибрати шпулю з філаментом?",
+ "Filament": "Філамент",
+ "FilamentTypeMismatch": "Матеріал активної шпулі ({spoolType}) не збігається з матеріалом G-коду ({fileType}).",
+ "Headline": "Spoolman",
+ "LastUsed": "Останнє використання",
+ "Location": "Місцезнаходження",
+ "Material": "Матеріал",
+ "Never": "Ніколи",
+ "NoActiveSpool": "Відстеження філаменту неактивне. Щоб почати, будь ласка, виберіть котушку.",
+ "NoResults": "За поточними критеріями пошуку шпулю не знайдено.",
+ "NoSpools": "Немає шпуль",
+ "NoSpoolSelected": "Шпулю не вибрано. Виберіть шпулю, інакше цей друк не відстежуватиметься.",
+ "OpenSpoolManager": "відкрити менеджер шпуль",
+ "Refresh": "оновити",
+ "Search": "Пошук",
+ "SelectSpool": "Виберіть шпулю",
+ "Today": "Сьогодні",
+ "TooLessFilament": "У поточній котушці може бути недостатньо філаменту для цього друку. ({spoolWeight}г з {fileWeight}г)",
+ "Weight": "Вага",
+ "Yesterday": "Вчора"
+ },
"StatusPanel": {
"CancelPrint": "Скасувати друк",
"ClearPrintStats": "Чітка статистика друку",
"Difference": "Різниця",
"EmptyGcodes": "Немає G-коду, що доступний.",
- "EmptyJobqueue": "Наразі в черзі роботи немає файлу.",
+ "EmptyJobqueue": "Наразі в черзі завдань немає файлу.",
"Estimate": "Оцінка",
- "ETA": "Час Закінченя",
+ "ETA": "Час закінчення",
"ExcludeObject": {
"Cancel": "скасувати",
"Excluded": "Виключений",
@@ -548,21 +684,33 @@
"ExcludeObjectHeadline": "Виключіть Об'єкт",
"ExcludeObjectText": "Ви дійсно хочете виключити \"{name}\"?"
},
- "Filament": "Пруток",
+ "Filament": "Філамент",
"File": "Файл",
"Files": "Файли",
"Flow": "Потік",
"Headline": "Статус",
- "Jobqueue": "Черга робіт",
- "JobqueueMoreFiles": "більше нема робіт | ще одна робота | {count} більше робіт",
+ "Jobqueue": "Черга завдань",
+ "JobqueueMoreFiles": "більше нема робіт | ще одне завдання | {count} більше завдань",
"Layer": "Шар",
"Max": "макс",
"ObjectHeight": "Висота Об'кта",
+ "PauseAtLayer": {
+ "Abort": "перервати",
+ "Accept": "прийняти",
+ "AtLayer": "на Шарі",
+ "Call": "Виклик",
+ "DescriptionPauseAtLayerActive": "Ця функція вже активна та викличе \"{call}\" на шарі {layer}. Якщо ви знову викличете команду, ці параметри буде перезаписано.",
+ "DescriptionPauseNextLayerActive": "Ця функція вже активна та викличе \"{call}\" на наступному шарі.",
+ "Layer": "Шар",
+ "NextLayer": "наступний шар",
+ "PauseAtLayer": "Пауза на шарі",
+ "Type": "Тип"
+ },
"PausePrint": "Призупинення друку",
"Print": "Друк",
"PrintTime": "Час Друку",
"ReprintJob": "Роздруковано",
- "Requested": "Запитуваний",
+ "Requested": "Встановлено",
"ResumePrint": "Відновити друк",
"Slicer": "Слайсер",
"Speed": "Швидкість",
@@ -581,7 +729,9 @@
"Target": "цільова температура",
"Temperature": "поточна температура"
},
- "Headline": "Температури",
+ "Headline": "Температура",
+ "HideMcuHostSensors": "Приховати датчики хоста/MCU",
+ "HideMonitors": "Приховати монітори",
"Max": "макс",
"Min": "мін",
"Name": "Ім'я",
@@ -599,18 +749,22 @@
"ToolheadControlPanel": {
"Absolute": "абсолютні",
"ALL": "ВСІ",
+ "ControlButtons": "Кнопки управління",
+ "CoordinateFields": "Поля координат",
"Headline": "Інструмент",
"PleaseConfigureSteps": "Будь ласка, налаштуйте кроки",
"Position": "Положення",
+ "PositionOutput": "Розташування",
"QGL": "QGL",
"Relative": "відносні",
"SettingsInterfaceControl": "Налаштування > Інтерфейс > Контроль",
- "SpeedFactor": "Коефіцієнт ШВИДКОСТІі",
+ "SpeedFactor": "Коефіцієнт ШВИДКОСТІ",
+ "ZOffset": "Z-Offset",
"ZTilt": "Z-Нахил"
},
"WebcamPanel": {
"All": "ВСІ",
- "FPS": "ФПС",
+ "FPS": "FPS",
"Headline": "Веб-камера",
"NoWebcam": "Немає веб-камери. Додайте веб -камеру \"Налаштування Інтерфейсу\" -> \"Веб-камери\".",
"UnknownWebcamService": "Невідома служба веб-камери"
@@ -618,22 +772,20 @@
"ZoffsetPanel": {
"Clear": "Очистити",
"Headline": "Z-Offset",
- "Later": "Згодом",
+ "Later": "Пізніше",
"Ok": "OK",
"Save": "Зберегти",
- "SaveConfig": "SAVE CONFIG",
- "SaveInfoDescription": "Новий Z-Offset був обчислений та зареєстрований. Натисніть на \"SAVE CONFIG\" Щоб зберегти його на printer.cfg та перезапустити Klipper.",
- "SaveInfoDescriptionPrint": "Новий Z-Offset був обчислений та зареєстрований. Після друку натисніть на \"SAVE CONFIG\" у верхній панелі, щоб зберегти його у printer.cfg та перезапустити Klipper.",
- "SaveInfoHeadline": "Інформація",
- "ToEndstop": "до Кінцевика",
- "ToProbe": "Зондувати"
+ "SaveConfig": "ЗБЕРЕГТИ КОНФІГУРАЦІЮ",
+ "SaveInfoDescription": "Новий Z-Offset був обчислений та зареєстрований. Натисніть на \"ЗБЕРЕГТИ КОНФІГУРАЦІЮ\" Щоб зберегти його на printer.cfg та перезапустити Klipper.",
+ "SaveInfoDescriptionPrint": "Новий Z-Offset був обчислений та зареєстрований. Після друку натисніть на \"ЗБЕРЕГТИ КОНФІГУРАЦІЮ\" у верхній панелі, щоб зберегти його у printer.cfg та перезапустити Klipper.",
+ "SaveInfoHeadline": "Інформація"
}
},
"PowerDeviceChangeDialog": {
"AreYouSure": "Ви впевнені?",
- "No": "НІТ",
+ "No": "НІ",
"TurnDeviceOff": "Поворот {device} ВИМК",
- "TurnDeviceOn": "Поворот {device} ВВІМК",
+ "TurnDeviceOn": "Поворот {device} УВІМК",
"Yes": "ТАК"
},
"Router": {
@@ -648,6 +800,13 @@
"Timelapse": "Таймлапс",
"Webcam": "Веб-камера"
},
+ "ScrewsTiltAdjust": {
+ "Accept": "прийняти",
+ "Base": "Основа",
+ "ErrorText": "Щось пішло не так під час процесу взяття проби.",
+ "Headline": "Гвинти регулювання нахилу",
+ "Retry": "повторити спробу"
+ },
"SelectPrinterDialog": {
"AddPrinter": "Додати Принтер",
"AddPrintersToJson": "Будь ласка, додайте принтери до config.json.",
@@ -659,7 +818,7 @@
"Hello": "Привіт і ласкаво просимо до віддаленого режиму Mainsail!",
"HostnameInvalid": "Недійсне ім'я хоста/IP",
"HostnameIp": "Ім'я хоста/IP",
- "HostnameRequired": "Ім'я хоста потрібно",
+ "HostnameRequired": "Необхідне ім'я хоста",
"Port": "Порт",
"PortRequired": "Потрібен порт",
"RememberToAdd": "Будь ласка, не забудьте додати '{cors}' у moonraker.conf всередині 'cors_domains'.",
@@ -676,10 +835,10 @@
"Console": "Консоль",
"CreateHeadline": "Створити фільтр",
"Direction": "Напрямок",
- "DirectionShell": "Останній запис унизу",
- "DirectionTable": "Останній запис у верхній частині",
+ "DirectionShell": "Останній запис внизу",
+ "DirectionTable": "Останній запис звурху",
"EditHeadline": "Редагувати фільтр",
- "EntryStyle": "Entry-Дизайн",
+ "EntryStyle": "Початковий дизайн",
"EntryStyleCompact": "компактний",
"EntryStyleDefault": "за замовчуванням",
"Filters": "Фільтри",
@@ -692,13 +851,14 @@
"UpdateButton": "Оновити фільтр"
},
"ControlTab": {
- "Bars": "Брус",
- "Circle": "Кола",
+ "Bars": "Вкладка",
+ "Circle": "Коло",
"Control": "Контроль",
"Cross": "Перехрестя",
"EnableXYHoming": "Увімкнути комбіноване наведення вісь X і Y до дому",
"EstimatedExtrusionInfo": "Показати орієнтовну інформацію про екструзію",
"EstimatedExtrusionInfoDescription": "Показати / приховати інформацію про орієнтовні екструзії на основі кількості екструзії та подачі",
+ "HideDuringPrint": "Приховати елементи керування віссю під час друку",
"InvertXMovement": "Інвертуйте напрямок руху осі X",
"InvertYMovement": "Інвертувати напрямок руху осі Y",
"InvertZMovement": "Інвертувати напрямок руху осі Z",
@@ -713,11 +873,13 @@
"MoveDistancesZInMm": "Дистанція переміщення вісі Z у мм",
"QuadGantryLevel": "Quad Gantry Вирівнювання{isDefault}",
"SpeedEInMms": "Швидкість екструзії (у мм/с)",
- "SpeedXY": "Швидкість руху вісєй X та Y",
+ "SpeedXY": "Швидкість руху вісі X та Y",
"SpeedZ": "Швидкість руху вісі Z",
"Style": "Стиль",
"ValueGreaterThan": "Значення повинно бути більшим, ніж {value}",
"ZOffsetIncrements": "Збільшення Z-Offset (в мм)",
+ "ZOffsetSaveOption": "Опція збереження Z-Offset",
+ "ZOffsetSaveOptionDescription": "Змініть параметр, щоб зберегти Z-Offset",
"ZTiltAdjust": "Налаштування Z-нахилу{isDefault}"
},
"DashboardTab": {
@@ -730,13 +892,16 @@
},
"Edit": "Редагувати",
"EditorTab": {
- "ConfirmUnsavedChanges": "Підказувати зберігати чи ніт не збережені зміни",
+ "ConfirmUnsavedChanges": "Підказувати зберігати чи ні не збережені зміни",
"ConfirmUnsavedChangesDescription": "Якщо ввімкнено, редактор вимагає підтвердження, щоб зберегти, або відкинути внесені зміни. Якщо відключено, зміни не зберігаються.",
"Editor": "Редактор",
"KlipperRestartMethod": "Метод перезапуску Klipper",
"KlipperRestartMethodDescription": "Виберіть, який метод перезавантаження буде використовуватися на 'Зберегти та перезапустити' під час редагування файлів конфігурації Klipper..",
- "UseEscToClose": "Використовуйте ESC для закриття редактора",
- "UseEscToCloseDescription": "Дозволяє клавішу ESC закрити редактор"
+ "Spaces": "Пробіли: {count}",
+ "TabSize": "Розмір відступів",
+ "TabSizeDescription": "Регулює кількість пробілів для табуляції",
+ "UseEscToClose": "ESC закриває редактор",
+ "UseEscToCloseDescription": "Дозволити клавішу ESC для закриття редактора"
},
"GCodeViewerTab": {
"BackgroundColor": "Колір фону",
@@ -746,9 +911,11 @@
"MaxFeed": "Максимальна швидкість подачі",
"MinFeed": "Мінімальна швидкість подачі",
"ProgressColor": "Колір прогресу",
- "ShowAxes": "Показати осі"
+ "ShowAxes": "Показати вісі"
},
"GeneralTab": {
+ "12hours": "12 годин ({time})",
+ "24hours": "24 години ({time})",
"Backup": "Резервна копія",
"BackupDialog": "Виберіть усі розділи, які ви хочете створити резервну копію:",
"CalcEstimateTime": "Оцінка розрахунку часу",
@@ -763,27 +930,42 @@
"FileRelative": "Позиція файлу (відносна)",
"Slicer": "Слайсер (M73)"
},
- "CannotReadJson": "Не вдається читати/розбирати резервний файл.",
+ "CannotReadJson": "Не вдається читати/розібрати резервний файл.",
+ "DateFormat": "Формат дати",
"DbConsoleHistory": "Історія Консолі",
- "DbHistoryJobs": "Історія роботи",
+ "DbHistoryJobs": "Історія завдань",
"DbHistoryTotals": "Загальна Історія",
+ "DBNavigation": "Навігація",
"DbTimelapseSettings": "Налаштування Таймлапсу",
"DbView": "Налаштування перегляду",
- "DbWebcams": "Веб -камери",
"EstimateValues": {
"Filament": "Пруток",
"File": "Файл",
"Slicer": "Слайсер"
},
+ "Everything": "Все",
"FactoryDialog": "Будь ласка, виберіть усі розділи, які ви хочете скинути:",
"FactoryReset": "Заводські налаштування",
"General": "Загальний",
"Language": "Мова",
- "MoonrakerDb": "Moonraker DB",
+ "MainsailSettingsMoonrakerDb": "Параметри Mainsail в БД Moonraker",
"PrinterName": "Ім'я принтера",
"Reset": "скинути",
"Restore": "Відновити",
- "RestoreDialog": "Будь ласка, виберіть усі розділи, які ви хочете відновити:"
+ "RestoreDialog": "Будь ласка, виберіть усі розділи, які ви хочете відновити:",
+ "TimeFormat": "Формат часу"
+ },
+ "HeightmapTab": {
+ "ColorSchemes": "Палітри кольорів",
+ "Heightmap": "Карта висот",
+ "IsDefault": "(за замовчуванням)",
+ "Schemes": {
+ "GrayScale": "Відтінки сірого",
+ "Hot": "Теплота",
+ "Hsv": "Hsv",
+ "Portland": "Портленд",
+ "Spring": "Весна"
+ }
},
"InterfaceSettings": "Налаштування інтерфейсу",
"MacrosTab": {
@@ -792,7 +974,7 @@
"AvailableMacros": "Доступні Макроси",
"ChangeMacroColor": "Змініть колір кнопки.",
"Color": "Колір",
- "CountMacros": "Жодних макросів не додано | {count} Макрос | {count} Макроси",
+ "CountMacros": "Жодних макросів не додано | {count} Макрос | {count} Макросів",
"Custom": "свій",
"CustomColor": "Свій Колір",
"DeletedMacro": "Видалений макрос",
@@ -813,20 +995,46 @@
"NoMacrosInGroup": "У цій групі немає макросів.",
"Primary": "основний",
"Secondary": "вторинний",
- "ShowInStatePaused": "Показати/приховати, якщо принтер призупинений.",
- "ShowInStatePrinting": "Показати/приховати, якщо принтер друкує.",
- "ShowInStateStandby": "Показати/приховати, якщо принтер буде в режимі очікування.",
+ "ShowInStatePaused": "Показати/приховати, коли принтер призупинений.",
+ "ShowInStatePrinting": "Показати/приховати, коли принтер друкує.",
+ "ShowInStateStandby": "Показати/приховати, коли принтер в режимі очікування.",
"Simple": "Простий",
"Status": "Статус",
"Success": "успіх",
"UnknownGroup": "Невідома Група",
"Warning": "УВАГА"
},
+ "MiscellaneousTab": {
+ "AddGroup": "додати групу",
+ "AddPreset": "додати предустановку",
+ "Color": "Колір",
+ "CreateGroup": "Створити групу",
+ "CreatePreset": "Створити предустановку",
+ "End": "Кінець",
+ "EndDescription": "Останній світлодіод цієї групи.",
+ "Groups": "Групи",
+ "GroupSubTitle": "Початок: {start}, кінець: {end}",
+ "LightGroups": "{name} - Групи",
+ "LightPresets": "{name} - Предустановки",
+ "Miscellaneous": "Різне",
+ "Name": "Ім'я",
+ "NoDevicesFound": "Пристроїв не знайдено",
+ "NoGroupFound": "Групи не знайдено",
+ "NoPresetFound": "Предустановки не знайдено",
+ "Presets": "Предустановки",
+ "Start": "Cтарт",
+ "StartDescription": "Перший світлодіод цієї групи.",
+ "UnableToLoadLight": "Не вдалося завантажити світло",
+ "UnableToLoadPreset": "Неможливо завантажити предналаштування"
+ },
+ "NavigationTab": {
+ "Navigation": "Навігація"
+ },
"PresetsTab": {
"AddPreset": "додати пресет",
"Cooldown": "ОХОЛОДЖЕННЯ Нагрівачів",
"CreateHeadline": "Створити пресет",
- "CustomGCode": "Свій Г-код",
+ "CustomGCode": "Свій G-код",
"EditCooldown": "Редагувати ОХОЛОДЖЕННЯ",
"EditHeadline": "Редагувати пресет",
"ErrorInvalidValue": "Недійсне значення",
@@ -849,6 +1057,7 @@
"UpdatePrinter": "Оновити принтер",
"UseConfigJson": "InstanceDB = JSON виявлений. Будь ласка, використовуйте config.json щоб змінити список принтерів."
},
+ "Store": "зберегти",
"TimelapseTab": {
"Autorender": "Автовізуалізація",
"AutorenderDescription": "Якщо ввімкнено, відео Таймлапс автоматично відображатиметься в кінці друку",
@@ -894,6 +1103,11 @@
"RetractDistanceDescription": "Довжина прутка, яку екструдер втягує.",
"RetractSpeed": "Швидкість втягування",
"RetractSpeedDescription": "Швидкість, з якою екструдер відтягує пруток.",
+ "RulesBetweenMinMax": "Значення має бути від {min} до {max}!",
+ "RulesMin": "Значення має бути не менше {min}!",
+ "RulesPositive": "Значення не має бути від'ємним!",
+ "RulesRequired": "Необхідно вказати значення!",
+ "RulesZeroAndPositive": "Значення має бути 0 або більше!",
"SaveFrames": "Зберегти зображення",
"SaveFramesDescription": "Збережіть кадри у zip-файлі для зовнішнього відображення",
"StreamDelayCompensation": "Компенсація затримки потоку",
@@ -911,12 +1125,15 @@
"UnretractSpeedDescription": "Швидкість, з якою екструдер подає пруток після втягування.",
"VariableFps": "Змінна FPS",
"VariableFpsDescription": "Якщо ввімкнено, кадри вихідного відео буде обчислено на основі довжини відео",
- "VariableFpsMax": "Змінна FPS макс",
- "VariableFpsMaxDescription": "",
- "VariableFpsMin": "Змінна FPS мін",
- "VariableFpsMinDescription": ""
+ "VariableFpsMax": "Динамічний FPS макс",
+ "VariableFpsMaxDescription": "Максимальне значення при динамічному FPS",
+ "VariableFpsMin": "Динамічний FPS мін",
+ "VariableFpsMinDescription": "Мінімальне значення при динамічному FPS"
},
"UiSettingsTab": {
+ "BedScrewsDialog": "Вікно гвинтів стола",
+ "BedScrewsDialogDescription": "Відобразити допоміжне діалогове вікно для BED_SCREWS_ADJUST.",
+ "BigThumbnailBackground": "Колір тла великої мініатюри",
"BoolBigThumbnail": "Велика мініатюра",
"BoolBigThumbnailDescription": "Відобразити велику мініатюру на панелі статусу під час друку.",
"BoolHideUploadAndPrintButton": "Приховати кнопку завантаження та друк",
@@ -925,40 +1142,67 @@
"ConfirmOnEmergencyStopDescription": "Показати діалогове вікно підтвердження АВАРІЙНОЇ ЗУПИНКИ",
"ConfirmOnPowerDeviceChange": "Вимагати підтвердження змін живлення пристрою",
"ConfirmOnPowerDeviceChangeDescription": "Показати діалогове вікно підтвердження щодо змін живлення пристрою",
+ "DefaultNavigationState": "Стандартний стан навігації",
+ "DefaultNavigationStateAlwaysClosed": "завжди закрита",
+ "DefaultNavigationStateAlwaysOpen": "завжди відкрита",
+ "DefaultNavigationStateDescription": "У якому стані має бути навігація за умовчанням.",
+ "DefaultNavigationStateLastState": "останній стан",
+ "DisableFanAnimation": "Вимкнути анімацію кулера",
+ "DisableFanAnimationDescription": "Це може зменшити навантаження на ваш браузер.",
"DisplayCANCEL_PRINT": "Відображати кнопку Скасування Друку",
"DisplayCANCEL_PRINTDescription": "Показує кнопку CANCEL_PRINT постійно - не потрібно підтвердження другого рівня.",
"GcodeThumbnails": "Ескіз G-Code",
"GcodeThumbnailsDescription": "Клацніть на кнопку, щоб дістатися до інструкцій.",
"Guide": "Інструкція",
+ "HideSaveConfigButtonForBedMesh": "Приховати кнопку SAVE_CONFIG для змін bed_mesh",
+ "HideSaveConfigButtonForBedMeshDescription": "Приховати SAVE_CONFIG, якщо тільки зміни bed_mesh очікують на збереження в Klipper.",
+ "HideUpdateWarnings": "Приховати попередження про оновлення",
+ "HideUpdateWarningsDescription": "Ця опція приховає всі попередження про оновлення в менеджері оновлень.",
"LockSliders": "Заблокуйте повзунки на пристроях сенсорного екрану",
"LockSlidersDelay": "Затримка блокування повзунка",
"LockSlidersDelayDescription": "Слайдери заблокуються після заданої затримки. Якщо встановлено на 0 або залишився порожнім, повзунки лише заблокують зміну/перезавантаження сторінки.",
"LockSlidersDescription": "Слайдери на сенсорному екрані потрібно розблокувати, перш ніж зміни будуть дозволені.",
"Logo": "Колір Логотипу",
+ "ManualProbeDialog": "Вікно помічника ручної проби",
+ "ManualProbeDialogDescription": "Відобразити допоміжне діалогове вікно для PROBE_CALIBRATE або Z_ENDSTOP_CALIBRATE.",
"NavigationStyle": "Стиль навігації",
"NavigationStyleDescription": "Змінити зовнішній вигляд навігації",
"NavigationStyleIconsAndText": "Піктограми + текст",
"NavigationStyleIconsOnly": "Тільки іконки",
+ "PowerDeviceName": "Пристрій живлення принтера",
+ "PowerDeviceNameDescription": "Виберіть, який пристрій живлення Moonraker слід використовувати для живлення принтера.",
"Primary": "Основний Колір",
- "ShowWebcamInNavigation": "Показати веб -камеру в навігації",
+ "ScrewsTiltAdjustDialog": "Вікно налаштування гвинтів нахилу стола",
+ "ScrewsTiltAdjustDialogDescription": "Відобразити допоміжне вікно для SCREWS_TILT_CALCULATE.",
+ "TempchartHeight": "Висота графіку температур",
+ "TempchartHeightDescription": "Змініть висоту графіка температури на інформаційній панелі.",
+ "Theme": "Тема",
+ "ThemeDark": "Темна",
+ "ThemeDescription": "Змініть загальний вигляд програми",
+ "ThemeLight": "Світла",
"UiSettings": "Налаштування в інтерфейсі"
},
+ "Update": "оновлення",
"WebcamsTab": {
"AddWebcam": "Додати веб-камеру",
"CreateWebcam": "Створити веб-камеру",
"EditCrowsnestConf": "Редагувати crowsnest.conf",
"EditWebcam": "Редагувати веб-камеру",
+ "EnableAudio": "Увімкнути аудіо",
"FlipWebcam": "Повернути веб-камеру:",
+ "HideFps": "Приховати лічильник FPS",
+ "Hlsstream": "Потік HLS",
"Horizontally": "горизонтально",
"IconBed": "Ліжко",
"IconCam": "Cam",
"IconDoor": "Двері",
"IconFilament": "Пруток",
"IconHot": "Гарячий",
- "IconMcu": "ЦПК",
+ "IconMcu": "MCU",
"IconNozzle": "Сопло",
"IconPrinter": "Прінтер",
- "Ipstream": "IP -камера",
+ "Ipstream": "IP-камера",
+ "JMuxerStream": "Необроблений потік h264 (jmuxer)",
"Mjpegstreamer": "MJPEG-Стример",
"MjpegstreamerAdaptive": "Adaptive MJPEG-Streamer (експериментальний)",
"Name": "Ім'я",
@@ -973,7 +1217,11 @@
"UrlStream": "URL-адреса Потоку",
"Uv4lMjpeg": "UV4L-MJPEG",
"Vertically": "вертикально",
- "Webcams": "Веб-камери"
+ "Webcams": "Веб-камери",
+ "WebrtcCameraStreamer": "WebRTC (camera-streamer)",
+ "WebrtcGo2rtc": "WebRTC (go2rtc)",
+ "WebrtcJanus": "WebRTC (janus-gateway)",
+ "WebrtcMediaMTX": "WebRTC (MediaMTX)"
}
},
"Timelapse": {
@@ -987,6 +1235,7 @@
"DeleteDirectory": "Видалити директорію",
"DeleteDirectoryQuestion": "Ви дійсно хочете видалити \"{name}\" директоріб із усім її вмістом?",
"DeleteSelectedQuestion": "Ви дійсно хочете видалити {count} вибрані файли?",
+ "DeleteSingleFileQuestion": "Ви дійсно хочете видалити файл \"{name}\"?",
"Download": "Завантажити",
"DuplicateLastframe": "Дублікат останнього кадру",
"Empty": "Не знайдено закінченого Таймлапсу.",
@@ -998,8 +1247,8 @@
"Framerate": "Частота кадрів",
"Frames": "Кадри",
"Free": "Вільно",
- "FreeDisk": "Вільний диск",
- "LastModified": "Датат Створення",
+ "FreeDisk": "Вільнр на диску",
+ "LastModified": "Дата Створення",
"MaxFramerate": "Макс. частота кадрів",
"MinFramerate": "Мін. частота кадрів",
"Name": "Ім'я",
diff --git a/src/locales/zh.json b/src/locales/zh.json
index a7d862a11..f33d907d1 100644
--- a/src/locales/zh.json
+++ b/src/locales/zh.json
@@ -6,7 +6,7 @@
"Headline": "浏览器已过时"
},
"DependencyDescription": "当前版本的{name}并不支持Mainsail的全部功能。{name}需要至少更新到{neededVersion}版本。",
- "DependencyName": "{name}依赖",
+ "DependencyName": "依赖:{name}",
"DismissAll": "忽略全部",
"KlipperWarnings": {
"DeprecatedOption": "在'{section}'标签中的'{option}'选项已被弃用,将在未来的版本中删除。",
@@ -16,17 +16,17 @@
"KlipperWarning": "Klipper警告"
},
"MoonrakerWarnings": {
- "MoonrakerComponent": "Moonraker: {component}",
+ "MoonrakerComponent": "Moonraker:{component}",
"MoonrakerFailedComponentDescription": "加载moonraker组件'{component}'时发生错误。请检查日志文件并修复此问题。",
"MoonrakerFailedInitComponentDescription": "初始化moonraker组件'{component}'时发生错误。请检查日志文件并修复此问题。",
- "MoonrakerInitComponent": "初始化 Moonraker: {component}",
+ "MoonrakerInitComponent": "初始化 Moonraker:{component}",
"MoonrakerWarning": "Moonraker警告",
"UnparsedConfigOption": "在[{section}]标签中检测到无法解析的配置'{option}: {value}'。这可能是一个不再可用的选项,也可能是组件加载失败的结果。在将来,这将导致启动错误。",
"UnparsedConfigSection": "检测到无法解析的标签[{section}]。这可能是一个不再可用的标签,也可能是组件加载失败的结果。在未来,这将导致启动错误。"
},
"Never": "永不",
"NextReboot": "下次重启",
- "NoNotification": "没有通知",
+ "NoNotification": "没有可用的通知",
"Notifications": "通知",
"Remind": "提醒:"
},
@@ -36,6 +36,9 @@
"NoEmptyAllowedError": "输入值不能为空!"
},
"Printers": "打印机列表",
+ "TextfieldWithCopy": {
+ "Copied": "复制"
+ },
"TheServiceWorker": {
"DescriptionNeedUpdate": "本地缓存已过时,需要进行更新。请点击下面的按钮来更新缓存。",
"TitleNeedUpdate": "PWA需要更新",
@@ -63,7 +66,7 @@
"Complete": "已完成 - {filename}",
"Error": "错误",
"Pause": "暂停打印",
- "PrinterOff": "打印机已关闭",
+ "PrinterOff": "打印机关闭",
"Printing": "已打印 {percent}% - {filename}",
"PrintingETA": "已打印 {percent}% - 预估: {eta} - {filename}"
},
@@ -120,13 +123,13 @@
}
},
"BedScrews": {
- "Abort": "取消",
+ "Abort": "中止",
"Accept": "接受",
"Adjusted": "已调整",
- "Description": "如果当前螺丝已调整,请点击已调整。未调整下继续请点击认可。",
+ "Description": "如果当前螺丝已调整,请点击已调整。如果不需要调整,请点击接受继续。",
"Headline": "热床调平螺丝",
"ScrewAccepted": "已接受的调平螺丝",
- "ScrewIndex": "螺丝索引",
+ "ScrewIndex": "螺丝编号",
"ScrewName": "螺丝名称",
"ScrewOutput": "{current}/{max}"
},
@@ -146,6 +149,20 @@
"SendCode": "输入要执行的代码...",
"SetupConsole": "设置控制台"
},
+ "DevicesDialog": {
+ "CanBusInfo": "只有未分配的节点才能被检测到。建议仅连接一个未分配的设备到CAN总线,以避免通信问题。更多详情,请点击链接:",
+ "ClickRefresh": "点击刷新按钮以搜索设备。",
+ "DevicePath": "设备路径",
+ "Formats": "格式",
+ "Headline": "设备",
+ "HideSystemEntries": "隐藏系统条目",
+ "LibcameraId": "Libcamera标识符",
+ "NoDeviceFound": "未发现设备。请检查连接并点击刷新按钮。",
+ "PathByHardware": "硬件路径",
+ "PathById": "标识符路径",
+ "Refresh": "刷新",
+ "Resolutions": "分辨率"
+ },
"Dialogs": {
"StartPrint": {
"Cancel": "取消",
@@ -153,11 +170,12 @@
"DoYouWantToStartFilenameFilament": "是否使用以下耗材开始打印文件{filename}?",
"Headline": "开始任务",
"Print": "打印",
- "Timelapse": "延时摄像"
+ "Timelapse": "延时摄影"
}
},
"Editor": {
"ConfigReference": "配置参考",
+ "DeviceDialog": "设备",
"DontSave": "不保存",
"Downloading": "正在下载",
"FailedSave": "上传{filename}失败!",
@@ -274,10 +292,10 @@
"Tracking": "正在追踪",
"Transparency": "透明度",
"Ultra": "极致",
- "VoxelMode": "体素模式(ASMBL)"
+ "VoxelMode": "三维像素模式(ASMBL)"
},
"Heightmap": {
- "Abort": "取消",
+ "Abort": "中止",
"BedMeshCalibrate": "校准床网",
"BedMeshRemove": "移除床网",
"Calibrate": "测量",
@@ -298,20 +316,16 @@
"InvalidNameAlreadyExists": "预设名称已经存在,请换一个名称。",
"InvalidNameAscii": "错误的名称,请删除所有非ascii字符后重试。",
"InvalidNameEmpty": "输入值不能为空!",
- "InvalidNameReserved": "预设 'default' 已保留,请选择其他预设名称。",
- "Later": "以后",
+ "InvalidNameReserved": "预设'default'已保留,请选择其他预设名称。",
"Mesh": "网格",
"Name": "名称",
"NoBedMeshHasBeenLoadedYet": "没有加载床网。",
"NoProfile": "没有预设可用",
- "Ok": "好的",
"Probed": "测量结果",
"Profiles": "预设",
"Remove": "删除",
- "RemoveSaveDescription": "预设床网已标记需删除。点击\"保存配置\"将会从printer.cfg中移除并重启Klipper。",
"Rename": "重命名",
"RenameBedMeshProfile": "重命名预设床网",
- "SAVE_CONFIG": "保存配置",
"ScaleGradient": "尺度梯度",
"ScaleZMax": "Z最大标度",
"TitleCalibrate": "校准新床网",
@@ -321,6 +335,7 @@
},
"History": {
"AddNote": "添加便条",
+ "AddToQueueSuccessful": "文件{filename}已添加到队列。",
"AllJobs": "全部",
"AvgPrinttime": "平均打印时长",
"Cancel": "取消",
@@ -483,7 +498,7 @@
}
},
"UpdatePanel": {
- "Abort": "放弃",
+ "Abort": "中止",
"AreYouSure": "你确定吗?",
"CheckForUpdates": "检查更新",
"Close": "关闭",
@@ -528,7 +543,7 @@
}
},
"ManualProbe": {
- "Abort": "取消",
+ "Abort": "中止",
"Accept": "接受",
"Advanced": "高级",
"Headline": "手动Probe"
@@ -680,7 +695,7 @@
"Max": "最高",
"ObjectHeight": "物体高度",
"PauseAtLayer": {
- "Abort": "取消",
+ "Abort": "中止",
"Accept": "接受",
"AtLayer": "所在层",
"Call": "调用",
@@ -776,8 +791,8 @@
"Router": {
"Console": "控制台",
"Dashboard": "控制面板",
- "G-Code Files": "G-Code 文件",
- "G-Code Viewer": "G-Code 预览",
+ "G-Code Files": "G-Code文件",
+ "G-Code Viewer": "G-Code预览",
"Heightmap": "高度图",
"History": "历史记录",
"Machine": "机器",
@@ -794,8 +809,8 @@
},
"SelectPrinterDialog": {
"AddPrinter": "添加打印机",
- "AddPrintersToJson": "请将打印机添加到 config.json 。",
- "CannotConnectTo": "无法连接到{host} 。",
+ "AddPrintersToJson": "请将打印机添加到config.json。",
+ "CannotConnectTo": "无法连接到{host}。",
"ChangePrinter": "切换打印机",
"Connecting": "正在连接到{host}",
"ConnectionFailed": "连接失败",
@@ -891,7 +906,7 @@
"GCodeViewerTab": {
"BackgroundColor": "背景颜色",
"ExtruderColor": "喷嘴颜色",
- "GCodeViewer": "G-Code 预览",
+ "GCodeViewer": "G-Code预览",
"GridColor": "网格颜色",
"MaxFeed": "最大挤出速度",
"MinFeed": "最小挤出速度",
@@ -1040,7 +1055,7 @@
"Port": "Moonraker服务端口",
"RemotePrinters": "打印机",
"UpdatePrinter": "更新打印机",
- "UseConfigJson": "检测到InstanceDB参数为JSON。请使用 config.json 来修改打印机列表。"
+ "UseConfigJson": "检测到InstanceDB参数为JSON。请使用config.json来修改打印机列表。"
},
"Store": "存储",
"TimelapseTab": {
@@ -1064,7 +1079,7 @@
"HyperlapseCycle": "Hyperlapse周期时间",
"HyperlapseCycleDescription": "每隔X秒获取一张快照",
"Mode": "模式",
- "ModeDescription": "选择Layermacro(基于层变化)或者Hyperlapse(基于时间)模式",
+ "ModeDescription": "选择Layermacro模式(基于层变化)或者Hyperlapse模式(基于时间)",
"OutputFramerate": "输出帧率",
"OutputFramerateDescription": "设置视频帧率。注意:如果可变FPS设置开启,此设置会被忽略",
"Parkhead": "放置打印头到停靠位置",
diff --git a/src/pages/History.vue b/src/pages/History.vue
index 84ef0fd35..4a304f4c4 100644
--- a/src/pages/History.vue
+++ b/src/pages/History.vue
@@ -17,11 +17,9 @@ import { Component, Mixins } from 'vue-property-decorator'
import BaseMixin from '@/components/mixins/base'
import HistoryListPanel from '@/components/panels/HistoryListPanel.vue'
import HistoryStatisticsPanel from '@/components/panels/HistoryStatisticsPanel.vue'
+
@Component({
- components: {
- HistoryStatisticsPanel,
- HistoryListPanel,
- },
+ components: { HistoryListPanel, HistoryStatisticsPanel },
})
export default class PageHistory extends Mixins(BaseMixin) {}
diff --git a/src/plugins/helpers.ts b/src/plugins/helpers.ts
index 8cbd54a94..719408ce8 100644
--- a/src/plugins/helpers.ts
+++ b/src/plugins/helpers.ts
@@ -112,30 +112,30 @@ export const formatFrequency = (frequency: number): string => {
return Math.max(frequency, 0.1).toFixed() + units[i]
}
-export const formatPrintTime = (totalSeconds: number): string => {
- if (totalSeconds) {
- let output = ''
+export const formatPrintTime = (totalSeconds: number, boolDays = true): string => {
+ if (!totalSeconds) return '--'
+ const output: string[] = []
+
+ if (boolDays) {
const days = Math.floor(totalSeconds / (3600 * 24))
if (days) {
totalSeconds %= 3600 * 24
- output += days + 'd'
+ output.push(`${days}d`)
}
+ }
- const hours = Math.floor(totalSeconds / 3600)
- totalSeconds %= 3600
- if (hours) output += ' ' + hours + 'h'
-
- const minutes = Math.floor(totalSeconds / 60)
- if (minutes) output += ' ' + minutes + 'm'
+ const hours = Math.floor(totalSeconds / 3600)
+ totalSeconds %= 3600
+ if (hours) output.push(`${hours}h`)
- const seconds = totalSeconds % 60
- if (seconds) output += ' ' + seconds.toFixed(0) + 's'
+ const minutes = Math.floor(totalSeconds / 60)
+ if (minutes) output.push(`${minutes}m`)
- return output
- }
+ const seconds = totalSeconds % 60
+ if (seconds) output.push(`${seconds.toFixed(0)}s`)
- return '--'
+ return output.join(' ')
}
export const sortFiles = (items: FileStateFile[] | null, sortBy: string[], sortDesc: boolean[]): FileStateFile[] => {
diff --git a/src/routes/index.ts b/src/routes/index.ts
index 6964df89b..94117ff3c 100644
--- a/src/routes/index.ts
+++ b/src/routes/index.ts
@@ -22,6 +22,7 @@ import {
const routes: AppRoute[] = [
{
+ name: 'dashboard',
title: 'Dashboard',
path: '/',
icon: mdiMonitorDashboard,
@@ -31,6 +32,7 @@ const routes: AppRoute[] = [
position: 10,
},
{
+ name: 'farm',
title: 'Printers',
path: '/allPrinters',
component: Farm,
@@ -38,6 +40,7 @@ const routes: AppRoute[] = [
showInNavi: false,
},
{
+ name: 'webcam',
title: 'Webcam',
path: '/cam',
icon: mdiWebcam,
@@ -45,8 +48,10 @@ const routes: AppRoute[] = [
alwaysShow: true,
showInNavi: true,
position: 20,
+ fullscreen: true,
},
{
+ name: 'console',
title: 'Console',
path: '/console',
icon: mdiConsoleLine,
@@ -57,6 +62,7 @@ const routes: AppRoute[] = [
position: 30,
},
{
+ name: 'heightmap',
title: 'Heightmap',
path: '/heightmap',
icon: mdiGrid,
@@ -67,6 +73,7 @@ const routes: AppRoute[] = [
position: 40,
},
{
+ name: 'gcodefiles',
title: 'G-Code Files',
path: '/files',
icon: mdiFileDocumentMultipleOutline,
@@ -75,8 +82,10 @@ const routes: AppRoute[] = [
showInNavi: true,
registeredDirectory: 'gcodes',
position: 50,
+ fullscreen: true,
},
{
+ name: 'gcodeviewer',
title: 'G-Code Viewer',
path: '/viewer',
icon: mdiVideo3d,
@@ -84,8 +93,10 @@ const routes: AppRoute[] = [
alwaysShow: true,
showInNavi: true,
position: 60,
+ fullscreen: true,
},
{
+ name: 'history',
title: 'History',
path: '/history',
icon: mdiHistory,
@@ -96,6 +107,7 @@ const routes: AppRoute[] = [
position: 70,
},
{
+ name: 'timelapse',
title: 'Timelapse',
path: '/timelapse',
icon: mdiTimelapse,
@@ -106,6 +118,7 @@ const routes: AppRoute[] = [
position: 80,
},
{
+ name: 'machine',
title: 'Machine',
path: '/config',
icon: mdiWrench,
@@ -127,6 +140,7 @@ const routes: AppRoute[] = [
export default routes
export interface AppRoute {
+ name?: string
title: string | null
path: string
redirect?: string
@@ -140,4 +154,5 @@ export interface AppRoute {
klipperIsConnected?: boolean
children?: AppRoute[]
position?: number
+ fullscreen?: boolean
}
diff --git a/src/store/gui/index.ts b/src/store/gui/index.ts
index 1e192c60f..8c23eedc4 100644
--- a/src/store/gui/index.ts
+++ b/src/store/gui/index.ts
@@ -14,6 +14,7 @@ import { navigation } from '@/store/gui/navigation'
import { notifications } from '@/store/gui/notifications'
import { presets } from '@/store/gui/presets'
import { remoteprinters } from '@/store/gui/remoteprinters'
+import { maintenance } from '@/store/gui/maintenance'
import { webcams } from '@/store/gui/webcams'
import { heightmap } from '@/store/gui/heightmap'
@@ -156,6 +157,7 @@ export const getDefaultState = (): GuiState => {
lockSlidersOnTouchDevices: true,
lockSlidersDelay: 1.5,
confirmOnEmergencyStop: false,
+ confirmOnCoolDown: false,
confirmOnPowerDeviceChange: false,
boolBigThumbnail: true,
bigThumbnailBackground: defaultBigThumbnailBackground,
@@ -241,6 +243,8 @@ export const getDefaultState = (): GuiState => {
'object_height',
],
selectedJobs: [],
+ showMaintenanceEntries: true,
+ showPrintJobs: true,
},
jobqueue: {
countPerPage: 10,
@@ -292,6 +296,7 @@ export const gui: Module = {
console,
gcodehistory,
macros,
+ maintenance,
miscellaneous,
navigation,
notifications,
diff --git a/src/store/gui/maintenance/actions.ts b/src/store/gui/maintenance/actions.ts
new file mode 100644
index 000000000..d8b96854a
--- /dev/null
+++ b/src/store/gui/maintenance/actions.ts
@@ -0,0 +1,99 @@
+import Vue from 'vue'
+import { ActionTree } from 'vuex'
+import { GuiMaintenanceState } from '@/store/gui/maintenance/types'
+import { RootState } from '@/store/types'
+import { v4 as uuidv4 } from 'uuid'
+
+export const actions: ActionTree = {
+ reset({ commit }) {
+ commit('reset')
+ },
+
+ init() {
+ Vue.$socket.emit(
+ 'server.database.get_item',
+ { namespace: 'maintenance' },
+ { action: 'gui/maintenance/initStore' }
+ )
+ },
+
+ async initStore({ commit, dispatch }, payload) {
+ await commit('reset')
+ await commit('initStore', payload)
+ await dispatch('socket/removeInitModule', 'gui/maintenance/init', { root: true })
+ },
+
+ upload(_, payload) {
+ Vue.$socket.emit('server.database.post_item', {
+ namespace: 'maintenance',
+ key: payload.id,
+ value: payload.value,
+ })
+ },
+
+ store({ commit, dispatch, state }, payload) {
+ const id = uuidv4()
+
+ commit('store', { id, values: payload.entry })
+ dispatch('upload', {
+ id,
+ value: state.entries[id],
+ })
+ },
+
+ update({ commit, dispatch }, payload) {
+ const id = payload.id
+ delete payload.id
+
+ commit('update', {
+ id: id,
+ entry: payload,
+ })
+ dispatch('upload', {
+ id: id,
+ value: payload,
+ })
+ },
+
+ delete({ commit }, payload) {
+ commit('delete', payload)
+ Vue.$socket.emit('server.database.delete_item', { namespace: 'maintenance', key: payload })
+ },
+
+ perform({ dispatch, state, rootState }, payload: { id: string; note: string }) {
+ const entry = state.entries[payload.id]
+ if (!entry) return
+
+ const totalFilament = rootState.server?.history?.job_totals?.total_filament_used ?? 0
+ const totalPrintTime = rootState.server?.history?.job_totals?.total_print_time ?? 0
+
+ entry.id = payload.id
+ entry.end_time = Date.now() / 1000
+ entry.end_filament = totalFilament
+ entry.end_printtime = totalPrintTime
+ entry.perform_note = payload.note.trim() || null
+
+ dispatch('update', entry)
+
+ if (entry.reminder.type === 'repeat') {
+ const date = new Date()
+
+ dispatch('store', {
+ entry: {
+ name: entry.name,
+ note: entry.note,
+ // divided by 1000 to get seconds, because history entries are also in seconds
+ start_time: date.getTime() / 1000,
+ end_time: null,
+ start_filament: totalFilament,
+ end_filament: null,
+ start_printtime: totalPrintTime,
+ end_printtime: null,
+ last_entry: payload.id,
+
+ reminder: { ...entry.reminder },
+ },
+ })
+ }
+ },
+}
diff --git a/src/store/gui/maintenance/getters.ts b/src/store/gui/maintenance/getters.ts
new file mode 100644
index 000000000..17f64b6c2
--- /dev/null
+++ b/src/store/gui/maintenance/getters.ts
@@ -0,0 +1,47 @@
+import { GetterTree } from 'vuex'
+import { GuiMaintenanceState, GuiMaintenanceStateEntry } from '@/store/gui/maintenance/types'
+
+// eslint-disable-next-line
+export const getters: GetterTree = {
+ getEntries: (state) => {
+ const entries: GuiMaintenanceStateEntry[] = []
+
+ Object.keys(state.entries).forEach((id: string) => {
+ entries.push({ ...state.entries[id], id })
+ })
+
+ return entries
+ },
+
+ getOverdueEntries: (state, getters, rootState) => {
+ const currentTotalPrintTime = rootState.server.history.job_totals.total_print_time ?? 0
+ const currentTotalFilamentUsed = rootState.server.history.job_totals.total_filament_used ?? 0
+ const currentDate = new Date().getTime() / 1000
+
+ const entries: GuiMaintenanceStateEntry[] = getters['getEntries'] ?? []
+
+ return entries.filter((entry) => {
+ if (entry.reminder.type === null || entry.end_time !== null) return false
+
+ if (entry.reminder.filament.bool) {
+ const end = entry.start_filament + (entry.reminder.filament.value ?? 0)
+
+ if (end <= currentTotalFilamentUsed) return true
+ }
+
+ if (entry.reminder.printtime.bool) {
+ const end = entry.start_printtime + (entry.reminder.printtime.value ?? 0)
+
+ if (end <= currentTotalPrintTime) return true
+ }
+
+ if (entry.reminder.date.bool) {
+ const end = entry.start_time + (entry.reminder.date.value ?? 0) * 24 * 60 * 60
+
+ if (end <= currentDate) return true
+ }
+
+ return false
+ })
+ },
+}
diff --git a/src/store/gui/maintenance/index.ts b/src/store/gui/maintenance/index.ts
new file mode 100644
index 000000000..fdfcc39b3
--- /dev/null
+++ b/src/store/gui/maintenance/index.ts
@@ -0,0 +1,23 @@
+import { Module } from 'vuex'
+import { GuiMaintenanceState } from '@/store/gui/maintenance/types'
+import { actions } from '@/store/gui/maintenance/actions'
+import { mutations } from '@/store/gui/maintenance/mutations'
+import { getters } from '@/store/gui/maintenance/getters'
+
+export const getDefaultState = (): GuiMaintenanceState => {
+ return {
+ entries: {},
+ }
+}
+
+// initial state
+const state = getDefaultState()
+
+// eslint-disable-next-line
+export const maintenance: Module = {
+ namespaced: true,
+ state,
+ getters,
+ actions,
+ mutations,
+}
diff --git a/src/store/gui/maintenance/mutations.ts b/src/store/gui/maintenance/mutations.ts
new file mode 100644
index 000000000..7f9dee174
--- /dev/null
+++ b/src/store/gui/maintenance/mutations.ts
@@ -0,0 +1,32 @@
+import Vue from 'vue'
+import { MutationTree } from 'vuex'
+import { GuiMaintenanceState } from '@/store/gui/maintenance/types'
+import { getDefaultState } from './index'
+
+export const mutations: MutationTree = {
+ reset(state) {
+ Object.assign(state, getDefaultState())
+ },
+
+ initStore(state, payload) {
+ Vue.set(state, 'entries', payload.value)
+ },
+
+ store(state, payload) {
+ Vue.set(state.entries, payload.id, payload.values)
+ },
+
+ update(state, payload) {
+ if (!(payload.id in state.entries)) return
+
+ const entry = { ...state.entries[payload.id] }
+ Object.assign(entry, payload.entry)
+ Vue.set(state.entries, payload.id, entry)
+ },
+
+ delete(state, payload) {
+ if (payload in state.entries) {
+ Vue.delete(state.entries, payload)
+ }
+ },
+}
diff --git a/src/store/gui/maintenance/types.ts b/src/store/gui/maintenance/types.ts
new file mode 100644
index 000000000..d01609d25
--- /dev/null
+++ b/src/store/gui/maintenance/types.ts
@@ -0,0 +1,38 @@
+export interface GuiMaintenanceState {
+ entries: {
+ [key: string]: GuiMaintenanceStateEntry
+ }
+}
+
+export interface GuiMaintenanceStateEntry {
+ id?: string
+ name: string
+ note: string
+ perform_note: string | null
+ start_time: number
+ end_time: number | null
+ start_filament: number
+ end_filament: number | null
+ start_printtime: number
+ end_printtime: number | null
+ last_entry: string | null
+
+ reminder: {
+ type: null | 'one-time' | 'repeat'
+
+ filament: {
+ bool: boolean
+ value: number | null
+ }
+
+ printtime: {
+ bool: boolean
+ value: number | null
+ }
+
+ date: {
+ bool: boolean
+ value: number | null
+ }
+ }
+}
diff --git a/src/store/gui/notifications/getters.ts b/src/store/gui/notifications/getters.ts
index 2384c1d90..6b612af47 100644
--- a/src/store/gui/notifications/getters.ts
+++ b/src/store/gui/notifications/getters.ts
@@ -8,6 +8,7 @@ import { PrinterStateKlipperConfigWarning } from '@/store/printer/types'
import { detect } from 'detect-browser'
import semver from 'semver'
import { minBrowserVersions } from '@/store/variables'
+import { GuiMaintenanceStateEntry } from '@/store/gui/maintenance/types'
export const getters: GetterTree = {
getNotifications: (state, getters) => {
@@ -34,6 +35,9 @@ export const getters: GetterTree = {
// klipper warnings
notifications = notifications.concat(getters['getNotificationsKlipperWarnings'])
+ // user-created reminders
+ notifications = notifications.concat(getters['getNotificationsOverdueMaintenance'])
+
// browser warnings
notifications = notifications.concat(getters['getNotificationsBrowserWarnings'])
@@ -361,6 +365,37 @@ export const getters: GetterTree = {
return notifications
},
+ getNotificationsOverdueMaintenance: (state, getters, rootState, rootGetters) => {
+ const notifications: GuiNotificationStateEntry[] = []
+ let entries: GuiMaintenanceStateEntry[] = rootGetters['gui/maintenance/getOverdueEntries']
+ if (entries.length == 0) return []
+
+ const date = rootState.server.system_boot_at ?? new Date()
+
+ // get all dismissed reminders and convert it to a string[]
+ const remindersDismisses = rootGetters['gui/notifications/getDismissByCategory']('maintenance').map(
+ (dismiss: GuiNotificationStateDismissEntry) => {
+ return dismiss.id
+ }
+ )
+
+ // filter all dismissed reminders
+ entries = entries.filter((entry) => !remindersDismisses.includes(entry.id))
+
+ entries.forEach((entry) => {
+ notifications.push({
+ id: `maintenance/${entry.id}`,
+ priority: 'high',
+ title: i18n.t('App.Notifications.MaintenanceReminder').toString(),
+ description: i18n.t('App.Notifications.MaintenanceReminderText', { name: entry.name }).toString(),
+ date,
+ dismissed: false,
+ })
+ })
+
+ return notifications
+ },
+
getDismiss: (state, getters, rootState) => {
const currentTime = new Date()
const systemBootAt = rootState.server.system_boot_at ?? new Date()
diff --git a/src/store/gui/reminders/actions.ts b/src/store/gui/reminders/actions.ts
new file mode 100644
index 000000000..2dc074945
--- /dev/null
+++ b/src/store/gui/reminders/actions.ts
@@ -0,0 +1,60 @@
+import Vue from 'vue'
+import { ActionTree } from 'vuex'
+import { GuiRemindersState } from '@/store/gui/reminders/types'
+import { RootState } from '@/store/types'
+import { v4 as uuidv4 } from 'uuid'
+
+export const actions: ActionTree = {
+ reset({ commit }) {
+ commit('reset')
+ },
+
+ init() {
+ Vue.$socket.emit('server.database.get_item', { namespace: 'reminders' }, { action: 'gui/reminders/initStore' })
+ },
+
+ async initStore({ commit, dispatch }, payload) {
+ await commit('reset')
+ await commit('initStore', payload)
+ await dispatch('socket/removeInitModule', 'gui/reminders/init', { root: true })
+ },
+
+ upload(_, payload) {
+ Vue.$socket.emit('server.database.post_item', { namespace: 'reminders', key: payload.id, value: payload.value })
+ },
+
+ store({ commit, dispatch, state }, payload) {
+ const id = uuidv4()
+
+ commit('store', { id, values: payload.values })
+ dispatch('upload', {
+ id,
+ value: state.reminders[id],
+ })
+ },
+
+ update({ commit, dispatch, state }, payload) {
+ commit('update', payload)
+ dispatch('upload', {
+ id: payload.id,
+ value: state.reminders[payload.id],
+ })
+ },
+
+ delete({ commit }, payload) {
+ commit('delete', payload)
+ Vue.$socket.emit('server.database.delete_item', { namespace: 'reminders', key: payload })
+ },
+
+ repeat({ dispatch, getters, state, rootState }, payload) {
+ if (!(payload.id in state.reminders)) return
+ const reminder = getters['getReminder'](payload.id)
+ const new_start_time = rootState.server?.history?.job_totals.total_print_time || 0
+ const snooze_epoch_time = Date.now()
+ dispatch('update', {
+ id: reminder.id,
+ snooze_print_hours_timestamps: [...reminder.snooze_print_hours_timestamps, new_start_time],
+ snooze_epoch_timestamps: [...reminder.snooze_epoch_timestamps, snooze_epoch_time],
+ })
+ },
+}
diff --git a/src/store/gui/reminders/getters.ts b/src/store/gui/reminders/getters.ts
new file mode 100644
index 000000000..40599e430
--- /dev/null
+++ b/src/store/gui/reminders/getters.ts
@@ -0,0 +1,29 @@
+import { GetterTree } from 'vuex'
+import { GuiRemindersState, GuiRemindersStateReminder } from '@/store/gui/reminders/types'
+
+// eslint-disable-next-line
+export const getters: GetterTree = {
+ getReminders: (state) => {
+ const reminders: GuiRemindersStateReminder[] = []
+
+ Object.keys(state.reminders).forEach((id: string) => {
+ reminders.push({ ...state.reminders[id], id })
+ })
+
+ return reminders
+ },
+
+ getReminder: (state, getters) => (id: string) => {
+ const reminders = getters['getReminders'] ?? []
+
+ return reminders.find((reminder: GuiRemindersStateReminder) => reminder.id === id)
+ },
+
+ getOverdueReminders: (state, getters, rootState) => {
+ const currentTotalPrintTime = rootState.server.history.job_totals.total_print_time
+ const reminders: GuiRemindersStateReminder[] = getters['getReminders'] ?? []
+ return reminders.filter(
+ (reminder) => reminder.time_delta - (currentTotalPrintTime - reminder.start_total_print_time) < 0
+ )
+ },
+}
diff --git a/src/store/gui/reminders/index.ts b/src/store/gui/reminders/index.ts
new file mode 100644
index 000000000..227263eba
--- /dev/null
+++ b/src/store/gui/reminders/index.ts
@@ -0,0 +1,23 @@
+import { Module } from 'vuex'
+import { GuiRemindersState } from '@/store/gui/reminders/types'
+import { actions } from '@/store/gui/reminders/actions'
+import { mutations } from '@/store/gui/reminders/mutations'
+import { getters } from '@/store/gui/reminders/getters'
+
+export const getDefaultState = (): GuiRemindersState => {
+ return {
+ reminders: {},
+ }
+}
+
+// initial state
+const state = getDefaultState()
+
+// eslint-disable-next-line
+export const reminders: Module = {
+ namespaced: true,
+ state,
+ getters,
+ actions,
+ mutations,
+}
diff --git a/src/store/gui/reminders/mutations.ts b/src/store/gui/reminders/mutations.ts
new file mode 100644
index 000000000..992f2bdf5
--- /dev/null
+++ b/src/store/gui/reminders/mutations.ts
@@ -0,0 +1,32 @@
+import Vue from 'vue'
+import { MutationTree } from 'vuex'
+import { GuiRemindersState } from '@/store/gui/reminders/types'
+import { getDefaultState } from './index'
+
+export const mutations: MutationTree = {
+ reset(state) {
+ Object.assign(state, getDefaultState())
+ },
+
+ initStore(state, payload) {
+ Vue.set(state, 'reminders', payload.value)
+ },
+
+ store(state, payload) {
+ Vue.set(state.reminders, payload.id, payload.values)
+ },
+
+ update(state, payload) {
+ if (payload.id in state.reminders) {
+ const reminder = { ...state.reminders[payload.id] }
+ Object.assign(reminder, payload)
+ Vue.set(state.reminders, payload.id, reminder)
+ }
+ },
+
+ delete(state, payload) {
+ if (payload in state.reminders) {
+ Vue.delete(state.reminders, payload)
+ }
+ },
+}
diff --git a/src/store/gui/reminders/types.ts b/src/store/gui/reminders/types.ts
new file mode 100644
index 000000000..7edc70ca8
--- /dev/null
+++ b/src/store/gui/reminders/types.ts
@@ -0,0 +1,16 @@
+export interface GuiRemindersState {
+ reminders: {
+ [key: string]: GuiRemindersStateReminder
+ }
+}
+
+export interface GuiRemindersStateReminder {
+ id: string
+ name: string
+ start_total_print_time: number
+ time_delta: number
+ repeating: boolean
+ snooze_print_hours_timestamps: number[]
+ snooze_epoch_timestamps: number[]
+ remaining_print_time?: number
+}
diff --git a/src/store/gui/types.ts b/src/store/gui/types.ts
index 930961603..7920ecf21 100644
--- a/src/store/gui/types.ts
+++ b/src/store/gui/types.ts
@@ -105,6 +105,7 @@ export interface GuiState {
lockSlidersOnTouchDevices: boolean
lockSlidersDelay: number
confirmOnEmergencyStop: boolean
+ confirmOnCoolDown: boolean
confirmOnPowerDeviceChange: boolean
boolBigThumbnail: boolean
bigThumbnailBackground: string
@@ -166,6 +167,8 @@ export interface GuiState {
hidePrintStatus: string[]
hideColums: string[]
selectedJobs: ServerHistoryStateJob[]
+ showMaintenanceEntries: boolean
+ showPrintJobs: boolean
}
jobqueue: {
countPerPage: number
diff --git a/src/store/printer/getters.ts b/src/store/printer/getters.ts
index 24d5d71d8..225b06bee 100644
--- a/src/store/printer/getters.ts
+++ b/src/store/printer/getters.ts
@@ -289,7 +289,15 @@ export const getters: GetterTree = {
getMiscellaneous: (state) => {
const output: PrinterStateMiscellaneous[] = []
- const supportedObjects = ['controller_fan', 'heater_fan', 'fan_generic', 'fan', 'output_pin']
+ const supportedObjects = [
+ 'controller_fan',
+ 'heater_fan',
+ 'fan_generic',
+ 'fan',
+ 'output_pin',
+ 'pwm_tool',
+ 'pwm_cycle_time',
+ ]
const controllableFans = ['fan_generic', 'fan']
@@ -308,10 +316,11 @@ export const getters: GetterTree = {
if (nameSplit[0].toLowerCase() === 'fan') scale = 255
- if (nameSplit[0].toLowerCase() === 'output_pin') {
+ if (['output_pin', 'pwm_tool', 'pwm_cycle_time'].includes(nameSplit[0])) {
controllable = true
pwm = false
if ('pwm' in settings) pwm = settings?.pwm ?? false
+ if (['pwm_tool', 'pwm_cycle_time'].includes(nameSplit[0])) pwm = true
if ('scale' in settings) scale = settings?.scale ?? 1
}
diff --git a/src/store/server/actions.ts b/src/store/server/actions.ts
index 93aeb4ce3..6aa39ce15 100644
--- a/src/store/server/actions.ts
+++ b/src/store/server/actions.ts
@@ -63,6 +63,10 @@ export const actions: ActionTree = {
dispatch('socket/addInitModule', 'gui/webcam/init', { root: true })
dispatch('gui/webcams/init', null, { root: true })
}
+ if (payload.namespaces?.includes('maintenance')) {
+ dispatch('socket/addInitModule', 'gui/maintenance/init', { root: true })
+ dispatch('gui/maintenance/init', null, { root: true })
+ }
commit('saveDbNamespaces', payload.namespaces)
diff --git a/src/store/server/history/getters.ts b/src/store/server/history/getters.ts
index 593ab593e..836f6bc76 100644
--- a/src/store/server/history/getters.ts
+++ b/src/store/server/history/getters.ts
@@ -292,7 +292,7 @@ export const getters: GetterTree = {
// find jobs via metadata
const jobs = state.jobs.filter((job) => {
- return job.metadata?.size === filesize && Math.round(job.metadata?.modified * 1000) === modified
+ return job.metadata?.size === filesize && Math.round((job.metadata?.modified ?? 0) * 1000) === modified
})
if (jobs.length) return jobs
if (job_id) return jobs.filter((job) => job.job_id === job_id)
@@ -303,7 +303,7 @@ export const getters: GetterTree = {
getPrintStatusByFilename: (state) => (filename: string, modified: number) => {
if (state.jobs.length) {
const job = state.jobs.find((job) => {
- return job.filename === filename && Math.round(job.metadata?.modified * 1000) === modified
+ return job.filename === filename && Math.round((job.metadata?.modified ?? 0) * 1000) === modified
})
return job?.status ?? ''
@@ -354,7 +354,7 @@ export const getters: GetterTree = {
}
},
- getFilterdJobList: (state, getters, rootState) => {
+ getFilteredJobList: (state, getters, rootState) => {
const hideStatus = rootState.gui.view.history.hidePrintStatus
return state.jobs.filter((job: ServerHistoryStateJob) => {
diff --git a/src/store/server/history/types.ts b/src/store/server/history/types.ts
index 90b5ce7e8..d28e23a22 100644
--- a/src/store/server/history/types.ts
+++ b/src/store/server/history/types.ts
@@ -1,3 +1,5 @@
+import { FileStateFileThumbnail } from '@/store/files/types'
+
export interface ServerHistoryState {
jobs: ServerHistoryStateJob[]
job_totals: {
@@ -18,7 +20,31 @@ export interface ServerHistoryStateJob {
filament_used: number
filename: string
// eslint-disable-next-line
- metadata: any
+ metadata: {
+ print_start_time?: number
+ job_id?: number
+ size?: number
+ slicer?: string
+ slicer_version?: string
+ layer_count?: number
+ layer_height?: number
+ first_layer_height?: number
+ object_height?: number
+ filament_total?: number
+ filament_weight_total?: number
+ estimated_time?: number
+ thumbnails?: FileStateFileThumbnail[]
+ first_layer_bed_temp?: number
+ first_layer_extr_temp?: number
+ gcode_start_byte?: number
+ gcode_end_byte?: number
+ filename?: string
+ filesize?: number
+ modified?: number
+ uuid?: string
+ nozzle_diameter?: number
+ [key: string]: any
+ }
note?: string
print_duration: number
status: string