diff --git a/src/components/panels/GcodefilesPanel.vue b/src/components/panels/GcodefilesPanel.vue
index bd903e684..46ab7d926 100644
--- a/src/components/panels/GcodefilesPanel.vue
+++ b/src/components/panels/GcodefilesPanel.vue
@@ -235,7 +235,7 @@
class="pa-0 mr-0"
@click.stop="select(!isSelected)">
-
+ |
{{ mdiFolder }}
@@ -271,6 +271,10 @@
{{ mdiFile }}
+
+
+ {{ mdiPin }}
+
|
{{ item.filename }} |
@@ -311,6 +315,16 @@
@closeDialog="closeStartPrint" />
+
+ {{ mdiPin }}
+ {{ $t('Files.Pin') }}
+
+
+ {{ mdiPinOff }}
+ {{ $t('Files.Unpin') }}
+
diff --git a/src/components/panels/Status/Gcodefiles.vue b/src/components/panels/Status/Gcodefiles.vue
index b65a32e48..16351805f 100644
--- a/src/components/panels/Status/Gcodefiles.vue
+++ b/src/components/panels/Status/Gcodefiles.vue
@@ -79,6 +79,14 @@
@closeDialog="closeDialog" />
+
+ {{ mdiPin }}
+ {{ $t('Files.Pin') }}
+
+
+ {{ mdiPinOff }}
+ {{ $t('Files.Unpin') }}
+
{{ mdiPlay }}
{{ $t('Files.PrintStart') }}
@@ -228,7 +236,7 @@ import Component from 'vue-class-component'
import { Mixins } from 'vue-property-decorator'
import BaseMixin from '@/components/mixins/base'
import ControlMixin from '@/components/mixins/control'
-import { FileStateGcodefile } from '@/store/files/types'
+import { FileStateFile, FileStateGcodefile } from '@/store/files/types'
import StartPrintDialog from '@/components/dialogs/StartPrintDialog.vue'
import {
mdiChevronDown,
@@ -243,6 +251,8 @@ import {
mdiRenameBox,
mdiDelete,
mdiCloseThick,
+ mdiPin,
+ mdiPinOff,
} from '@mdi/js'
import Panel from '@/components/ui/Panel.vue'
import { defaultBigThumbnailBackground } from '@/store/variables'
@@ -278,6 +288,8 @@ export default class StatusPanelGcodefiles extends Mixins(BaseMixin, ControlMixi
mdiRenameBox = mdiRenameBox
mdiDelete = mdiDelete
mdiCloseThick = mdiCloseThick
+ mdiPin = mdiPin
+ mdiPinOff = mdiPinOff
private deleteDialog = false
private showDialogBool = false
@@ -499,6 +511,14 @@ export default class StatusPanelGcodefiles extends Mixins(BaseMixin, ControlMixi
window.open(href)
}
+ pinFile(item: FileStateFile) {
+ this.$store.dispatch('gui/addPinnedFile', item.filename)
+ }
+
+ unPinFile(item: FileStateFile) {
+ this.$store.dispatch('gui/removePinnedFile', item.filename)
+ }
+
editFile(item: FileStateGcodefile) {
const pos = item.filename.lastIndexOf('/')
const path = pos > 0 ? item.filename.slice(0, pos + 1) : ''
diff --git a/src/components/panels/Status/PinnedGcodefiles.vue b/src/components/panels/Status/PinnedGcodefiles.vue
new file mode 100644
index 000000000..61982c34e
--- /dev/null
+++ b/src/components/panels/Status/PinnedGcodefiles.vue
@@ -0,0 +1,593 @@
+
+
+
+
+ {{ $t('Panels.StatusPanel.EmptyGcodes') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ mdiFile }}
+
+
+
+
+
+
+
+ {{ mdiFile }}
+
+ |
+
+ {{ item.filename }}
+
+ {{ getDescription(item) }}
+
+ |
+
+
+
+
+
+ {{ getStatusIcon(item.last_status) }}
+
+
+
+ {{ item.last_status.replace(/_/g, ' ') }}
+
+ |
+
+
+
+
+
+
+
+
+ {{ mdiPinOff }}
+ {{ $t('Files.Unpin') }}
+
+
+ {{ mdiPlay }}
+ {{ $t('Files.PrintStart') }}
+
+
+ {{ mdiPlaylistPlus }}
+ {{ $t('Files.AddToQueue') }}
+
+
+ {{ mdiPlaylistPlus }}
+ {{ $t('Files.AddBatchToQueue') }}
+
+
+ {{ mdiFire }}
+ {{ $t('Files.Preheat') }}
+
+
+ {{ mdiVideo3d }}
+ {{ $t('Files.View3D') }}
+
+
+ {{ mdiCloudDownload }}
+ {{ $t('Files.Download') }}
+
+
+ {{ mdiFileDocumentEditOutline }}
+ {{ $t('Files.EditFile') }}
+
+
+ {{ mdiRenameBox }}
+ {{ $t('Files.Rename') }}
+
+
+ {{ mdiDelete }}
+ {{ $t('Files.Delete') }}
+
+
+
+
+
+
+
+ {{ mdiCloseThick }}
+
+
+
+
+
+
+
+ {{ $t('Files.Cancel') }}
+ {{ $t('Files.Rename') }}
+
+
+
+
+
+
+
+
+
+ {{ mdiCloseThick }}
+
+
+
+
+ {{ $t('Files.DeleteSingleFileQuestion', { name: filename }) }}
+
+
+
+
+
+ {{ $t('Files.Cancel') }}
+
+
+ {{ $t('Files.Delete') }}
+
+
+
+
+
+
+
+
+
+ {{ mdiCloseThick }}
+
+
+
+
+
+
+
+
+ {{ mdiChevronUp }}
+
+
+ {{ mdiChevronDown }}
+
+
+
+
+
+
+
+ {{ $t('Files.Cancel') }}
+ {{ $t('Files.AddToQueue') }}
+
+
+
+
+
+
+
+
+
diff --git a/src/components/panels/StatusPanel.vue b/src/components/panels/StatusPanel.vue
index a193f0a5b..1335d11a8 100644
--- a/src/components/panels/StatusPanel.vue
+++ b/src/components/panels/StatusPanel.vue
@@ -89,12 +89,18 @@
- {{ $t('Panels.StatusPanel.Status') }}
- {{ $t('Panels.StatusPanel.Files') }}
+
+ {{ mdiPrinter3d }}
+
+
+ {{ mdiFile }}
+
+
+ {{ mdiPin }}
+
-
- {{ $t('Panels.StatusPanel.Jobqueue') }}
-
+ {{ mdiFormatListCheckbox }}
+
@@ -105,6 +111,9 @@
+
+
+
@@ -121,6 +130,7 @@ import MinSettingsPanel from '@/components/panels/MinSettingsPanel.vue'
import KlippyStatePanel from '@/components/panels/KlippyStatePanel.vue'
import StatusPanelPrintstatus from '@/components/panels/Status/Printstatus.vue'
import StatusPanelGcodefiles from '@/components/panels/Status/Gcodefiles.vue'
+import StatusPanelPinnedGcodefiles from '@/components/panels/Status/PinnedGcodefiles.vue'
import StatusPanelJobqueue from '@/components/panels/Status/Jobqueue.vue'
import StatusPanelExcludeObject from '@/components/panels/Status/ExcludeObject.vue'
import StatusPanelPrintstatusThumbnail from '@/components/panels/Status/PrintstatusThumbnail.vue'
@@ -139,6 +149,10 @@ import {
mdiCloseCircle,
mdiLayersPlus,
mdiDotsVertical,
+ mdiFile,
+ mdiPin,
+ mdiFormatListCheckbox,
+ mdiPrinter3d,
} from '@mdi/js'
import { PrinterStateMacro } from '@/store/printer/types'
@@ -149,6 +163,7 @@ import { PrinterStateMacro } from '@/store/printer/types'
Panel,
StatusPanelExcludeObject,
StatusPanelGcodefiles,
+ StatusPanelPinnedGcodefiles,
StatusPanelJobqueue,
StatusPanelPrintstatus,
StatusPanelPrintstatusThumbnail,
@@ -161,6 +176,10 @@ export default class StatusPanel extends Mixins(BaseMixin) {
mdiCloseCircle = mdiCloseCircle
mdiDotsVertical = mdiDotsVertical
mdiAlertOutline = mdiAlertOutline
+ mdiPin = mdiPin
+ mdiFile = mdiFile
+ mdiFormatListCheckbox = mdiFormatListCheckbox
+ mdiPrinter3d = mdiPrinter3d
private boolShowObjects = false
private boolShowPauseAtLayer = false
diff --git a/src/locales/en.json b/src/locales/en.json
index 643ccf94a..74bbe9872 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -223,6 +223,7 @@
"NewDirectory": "New Directory",
"NozzleDiameter": "Nozzle Diameter",
"ObjectHeight": "Object Height",
+ "Pin": "Pin",
"Preheat": "Preheat",
"PrintedFiles": "Printed files",
"PrintStart": "Print start",
@@ -242,6 +243,7 @@
"SuccessfullyRenamed": "Successfully renamed {filename}.",
"SuccessfullyUploaded": "Upload of {filename} successful!",
"Total": "Total",
+ "Unpin": "Unpin",
"UploadNewGcode": "Upload new G-Code",
"Used": "Used",
"View3D": "View 3D"
diff --git a/src/plugins/helpers.ts b/src/plugins/helpers.ts
index 0641ec209..9213b4a8d 100644
--- a/src/plugins/helpers.ts
+++ b/src/plugins/helpers.ts
@@ -167,6 +167,9 @@ export const sortFiles = (items: FileStateFile[] | null, sortBy: string[], sortD
// Then make sure directories come first
items.sort((a: any, b: any) => (a.isDirectory === b.isDirectory ? 0 : a.isDirectory ? -1 : 1))
+
+ // Place pinned files on top
+ items.sort((a: any, b: any) => (a.isPinned === b.isPinned ? 0 : a.isPinned ? -1 : 1))
}
return items ?? []
diff --git a/src/store/files/actions.ts b/src/store/files/actions.ts
index a67e1f114..73dddcf44 100644
--- a/src/store/files/actions.ts
+++ b/src/store/files/actions.ts
@@ -11,6 +11,7 @@ import { RootState } from '@/store/types'
import i18n from '@/plugins/i18n'
import { hiddenDirectories, validGcodeExtensions } from '@/store/variables'
import axios from 'axios'
+import store from '@/store'
export const actions: ActionTree = {
reset({ commit }) {
@@ -279,6 +280,7 @@ export const actions: ActionTree = {
if (payload.error) {
Vue.$toast.error(payload.error.message)
} else {
+ store.dispatch('gui/removePinnedFile', payload.item.path)
const delPath = payload.item.path.substr(payload.item.path.lastIndexOf('/') + 1)
const fileExtension = payload.item.path.substr(payload.item.path.lastIndexOf('.') + 1)
diff --git a/src/store/files/getters.ts b/src/store/files/getters.ts
index e55b4deae..b292b58cd 100644
--- a/src/store/files/getters.ts
+++ b/src/store/files/getters.ts
@@ -8,6 +8,7 @@ import {
import { GetterTree } from 'vuex'
import { FileState, FileStateFile, FileStateGcodefile } from '@/store/files/types'
import { ServerHistoryStateJob } from '@/store/server/history/types'
+import store from '@/store'
// eslint-disable-next-line
export const getters: GetterTree = {
@@ -183,6 +184,7 @@ export const getters: GetterTree = {
}
}
+ tmp.isPinned = store?.state?.gui?.view.gcodefiles.pinnedFiles.some((f) => f === tmp.filename) || false
if (boolShowPrintedFiles) output.push(tmp)
else if (tmp.count_printed === 0) output.push(tmp)
})
diff --git a/src/store/files/types.ts b/src/store/files/types.ts
index 55197c159..23c593526 100644
--- a/src/store/files/types.ts
+++ b/src/store/files/types.ts
@@ -43,6 +43,7 @@ export interface FileStateFile {
}
export interface FileStateGcodefile extends FileStateFile {
+ isPinned?: boolean
preheat_gcode: string | null
small_thumbnail: string | null
big_thumbnail: string | null
diff --git a/src/store/gui/actions.ts b/src/store/gui/actions.ts
index ea4dd7363..9ee81338d 100644
--- a/src/store/gui/actions.ts
+++ b/src/store/gui/actions.ts
@@ -199,6 +199,22 @@ export const actions: ActionTree = {
Vue.$socket.emit('server.database.post_item', { namespace: 'mainsail', key: keyName, value: newState })
},
+ addPinnedFile({ commit, dispatch, state }, value) {
+ commit('addPinnedFile', value)
+ dispatch('updateSettings', {
+ keyName: 'gcodefiles.pinnedFiles',
+ newVal: state.view.gcodefiles.pinnedFiles,
+ })
+ },
+
+ removePinnedFile({ commit, dispatch, state }, value) {
+ commit('removePinnedFile', value)
+ dispatch('updateSettings', {
+ keyName: 'gcodefiles.pinnedFiles',
+ newVal: state.view.gcodefiles.pinnedFiles,
+ })
+ },
+
setGcodefilesMetadata({ commit, dispatch, state }, data) {
commit('setGcodefilesMetadata', data)
dispatch('updateSettings', {
diff --git a/src/store/gui/index.ts b/src/store/gui/index.ts
index 1e192c60f..785b335b3 100644
--- a/src/store/gui/index.ts
+++ b/src/store/gui/index.ts
@@ -214,6 +214,7 @@ export const getDefaultState = (): GuiState => {
],
currentPath: '',
selectedFiles: [],
+ pinnedFiles: [],
},
heightmap: {
probed: true,
diff --git a/src/store/gui/mutations.ts b/src/store/gui/mutations.ts
index 2fcc87e50..2be0a8f34 100644
--- a/src/store/gui/mutations.ts
+++ b/src/store/gui/mutations.ts
@@ -43,6 +43,16 @@ export const mutations: MutationTree = {
Vue.set(state.view.gcodefiles, 'hideMetadataColumns', array)
},
+ addPinnedFile(state, value) {
+ const array = [value, ...state.view.gcodefiles.pinnedFiles]
+ Vue.set(state.view.gcodefiles, 'pinnedFiles', array)
+ },
+
+ removePinnedFile(state, value) {
+ const array = state.view.gcodefiles.pinnedFiles.filter((n) => n !== value)
+ Vue.set(state.view.gcodefiles, 'pinnedFiles', array)
+ },
+
setGcodefilesShowHiddenFiles(state, value) {
Vue.set(state.view.gcodefiles, 'showHiddenFiles', value)
},
diff --git a/src/store/gui/types.ts b/src/store/gui/types.ts
index 930961603..a8a96a2e1 100644
--- a/src/store/gui/types.ts
+++ b/src/store/gui/types.ts
@@ -150,6 +150,7 @@ export interface GuiState {
orderMetadataColumns: string[]
currentPath: string
selectedFiles: FileStateGcodefile[]
+ pinnedFiles: string[]
}
heightmap: {
probed: boolean
|