diff --git a/package-lock.json b/package-lock.json index 2ed0ad947..577ed9390 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "duetwebcontrol", - "version": "2.0.0", + "version": "2.0.1", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -11439,6 +11439,11 @@ "safe-buffer": "^5.0.1" } }, + "turbo-crc32": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/turbo-crc32/-/turbo-crc32-1.0.1.tgz", + "integrity": "sha512-8yyRd1ZdNp+AQLGqi3lTaA2k81JjlIZOyFQEsi7GQWBgirnQOxjqVtDEbYHM2Z4yFdJ5AQw0fxBLLnDCl6RXoQ==" + }, "tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", diff --git a/package.json b/package.json index 25a05c4a5..bb118be0b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "duetwebcontrol", - "version": "2.0.0", - "repository": "github:chrishamm/DuetWebControl#next", + "version": "2.0.1", + "repository": "github:chrishamm/DuetWebControl", "homepage": "https://forum.duet3d.com/category/27/duet-web-control", "license": "GPL-3.0", "author": "Christian Hammacher (https://chrishamm.io)", @@ -20,7 +20,8 @@ "piecon": "^0.5.0", "roboto-fontface": "*", "three": "^0.105.2", - "three-orbitcontrols": "^2.102.2", + "three-orbitcontrols": "2.102.2", + "turbo-crc32": "^1.0.1", "vue": "^2.6.10", "vue-i18n": "^8.14.1", "vue-router": "^3.1.3", diff --git a/src/App.vue b/src/App.vue index 3e4ac6c0e..f79796be7 100644 --- a/src/App.vue +++ b/src/App.vue @@ -44,6 +44,10 @@ input::-webkit-inner-spin-button { a:not(:hover) { text-decoration: none; } + +.v-item-group.theme--dark .v-btn__content { + color: #FFF !important; +} @@ -94,18 +96,30 @@ export default { }, data() { return { - innerValue: '' + innerValue: '', + valueChanged: false } }, methods: { ...mapActions('machine', ['upload']), - close() { + setInnerValue(value) { + if (value != this.innerValue) { + this.valueChanged = true; + this.innerValue = value; + } + }, + close(fileSaved) { + if (this.valueChanged && !fileSaved && !confirm(this.$t('dialog.fileEdit.confirmClose'))) { + return; + } + this.$emit('input', ''); this.$emit('update:shown', false); + this.$root.$emit('dialog-closing') }, async save() { const content = new Blob([this.innerValue]); - this.close(); + this.close(true); try { await this.upload({ filename: this.filename, content }); @@ -115,10 +129,11 @@ export default { } }, onBeforeLeave(e) { - // Cancel the event - e.preventDefault(); - // Chrome requires returnValue to be set - e.returnValue = ''; + if (this.valueChanged) { + // Cancel the event. Chrome also requires returnValue to be set + e.preventDefault(); + e.returnValue = ''; + } }, onTextareaTab(e) { const originalSelectionStart = e.target.selectionStart; @@ -131,18 +146,19 @@ export default { watch: { shown(to) { // Set textarea content + this.valueChanged = false; this.innerValue = this.value; - // Notify users that they may not have saved their changes yet if (to) { + // Notify users that they may not have saved their changes yet window.addEventListener('beforeunload', this.onBeforeLeave); + + // Auto-focus textarea + const textarea = this.$refs.textarea; + setTimeout(function() { textarea.focus(); }, 100); } else { window.removeEventListener('beforeunload', this.onBeforeLeave); } - - // Auto-focus textarea - const textarea = this.$refs.textarea; - setTimeout(function() { textarea.focus(); }, 100); } } } diff --git a/src/components/inputs/CodeInput.vue b/src/components/inputs/CodeInput.vue index 3bd3b627d..0b0225c5a 100644 --- a/src/components/inputs/CodeInput.vue +++ b/src/components/inputs/CodeInput.vue @@ -67,44 +67,50 @@ export default { this.send(); } }, + hasUnprecedentedParameters: (code) => !code || code.trim() === '' || /(M23|M30|M32|M36)[^0-9]/i.test(code), async send() { this.$refs.input.isMenuActive = false; // FIXME There must be a better solution than this const code = (this.code.constructor === String) ? this.code : this.code.value; if (code && code.trim() !== '' && !this.sendingCode) { - // Convert the input to upper-case and remove comments let codeToSend = '', inQuotes = false, inWhiteSpace = false; - for (let i = 0; i < code.length; i++) { - const char = code[i]; - if (inQuotes) { - if (i < code.length - 1 && char === '\\' && code[i + 1] === '"') { - codeToSend += '\\"'; - i++; + if (this.hasUnprecedentedParameters(code)) { + // Don't convert certain codes to upper-case + codeToSend = code.trim(); + } else { + // Convert code to upper-case and remove comments + for (let i = 0; i < code.length; i++) { + const char = code[i]; + if (inQuotes) { + if (i < code.length - 1 && char === '\\' && code[i + 1] === '"') { + codeToSend += '\\"'; + i++; + } else { + if (char === '"') { + inQuotes = false; + } + codeToSend += char; + } } else { if (char === '"') { - inQuotes = false; - } - codeToSend += char; - } - } else { - if (char === '"') { - // don't convert escaped strings - inQuotes = true; - } else if (char === ' ' || char === '\t') { - // remove duplicate white spaces - if (inWhiteSpace) { - continue; + // don't convert escaped strings + inQuotes = true; + } else if (char === ' ' || char === '\t') { + // remove duplicate white spaces + if (inWhiteSpace) { + continue; + } + inWhiteSpace = true; + } else if (char === ';' || char === '(') { + // stop when comments start + break; } - inWhiteSpace = true; - } else if (char === ';' || char === '(') { - // stop when comments start - break; + inWhiteSpace = false; + codeToSend += char.toUpperCase(); } - inWhiteSpace = false; - codeToSend += char.toUpperCase(); } + codeToSend = codeToSend.trim(); } - codeToSend = codeToSend.trim(); // Send the code and wait for completion this.sendingCode = true; diff --git a/src/components/inputs/ToolInput.vue b/src/components/inputs/ToolInput.vue index 3cfeae04a..dc65c1b2e 100644 --- a/src/components/inputs/ToolInput.vue +++ b/src/components/inputs/ToolInput.vue @@ -1,5 +1,11 @@ + + diff --git a/src/components/lists/BaseFileList.vue b/src/components/lists/BaseFileList.vue index 8ec96b35f..73e1d4828 100644 --- a/src/components/lists/BaseFileList.vue +++ b/src/components/lists/BaseFileList.vue @@ -20,7 +20,7 @@ th.checkbox { @@ -45,24 +45,24 @@ th.checkbox { diff --git a/src/components/lists/EventList.vue b/src/components/lists/EventList.vue index b353eba2c..bd04f0e19 100644 --- a/src/components/lists/EventList.vue +++ b/src/components/lists/EventList.vue @@ -14,13 +14,18 @@ td.title-cell { .message { white-space: pre-wrap; } + +th:last-child { + padding-right: 0 !important; + width: 1%; +} - - - - - clear_all {{ $t('list.eventLog.clear') }} - - - font_download {{ $t('list.eventLog.downloadText') }} - - - cloud_download {{ $t('list.eventLog.downloadCSV') }} - - - @@ -80,13 +92,6 @@ export default { }, data() { return { - contextMenu: { - shown: false, - touchTimer: undefined, - item: null, - x: 0, - y: 0 - }, headers: [ { text: () => i18n.t('list.eventLog.date'), @@ -139,30 +144,6 @@ export default { this.pagination.descending = false; } }, - onItemTouchStart(item, e) { - const that = this; - this.contextMenu.touchTimer = setTimeout(function() { - that.contextMenu.touchTimer = undefined; - that.onItemContextmenu(item, { clientX: e.targetTouches[0].clientX, clientY: e.targetTouches[0].clientY }); - }, 1000); - }, - onItemTouchEnd() { - if (this.contextMenu.touchTimer) { - clearTimeout(this.contextMenu.touchTimer); - this.contextMenu.touchTimer = undefined; - } - }, - onItemContextmenu(item, e) { - this.onItemTouchEnd(); - - this.contextMenu.shown = false; - this.contextMenu.item = item; - this.contextMenu.x = e.clientX; - this.contextMenu.y = e.clientY; - this.$nextTick(() => { - this.contextMenu.shown = true; - }); - }, downloadText() { let textContent = ''; this.events.forEach(function(e) { @@ -171,7 +152,7 @@ export default { textContent += `${e.date.toLocaleString()}: ${message ? (title + ": " + message) : title}\r\n`; }); - const file = new File([textContent], "console.txt", {type: "text/plain;charset=utf-8"}); + const file = new File([textContent], 'console.txt', { type: 'text/plain;charset=utf-8' }); saveAs(file); }, downloadCSV() { @@ -182,7 +163,7 @@ export default { csvContent += `"${e.date.toLocaleDateString()}","${e.date.toLocaleTimeString()}","${title}","${message}"\r\n`; }); - const file = new File([csvContent], "console.csv", {type: "text/csv;charset=utf-8"}); + const file = new File([csvContent], 'console.csv', { type: 'text/csv;charset=utf-8' }); saveAs(file); } }, diff --git a/src/components/lists/JobFileList.vue b/src/components/lists/JobFileList.vue index 093db3da7..5baee237a 100644 --- a/src/components/lists/JobFileList.vue +++ b/src/components/lists/JobFileList.vue @@ -50,11 +50,12 @@ import { mapState, mapGetters, mapActions, mapMutations } from 'vuex' import i18n from '../../i18n' -import { DisconnectedError } from '../../utils/errors.js' +import { DisconnectedError, InvalidPasswordError } from '../../utils/errors.js' import Path from '../../utils/path.js' export default { computed: { + ...mapState('machine/cache', ['fileInfos']), ...mapState('machine/model', ['state', 'storages']), ...mapState('settings', ['language']), ...mapGetters(['isConnected', 'uiFrozen']), @@ -133,7 +134,7 @@ export default { }, methods: { ...mapActions('machine', ['sendCode', 'getFileInfo']), - ...mapMutations('machine/cache', ['clearFileInfo']), + ...mapMutations('machine/cache', ['clearFileInfo', 'setFileInfo']), async selectStorage(index) { const storage = this.storages[index]; let mountSuccess = true, mountResponse; @@ -170,51 +171,55 @@ export default { if (this.fileinfoDirectory === directory) { if (this.isConnected && fileIndex < fileCount) { const file = this.filelist[fileIndex]; - let height = null, layerHeight = null, filament = [], generatedBy = null, printTime = null, simulatedTime = null; - - this.fileinfoProgress = fileIndex; - try { - // Request file info - if (!file.isDirectory) { - const fileInfo = await this.getFileInfo(Path.combine(directory, file.name)); + if (!file.isDirectory) { + try { + // Get the fileinfo either from our cache or from the Duet + const filename = Path.combine(directory, file.name); + let fileInfo = this.fileInfos[filename]; + if (!fileInfo) { + fileInfo = await this.getFileInfo(filename); + this.setFileInfo({ filename, fileInfo }); + } // Start again if the number of files has changed if (fileCount !== this.filelist.length) { - this.fileinfoProgress = 0; - this.$nextTick(() => this.requestFileInfo(directory, 0, this.filelist.length)); + fileIndex = -1; + fileCount = this.filelist.length; return; } // Set file info - height = fileInfo.height; - layerHeight = fileInfo.layerHeight; - filament = fileInfo.filament; - generatedBy = fileInfo.generatedBy; - if (fileInfo.printTime) { printTime = fileInfo.printTime; } - if (fileInfo.simulatedTime) { simulatedTime = fileInfo.simulatedTime; } - } - } catch (e) { - if (e instanceof DisconnectedError) { - this.fileinfoProgress = -1; - this.fileinfoDirectory = undefined; - return; - } + file.height = fileInfo.height; + file.layerHeight = fileInfo.layerHeight; + file.filament = fileInfo.filament; + file.generatedBy = fileInfo.generatedBy; + file.printTime = fileInfo.printTime ? fileInfo.printTime : null; + file.simulatedTime = fileInfo.simulationTime ? fileInfo.simulatedTime : null; - console.warn(e); - this.$log('error', this.$t('error.fileinfoRequestFailed', [file.name]), e.message); - } + // Update progress + this.fileinfoProgress = fileIndex; + } catch (e) { + // Invalidate file info + file.height = null; + file.layerHeight = null; + file.filament = []; + file.generatedBy = null; + file.printTime = null; + file.simulatedTime = null; - // Set file info - file.height = height; - file.layerHeight = layerHeight; - file.filament = filament; - file.generatedBy = generatedBy; - file.printTime = printTime; - file.simulatedTime = simulatedTime; + // Deal with the error. If the connection has been terminated, the next call will invalidate everything + if (!(e instanceof DisconnectedError) && !(e instanceof InvalidPasswordError)) { + console.warn(e); + this.$log('error', this.$t('error.fileinfoRequestFailed', [file.name]), e.message); + } + } + } // Move on to the next item - await this.requestFileInfo(directory, fileIndex + 1, fileCount); + this.fileinfoProgress = fileIndex; + this.requestFileInfo(directory, fileIndex + 1, fileCount); } else { + // No longer connected or finished this.fileinfoProgress = -1; this.fileinfoDirectory = undefined; } @@ -233,6 +238,7 @@ export default { item.simulatedTime = null; } }); + this.requestFileInfo(directory, 0, this.filelist.length); } }, diff --git a/src/components/panels/MovementPanel.vue b/src/components/panels/MovementPanel.vue index 432e8cdf0..0deb641d8 100644 --- a/src/components/panels/MovementPanel.vue +++ b/src/components/panels/MovementPanel.vue @@ -108,7 +108,7 @@ - keyboard_arrow_left {{ axis.letter + -moveSteps(axis.letter)[index - 1] }} + keyboard_arrow_left {{ axis.letter + showSign(-moveSteps(axis.letter)[index - 1]) }} @@ -125,7 +125,7 @@ - {{ axis.letter + '+' + moveSteps(axis.letter)[numMoveSteps - index] }} keyboard_arrow_right + {{ axis.letter + showSign(moveSteps(axis.letter)[numMoveSteps - index]) }} keyboard_arrow_right @@ -192,6 +192,7 @@ export default { } return classes; }, + showSign: (value) => (value > 0) ? `+${value}` : value, showMoveStepDialog(axis, index) { this.moveStepDialog.axis = axis; this.moveStepDialog.index = index; diff --git a/src/components/panels/SettingsCommunicationPanel.vue b/src/components/panels/SettingsCommunicationPanel.vue index 94f816530..a02112c9e 100644 --- a/src/components/panels/SettingsCommunicationPanel.vue +++ b/src/components/panels/SettingsCommunicationPanel.vue @@ -23,9 +23,14 @@ + + + @@ -59,6 +64,10 @@ export default { fileTransferRetryThreshold: { get() { return Math.round(this.settings.fileTransferRetryThreshold / 1024); }, set(value) { if (this.isNumber(value) && value > 0) { this.update({ fileTransferRetryThreshold: Math.round(value * 1024) }); } } + }, + crcUploads: { + get() { return this.settings.crcUploads; }, + set(value) { this.update({ crcUploads: value }); } } }, methods: mapMutations('machine/settings', ['update']) diff --git a/src/components/panels/SettingsEndstopsPanel.vue b/src/components/panels/SettingsEndstopsPanel.vue new file mode 100644 index 000000000..81486ae07 --- /dev/null +++ b/src/components/panels/SettingsEndstopsPanel.vue @@ -0,0 +1,80 @@ + + + + + diff --git a/src/components/panels/SettingsMachinePanel.vue b/src/components/panels/SettingsMachinePanel.vue index a9e36d918..c41c1ccf2 100644 --- a/src/components/panels/SettingsMachinePanel.vue +++ b/src/components/panels/SettingsMachinePanel.vue @@ -9,11 +9,11 @@ - - + + - + diff --git a/src/components/panels/StatusPanel.vue b/src/components/panels/StatusPanel.vue index 460369956..8489208ae 100644 --- a/src/components/panels/StatusPanel.vue +++ b/src/components/panels/StatusPanel.vue @@ -245,9 +245,11 @@ export default { displayAxisPosition(axis) { let position = NaN; if (this.displayToolPosition) { + if (axis.drives.length > 0) { + position = this.move.drives[axis.drives[0]].position; + } + } else { position = axis.machinePosition; - } else if (axis.drives.length > 0) { - position = this.move.drives[axis.drives[0]].position; } return (axis.letter === 'Z') ? this.$displayZ(position, false) : this.$display(position, 1); }, diff --git a/src/components/panels/index.js b/src/components/panels/index.js index 614f5db9f..11762290d 100644 --- a/src/components/panels/index.js +++ b/src/components/panels/index.js @@ -18,6 +18,7 @@ import SettingsAboutPanel from './SettingsAboutPanel.vue' import SettingsAppearancePanel from './SettingsAppearancePanel.vue' import SettingsCommunicationPanel from './SettingsCommunicationPanel.vue' import SettingsElectronicsPanel from './SettingsElectronicsPanel.vue' +import SettingsEndstopsPanel from './SettingsEndstopsPanel.vue' import SettingsGeneralPanel from './SettingsGeneralPanel.vue' import SettingsListItemsPanel from './SettingsListItemsPanel.vue' import SettingsMachinePanel from './SettingsMachinePanel.vue' @@ -44,6 +45,7 @@ Vue.component('settings-about-panel', SettingsAboutPanel) Vue.component('settings-apperance-panel', SettingsAppearancePanel) Vue.component('settings-communication-panel', SettingsCommunicationPanel) Vue.component('settings-electronics-panel', SettingsElectronicsPanel) +Vue.component('settings-endstops-panel', SettingsEndstopsPanel) Vue.component('settings-general-panel', SettingsGeneralPanel) Vue.component('settings-machine-panel', SettingsMachinePanel) Vue.component('settings-list-items-panel', SettingsListItemsPanel) @@ -71,6 +73,7 @@ export default { SettingsAppearancePanel, SettingsCommunicationPanel, SettingsElectronicsPanel, + SettingsEndstopsPanel, SettingsGeneralPanel, SettingsMachinePanel, SettingsListItemsPanel, diff --git a/src/i18n/de.js b/src/i18n/de.js index d72c09506..b87c4602f 100644 --- a/src/i18n/de.js +++ b/src/i18n/de.js @@ -146,7 +146,8 @@ export default { fileEdit: { gcodeReference: 'G-Code-Referenz', menuReference: 'Menüreference', - save: 'Speichern' + save: 'Speichern', + confirmClose: 'Die Datei wurde geändert. Wenn Sie fortfahren gehen Ihre Änderungen verloren.' }, meshEdit: { title: 'Gitterparameter ändern', @@ -570,6 +571,7 @@ export default { updateInterval: 'Aktualisierungsintervall ({0})', extendedUpdateEvery: 'Erweitertes Statusupdateintervall', fileTransferRetryThreshold: 'Grenzwert für erneute Versuche von Dateiübetragungen ({0})', + crcUploads: 'CRC32-Prüfsummen für Uploads verwenden', unavailable: 'Keine Einstellungen verfügbar' }, settingsElectronics: { @@ -580,6 +582,11 @@ export default { dwsFirmware: 'Version von Duet WiFi Server: {0}', updateNote: 'Hinweis: Sie können Aktualisierungen auf der System-Seite installieren.' }, + settingsEndstops: { + caption: 'Endlagenschalter', + index: 'Index', + triggered: 'Ausgelöst' + }, settingsGeneral: { caption: 'Allgemein', factoryReset: 'Werkseinstellungen wiederherstellen', @@ -626,7 +633,7 @@ export default { caption: 'Status', mode: 'Modus: {0}', toolPosition: 'Werkzeugposition', - machinePosition: 'Machinenposition', + machinePosition: 'Maschinenposition', extruders: 'Extruder', extruderDrive: 'Motor {0}', speeds: 'Geschwindigkeiten', diff --git a/src/i18n/en.js b/src/i18n/en.js index b0f5e696e..f0e84fdbb 100644 --- a/src/i18n/en.js +++ b/src/i18n/en.js @@ -146,7 +146,8 @@ export default { fileEdit: { gcodeReference: 'G-Code Reference', menuReference: 'Menu Reference', - save: 'Save' + save: 'Save', + confirmClose: 'The file has been changed. If you proceed, your changes will be lost.' }, meshEdit: { title: 'Set Mesh Parameters', @@ -570,6 +571,7 @@ export default { updateInterval: 'Update interval ({0})', extendedUpdateEvery: 'Extended status update interval', fileTransferRetryThreshold: 'Retry threshold for file transfers ({0})', + crcUploads: 'Use CRC32 checksums for uploads', unavailable: 'No settings available' }, settingsElectronics: { @@ -580,6 +582,11 @@ export default { dwsFirmware: 'Duet WiFi Server Version: {0}', updateNote: 'Note: You can install updates on the System page.' }, + settingsEndstops: { + caption: 'Endstops', + index: 'Index', + triggered: 'Triggered' + }, settingsGeneral: { caption: 'General', factoryReset: 'Revert to factory defaults', diff --git a/src/i18n/fr.js b/src/i18n/fr.js index 458263d83..510dcbdfe 100644 --- a/src/i18n/fr.js +++ b/src/i18n/fr.js @@ -147,7 +147,8 @@ export default { fileEdit: { gcodeReference: 'Références G-Code', menuReference: 'Référence Menu', - save: 'Sauvegarder' + save: 'Sauvegarder', + confirmClose: 'Le fichier a été modifié. Si vous continuez, vos modifications seront perdues.' }, meshEdit: { title: 'Définir Paramètres de Maillage', @@ -526,7 +527,7 @@ export default { caption: 'Estimations basée sur', filament: 'Utilisation de Filament', file: 'Progrès du Fichier', - layer: 'Progrès du Fichier', + layer: 'Durée de la Dernière Couche', slicer: 'Trancheur', simulation: 'Simulation' }, @@ -548,7 +549,7 @@ export default { editMesh: 'Définir Zone pour la Mesh Compensation (M557)', runMesh: 'Lancer Mesh Compensation (G29)', loadMesh: 'Charger la Carte de Hauteur Sauvegardée (G29 S1)', - axesNotHomed: 'L\'axe suivant n\'a pas été à son origine:|Les axes suivantss n\'ont pas été à leur origine:', + axesNotHomed: 'L\'axe suivant n\'a pas été à son origine:|Les axes suivants n\'ont pas été à leur origine:', noAxes: 'Pas d\'axes' }, settingsAbout: { @@ -571,6 +572,7 @@ export default { updateInterval: 'Intervalle de mise à jour ({0})', extendedUpdateEvery: 'Intervalle de mise à jour du statut étendu', fileTransferRetryThreshold: 'Limite d\'essais pour le transfert de fichiers ({0})', + crcUploads: 'Utiliser les sommes de contrôle CRC32 pour les téléchargements', unavailable: 'Aucun réglage disponible.', }, settingsElectronics: { @@ -581,6 +583,11 @@ export default { dwsFirmware: 'Duet WiFi Server Version: {0}', updateNote: 'Remarque: Vous pouvez installer les mises à jour sur la page Système.' }, + settingsEndstops: { + caption: 'Interrupteur de position', + index: 'Index', + triggered: 'Déclenché' + }, settingsGeneral: { caption: 'Général', factoryReset: 'Revenir aux paramètres d\'usine', diff --git a/src/i18n/ru.js b/src/i18n/ru.js index 3e11803dc..3331f3423 100644 --- a/src/i18n/ru.js +++ b/src/i18n/ru.js @@ -146,7 +146,8 @@ fileEdit: { gcodeReference: 'Информация о G-коде', menuReference: 'Информация о меню', - save: 'Save' + save: 'Save', + confirmClose: 'Файл был изменен. Если вы продолжите, ваши изменения будут потеряны.' }, meshEdit: { title: 'Set Mesh Parameters', @@ -570,6 +571,7 @@ updateInterval: 'Интервал обновления({0})', extendedUpdateEvery: 'Интервал обновления расширенного статуса', fileTransferRetryThreshold: 'Порог повтора для передачи файлов ({0})', + crcUploads: 'Для загрузки используйте контрольные суммы CRC32', unavailable: 'Нет доступных настроек' }, settingsElectronics: { @@ -580,6 +582,11 @@ dwsFirmware: 'Версия сервера Duet WiFi: {0}', updateNote: 'Вы можете установить обновления в Менеджере файлов - Система.' }, + settingsEndstops: { + caption: 'Конечный выключатель', + index: 'указатель', + triggered: 'инициированный' + }, settingsGeneral: { caption: 'Установки', factoryReset: 'Вернуть заводские установки', diff --git a/src/i18n/zh_cn.js b/src/i18n/zh_cn.js index aef6981ca..49d052c6a 100644 --- a/src/i18n/zh_cn.js +++ b/src/i18n/zh_cn.js @@ -148,7 +148,8 @@ export default { fileEdit: { gcodeReference: 'G-Code参考', menuReference: '菜单参考', - save: '保存' + save: '保存', + confirmClose: 'The file has been changed. If you proceed, your changes will be lost.' }, meshEdit: { title: '设置网格参数', @@ -571,6 +572,7 @@ export default { updateInterval: '更新间隔({0})', extendedUpdateEvery: '扩展状态更新间隔', fileTransferRetryThreshold: '重试文件传输的阈值({0})', + crcUploads: 'Use CRC32 checksums for uploads', unavailable: '没有可用的设置' }, settingsElectronics: { @@ -589,6 +591,11 @@ export default { cacheStorageLocal: '将缓存保存在本地存储中', cacheSaveDelay: '缓存更改的更新延迟({0})' }, + settingsEndstops: { + caption: 'Endstops', + index: 'Index', + triggered: 'Triggered' + }, settingsListItems: { caption: '列表项目', toolTemperatures: '工具温度', diff --git a/src/plugins/logging.js b/src/plugins/logging.js index abc8b79ee..20b9e0999 100644 --- a/src/plugins/logging.js +++ b/src/plugins/logging.js @@ -34,13 +34,13 @@ export function logCode(code = '', response, hostname = store.state.selectedMach // Log it const responseLines = toLog.split("\n") if (hostname === store.state.selectedMachine) { - let title = code, message = responseLines.reduce((a, b) => `${a}
${b}`); + let title = code, message = responseLines.reduce((a, b) => `${a}
${b}`); if (responseLines.length > 3) { title = (code === '') ? i18n.t('notification.responseTooLong') : code; message = (code === '') ? '' : i18n.t('notification.responseTooLong'); } else if (code === '') { title = responseLines[0]; - message = (responseLines.length > 1) ? responseLines.slice(1).reduce((a, b) => `${a}
${b}`) : ''; + message = (responseLines.length > 1) ? responseLines.slice(1).reduce((a, b) => `${a}
${b}`) : ''; } makeNotification(type, title, message); diff --git a/src/plugins/toast.js b/src/plugins/toast.js index 38da4cf4c..6114205b0 100644 --- a/src/plugins/toast.js +++ b/src/plugins/toast.js @@ -17,7 +17,7 @@ const defaults = { let settings, openNotifications = [] -export function makeNotification(type, title, message = '', timeout) { +export function makeNotification(type, title, message, timeout) { // If there is already an equal notification, reset its time and don't display a new one const equalNotification = openNotifications.find(item => item.type === type && item.title == title && item.message === message); if (equalNotification) { @@ -28,8 +28,8 @@ export function makeNotification(type, title, message = '', timeout) { // Prepare and show new toast const item = {}, options = Object.assign({ class: 'new-toast', - title: title.replace(/\n/g, '
'), - message: message.replace(/\n/g, '
'), + title: title.replace(/\n/g, '
'), + message: message ? message.replace(/\n/g, '
') : '', onClosed() { openNotifications = openNotifications.filter(notification => notification !== item); }, @@ -123,7 +123,7 @@ export function makeFileTransferNotification(type, destination, cancelSource, nu export function showMessage(message) { const options = Object.assign({ title: i18n.t('notification.message'), - message: message.replace(/\n/g, '
'), + message: message.replace(/\n/g, '
'), timeout: false }, defaults); diff --git a/src/routes/Settings/Machine.vue b/src/routes/Settings/Machine.vue index 49556ec03..fa5442356 100644 --- a/src/routes/Settings/Machine.vue +++ b/src/routes/Settings/Machine.vue @@ -1,6 +1,6 @@