ui: V09 redesign - Control/Program/Console/Settings shell
Replaces the legacy side-menu chrome with a 4-tab top header. - index.pug: tablet/kiosk fit-to-viewport script, header tab nav, estop/state badges in header. - app.js: route hash to (control|program|console|<settings-family>), multi-section settings shell. - control-view: header DRO, jog grid, MDI/probe/macros panels. - program-view + program-mixin: file browser + toolpath preview + run/pause/stop, replaces the legacy 'macros' tab content. - console-view: MDI shell, message log, indicators. - settings-shell-view: rail-driven inner pages (Display & Units, Probing, G-code & Motion, Macros, Network, etc.). - settings-view: filter Svelte SettingsView to one rail section. - SettingsView.svelte: tag every section with data-sec=… so the filter above can hide non-matching ones. - style.styl: ~2700 lines of V09 layout, DRO, jog grid, status strip, and tablet/kiosk variants. No A-axis / auxiliary-axis content lives on this branch.
This commit is contained in:
67
src/pug/templates/console-view.pug
Normal file
67
src/pug/templates/console-view.pug
Normal file
@@ -0,0 +1,67 @@
|
||||
script#console-view-template(type="text/x-template")
|
||||
.console-page
|
||||
.console-card
|
||||
.ptab-bar
|
||||
button.ptab(:class="{active: sub === 'mdi'}", @click="select_sub('mdi')")
|
||||
.fa.fa-keyboard
|
||||
| MDI
|
||||
button.ptab(:class="{active: sub === 'messages'}", @click="select_sub('messages')")
|
||||
.fa.fa-comment-dots
|
||||
| Messages
|
||||
span.ptab-badge(v-if="unread_messages") {{unread_messages}}
|
||||
button.ptab(:class="{active: sub === 'indicators'}", @click="select_sub('indicators')")
|
||||
.fa.fa-bell
|
||||
| Indicators
|
||||
|
||||
// ----- MDI -----
|
||||
.mdi-pane(v-show="sub === 'mdi'")
|
||||
.mdi-input
|
||||
span.prompt G>
|
||||
input(type="text", v-model="mdi", :disabled="!can_mdi",
|
||||
@keyup.enter="submit_mdi", placeholder="enter a G-code command…")
|
||||
button.mdi-send(:disabled="!can_mdi || !mdi", @click="submit_mdi")
|
||||
.fa.fa-paper-plane
|
||||
| SEND
|
||||
.mdi-keys
|
||||
button.mkey(@click="prepend('G0 ')") G0
|
||||
button.mkey(@click="prepend('G1 ')") G1
|
||||
button.mkey(@click="prepend('G2 ')") G2
|
||||
button.mkey(@click="prepend('G3 ')") G3
|
||||
button.mkey(@click="prepend('G28 ')") G28
|
||||
button.mkey(@click="prepend('G92 ')") G92
|
||||
button.mkey(@click="prepend('M3 ')") M3
|
||||
button.mkey(@click="prepend('M5 ')") M5
|
||||
button.mkey(@click="append('X')") X
|
||||
button.mkey(@click="append('Y')") Y
|
||||
button.mkey(@click="append('Z')") Z
|
||||
button.mkey(@click="append('W')") W
|
||||
button.mkey(@click="append('F')") F
|
||||
button.mkey(@click="append('S')") S
|
||||
button.mkey.clear(@click="mdi = ''") CLEAR
|
||||
button.mkey.send(:disabled="!can_mdi || !mdi", @click="submit_mdi") SEND ↵
|
||||
|
||||
em Machine units: #[strong {{mach_units}}]. G20/G21 to switch.
|
||||
|
||||
.mdi-history(:class="{placeholder: !history.length}")
|
||||
span.mdi-empty(v-if="!history.length") MDI history will display here.
|
||||
.h-row(v-for="item in history", @click="load_history($index)",
|
||||
track-by="$index")
|
||||
span.h-cmd {{item}}
|
||||
span.h-status ↻
|
||||
|
||||
// ----- Messages -----
|
||||
.messages-pane(v-show="sub === 'messages'")
|
||||
.msg-empty(v-if="!$root.messages_log.length")
|
||||
.fa.fa-circle-check
|
||||
| No messages.
|
||||
.msg(v-for="m in $root.messages_log",
|
||||
:class="m.level === 'warning' ? 'warn' : 'info'", track-by="$index")
|
||||
.mi
|
||||
.fa(:class="m.level === 'warning' ? 'fa-triangle-exclamation' : 'fa-circle-info'")
|
||||
div
|
||||
.mtitle {{m.text}}
|
||||
.mtime ID {{m.id}}
|
||||
|
||||
// ----- Indicators -----
|
||||
.indicators-pane(v-show="sub === 'indicators'")
|
||||
indicators(:state="state", :template="template")
|
||||
@@ -1,475 +1,296 @@
|
||||
script#control-view-template(type="text/x-template")
|
||||
#control
|
||||
.control-page
|
||||
// ----- Modal dialogs (kept verbatim from legacy) -----
|
||||
message(:show.sync="showGcodeMessage")
|
||||
h3(slot="header") Processing New File
|
||||
|
||||
div(slot="body")
|
||||
h3 Please wait..
|
||||
p Simulating GCode to check for errors, calculate ETA and generate 3D view.
|
||||
|
||||
div(slot="footer")
|
||||
label Simulating {{(toolpath_progress || 0) | percent}}
|
||||
|
||||
|
||||
message(:show.sync="showNoGcodeMessage")
|
||||
h3(slot="header") GCode Not Set
|
||||
div(slot="body")
|
||||
p Configure the GCode for the selected macro to use it
|
||||
|
||||
div(slot="footer")
|
||||
button.pure-button(@click="showNoGcodeMessage=false") OK
|
||||
h3(slot="header") GCode Not Set
|
||||
div(slot="body")
|
||||
p Configure the GCode for the selected macro to use it
|
||||
div(slot="footer")
|
||||
button.pure-button(@click="showNoGcodeMessage=false") OK
|
||||
|
||||
message(:show.sync="macrosLoading")
|
||||
h3(slot="header") Run Macro?
|
||||
div(slot="body")
|
||||
p
|
||||
| The macro file
|
||||
strong {{state.selected}}
|
||||
| is being loaded.
|
||||
|
||||
div(slot="footer")
|
||||
button.pure-button(@click="macrosLoading=false") Cancel
|
||||
button.pure-button.pure-button-primary(@click="start_pause") Run
|
||||
|
||||
h3(slot="header") Run Macro?
|
||||
div(slot="body")
|
||||
p
|
||||
| The macro file
|
||||
strong {{state.selected}}
|
||||
| is being loaded.
|
||||
div(slot="footer")
|
||||
button.pure-button(@click="macrosLoading=false") Cancel
|
||||
button.pure-button.pure-button-primary(@click="start_pause") Run
|
||||
|
||||
message(:show.sync="GCodeNotFound")
|
||||
h3(slot="header") File not found
|
||||
div(slot="body")
|
||||
p It seems like the file you selected cannot be found. Try uploading again.
|
||||
p It seems like the file you selected cannot be found. Try uploading again.
|
||||
div(slot="footer")
|
||||
button.pure-button.button-error(@click="GCodeNotFound=false")
|
||||
| OK
|
||||
|
||||
button.pure-button.button-error(@click="GCodeNotFound=false") OK
|
||||
|
||||
message(:show.sync="show_probe_dialog")
|
||||
h3(slot="header") Probe Rotary
|
||||
h3(slot="header") Choose probe type
|
||||
div(slot="body")
|
||||
p Pick which probe routine to run.
|
||||
button.pure-button(:class="state['pw'] ? '' : 'load-on'", @click="showProbeDialog('xyz')") Probe XYZ
|
||||
button.pure-button(:class="state['pw'] ? '' : 'load-on'", @click="showProbeDialog('z')") Probe Z
|
||||
div(slot="footer")
|
||||
button.pure-button(@click="show_probe_dialog=false") Cancel
|
||||
|
||||
// ----- Main grid: jog | (DRO + status strip) -----
|
||||
.control-grid
|
||||
|
||||
table(style="table-layout: fixed; width: 100%;")
|
||||
tr(style="height: fit-content;")
|
||||
td(style="white-space: nowrap; width: 410px;", rowspan="2")
|
||||
table.control-buttons(table-layout="fixed")
|
||||
colgroup
|
||||
col(style="width:100px")
|
||||
col(style="width:100px")
|
||||
col(style="width:100px")
|
||||
col(style="width:100px")
|
||||
tr
|
||||
td(style="height:100px",align="center")
|
||||
button(@click="jog_fn(-1,1,0,0)")
|
||||
.fa.fa-arrow-right(style="transform: rotate(-135deg);")
|
||||
td(style="height:100px",align="center")
|
||||
button(@click="jog_fn(0,1,0,0)") Y+
|
||||
td(style="height:100px",align="center")
|
||||
button(@click="jog_fn(1,1,0,0)")
|
||||
.fa.fa-arrow-right(style="transform: rotate(-45deg);")
|
||||
td(style="height:100px",align="center")
|
||||
button(,@click="jog_fn(0,0,1,0)") Z+
|
||||
tr
|
||||
td(style="height:100px",align="center")
|
||||
button(@click="jog_fn(-1,0,0,0)") X-
|
||||
td(style="height:100px",align="center")
|
||||
button(@click="showMoveToZeroDialog('xy')")
|
||||
| XY
|
||||
br
|
||||
| Origin
|
||||
td(style="height:100px",align="center")
|
||||
button(@click="jog_fn(1,0,0,0)") X+
|
||||
td(style="height:100px",align="center")
|
||||
button(@click="showMoveToZeroDialog('z')")
|
||||
| Z
|
||||
br
|
||||
| Origin
|
||||
tr
|
||||
td(style="height:100px",align="center")
|
||||
button(@click="jog_fn(-1,-1,0,0)")
|
||||
.fa.fa-arrow-right(style="transform: rotate(135deg);")
|
||||
td(style="height:100px",align="center")
|
||||
button(@click="jog_fn(0,-1,0,0)") Y-
|
||||
td(style="height:100px",align="center")
|
||||
button(@click="jog_fn(1,-1,0,0)")
|
||||
.fa.fa-arrow-right(style="transform: rotate(45deg);")
|
||||
td(style="height:100px",align="center")
|
||||
button(@click="jog_fn(0,0,-1,0)") Z-
|
||||
tr
|
||||
td(style="height:100px",align="center")
|
||||
button(:style="getJogIncrStyle('fine')", @click="jog_incr = 'fine'")
|
||||
span {{jog_incr_amounts[display_units].fine}}#[span.jog-units {{metric ? 'mm' : 'in'}}]
|
||||
td(style="height:100px",align="center")
|
||||
button(:style="getJogIncrStyle('small')", @click="jog_incr = 'small'")
|
||||
span {{jog_incr_amounts[display_units].small}}#[span.jog-units {{metric ? 'mm' : 'in'}}]
|
||||
td(style="height:100px",align="center")
|
||||
button(:style="getJogIncrStyle('medium')", @click="jog_incr = 'medium'")
|
||||
span {{jog_incr_amounts[display_units].medium}}#[span.jog-units {{metric ? 'mm' : 'in'}}]
|
||||
td(style="height:100px",align="center")
|
||||
button(:style="getJogIncrStyle('large')", @click="jog_incr = 'large'")
|
||||
span {{jog_incr_amounts[display_units].large}}#[span.jog-units {{metric ? 'mm' : 'in'}}]
|
||||
// ===== JOG =====
|
||||
// Hidden only while a G-code program is running / paused /
|
||||
// stopping. Jogging / homing / MDI moves do not hide it.
|
||||
.jog-card(v-if="!is_program_executing")
|
||||
.jog-head
|
||||
.jog-title
|
||||
| Jog
|
||||
span.step-pre · step
|
||||
span.step {{jog_incr_amounts[display_units][jog_incr]}}#[span.unit {{metric ? 'mm' : 'in'}}]
|
||||
.step-seg
|
||||
button(:class="{active: jog_incr === 'fine'}", @click="jog_incr = 'fine'")
|
||||
| {{jog_incr_amounts[display_units].fine}}
|
||||
button(:class="{active: jog_incr === 'small'}", @click="jog_incr = 'small'")
|
||||
| {{jog_incr_amounts[display_units].small}}
|
||||
button(:class="{active: jog_incr === 'medium'}", @click="jog_incr = 'medium'")
|
||||
| {{jog_incr_amounts[display_units].medium}}
|
||||
button(:class="{active: jog_incr === 'large'}", @click="jog_incr = 'large'")
|
||||
| {{jog_incr_amounts[display_units].large}}
|
||||
|
||||
tr(v-if="state['2an'] == 3")
|
||||
td(style="height:100px", align="center", colspan="1")
|
||||
button(@click="show_probe_dialog=true")
|
||||
| Probe
|
||||
br
|
||||
| Rotary
|
||||
.jog-grid
|
||||
// Row 1
|
||||
button.jbtn.dir(@click="jog_fn(-1, 1, 0, 0)", title="X- Y+")
|
||||
.fa.fa-arrow-up.ico(style="transform: rotate(-45deg)")
|
||||
button.jbtn(@click="jog_fn(0, 1, 0, 0)") Y+
|
||||
button.jbtn.dir(@click="jog_fn(1, 1, 0, 0)", title="X+ Y+")
|
||||
.fa.fa-arrow-up.ico(style="transform: rotate(45deg)")
|
||||
button.jbtn(@click="jog_fn(0, 0, 1, 0)") Z+
|
||||
|
||||
td(style="height:100px", align="center", colspan="1")
|
||||
button(@click="jog_fn(0,0,0,-1)", style="display: grid;justify-content: center;align-items: center;padding: 14px;")
|
||||
| A-
|
||||
.fa.fa-rotate-left
|
||||
|
||||
td(style="height:100px", align="center", colspan="1")
|
||||
button(@click="showMoveToZeroDialog('a')")
|
||||
| A
|
||||
br
|
||||
| Origin
|
||||
// Row 2
|
||||
button.jbtn(@click="jog_fn(-1, 0, 0, 0)") X−
|
||||
button.jbtn(@click="showMoveToZeroDialog('xy')")
|
||||
span.lbl XY
|
||||
span Origin
|
||||
button.jbtn(@click="jog_fn(1, 0, 0, 0)") X+
|
||||
button.jbtn(@click="showMoveToZeroDialog('z')")
|
||||
span.lbl Z
|
||||
span Origin
|
||||
|
||||
td(style="height:100px", align="center", colspan="1")
|
||||
button(@click="jog_fn(0,0,0,1)", style="display: grid;justify-content: center;align-items: center;padding: 14px;")
|
||||
| A+
|
||||
.fa.fa-rotate-right
|
||||
// Row 3
|
||||
button.jbtn.dir(@click="jog_fn(-1, -1, 0, 0)", title="X- Y-")
|
||||
.fa.fa-arrow-down.ico(style="transform: rotate(45deg)")
|
||||
button.jbtn(@click="jog_fn(0, -1, 0, 0)") Y−
|
||||
button.jbtn.dir(@click="jog_fn(1, -1, 0, 0)", title="X+ Y-")
|
||||
.fa.fa-arrow-down.ico(style="transform: rotate(-45deg)")
|
||||
button.jbtn(@click="jog_fn(0, 0, -1, 0)") Z−
|
||||
|
||||
tr(v-else)
|
||||
td(style="height:100px", align="center", colspan="2")
|
||||
button(:class="state['pw'] ? '' : 'load-on'",
|
||||
style="height:100px;width:200px",
|
||||
@click="showProbeDialog('xyz')")
|
||||
| Probe XYZ
|
||||
// Row 4 — A axis (rotary) when rotary is enabled.
|
||||
template(v-if="state['2an'] == 3")
|
||||
button.jbtn.dir(@click="jog_fn(0, 0, 0, -1)")
|
||||
.fa.fa-rotate-left.ico
|
||||
span.lbl A−
|
||||
button.jbtn.ghost(@click="showMoveToZeroDialog('a')")
|
||||
span.lbl A
|
||||
span Origin
|
||||
button.jbtn.dir(@click="jog_fn(0, 0, 0, 1)")
|
||||
.fa.fa-rotate-right.ico
|
||||
span.lbl A+
|
||||
button.jbtn(@click="show_probe_dialog=true",
|
||||
:class="{'load-on': !state['pw']}")
|
||||
.fa.fa-bullseye.ico
|
||||
span.lbl Probe
|
||||
|
||||
td(style="height:100px", align="center", colspan="2")
|
||||
button(:class="state['pw'] ? '' : 'load-on'",
|
||||
style="height:100px;width:200px",
|
||||
@click="showProbeDialog('z')")
|
||||
| Probe Z
|
||||
// Row 4 — fallback probe / zero / home shortcuts
|
||||
template(v-if="state['2an'] != 3")
|
||||
button.jbtn(@click="showProbeDialog('xyz')",
|
||||
:class="{'load-on': !state['pw']}")
|
||||
.fa.fa-bullseye.ico
|
||||
span.lbl Probe XYZ
|
||||
button.jbtn.ghost(@click="zero()", :disabled="!can_set_axis")
|
||||
.fa.fa-location-dot.ico
|
||||
span.lbl Zero all
|
||||
button.jbtn(@click="showProbeDialog('z')",
|
||||
:class="{'load-on': !state['pw']}")
|
||||
.fa.fa-bullseye.ico
|
||||
span.lbl Probe Z
|
||||
button.jbtn.ghost(@click="home()")
|
||||
.fa.fa-home.ico
|
||||
span.lbl Home all
|
||||
|
||||
td(style="vertical-align: top;")
|
||||
table.axes
|
||||
tr(:class="axes.klass")
|
||||
th.name Axis
|
||||
th.position Position
|
||||
th.absolute Absolute
|
||||
th.offset Offset
|
||||
th.state State
|
||||
th.tstate Toolpath
|
||||
th.actions
|
||||
button.pure-button(disabled, style="height:60px;width:60px;display:none;")
|
||||
|
||||
button.pure-button(:disabled="!can_set_axis",
|
||||
title="Zero all axis offsets.", @click="zero()",style="height:60px;width:60px")
|
||||
.fa.fa-map-marker
|
||||
|
||||
button.pure-button(title="Home all axes.", @click="home()",
|
||||
:disabled="!is_idle",style="height:60px;width:60px")
|
||||
.fa.fa-home
|
||||
|
||||
each axis in 'xyzabc'
|
||||
tr.axis(:class=`${axis}.klass`, v-if=`${axis}.enabled`,
|
||||
:title=`${axis}.title`)
|
||||
th.name= axis
|
||||
td.position: unit-value(:value=`${axis}.pos`, precision=4)
|
||||
td.absolute: unit-value(:value=`${axis}.abs`, precision=3)
|
||||
td.offset: unit-value(:value=`${axis}.off`, precision=3)
|
||||
td.state
|
||||
.fa(:class=`'fa-' + ${axis}.icon`)
|
||||
| {{#{axis}.state}}
|
||||
td.tstate(:class=`${axis}.tklass`, :title=`${axis}.toolmsg`, @click=`showToolpathMessageDialog('${axis}')`)
|
||||
.fa(:class=`'fa-' + ${axis}.ticon`)
|
||||
| {{#{axis}.tstate}}
|
||||
|
||||
th.actions
|
||||
button.pure-button(:disabled="!can_set_axis",
|
||||
title=`Set {{'${axis}' | upper}} axis position.`,
|
||||
@click=`show_set_position('${axis}')`, style="height:60px;width:60px")
|
||||
.fa.fa-cog
|
||||
|
||||
button.pure-button(:disabled="!can_set_axis",
|
||||
title=`Zero {{'${axis}' | upper}} axis offset.`,
|
||||
@click=`zero('${axis}')`, style="height:60px;width:60px")
|
||||
.fa.fa-map-marker
|
||||
|
||||
button.pure-button(:disabled="!is_idle", @click=`home('${axis}')`,
|
||||
title=`Home {{'${axis}' | upper}} axis.`, style="height:60px;width:60px")
|
||||
.fa.fa-home
|
||||
|
||||
tr(style="vertical-align: top;")
|
||||
td
|
||||
table(width="100%")
|
||||
tr
|
||||
td(style="text-align:center")
|
||||
table.info
|
||||
tr
|
||||
th State
|
||||
td(:class="{attention: highlight_state}") {{mach_state}}
|
||||
|
||||
tr
|
||||
th Message
|
||||
td.message(:class="{attention: highlight_state}")
|
||||
| {{message.replace(/^#/, '')}}
|
||||
|
||||
tr
|
||||
th Display Units
|
||||
td.units
|
||||
select(v-model="display_units")
|
||||
option(value="METRIC") METRIC
|
||||
option(value="IMPERIAL") IMPERIAL
|
||||
|
||||
tr(title="Active tool")
|
||||
th Tool
|
||||
td {{state.tool || 0}}
|
||||
|
||||
td
|
||||
table.info
|
||||
tr(
|
||||
title="Current velocity in {{metric ? 'meters' : 'inches'}} per minute")
|
||||
th Velocity
|
||||
td
|
||||
unit-value(:value="state.v", precision="2", unit="", iunit="",
|
||||
scale="0.0254")
|
||||
| {{metric ? ' m/min' : ' IPM'}}
|
||||
|
||||
tr(title="Programmed feed rate.")
|
||||
th Feed
|
||||
td
|
||||
unit-value(:value="state.feed", precision="2", unit="", iunit="")
|
||||
| {{metric ? ' mm/min' : ' IPM'}}
|
||||
|
||||
tr(title="Programed and actual speed.")
|
||||
th Speed
|
||||
td
|
||||
| {{state.speed || 0 | fixed 0}}
|
||||
span(v-if="!isNaN(state.s)") ({{state.s | fixed 0}})
|
||||
= ' RPM'
|
||||
|
||||
tr(title="Load switch states.")
|
||||
th Loads
|
||||
td
|
||||
span(:class="state['1oa'] ? 'load-on' : ''")
|
||||
| 1:{{state['1oa'] ? 'On' : 'Off'}}
|
||||
|
|
||||
span(:class="state['2oa'] ? 'load-on' : ''")
|
||||
| 2:{{state['2oa'] ? 'On' : 'Off'}}
|
||||
|
||||
td
|
||||
table.info
|
||||
tr
|
||||
th Remaining
|
||||
td(title="Total run time (days:hours:mins:secs)").
|
||||
#[span(v-if="plan_time_remaining") {{plan_time_remaining | time}} of]
|
||||
{{toolpath.time | time}}
|
||||
|
||||
tr
|
||||
th ETA
|
||||
td.eta {{eta}}
|
||||
|
||||
tr
|
||||
th Line
|
||||
td
|
||||
| {{0 <= state.line ? state.line : 0 | number}}
|
||||
span(v-if="toolpath.lines")
|
||||
| of {{toolpath.lines | number}}
|
||||
|
||||
tr
|
||||
th Progress
|
||||
td.progress
|
||||
label {{(progress || 0) | percent}}
|
||||
.bar(:style="'width:' + (progress || 0) * 100 + '%'")
|
||||
|
||||
.macros-div(class="present")
|
||||
button.macros-button(title="Click to run Macros",v-for="(index,macros) in state.macros",
|
||||
@click="run_macro(index)",:disabled="!is_ready",v-bind:style="{ backgroundColor: macros.color }") {{macros.name}}
|
||||
|
||||
.tabs
|
||||
|
||||
input#tab1(type="radio", name="tabs",checked="" @click="tab = 'auto'")
|
||||
label(for="tab1", title="Run GCode programs",style="height:50px;width:100px") Auto
|
||||
|
||||
input#tab2(type="radio", name="tabs", @click="tab = 'mdi'")
|
||||
label(for="tab2", title="Manual GCode entry",style="height:50px;width:100px") MDI
|
||||
|
||||
input#tab3(type="radio", name="tabs", @click="tab = 'messages'")
|
||||
label(for="tab3",style="height:50px;width:100px") Messages
|
||||
|
||||
input#tab4(type="radio", name="tabs", @click="tab = 'indicators'")
|
||||
label(for="tab4",style="height:50px;width:100px") Indicators
|
||||
|
||||
|
||||
|
||||
|
||||
section#content1.tab-content.pure-form
|
||||
.toolbar.pure-control-group
|
||||
button.pure-button(:class="{'attention': is_holding}",
|
||||
title="{{is_running ? 'Pause' : 'Start'}} program.",
|
||||
@click="start_pause", :disabled="!state.selected",
|
||||
style="height:100px;width:100px;font-weight:normal")
|
||||
img(v-if="is_running" src="images/pause_gcode.png" style="height: 55px;")
|
||||
img(v-else src="images/play_gcode.png" style="height: 55px;")
|
||||
|
||||
button.pure-button(title="Stop program.", @click="stop", style="height:100px;width:100px;font-weight:normal")
|
||||
img(src="images/stop.png" style="height: 55px;")
|
||||
|
||||
button.pure-button(title="Pause program at next optional stop (M1).",
|
||||
@click="optional_pause", v-if="false", style="height:100px;width:100px;font-weight:normal")
|
||||
.fa.fa-stop-circle-o
|
||||
|
||||
message(:show.sync="uploading_files")
|
||||
h3(slot="header") Files uploading
|
||||
div(slot="body")
|
||||
h3 Please wait...
|
||||
p
|
||||
p The files are currently being uploaded.
|
||||
p Do not close the window.
|
||||
div(slot="footer")
|
||||
|
||||
button.pure-button(title="Execute one program step.", @click="step",
|
||||
:disabled="(!is_ready && !is_holding) || !state.selected",
|
||||
v-if="false", style="height:100px;width:100px;font-weight:normal")
|
||||
.fa.fa-step-forward
|
||||
|
||||
button.pure-button(title="Upload a new GCode folder.", @click="open_folder",
|
||||
:disabled="!is_ready",style="height:100px;width:100px;font-weight:normal")
|
||||
img(src="images/upload_folder.png" style="height: 65px;")
|
||||
|
||||
form.gcode-folder-input.file-upload
|
||||
input#folderInput(type="file", @change="upload_folder", :disabled="!is_ready",
|
||||
webkitdirectory, directory)
|
||||
|
||||
button.pure-button(title="Upload a new GCode program.", @click="open_file",
|
||||
:disabled="!is_ready",style="height:100px;width:100px;font-weight:normal")
|
||||
img(src="images/upload_gcode.png" style="height: 65px;")
|
||||
|
||||
form.gcode-file-input.file-upload
|
||||
input(type="file", @change="upload_file", :disabled="!is_ready",
|
||||
accept=".nc,.ngc,.gcode,.gc", multiple)
|
||||
|
||||
a(:disabled="!state.selected", download,
|
||||
:href="'/api/file/' + state.selected",
|
||||
title="Download the selected GCode program.")
|
||||
button.pure-button(:disabled="!state.selected", style="height:100px;width:100px")
|
||||
img(src="images/download_gcode.png" style="height: 65px;")
|
||||
|
||||
button.pure-button(title="Delete current GCode program.",
|
||||
@click="deleteGCode = true",
|
||||
:disabled="!state.selected || !is_ready",style="height:100px;width:100px;font-weight:normal")
|
||||
img(src="images/delete_gcode.png" style="height: 55px;")
|
||||
|
||||
message.error-message(:show.sync="deleteGCode")
|
||||
h3(slot="header") Select files to delete:
|
||||
div(slot="body")
|
||||
input.search-bar(type="text", v-model="search_query", placeholder="Search Files...")
|
||||
.container
|
||||
.folders
|
||||
h3 Folders
|
||||
div(v-for="(index, folder) in state.gcode_list", :key="index", @click="populateFiles(index)",
|
||||
class="folder-item", :class="{ selected: index === selected_folder_index }") {{ folder.name }}
|
||||
.files
|
||||
h3 Files
|
||||
label.file-item(v-for="item in gcode_filtered_files" :key="item")
|
||||
input(type="checkbox" :value="item" v-model="selected_items_to_delete")
|
||||
| {{ item }}
|
||||
div(slot="footer")
|
||||
button.pure-button(@click="cancel_delete",style="height:50px") Cancel
|
||||
//- button.pure-button.button-error(@click="delete_all_except_macros")
|
||||
//- .fa.fa-trash
|
||||
//- | All
|
||||
button.pure-button.button-success(@click="delete_current",style="height:50px")
|
||||
.fa.fa-trash
|
||||
| Selected
|
||||
|
||||
.drop-down-container
|
||||
message(:show.sync="create_folder")
|
||||
h3(slot="header") Enter folder name:
|
||||
div(slot="body")
|
||||
input.input-name(type="text",minlength='1',maxlength='15',style ="margin-top:1rem;margin-bottom:2rem;",
|
||||
id="folder-name" ,v-model="folder_name",@keypress="edited_folder_name")
|
||||
|
||||
div(slot="footer")
|
||||
button.pure-button(@click="cancel_new_folder") Cancel
|
||||
button.pure-button.button-success(@click="create_new_folder",:disabled="!edited")
|
||||
| Create
|
||||
|
||||
message(:show.sync="confirmDelete")
|
||||
h3(slot="header") Delete Folder?
|
||||
div(slot="body")
|
||||
p Are you sure to delete the folder?
|
||||
|
||||
div(slot="footer")
|
||||
button.pure-button(@click="confirmDelete=false") Cancel
|
||||
button.pure-button.button-error(@click="delete_folder") Folder only
|
||||
button.pure-button.button-success(@click="delete_folder_and_files") Folder and files
|
||||
|
||||
button.pure-button(title="Create a new folder.", @click="create_folder=true",
|
||||
:disabled="!is_ready",style="height:100%")
|
||||
| Create Folder
|
||||
|
||||
button.pure-button(title="Delete a folder.", @click="confirmDelete=true",
|
||||
:disabled="!is_ready",style="height:100%;margin-left:5px")
|
||||
| Delete Folder
|
||||
|
||||
select(title="Select previously uploaded GCode folder.",
|
||||
v-model="state.folder", @change="reset_gcode", :disabled="!is_ready",
|
||||
style="max-width:100%;margin-left:5px")
|
||||
option( selected='' value='default') Default folder
|
||||
option(v-for="file in gcode_folders", :value="file") {{file}}
|
||||
|
||||
select(title="Select previously uploaded GCode programs.",
|
||||
v-model="state.selected", @change="load", :disabled="!is_ready",
|
||||
style="max-width:300px;margin-left:5px")
|
||||
option(v-for="file in gcode_files", :value="file") {{file}}
|
||||
|
||||
button.pure-button(@click="toggle_sorting", :disabled="!is_ready",
|
||||
style="height:75%")
|
||||
| {{files_sortby}}
|
||||
|
||||
.progress(v-if="toolpath_progress && toolpath_progress < 1",
|
||||
title="Simulating GCode to check for errors, calculate ETA and " +
|
||||
"generate 3D view. You can run GCode before the simulation " +
|
||||
"finishes.")
|
||||
div(:style="'width:' + (toolpath_progress || 0) * 100 + '%'")
|
||||
label Simulating {{(toolpath_progress || 0) | percent}}
|
||||
|
||||
path-viewer(:toolpath="toolpath", :state="state", :config="config")
|
||||
gcode-viewer
|
||||
|
||||
section#content2.tab-content
|
||||
.mdi.pure-form(title="Manual GCode entry.")
|
||||
button.pure-button(:disabled="!can_mdi",
|
||||
:class="{'attention': is_holding}",
|
||||
title="{{is_running ? 'Pause' : 'Start'}} command.",
|
||||
@click="mdi_start_pause",style="height:100px;width:100px")
|
||||
.fa(:class="is_running ? 'fa-pause' : 'fa-play'")
|
||||
|
||||
button.pure-button(title="Stop command.", @click="stop",style="height:100px;width:100px")
|
||||
// ===== NOW RUNNING (replaces jog grid only while a G-code
|
||||
// program is actually executing). Jogging is excluded.
|
||||
.running-panel(v-if="is_program_executing")
|
||||
.running-top
|
||||
div
|
||||
.running-file
|
||||
.fa.fa-file-code
|
||||
span(v-if="state.selected") {{state.selected}}
|
||||
span(v-else) {{(mach_state || 'BUSY').toLowerCase()}}
|
||||
.running-meta
|
||||
span(v-if="is_running") {{ (mach_state || 'RUNNING').toLowerCase() }}
|
||||
span(v-if="is_holding") paused
|
||||
span(v-if="is_holding && pause_reason") · {{pause_reason}}
|
||||
span(v-if="is_stopping") stopping
|
||||
span(v-if="toolpath.lines") · line {{state.line || 0 | number}} / {{toolpath.lines | number}}
|
||||
span(v-if="plan_time_remaining") · ETA {{plan_time_remaining | time}}
|
||||
.running-pct
|
||||
| {{((progress || 0) * 100) | fixed 0}}
|
||||
span %
|
||||
.running-progress
|
||||
div(:style="'width:' + ((progress || 0) * 100) + '%'")
|
||||
.running-stats
|
||||
.running-stat
|
||||
.lbl Velocity
|
||||
.val
|
||||
unit-value(:value="state.v", precision="2", unit="", iunit="", scale="0.0254")
|
||||
| {{metric ? 'm/min' : 'IPM'}}
|
||||
.running-stat
|
||||
.lbl Feed
|
||||
.val
|
||||
unit-value(:value="state.feed", precision="0", unit="", iunit="")
|
||||
| {{metric ? 'mm/min' : 'IPM'}}
|
||||
.running-stat
|
||||
.lbl Spindle
|
||||
.val
|
||||
| {{(state.speed || 0) | fixed 0}}
|
||||
span(v-if="state.s != null && !isNaN(state.s)") ({{state.s | fixed 0}})
|
||||
| RPM
|
||||
.running-stat
|
||||
.lbl Tool
|
||||
.val T{{state.tool || 0}}
|
||||
.running-row
|
||||
// While RUNNING the primary action is Pause; while HOLDING / STOPPING it's Resume.
|
||||
button.tx-btn.pause(v-if="is_running", @click="pause()")
|
||||
.fa.fa-pause
|
||||
span.lbl PAUSE
|
||||
button.tx-btn.run(v-if="is_holding || is_stopping", @click="unpause()")
|
||||
.fa.fa-play
|
||||
span.lbl RESUME
|
||||
button.tx-btn.stop(@click="stop()")
|
||||
.fa.fa-stop
|
||||
span.lbl STOP
|
||||
button.tx-btn.step(v-if="is_holding", @click="step()")
|
||||
.fa.fa-forward-step
|
||||
span.lbl STEP
|
||||
|
||||
input(v-model="mdi", :disabled="!can_mdi", @keyup.enter="submit_mdi")
|
||||
// ===== DRO + status strip =====
|
||||
.right-col
|
||||
|
||||
div
|
||||
em The machine is currently operating in #[strong {{mach_units}}] units. Use G20/G21 to switch units.
|
||||
.dro-card
|
||||
.dro-head
|
||||
div Axis
|
||||
div Position
|
||||
div Absolute
|
||||
div Offset
|
||||
.actions-cell
|
||||
// Master Home All. Each row's Actions cell has a per-axis
|
||||
// home button; this header-level button homes every
|
||||
// enabled axis (legacy Onefinity behavior).
|
||||
button.icon-btn(:disabled="!is_idle",
|
||||
title="Home all axes.", @click="home_all()")
|
||||
.fa.fa-house-chimney
|
||||
|
||||
.history(:class="{placeholder: !history}")
|
||||
span(v-if="!history.length") MDI history displays here.
|
||||
ul
|
||||
li(v-for="item in history", @click="load_history($index)",
|
||||
track-by="$index")
|
||||
| {{item}}
|
||||
// Per-axis rows — keep unit-value + bindings from axis-vars
|
||||
each axis in 'xyzabc'
|
||||
.dro-row(:class=`${axis}.klass + ' ' + ${axis}.tklass`,
|
||||
v-if=`${axis}.enabled`,
|
||||
:title=`${axis}.toolmsg ? (${axis}.title + ' — ' + ${axis}.toolmsg) : ${axis}.title`)
|
||||
.dro-axis(:class=`'axis-' + '${axis}'`)= axis.toUpperCase()
|
||||
.dro-pos: unit-value(:value=`${axis}.pos`, precision=4)
|
||||
.dro-sec: unit-value(:value=`${axis}.abs`, precision=3)
|
||||
.dro-sec: unit-value(:value=`${axis}.off`, precision=3)
|
||||
.actions-cell
|
||||
button.icon-btn(:disabled="!can_set_axis",
|
||||
:title=`'Set ${axis.toUpperCase()} axis position.'`,
|
||||
@click=`show_set_position('${axis}')`)
|
||||
.fa.fa-gear
|
||||
button.icon-btn(:class=`${axis}.tklass.indexOf('error') !== -1 ? 'state-red' : (${axis}.tklass.indexOf('warn') !== -1 ? 'state-amber' : 'state-green')`,
|
||||
:disabled="!can_set_axis",
|
||||
:title=`${axis}.toolmsg || ('Zero ${axis.toUpperCase()} axis offset.')`,
|
||||
@click=`zero('${axis}')`)
|
||||
.fa.fa-location-dot
|
||||
button.icon-btn(:class=`${axis}.klass.indexOf('error') !== -1 ? 'state-red' : (${axis}.homed ? 'state-green' : 'state-amber')`,
|
||||
:disabled="!is_idle",
|
||||
:title=`${axis}.title`,
|
||||
@click=`home('${axis}')`)
|
||||
.fa.fa-home
|
||||
|
||||
section#content3.tab-content
|
||||
console
|
||||
// ----- Status strip -----
|
||||
.status-strip
|
||||
.stat-card
|
||||
.stat-label State
|
||||
.stat-val(:class="state_kpi_class") {{mach_state || '--'}}
|
||||
.stat-sub(v-if="message") {{message.replace(/^#/, '')}}
|
||||
.stat-sub(v-else) No alerts
|
||||
|
||||
section#content4.tab-content
|
||||
indicators(:state="state", :template="template")
|
||||
.stat-card
|
||||
.stat-label Velocity / Feed
|
||||
.stat-val
|
||||
unit-value(:value="state.v", precision="2", unit="", iunit="",
|
||||
scale="0.0254")
|
||||
| ·
|
||||
unit-value(:value="state.feed", precision="0", unit="", iunit="")
|
||||
.stat-sub {{metric ? 'm/min · mm/min' : 'IPM · IPM'}}
|
||||
|
||||
.override(title="Feed rate override.")
|
||||
label Feed
|
||||
input(type="range", min="0", max="2", step="0.01",
|
||||
v-model="feed_override", @change="override_feed")
|
||||
span.percent {{feed_override | percent 0}}
|
||||
.stat-card.stat-tappable(@click="overrides_open = !overrides_open",
|
||||
:class="{open: overrides_open}", title="Tap to adjust feed/spindle override")
|
||||
.stat-label Spindle
|
||||
.stat-val
|
||||
| {{(state.speed || 0) | fixed 0}}
|
||||
span(v-if="state.s != null && !isNaN(state.s)") ({{state.s | fixed 0}})
|
||||
.stat-sub
|
||||
| RPM (commanded / actual)
|
||||
.fa.fa-sliders.tap-hint(title="Open override drawer")
|
||||
|
||||
.override(title="Spindle speed override.")
|
||||
label Speed
|
||||
input(type="range", min="0", max="2", step="0.01",
|
||||
v-model="speed_override", @change="override_speed")
|
||||
span.percent {{speed_override | percent 0}}
|
||||
.stat-card
|
||||
.stat-label Job
|
||||
.stat-val
|
||||
| {{0 <= state.line ? state.line : 0 | number}}
|
||||
span(v-if="toolpath.lines")
|
||||
| / {{toolpath.lines | number}}
|
||||
.stat-sub(v-if="plan_time_remaining || toolpath.time")
|
||||
| Line · {{plan_time_remaining ? (plan_time_remaining | time) : (toolpath.time | time)}} remaining
|
||||
.stat-sub(v-else) Line · ETA --
|
||||
|
||||
|
||||
// ----- Macro row (slice 0..7); full list lives in Settings → Macros -----
|
||||
// The colored left stripe (.has-color) is suppressed for white,
|
||||
// near-white and other default placeholder colors so unconfigured
|
||||
// macros render as clean slate tiles instead of looking lopsided.
|
||||
.macro-row(v-if="state.macros && state.macros.length")
|
||||
button.macro-btn(v-for="(index, macros) in state.macros.slice(0, 8)",
|
||||
title="Click to run macro",
|
||||
@click="run_macro(index)",
|
||||
:disabled="!is_ready",
|
||||
:class="{'has-color': has_macro_color(macros)}",
|
||||
:style="has_macro_color(macros) ? {borderLeftColor: macros.color} : {}")
|
||||
span.mnum {{index + 1}}
|
||||
span.mname {{macros.name || ('Macro ' + (index + 1))}}
|
||||
|
||||
// ----- Override drawer (anchored to bottom; toggled by Spindle KPI tile) -----
|
||||
.override-drawer(:class="{open: overrides_open}")
|
||||
.od-head
|
||||
.od-title
|
||||
.fa.fa-sliders
|
||||
| Overrides
|
||||
button.od-close(@click="overrides_open = false") ✕
|
||||
.od-body
|
||||
.od-row
|
||||
label Feed
|
||||
input(type="range", min="0", max="2", step="0.01",
|
||||
v-model="feed_override", @change="override_feed")
|
||||
.od-val {{feed_override | percent 0}}
|
||||
button.od-reset(@click="feed_override = 1; override_feed()") Reset 100%
|
||||
.od-row
|
||||
label Spindle
|
||||
input(type="range", min="0", max="2", step="0.01",
|
||||
v-model="speed_override", @change="override_speed")
|
||||
.od-val {{speed_override | percent 0}}
|
||||
button.od-reset(@click="speed_override = 1; override_speed()") Reset 100%
|
||||
|
||||
142
src/pug/templates/program-view.pug
Normal file
142
src/pug/templates/program-view.pug
Normal file
@@ -0,0 +1,142 @@
|
||||
script#program-view-template(type="text/x-template")
|
||||
.program-page
|
||||
|
||||
// ----- Modal dialogs -----
|
||||
message(:show.sync="showGcodeMessage")
|
||||
h3(slot="header") Processing New File
|
||||
div(slot="body")
|
||||
h3 Please wait..
|
||||
p Simulating GCode to check for errors, calculate ETA and generate 3D view.
|
||||
div(slot="footer")
|
||||
label Simulating {{(toolpath_progress || 0) | percent}}
|
||||
|
||||
message(:show.sync="GCodeNotFound")
|
||||
h3(slot="header") File not found
|
||||
div(slot="body")
|
||||
p It seems like the file you selected cannot be found. Try uploading again.
|
||||
div(slot="footer")
|
||||
button.pure-button.button-error(@click="GCodeNotFound=false") OK
|
||||
|
||||
message(:show.sync="uploading_files")
|
||||
h3(slot="header") Files uploading
|
||||
div(slot="body")
|
||||
h3 Please wait...
|
||||
p
|
||||
p The files are currently being uploaded.
|
||||
p Do not close the window.
|
||||
div(slot="footer")
|
||||
|
||||
message.error-message(:show.sync="deleteGCode")
|
||||
h3(slot="header") Select files to delete:
|
||||
div(slot="body")
|
||||
input.search-bar(type="text", v-model="search_query", placeholder="Search Files...")
|
||||
.container
|
||||
.folders
|
||||
h3 Folders
|
||||
div(v-for="(index, folder) in state.gcode_list", :key="index",
|
||||
@click="populateFiles(index)",
|
||||
class="folder-item",
|
||||
:class="{ selected: index === selected_folder_index }") {{ folder.name }}
|
||||
.files
|
||||
h3 Files
|
||||
label.file-item(v-for="item in gcode_filtered_files", :key="item")
|
||||
input(type="checkbox", :value="item", v-model="selected_items_to_delete")
|
||||
| {{ item }}
|
||||
div(slot="footer")
|
||||
button.pure-button(@click="cancel_delete", style="height:50px") Cancel
|
||||
button.pure-button.button-success(@click="delete_current", style="height:50px")
|
||||
.fa.fa-trash
|
||||
| Selected
|
||||
|
||||
message(:show.sync="create_folder")
|
||||
h3(slot="header") Enter folder name:
|
||||
div(slot="body")
|
||||
input.input-name(type="text", minlength="1", maxlength="15",
|
||||
style="margin-top:1rem;margin-bottom:2rem;",
|
||||
id="folder-name", v-model="folder_name", @keypress="edited_folder_name")
|
||||
div(slot="footer")
|
||||
button.pure-button(@click="cancel_new_folder") Cancel
|
||||
button.pure-button.button-success(@click="create_new_folder", :disabled="!edited") Create
|
||||
|
||||
message(:show.sync="confirmDelete")
|
||||
h3(slot="header") Delete Folder?
|
||||
div(slot="body")
|
||||
p Are you sure to delete the folder?
|
||||
div(slot="footer")
|
||||
button.pure-button(@click="confirmDelete=false") Cancel
|
||||
button.pure-button.button-error(@click="delete_folder") Folder only
|
||||
button.pure-button.button-success(@click="delete_folder_and_files") Folder and files
|
||||
|
||||
.program-card
|
||||
|
||||
// Action bar (RUN / STOP / Upload / Download / Delete)
|
||||
.action-bar
|
||||
button.action-btn.run(:class="{'attention': is_holding}",
|
||||
@click="start_pause", :disabled="!state.selected",
|
||||
:title="is_running ? 'Pause program.' : 'Start program.'")
|
||||
.fa.fa-play.ico(v-if="!is_running")
|
||||
.fa.fa-pause.ico(v-else)
|
||||
span {{is_running ? 'PAUSE' : 'RUN'}}
|
||||
button.action-btn.stop(@click="stop", title="Stop program.")
|
||||
.fa.fa-stop.ico
|
||||
span STOP
|
||||
button.action-btn(@click="open_folder", :disabled="!is_ready",
|
||||
title="Upload a new GCode folder.")
|
||||
.fa.fa-folder-plus.ico
|
||||
span UPLOAD FOLDER
|
||||
form.gcode-folder-input.file-upload
|
||||
input#folderInput(type="file", @change="upload_folder",
|
||||
:disabled="!is_ready", webkitdirectory, directory)
|
||||
button.action-btn(@click="open_file", :disabled="!is_ready",
|
||||
title="Upload a new GCode program.")
|
||||
.fa.fa-file-arrow-up.ico
|
||||
span UPLOAD FILE
|
||||
form.gcode-file-input.file-upload
|
||||
input(type="file", @change="upload_file", :disabled="!is_ready",
|
||||
accept=".nc,.ngc,.gcode,.gc", multiple)
|
||||
a(:href="state.selected ? '/api/file/' + state.selected : '#'",
|
||||
download, :class="{disabled: !state.selected}",
|
||||
title="Download the selected GCode program.")
|
||||
button.action-btn(:disabled="!state.selected")
|
||||
.fa.fa-file-arrow-down.ico
|
||||
span DOWNLOAD FILE
|
||||
button.action-btn.danger(@click="deleteGCode = true",
|
||||
:disabled="!state.selected || !is_ready",
|
||||
title="Delete current GCode program.")
|
||||
.fa.fa-trash.ico
|
||||
span DELETE
|
||||
|
||||
// File / folder selectors
|
||||
.file-bar
|
||||
button.file-btn(@click="create_folder=true", :disabled="!is_ready")
|
||||
.fa.fa-folder-plus
|
||||
| Create Folder
|
||||
button.file-btn(@click="confirmDelete=true", :disabled="!is_ready")
|
||||
.fa.fa-folder-minus
|
||||
| Delete Folder
|
||||
select.file-select(title="Select previously uploaded GCode folder.",
|
||||
v-model="state.folder", @change="reset_gcode", :disabled="!is_ready")
|
||||
option(selected, value="default") Default folder
|
||||
option(v-for="file in gcode_folders", :value="file") {{file}}
|
||||
select.file-select.primary(title="Select previously uploaded GCode programs.",
|
||||
v-model="state.selected", @change="load", :disabled="!is_ready")
|
||||
option(value="") (no file)
|
||||
option(v-for="file in gcode_files", :value="file") {{file}}
|
||||
button.file-btn(@click="toggle_sorting", :disabled="!is_ready")
|
||||
.fa.fa-arrow-down-wide-short
|
||||
| {{files_sortby}}
|
||||
|
||||
// Body: gcode listing on the left, 3D viewer on the right.
|
||||
// The 3D path-viewer is suppressed when the UI is loaded by
|
||||
// the Pi's onboard kiosk browser — the VideoCore IV cannot
|
||||
// run three.js at a usable frame rate. Off-Pi clients still
|
||||
// see the full split.
|
||||
.program-body(:class="{'no-preview': is_kiosk}")
|
||||
gcode-viewer
|
||||
path-viewer(v-if="!is_kiosk", :toolpath="toolpath",
|
||||
:state="state", :config="config")
|
||||
|
||||
.progress-bar(v-if="toolpath_progress && toolpath_progress < 1",
|
||||
title="Simulating GCode to check for errors, calculate ETA and generate 3D view.")
|
||||
div(:style="'width:' + (toolpath_progress || 0) * 100 + '%'")
|
||||
label Simulating {{(toolpath_progress || 0) | percent}}
|
||||
56
src/pug/templates/settings-shell.pug
Normal file
56
src/pug/templates/settings-shell.pug
Normal file
@@ -0,0 +1,56 @@
|
||||
script#settings-shell-view-template(type="text/x-template")
|
||||
.settings-shell
|
||||
aside.settings-rail
|
||||
// Use a single v-for over a data-driven items array so every
|
||||
// rail item shares the same compiled :class binding template.
|
||||
// This sidesteps a Vue 1 quirk where sibling-with-different-
|
||||
// expression :class bindings sometimes fail to re-evaluate on
|
||||
// hash navigation, leaving stale `.active` classes.
|
||||
template(v-for="item in rail_items")
|
||||
.set-section(v-if="item.section") {{item.section}}
|
||||
a.set-item(v-if="!item.section", :class="{active: is_active(item)}",
|
||||
:href="item.href", @click="on_rail_click(item, $event)")
|
||||
.fa(:class="item.icon")
|
||||
| {{item.label}}
|
||||
.set-rail-foot
|
||||
button.sp-shutdown(@click="showShutdownDialog")
|
||||
.fa.fa-power-off
|
||||
| Shutdown
|
||||
button.sp-save(:disabled="!$root.modified", @click="$root.save()")
|
||||
.fa.fa-save
|
||||
| Save{{$root.modified ? '*' : ''}}
|
||||
|
||||
.settings-content
|
||||
// Explicit v-if cascade so the inner template swaps reactively
|
||||
// when sub changes (Vue 1's `<component :is>` does not always
|
||||
// re-evaluate dynamic strings inside a kept-alive parent).
|
||||
// The Svelte settings views read many config keys eagerly on
|
||||
// attach (settings.units, settings.easy-adapter, motion.*),
|
||||
// so we gate the inner mount on config_ready.
|
||||
settings-view-inner(v-if="sub === 'settings' && config_ready",
|
||||
section="display",
|
||||
:index="index", :config="config", :template="template", :state="state")
|
||||
settings-view-inner(v-if="sub === 'probing' && config_ready",
|
||||
section="probing",
|
||||
:index="index", :config="config", :template="template", :state="state")
|
||||
settings-view-inner(v-if="sub === 'gcode' && config_ready",
|
||||
section="gcode",
|
||||
:index="index", :config="config", :template="template", :state="state")
|
||||
admin-general-view(v-if="sub === 'admin-general' && config_ready",
|
||||
:index="index", :config="config", :template="template", :state="state")
|
||||
admin-network-view(v-if="sub === 'admin-network' && config_ready",
|
||||
:index="index", :config="config", :template="template", :state="state")
|
||||
motor-view(v-if="sub === 'motor' && config_ready",
|
||||
:index="index", :config="config", :template="template", :state="state")
|
||||
tool-view(v-if="sub === 'tool' && config_ready",
|
||||
:index="index", :config="config", :template="template", :state="state")
|
||||
io-view(v-if="sub === 'io' && config_ready",
|
||||
:index="index", :config="config", :template="template", :state="state")
|
||||
macros-view(v-if="sub === 'macros' && config_ready",
|
||||
:index="index", :config="config", :template="template", :state="state")
|
||||
help-view(v-if="sub === 'help' && config_ready",
|
||||
:index="index", :config="config", :template="template", :state="state")
|
||||
cheat-sheet-view(v-if="sub === 'cheat-sheet' && config_ready",
|
||||
:index="index", :config="config", :template="template", :state="state")
|
||||
.settings-loading(v-if="!config_ready")
|
||||
| Loading configuration…
|
||||
Reference in New Issue
Block a user