feat: new design of the web UI (#408)

* refactor(appbar): change buttons to new design

* refactor(appbar): change height and add pritnername

* refactor: small sidebar

* refactor(vuetify): define parts of the mainsail theme and expose colors to css

Signed-off-by: steadyjaw <martin.keilaus@gmail.com>

* refactor: ui settings add missing divider

Signed-off-by: steadyjaw <martin.keilaus@gmail.com>

* refactor: add farm printer selection and menu to new top and sidebar, added icons to version-tooltip

Signed-off-by: steadyjaw <martin.keilaus@gmail.com>

* refactor: make topbar buttons hideable via ui settings

Signed-off-by: steadyjaw <martin.keilaus@gmail.com>

* refactor: move miniConsole buttons to toolbar and make'em collapsible

Signed-off-by: steadyjaw <martin.keilaus@gmail.com>

* refactor: set matching colors for buttons on console page

Signed-off-by: steadyjaw <martin.keilaus@gmail.com>

* refactor: use colornames from theme and set play/resume button to green

Signed-off-by: steadyjaw <martin.keilaus@gmail.com>

* fix: correct visibility of hide buttons on collapse

Signed-off-by: steadyjaw <martin.keilaus@gmail.com>

* feature: add virtual tab button to console-input on touch devices

Signed-off-by: steadyjaw <martin.keilaus@gmail.com>

* refactor: remove hide e-stop button setting

Signed-off-by: steadyjaw <martin.keilaus@gmail.com>

* fix: add reactivity to hide upload and print button functionality

Signed-off-by: steadyjaw <martin.keilaus@gmail.com>

* refactor: move logo to topbar and clip menu under topbar

Signed-off-by: steadyjaw <martin.keilaus@gmail.com>

* refactor: remove unused TheSidebarPrinterMenu

Signed-off-by: steadyjaw <martin.keilaus@gmail.com>

* feature: create and add about component to sidebar ...no dialog on click yet

Signed-off-by: steadyjaw <martin.keilaus@gmail.com>

* fix: correct appearance of the close icon from interface settings dialog

Signed-off-by: steadyjaw <martin.keilaus@gmail.com>

* Revert "fix: correct appearance of the close icon from interface settings dialog"

This reverts commit f66e29d4f2da5d576bc22c35adfcd26bd1670ff7.
This commit is contained in:
Martin 2021-11-13 16:07:15 +01:00 committed by GitHub
parent d6e4fc09d6
commit ced16fbc1b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 497 additions and 209 deletions

113
public/img/klipper.svg Normal file
View File

@ -0,0 +1,113 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="100%"
height="100%"
viewBox="0 0 768 624"
version="1.1"
xml:space="preserve"
style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;"
id="svg40"
sodipodi:docname="klipper.svg"
inkscape:version="0.92.4 (unknown)"
inkscape:export-filename="/home/kevin/src/reprap/firmware/klipper/logs/logo-20190416/klipper6.svg.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"><metadata
id="metadata46"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
id="defs44">
</defs><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1030"
id="namedview42"
showgrid="false"
inkscape:zoom="0.95"
inkscape:cx="327.89418"
inkscape:cy="289.76441"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg40"
showguides="false"
showborder="false"><inkscape:grid
type="xygrid"
id="grid905" /></sodipodi:namedview>
<path
style="fill:#3c4b5a"
d="m 30,34 v 140 l 260,255 115,-5 h 80 L 730,174 V 34 H 720 L 385,374 40,34 Z"
id="path2"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccccccc" /><path
style="fill:#3c4b5a;stroke-width:1.03280997"
d="m 140,418 -58,57 1,25 v 19 l 57,57 h 27 v -1 L 91,496.5 167,419 v -1 z"
id="path8"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccccccc" /><g
transform="translate(-1.08348e-4,0.048)"
id="g17">
<path
d="m 83.041215,464.92108 h 0.001 l -0.04211,27.03092 h 11 l 73.000005,-74 v 14.301 l -63,64.199 63,65.5 v 14 l -73.000002,-75 h -13 c -2,0 -4,-2 -4,-4 h -2 v -33 h 2 c 0,-1 1,-2 3.00004,-2 1.99996,0 2.99996,1 2.99996,2 z"
style="fill:#b12f35"
id="path15"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccccccccccccc" />
</g><g
id="path839"
transform="matrix(1.03281,0,0,1.0326797,24.31099,-21.921569)">
<rect
x="21"
y="426"
width="30.015202"
height="153"
style="fill:#3c4b5a;fill-rule:nonzero"
id="rect12" />
</g><rect
x="247"
y="469"
width="24"
height="107"
style="fill:#3c4b5a;stroke-width:1.19609642"
id="rect23" /><path
d="m 371.98888,521.45953 c 0,-10.8903 -2.45322,-19.36013 -7.36207,-25.4107 -4.90765,-6.05057 -12.80741,-9.07465 -23.69929,-9.07465 -2.28639,0 -4.6724,0.10081 -7.16044,0.30241 -2.48802,0.20161 -4.94125,0.63842 -7.36207,1.31044 v 60.29926 c 2.15197,1.47844 5.00967,2.85727 8.57188,4.13411 3.56342,1.27684 7.36208,1.91526 11.39597,1.91526 8.87554,0 15.36385,-3.02409 19.46496,-9.07465 4.10111,-6.05057 6.15106,-14.18439 6.15106,-24.40148 z M 397,521.05632 c 0,8.06663 -1.04178,15.46123 -3.12654,22.18261 -2.08476,6.72259 -5.07568,12.50434 -8.97515,17.34409 -3.89947,4.83973 -8.77472,8.60423 -14.62333,11.29351 -5.8498,2.68927 -12.47254,4.0333 -19.86702,4.0333 -4.97606,0 -9.54765,-0.60481 -13.71597,-1.81445 -4.16832,-1.21082 -7.59731,-2.55487 -10.28698,-4.03451 V 611 H 302 V 471.64736 c 4.97486,-1.34524 11.09352,-2.62207 18.35477,-3.83171 C 327.61602,466.60482 335.28055,466 343.34834,466 c 8.33664,0 15.79953,1.27684 22.38866,3.83171 6.58794,2.55487 12.20251,6.21857 16.84131,10.9911 4.6388,4.77253 8.20221,10.5543 10.69024,17.34408 2.48684,6.78859 3.73145,14.4184 3.73145,22.88943 z"
style="fill:#3c4b5a;fill-rule:nonzero;stroke-width:1.20011997"
id="path25"
inkscape:connector-curvature="0" /><path
d="m 492.9892,521.45953 c 0,-10.8903 -2.4544,-19.36013 -7.36198,-25.4107 -4.90759,-6.05057 -12.80725,-9.07465 -23.699,-9.07465 -2.28636,0 -4.67354,0.10081 -7.16034,0.30241 -2.488,0.20161 -4.94119,0.63842 -7.36197,1.31044 v 60.29926 c 2.15074,1.47844 5.00839,2.85727 8.57176,4.13411 3.56337,1.27684 7.36198,1.91526 11.39582,1.91526 8.87423,0 15.36246,-3.02409 19.46352,-9.07465 4.10106,-6.05057 6.15219,-14.18439 6.15219,-24.40148 z M 518,521.05632 c 0,8.06663 -1.04297,15.46123 -3.1265,22.18261 -2.08474,6.72259 -5.07681,12.50434 -8.97624,17.34409 -3.89942,4.83973 -8.77341,8.60423 -14.62314,11.29351 -5.84854,2.68927 -12.47119,4.0333 -19.86677,4.0333 -4.9748,0 -9.54633,-0.60481 -13.7146,-1.81445 -4.16827,-1.21082 -7.59722,-2.55487 -10.28684,-4.03451 V 611 H 423 V 471.64736 c 4.97599,-1.34524 11.09337,-2.62207 18.35454,-3.83171 C 448.61569,466.60482 456.28012,466 464.34782,466 c 8.33773,0 15.80052,1.27684 22.38838,3.83171 6.58905,2.55487 12.20235,6.21857 16.84229,10.9911 4.63875,4.77253 8.20211,10.5543 10.69012,17.34408 2.48679,6.78859 3.73139,14.4184 3.73139,22.88943 z"
style="fill:#3c4b5a;fill-rule:nonzero;stroke-width:1.20011246"
id="path27"
inkscape:connector-curvature="0" /><path
d="m 537,521.00591 c 0,-9.30123 1.37492,-17.45591 4.12595,-24.46521 2.74984,-7.00931 6.40632,-12.83838 10.96942,-17.48839 4.56191,-4.65121 9.79451,-8.15587 15.699,-10.51396 5.90328,-2.3593 11.94191,-3.53835 18.11469,-3.53835 14.49054,0 25.79531,4.51527 33.91308,13.5458 8.11897,9.03174 12.17786,22.51016 12.17786,40.43768 0,1.34749 -0.0335,2.8646 -0.1006,4.54896 -0.0671,1.68555 -0.16768,3.20147 -0.30182,4.55015 h -69.43942 c 0.67068,8.49155 3.65646,15.06171 8.95733,19.71294 5.29967,4.65001 12.98149,6.97562 23.04547,6.97562 5.9033,0 11.30477,-0.5402 16.20202,-1.61818 4.89726,-1.07799 8.75493,-2.22335 11.57304,-3.43729 l 3.22052,20.01612 c -1.34138,0.67495 -3.18699,1.38237 -5.53561,2.12349 -2.34742,0.74112 -5.03139,1.41485 -8.05071,2.02242 -3.01811,0.60637 -6.27217,1.11168 -9.76096,1.51592 -3.4888,0.40425 -7.04467,0.60637 -10.66761,0.60637 -9.25915,0 -17.30985,-1.38116 -24.15211,-4.14471 -6.84346,-2.76354 -12.47968,-6.60507 -16.90744,-11.52457 -4.42778,-4.91951 -7.71537,-10.7161 -9.86278,-17.38854 C 538.0731,536.27095 537,528.95845 537,521.00591 Z m 70.64548,-10.91821 c 0,-3.36991 -0.46948,-6.57138 -1.40846,-9.60442 -0.93897,-3.03303 -2.31508,-5.66063 -4.12595,-7.88517 -1.81207,-2.22335 -4.02535,-3.97627 -6.64226,-5.25638 -2.61689,-1.28131 -5.73561,-1.92137 -9.35855,-1.92137 -3.75708,0 -7.04466,0.70743 -9.86278,2.1235 -2.8181,1.41484 -5.19906,3.26883 -7.14527,5.55956 -1.94501,2.29192 -3.45526,4.92071 -4.52837,7.88516 -1.0731,2.96567 -1.81206,5.99871 -2.21447,9.09912 z"
style="fill:#3c4b5a;fill-rule:nonzero;stroke-width:1.20038378"
id="path29"
inkscape:connector-curvature="0" /><path
d="m 717.7869,492.26931 c -2.00567,-0.67394 -4.78138,-1.38159 -8.32593,-2.12292 -3.54453,-0.74135 -7.65736,-1.11202 -12.33845,-1.11202 -2.67542,0 -5.51799,0.26957 -8.52649,0.80874 -3.0097,0.53915 -5.11684,1.01091 -6.32025,1.41528 V 576 H 658 V 475.28104 c 4.68109,-1.75347 10.53216,-3.40464 17.55439,-4.95472 C 682.57663,468.77504 690.36771,468 698.92762,468 c 1.60574,0 3.47769,0.10109 5.61827,0.30328 2.13938,0.20218 4.27996,0.47176 6.42053,0.80873 2.13939,0.33697 4.2131,0.74255 6.21878,1.21431 2.00687,0.47176 3.6114,0.90983 4.8148,1.3142 z"
style="fill:#3c4b5a;fill-rule:nonzero;stroke-width:1.19865453"
id="path31"
inkscape:connector-curvature="0" /><path
sodipodi:nodetypes="cccccccccccccc"
d="M 259,424 H 360 V 359 L 30,34 H 90 L 385,324 670,34 h 60 L 410,359 v 84.99783 C 410,449 405,454 400,454 H 259 c -12,0 -14,-10 -14,-15 0,-5 2,-15 14,-15 z"
style="fill:#b12f35"
id="path35"
inkscape:connector-curvature="0" />
<rect
id="rect12-6"
style="clip-rule:evenodd;fill:#3c4b5a;fill-rule:nonzero;stroke-width:1.03280997;stroke-linejoin:round;stroke-miterlimit:1.41420996"
height="158"
width="24"
y="418"
x="191" /></svg>

After

Width:  |  Height:  |  Size: 8.0 KiB

View File

@ -7,14 +7,19 @@
:fullscreen="isMobile"
>
<template #activator="{ on, attrs }">
<v-btn
class="gcode-command-btn px-2 minwidth-0"
color="grey darken-3"
:small="isMini"
v-bind="attrs"
v-on="on">
<v-icon>mdi-help</v-icon>
</v-btn>
<template v-if="inToolbar">
<v-btn icon v-bind="attrs" v-on="on"><v-icon small>mdi-help</v-icon></v-btn>
</template>
<template v-else>
<v-btn
class="gcode-command-btn px-2 minwidth-0"
color="lightgray"
:small="isMini"
v-bind="attrs"
v-on="on">
<v-icon>mdi-help</v-icon>
</v-btn>
</template>
</template>
<template #default>
<panel :title="$t('Console.CommandList')" icon="mdi-help" card-class="command-help-dialog" :margin-bottom="false">
@ -72,6 +77,8 @@ import Panel from '@/components/ui/Panel.vue'
})
export default class CommandHelpModal extends Mixins(BaseMixin) {
@Prop({ required: false, default: false }) isMini!: boolean;
@Prop({ required: false, default: false }) inToolbar!: boolean;
cmdListSearch = '';
isOpen = false;

View File

@ -1,6 +1,6 @@
<template>
<div>
<v-btn color="grey darken-3" class="ml-5 minwidth-0 px-2" @click="showSettings = true">
<v-btn icon tile large @click="showSettings = true">
<v-icon>mdi-cogs</v-icon>
</v-btn>
<v-dialog v-model="showSettings" width="900" persistent :fullscreen="isMobile">

View File

@ -1,66 +1,70 @@
<style>
.sidebar-wrapper .v-navigation-drawer__content {
padding-bottom: 3em;
.nav-logo {
height: 32px;
}
#sidebarVersions {
position: absolute;
left: 0;
bottom: 0;
.small-list-item {
height: 48px;
}
@media screen and (max-width: 1024px) {
.sidebar-wrapper .v-navigation-drawer__content {
padding-bottom: 0;
}
#sidebarVersions {
display: none;
}
.no-text-decoration {
text-decoration: none;
background-color: transparent;
}
.no-background:before {
background-color: rgba(255, 255, 255, 0) !important;
}
</style>
<style scoped>
.active-nav-item {
border-right: 4px solid var(--v-primary-base);
}
.nowrap {
white-space: nowrap !important;
}
</style>
<template>
<v-navigation-drawer class="sidebar-wrapper" persistent v-model="naviDrawer" mobile-breakpoint="1280" enable-resize-watcher fixed app :src="sidebarBackground">
<div id="nav-header">
<template v-if="sidebarLogo">
<img :src="sidebarLogo" style="height: 40px;" class="mr-3" alt="Logo" />
</template>
<template v-else>
<mainsail-logo :color="logoColor" style="height: 40px;" class="mr-3"></mainsail-logo>
</template>
<v-toolbar-title>{{ printerName }}</v-toolbar-title>
</div>
<ul class="navi" :expand="$vuetify.breakpoint.mdAndUp">
<the-sidebar-printer-menu></the-sidebar-printer-menu>
<li v-for="(category, index) in naviPoints" :key="index" :prepend-icon="category.icon"
:class="[category.path !== '/' && currentPage.includes(category.path) ? 'active' : '', 'nav-item']"
:value="true"
>
<router-link
style="position: relative;"
slot="activator" class="nav-link" exact :to="category.path" @click.prevent
v-if="showInNavi(category)">
<v-icon>mdi-{{ category.icon }}</v-icon>
<span class="nav-title">{{ $t(`Router.${category.title}`) }}</span>
<v-icon class="nav-arrow" v-if="category.children && category.children.length > 0">mdi-chevron-down</v-icon>
</router-link>
<v-navigation-drawer v-model="naviDrawer" :src="sidebarBackground" :mini-variant="!boolWideNavDrawer" :key="boolWideNavDrawer ? 'wide' : 'mini'" width="200px" clipped app>
<v-list class="pr-0 pt-0 ml-0">
<v-list-item-group active-class="active-nav-item">
<template v-if="countPrinters">
<v-list-item
router to="/allPrinters"
class="small-list-item mt-1"
>
<v-list-item-icon class="my-3 mr-3">
<v-icon>mdi-view-dashboard-outline</v-icon>
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title tile>{{ $t("App.Printers")}}</v-list-item-title>
</v-list-item-content>
<ul class="child">
<li v-for="(page, pageIndex) in category.children" class="nav-item" v-bind:key="`${index}-${pageIndex}`">
<router-link :to="page.path" class="nav-link" @click.prevent v-if="klippy_state !== 'error' || page.alwaysShow">
<v-icon>mdi-{{ page.icon }}</v-icon>
<span class="nav-title">{{ $t(`Router.${page.title}`) }}</span>
</router-link>
</li>
</ul>
</li>
</ul>
<p id="sidebarVersions" class="mb-0 text-body-2 pl-3 pb-2">
v{{ mainsailVersion }}
<span class="" v-if="klipperVersion"><br />{{ klipperVersion }}</span>
</p>
</v-navigation-drawer>
</v-list-item>
<v-divider class="my-1"></v-divider>
</template>
<div v-for="(category, index) in naviPoints" :key="index">
<v-list-item
router :to="category.path"
v-if="showInNavi(category)"
class="small-list-item"
>
<v-list-item-icon class="my-3 mr-3">
<v-icon>mdi-{{ category.icon }}</v-icon>
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title tile>{{ $t(`Router.${category.title}`) }}</v-list-item-title>
</v-list-item-content>
</v-list-item>
</div>
</v-list-item-group>
</v-list>
<template v-slot:append>
<v-list-item class="small-list-item mb-2">
<v-list-item-icon>
<about-modal></about-modal>
</v-list-item-icon>
</v-list-item>
</template>
</v-navigation-drawer>
</template>
<script lang="ts">
@ -70,19 +74,15 @@ import {Mixins} from 'vue-property-decorator'
import BaseMixin from '@/components/mixins/base'
import {PrinterStateKlipperConfig} from '@/store/printer/types'
import TheSelectPrinterDialog from '@/components/TheSelectPrinterDialog.vue'
import TheSidebarPrinterMenu from '@/components/TheSidebarPrinterMenu.vue'
import MainsailLogo from '@/components/ui/MainsailLogo.vue'
import AboutModal from '@/components/modals/AboutModal.vue'
@Component({
components: {
MainsailLogo,
TheSidebarPrinterMenu,
TheSelectPrinterDialog
TheSelectPrinterDialog,
AboutModal
}
})
export default class TheSidebar extends Mixins(BaseMixin) {
export default class TheSidebarAlt extends Mixins(BaseMixin) {
get naviDrawer(): boolean {
return this.$store.state.naviDrawer
}
@ -91,37 +91,18 @@ export default class TheSidebar extends Mixins(BaseMixin) {
this.$store.dispatch('setNaviDrawer', newVal)
}
get logoColor(): string {
return this.$store.state.gui.theme.logo
}
get sidebarLogo(): string {
return this.$store.getters['files/getSidebarLogo']
get boolWideNavDrawer() {
return this.$store.state.gui.dashboard.boolWideNavDrawer ?? false
}
get sidebarBackground(): string {
return this.$store.getters['files/getSidebarBackground']
}
get mainsailVersion(): string {
return this.$store.state.packageVersion
}
get klipperVersion():string {
return this.$store.state.printer?.software_version ?? ''
}
get naviPoints(): AppRoute[] {
return routes.filter((element) => element.showInNavi)
}
get printerName():string {
if (this.$store.state.gui.general.printername.length)
return this.$store.state.gui.general.printername
return this.$store.state.printer.hostname
}
get klippy_state(): string {
return this.$store.state.server.klippy_state
}
@ -150,6 +131,10 @@ export default class TheSidebar extends Mixins(BaseMixin) {
return this.$store.getters['server/updateManager/isUpdateAvailable']
}
get countPrinters() {
return this.$store.getters['farm/countPrinters']
}
showInNavi(route: AppRoute): boolean {
if (['shutdown', 'error', 'disconnected'].includes(this.klippy_state) && !route.alwaysShow) return false

View File

@ -1,84 +0,0 @@
<style scoped>
</style>
<template>
<div v-if="displayMenuPoint">
<li :class="currentPage === '/allPrinters' ? 'nav-item active' : 'nav-item '">
<div
class="nav-link "
@click.prevent
@click="switchToPrinters"
role="button"
>
<v-icon>mdi-view-dashboard-outline</v-icon>
<span class="nav-title">{{ $t("App.Printers")}}</span>
<v-menu bottom :offset-x="true">
<template v-slot:activator="{ on, attrs }">
<v-icon class="nav-arrow right" v-bind="attrs" v-on="on" >mdi-chevron-down</v-icon>
</template>
<v-list dense>
<v-list-item two-line v-for="printer in printers" v-bind:key="printer._namespace" @click="changePrinter(printer)" :disabled="!printer.socket.isConnected" link>
<v-list-item-content>
<v-list-item-title>{{ getPrinterName(printer._namespace) }}</v-list-item-title>
<v-list-item-subtitle>{{ getPrinterDescription(printer)}}</v-list-item-subtitle>
</v-list-item-content>
</v-list-item>
</v-list>
</v-menu>
</div>
<v-item-group class="v-btn-toggle mx-4 d-block row" name="printers" v-if="false">
<v-btn class="col" color="primary" @click="switchToPrinters">{{ $t("App.Printers")}}</v-btn>
</v-item-group>
</li>
<v-divider class="my-4"></v-divider>
</div>
</template>
<script lang="ts">
import {Component, Mixins} from 'vue-property-decorator'
import BaseMixin from './mixins/base'
import router from '@/plugins/router'
import {FarmPrinterState} from '@/store/farm/printer/types'
@Component
export default class TheSidebarPrinterMenu extends Mixins(BaseMixin) {
get displayMenuPoint() {
return (this.remoteMode && this.countPrinters > 1) || (!this.remoteMode && this.countPrinters)
}
get printers() {
return this.$store.getters['farm/getPrinters']
}
get countPrinters() {
return this.$store.getters['farm/countPrinters']
}
get currentPage() {
return this.$route.fullPath
}
switchToPrinters() {
router.push('/allPrinters')
}
getPrinterName(namespace: string) {
return this.$store.getters['farm/'+namespace+'/getPrinterName']
}
getPrinterDescription(printer: FarmPrinterState) {
return this.$store.getters['farm/'+printer._namespace+'/getStatus']
}
changePrinter(printer: FarmPrinterState) {
if (printer.socket.isConnected) {
this.$store.dispatch('changePrinter', { printer: printer._namespace })
}
}
}
</script>

View File

@ -8,7 +8,7 @@
<div>
<v-menu bottom left :offset-y="true" :close-on-content-click="false" v-model="showMenu">
<template v-slot:activator="{ on, attrs }">
<v-btn color="grey darken-3" v-bind="attrs" v-on="on" class="ml-5 minwidth-0 px-2">
<v-btn icon tile large v-bind="attrs" v-on="on">
<v-icon>mdi-power-standby</v-icon>
</v-btn>
</template>

View File

@ -6,35 +6,51 @@
<template>
<div>
<v-app-bar app elevate-on-scroll>
<v-app-bar app elevate-on-scroll height="48px" clipped-left>
<v-app-bar-nav-icon @click.stop="naviDrawer = !naviDrawer"></v-app-bar-nav-icon>
<router-link to="/">
<template v-if="sidebarLogo">
<img :src="sidebarLogo" style="height: 32px;" class="nav-logo ml-4 mr-1 d-none d-sm-flex" alt="Logo" />
</template>
<template v-else>
<mainsail-logo :color="logoColor" style="height: 32px;" class="nav-logo ml-4 mr-1 d-none d-sm-flex" router to="/" :ripple="false"></mainsail-logo>
</template>
</router-link>
<v-toolbar-title class="text-no-wrap ml-0 pl-2 mr-2">{{ printerName }}</v-toolbar-title>
<printer-selector v-if="countPrinters"></printer-selector>
<v-spacer></v-spacer>
<the-throttled-states></the-throttled-states>
<input type="file" ref="fileUploadAndStart" :accept="validGcodeExtensions.join(', ')" style="display: none" @change="uploadAndStart" />
<v-btn
<v-btn tile large
:icon="$vuetify.breakpoint.smAndDown"
:text="$vuetify.breakpoint.mdAndUp"
color="primary"
class="mr-5 button-min-width-auto px-3 d-none d-sm-flex"
class="button-min-width-auto px-3 d-none d-sm-flex save-config-button"
v-if="klippyIsConnected && saveConfigPending"
:disabled="printerIsPrinting"
:loading="loadings.includes('topbarSaveConfig')"
@click="saveConfig">
<v-icon class="d-md-none">mdi-content-save</v-icon><span class="d-none d-md-inline">{{ $t("App.TopBar.SAVE_CONFIG") }}</span>
</v-btn>
<v-btn
<v-btn tile large
:icon="$vuetify.breakpoint.smAndDown"
:text="$vuetify.breakpoint.mdAndUp"
color="primary"
class="mr-5 button-min-width-auto px-3 d-none d-sm-flex"
v-if="klippyIsConnected && ['standby', 'complete', 'cancelled'].includes(printer_state)"
class="button-min-width-auto px-3 d-none d-sm-flex upload-and-start-button"
v-if="klippyIsConnected && ['standby', 'complete', 'cancelled'].includes(printer_state) && !boolHideUploadAndPrintButton"
:loading="loadings.includes('btnUploadAndStart')"
@click="btnUploadAndStart">
<v-icon class="mr-md-2">mdi-file-upload</v-icon><span class="d-none d-md-inline">{{ $t("App.TopBar.UploadPrint") }}</span>
</v-btn>
<v-btn
<v-btn tile large
:icon="$vuetify.breakpoint.smAndDown"
:text="$vuetify.breakpoint.mdAndUp"
color="error"
class="button-min-width-auto px-3"
class="button-min-width-auto px-3 emergency-button"
v-if="klippyIsConnected"
:loading="loadings.includes('topbarEmergencyStop')"
@click="btnEmergencyStop">
<v-icon class="mr-md-2">mdi-alert-circle-outline</v-icon><span class="d-none d-md-flex">{{ $t("App.TopBar.EmergencyStop") }}</span>
<v-icon class="mr-md-2">mdi-alert-circle-outline</v-icon><span class="d-none d-md-inline">{{ $t("App.TopBar.EmergencyStop") }}</span>
</v-btn>
<the-settings-menu></the-settings-menu>
<the-top-corner-menu></the-top-corner-menu>
@ -90,6 +106,8 @@ import TheTopCornerMenu from '@/components/TheTopCornerMenu.vue'
import TheSettingsMenu from '@/components/TheSettingsMenu.vue'
import TheThrottledStates from '@/components/TheThrottledStates.vue'
import Panel from '@/components/ui/Panel.vue'
import PrinterSelector from '@/components/ui/PrinterSelector.vue'
import MainsailLogo from '@/components/ui/MainsailLogo.vue'
type uploadSnackbar = {
status: boolean
@ -109,7 +127,9 @@ type uploadSnackbar = {
Panel,
TheThrottledStates,
TheSettingsMenu,
TheTopCornerMenu
TheTopCornerMenu,
PrinterSelector,
MainsailLogo
}
})
export default class TheTopbar extends Mixins(BaseMixin) {
@ -154,6 +174,33 @@ export default class TheTopbar extends Mixins(BaseMixin) {
return this.$store.state.printer.configfile?.save_config_pending ?? false
}
get printerName():string {
if (this.$store.state.gui.general.printername.length)
return this.$store.state.gui.general.printername
return this.$store.state.printer.hostname
}
get boolWideNavDrawer() {
return this.$store.state.gui.dashboard.boolWideNavDrawer ?? false
}
get countPrinters() {
return this.$store.getters['farm/countPrinters']
}
get boolHideUploadAndPrintButton() {
return this.$store.state.gui.dashboard.boolHideUploadAndPrintButton ?? false
}
get sidebarLogo(): string {
return this.$store.getters['files/getSidebarLogo']
}
get logoColor(): string {
return this.$store.state.gui.theme.logo
}
btnEmergencyStop() {
const confirmOnEmergencyStop = this.$store.state.gui.general.confirmOnEmergencyStop
if (confirmOnEmergencyStop) {

View File

@ -59,4 +59,8 @@ export default class BaseMixin extends Vue {
get isWidescreen() {
return this.$vuetify.breakpoint.xl
}
get isTouchDevice() {
return (('ontouchstart' in window) || (navigator.maxTouchPoints > 0))
}
}

View File

@ -0,0 +1,86 @@
<style scoped>
.klipper-logo {
transform: rotate(90deg);
}
.moonraker-logo {
transform: rotate(45deg);
color: #EBC815;
}
.version-container {
display: grid;
grid-template-columns: 20px auto;
}
</style>
<template>
<v-dialog
transition="dialog-bottom-transition"
max-width="600"
scrollable
v-model="isOpen"
>
<template v-slot:activator="{ an }">
<v-tooltip right color="panel">
<template v-slot:activator="{ on, attrs }">
<!-- <v-icon v-bind="attrs" @mouseenter="on.mouseenter" @mouseleave="on.mouseleave" @click.stop="isOpen = true">mdi-help-circle-outline</v-icon> -->
<v-icon v-on="an" v-bind="attrs" @mouseenter="on.mouseenter" @mouseleave="on.mouseleave">mdi-help-circle-outline</v-icon>
</template>
<span class="dark version-container">
<div><img height="12" src="/img/logo.svg"></div>
<div>v{{ mainsailVersion }}</div>
<div><v-icon small class="moonraker-logo">mdi-moon-waning-crescent</v-icon></div>
<div>{{ moonrakerVersion }}</div>
<div><img height="12" src="/img/klipper.svg" class="klipper-logo"></div>
<div>{{ klipperVersion }}</div>
</span>
</v-tooltip>
</template>
<template v-slot:default>
<panel title="About" :margin-bottom="false">
<template v-slot:buttons>
<v-btn icon tile @click="isOpen = false"><v-icon>mdi-close-thick</v-icon></v-btn>
</template>
<v-card-text>
<v-container fliud class="align-self-start">
<v-row tag="logo">
<v-col cols="auto">
<v-img height="80" contain src="https://docs.mainsail.xyz/assets/img/logo-mainsail.png"></v-img>
</v-col>
</v-row>
<v-row tag="data1" justify="center">
<v-col class="pr-3 text-right">Version</v-col>
<v-col>v2.1.0-alpha</v-col>
</v-row>
</v-container>
</v-card-text>
</panel>
</template>
</v-dialog>
</template>
<script lang="ts">
import BaseMixin from '../mixins/base'
import {Mixins} from 'vue-property-decorator'
import Component from 'vue-class-component'
import Panel from '@/components/ui/Panel.vue'
@Component({
components: {Panel}
})
export default class AboutModal extends Mixins(BaseMixin) {
private isOpen = false;
get mainsailVersion(): string {
return this.$store.state.packageVersion
}
get klipperVersion():string {
return this.$store.state.printer?.software_version ?? ''
}
get moonrakerVersion():string {
return this.$store.state.server?.moonraker_version ?? ''
}
}
</script>

View File

@ -16,7 +16,26 @@
:title="$t('Panels.MiniconsolePanel.Headline')"
:collapsible="true"
card-class="miniconsole-panel"
:hideButtonsOnCollapse="true"
>
<template v-slot:buttons>
<command-help-modal @onCommand="gcode = $event" :inToolbar="true" ></command-help-modal>
<v-menu :offset-y="true" :close-on-content-click="false" :title="$t('Panels.MiniconsolePanel.SetupConsole')">
<template v-slot:activator="{ on, attrs }">
<v-btn icon v-bind="attrs" v-on="on"><v-icon small>mdi-filter</v-icon></v-btn>
</template>
<v-list>
<v-list-item class="minHeight36">
<v-checkbox class="mt-0" v-model="hideWaitTemperatures" hide-details :label="$t('Panels.MiniconsolePanel.HideTemperatures')"></v-checkbox>
</v-list-item>
<v-list-item class="minHeight36" v-for="(filter, index) in customFilters" v-bind:key="index">
<v-checkbox class="mt-0" v-model="filter.bool" @change="toggleFilter(filter)" hide-details :label="filter.name"></v-checkbox>
</v-list-item>
</v-list>
</v-menu>
</template>
<div class="d-flex flex-column">
<v-card-text :class="consoleDirection === 'table' ? 'order-1' : 'order-2'">
<v-row>
@ -39,25 +58,14 @@
hide-details
outlined
dense
:prepend-icon="isTouchDevice ? 'mdi-chevron-double-right' : ''"
@click:prepend="getAutocomplete"
append-icon="mdi-send"
@click:append="doSend"
></v-textarea>
</v-col>
<v-col class="col-auto">
<command-help-modal @onCommand="gcode = $event" ></command-help-modal>
<v-menu :offset-y="true" :close-on-content-click="false" :title="$t('Panels.MiniconsolePanel.SetupConsole')">
<template v-slot:activator="{ on, attrs }">
<v-btn class="px-2 minwidth-0" color="grey darken-3 ml-3" v-bind="attrs" v-on="on"><v-icon>mdi-filter</v-icon></v-btn>
</template>
<v-list>
<v-list-item class="minHeight36">
<v-checkbox class="mt-0" v-model="hideWaitTemperatures" hide-details :label="$t('Panels.MiniconsolePanel.HideTemperatures')"></v-checkbox>
</v-list-item>
<v-list-item class="minHeight36" v-for="(filter, index) in customFilters" v-bind:key="index">
<v-checkbox class="mt-0" v-model="filter.bool" @change="toggleFilter(filter)" hide-details :label="filter.name"></v-checkbox>
</v-list-item>
</v-list>
</v-menu>
</v-col>
</v-row>
</v-card-text>
@ -124,6 +132,7 @@ export default class MiniconsolePanel extends Mixins(BaseMixin) {
return this.$store.getters['server/getConsoleEvents'](this.consoleDirection === 'table', 250)
}
@Watch('events')
eventsChanged() {
if (this.consoleDirection === 'shell'){

View File

@ -363,28 +363,28 @@ export default class StatusPanel extends Mixins(BaseMixin) {
return [
{
text: this.$t('Panels.StatusPanel.PausePrint'),
color: 'orange',
color: 'warning',
icon: 'mdi-pause',
loadingName: 'statusPrintPause',
status: ['printing'],
click: this.btnPauseJob
}, {
text: this.$t('Panels.StatusPanel.ResumePrint'),
color: 'orange',
color: 'success',
icon: 'mdi-play',
loadingName: 'statusPrintResume',
status: ['paused'],
click: this.btnResumeJob
}, {
text: this.$t('Panels.StatusPanel.ExcludeObject.ExcludeObject'),
color: 'orange',
color: 'warning',
icon: 'mdi-selection-remove',
loadingName: '',
status: this.printing_objects.length ? ['paused', 'printing'] : [],
click: this.btnExcludeObject
}, {
text: this.$t('Panels.StatusPanel.CancelPrint'),
color: 'red',
color: 'error',
icon: 'mdi-stop',
loadingName: 'statusPrintCancel',
status: this.$store.state.gui.general.displayCancelPrint ? ['paused', 'printing'] : ['paused'],

View File

@ -59,6 +59,14 @@
<settings-row :title="$t('Settings.UiSettingsTab.ConfirmOnPowerDeviceChange')" :sub-title="$t('Settings.UiSettingsTab.ConfirmOnPowerDeviceChangeDescription')" :dynamicSlotWidth="true">
<v-switch v-model="confirmOnPowerDeviceChange" hide-details class="mt-0"></v-switch>
</settings-row>
<v-divider class="my-2"></v-divider>
<settings-row :title="$t('Settings.UiSettingsTab.BoolWideNavDrawer')" :sub-title="$t('Settings.UiSettingsTab.BoolWideNavDrawerDescription')" :dynamicSlotWidth="true">
<v-switch v-model="boolWideNavDrawer" hide-details class="mt-0"></v-switch>
</settings-row>
<v-divider class="my-2"></v-divider>
<settings-row :title="$t('Settings.UiSettingsTab.BoolHideUploadAndPrintButton')" :sub-title="$t('Settings.UiSettingsTab.BoolHideUploadAndPrintButtonDescription')" :dynamicSlotWidth="true">
<v-switch v-model="boolHideUploadAndPrintButton" hide-details class="mt-0"></v-switch>
</settings-row>
</v-card-text>
</v-card>
</div>
@ -143,6 +151,22 @@ export default class SettingsUiSettingsTab extends Mixins(BaseMixin) {
this.$store.dispatch('gui/saveSetting', {name: 'general.confirmOnPowerDeviceChange', value: newVal })
}
get boolWideNavDrawer() {
return this.$store.state.gui.dashboard.boolWideNavDrawer ?? false
}
set boolWideNavDrawer(newVal) {
this.$store.dispatch('gui/saveSetting', {name: 'dashboard.boolWideNavDrawer', value: newVal })
}
get boolHideUploadAndPrintButton() {
return this.$store.state.gui.dashboard.boolHideUploadAndPrintButton ?? false
}
set boolHideUploadAndPrintButton(newVal) {
this.$store.dispatch('gui/toggleHideUploadAndPrintBtn', newVal)
}
clearColorObject(color: any): string {
if (typeof color === 'object' && 'hex' in color)
color = color.hex

View File

@ -34,8 +34,10 @@
</v-toolbar-title>
<slot name="buttons-title"></slot>
<v-spacer></v-spacer>
<v-toolbar-items v-if="hasButtonsSlot || collapsible">
<slot name="buttons"></slot>
<v-toolbar-items v-show="hasButtonsSlot || collapsible">
<div class="d-flex align-center" v-if="expand || !hideButtonsOnCollapse">
<slot name="buttons"></slot>
</div>
<v-btn
v-if="collapsible"
@click="expand = !expand"
@ -69,6 +71,7 @@ export default class Panel extends Mixins(BaseMixin) {
@Prop({ default: '' }) readonly toolbarClass!: string
@Prop({ default: false }) readonly loading!: boolean
@Prop({ default: true }) readonly marginBottom!: boolean
@Prop({ default: false }) readonly hideButtonsOnCollapse!: boolean
get expand() {
return this.$store.getters['gui/getPanelExpand'](this.cardClass)

View File

@ -0,0 +1,66 @@
<style scoped>
</style>
<template>
<v-menu bottom :offset-x="true">
<template v-slot:activator="{ on, attrs }">
<v-icon class="nav-arrow right" v-bind="attrs" v-on="on" >mdi-chevron-down</v-icon>
</template>
<v-list dense>
<v-list-item two-line v-for="printer in printers" v-bind:key="printer._namespace" @click="changePrinter(printer)" :disabled="!printer.socket.isConnected" link>
<v-list-item-content>
<v-list-item-title>{{ getPrinterName(printer._namespace) }}</v-list-item-title>
<v-list-item-subtitle>{{ getPrinterDescription(printer)}}</v-list-item-subtitle>
</v-list-item-content>
</v-list-item>
</v-list>
</v-menu>
</template>
<script lang="ts">
import {Component, Mixins} from 'vue-property-decorator'
import BaseMixin from '../mixins/base'
import router from '@/plugins/router'
import {FarmPrinterState} from '@/store/farm/printer/types'
@Component
export default class PrinterSelector extends Mixins(BaseMixin) {
get displayMenuPoint() {
return (this.remoteMode && this.countPrinters > 1) || (!this.remoteMode && this.countPrinters)
}
get printers() {
return this.$store.getters['farm/getPrinters']
}
get countPrinters() {
return this.$store.getters['farm/countPrinters']
}
get currentPage() {
return this.$route.fullPath
}
switchToPrinters() {
router.push('/allPrinters')
}
getPrinterName(namespace: string) {
return this.$store.getters['farm/'+namespace+'/getPrinterName']
}
getPrinterDescription(printer: FarmPrinterState) {
return this.$store.getters['farm/'+printer._namespace+'/getStatus']
}
changePrinter(printer: FarmPrinterState) {
if (printer.socket.isConnected) {
this.$store.dispatch('changePrinter', { printer: printer._namespace })
}
}
}
</script>

View File

@ -670,6 +670,10 @@
"ShowWebcamInNavigation": "Show Webcam in navigation",
"BoolBigThumbnail": "Large thumbnail",
"BoolBigThumbnailDescription": "Display a large thumbnail in the status panel during a print.",
"BoolWideNavDrawer": "Wide Navigation Drawer",
"BoolWideNavDrawerDescription": "Display a wider Navigation Toolbar",
"BoolHideUploadAndPrintButton": "Hide Upload and Print Button",
"BoolHideUploadAndPrintButtonDescription": "Show or hide the \"Upload and Print\" button in the topbar.",
"DisplayCANCEL_PRINT": "Display CANCEL_PRINT",
"DisplayCANCEL_PRINTDescription": "Shows the CANCEL_PRINT button permanently - no second layer confirmation needed.",
"DisplayZOffset": "Show Z-Offset-Panel",

View File

@ -32,6 +32,8 @@
hide-details
outlined
dense
:prepend-icon="isTouchDevice ? 'mdi-chevron-double-right' : ''"
@click:prepend="getAutocomplete"
append-icon="mdi-send"
@click:append="doSend"
></v-textarea>

View File

@ -6,7 +6,16 @@ import Vuetify from 'vuetify/lib'
Vue.use(Vuetify)
export default new Vuetify({
theme: { dark: true },
theme: {
dark: true,
themes: {
dark: {
panel: '#1e1e1e',
toolbar: '#272727'
}
},
options: { customProperties: true },
},
icons: {
iconfont: 'mdi',
},

View File

@ -245,4 +245,12 @@ export const actions: ActionTree<GuiState, RootState> = {
value: newVal
})
},
toggleHideUploadAndPrintBtn({commit, dispatch, state}, payload) {
commit('toggleHideUploadAndPrintBtn', payload)
dispatch('updateSettings', {
keyName: 'dashboard.boolHideUploadAndPrintButton',
newVal: state.dashboard.boolHideUploadAndPrintButton
})
}
}

View File

@ -31,6 +31,7 @@ export const getDefaultState = (): GuiState => {
dashboard: {
boolTempchart: true,
boolBigThumbnail: true,
boolWideNavDrawer: false,
macroManagement: 'simple',
hiddenMacros: [],
hiddenTempChart: [],

View File

@ -102,4 +102,8 @@ export const mutations: MutationTree<GuiState> = {
layoutArray.splice(payload.index, 1)
Vue.set(state.dashboard, payload.layoutname, layoutArray)
},
toggleHideUploadAndPrintBtn(state, payload) {
Vue.set(state.dashboard, 'boolHideUploadAndPrintButton', payload)
}
}