Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
555dca8
Add macro tabs system and service tracking features
Q1WP Dec 3, 2025
13fc92e
Improve service page UX with save indicators and layout refinements
Q1WP Dec 3, 2025
c2e7d16
Improve macros page layout and add delete confirmations
Q1WP Dec 3, 2025
fbff977
Refine service and macros page layouts
Q1WP Dec 3, 2025
ef3d1af
Unify Add button placement and polish UI consistency
Q1WP Dec 4, 2025
6243458
Add system alerts for upgrades and service, hide camera when unavailable
Q1WP Dec 4, 2025
7377775
Macros single row layout, remove spindle hours from service tracking
Q1WP Dec 4, 2025
ebd65d4
Macros single row layout, remove spindle hours from service tracking
Q1WP Dec 4, 2025
8cc0da2
Macros single row layout, remove spindle hours from service tracking
Q1WP Dec 4, 2025
8f09846
Fix motion timer tracking, improve macros input spacing
Q1WP Dec 4, 2025
c471a86
Fix macros hex input font size and name/file column spacing
Q1WP Dec 4, 2025
f5f484b
Fix macros table column spacing and hex input font size
Q1WP Dec 4, 2025
369c8c7
Fine-tune macros table column spacing
Q1WP Dec 4, 2025
a370697
Adjust macros hex input font and name column spacing
Q1WP Dec 4, 2025
1c2e16e
Tweak macro row spacing
Q1WP Dec 4, 2025
d2acade
Fix service alert not appearing in header
Q1WP Dec 5, 2025
ea6a4a4
Fix service alert not appearing in header
Q1WP Dec 5, 2025
888039b
Fix service alerts and remove dashboard view
Q1WP Dec 5, 2025
d9a8807
Remove user-selectable colors from service items for cleaner UX
Q1WP Dec 5, 2025
0dad3d3
Fixed duplicate listeners
Q1WP Dec 7, 2025
455e31c
feat: add dedicated Macro Safety Settings page
Q1WP Dec 10, 2025
beaf320
Move Safety settings to Macros page as dedicated tab
Q1WP Dec 14, 2025
fc151de
Remove Settings→Macros, enlarge checkboxes on Macros page
Q1WP Dec 14, 2025
888ff12
fix(view-control.pug): convert macro index fallback to String
Q1WP Dec 15, 2025
6a3f04b
fix(view-control.pug): use string coercion for macro name fallback
Q1WP Dec 15, 2025
9b7723c
fix(macros): remove duplicate confirm checkbox from macros tab
Q1WP Dec 18, 2025
18907c8
Fix macro isolation and modal state bugs
Q1WP Dec 23, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 54 additions & 2 deletions src/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ module.exports = new Vue({
motors: [{}, {}, {}, {}],
version: '<loading>'
},
state: {messages: []},
state: {messages: [], service_due_count: 0},
crosshair: cookie.get_bool('crosshair', false),
selected_program: new Program(this.$api, cookie.get('selected-path')),
active_program: undefined,
Expand All @@ -56,7 +56,10 @@ module.exports = new Vue({
errorMessage: '',
checkedUpgrade: false,
latestVersion: '',
webGLSupported: util.webgl_supported()
webGLSupported: util.webgl_supported(),
// Alert dismissal state (session-based, resets on browser close)
upgrade_dismissed: sessionStorage.getItem('upgrade_dismissed') === 'true',
service_dismissed: sessionStorage.getItem('service_dismissed') === 'true'
}
},

Expand All @@ -69,6 +72,8 @@ module.exports = new Vue({
'view-editor': require('./view-editor'),
'view-settings': require('./view-settings'),
'view-files': require('./view-files'),
'view-macros': require('./view-macros'),
'view-service': require('./view-service'),
'view-camera': {template: '#view-camera-template'},
'view-docs': require('./view-docs')
},
Expand All @@ -87,6 +92,24 @@ module.exports = new Vue({

'state.first_file'(value) {
if (!this.selected_program.path) this.select_path(value)
},


// Reset upgrade dismissal when version changes
latestVersion(newVersion, oldVersion) {
if (oldVersion && newVersion !== oldVersion) {
this.upgrade_dismissed = false
sessionStorage.removeItem('upgrade_dismissed')
}
},


// Reset service dismissal when due count changes (new items become due)
'state.service_due_count'(newCount, oldCount) {
if (oldCount !== undefined && newCount > oldCount) {
this.service_dismissed = false
sessionStorage.removeItem('service_dismissed')
}
}
},

Expand Down Expand Up @@ -163,6 +186,23 @@ module.exports = new Vue({
show_upgrade() {
if (!this.latestVersion) return false
return util.compare_versions(this.config.version, this.latestVersion) < 0
},


show_upgrade_alert() {
return this.show_upgrade && !this.upgrade_dismissed
},


show_service_alert() {
let count = this.state.service_due_count || 0
return count > 0 && !this.service_dismissed
},


camera_available() {
// Use state from backend, default to true if not yet received
return this.state.camera_available !== false
}
},

Expand Down Expand Up @@ -197,6 +237,18 @@ module.exports = new Vue({


methods: {
dismiss_upgrade() {
this.upgrade_dismissed = true
sessionStorage.setItem('upgrade_dismissed', 'true')
},


dismiss_service() {
this.service_dismissed = true
sessionStorage.setItem('service_dismissed', 'true')
},


async check_login() {
this.authorized = await this.$api.get('auth/login')
},
Expand Down
98 changes: 0 additions & 98 deletions src/js/settings-macros.js

This file was deleted.

134 changes: 129 additions & 5 deletions src/js/view-control.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ module.exports = {
jog_adjust: parseInt(cookie.get('jog-adjust', 2)),
tab: 'auto',
highlighted_line: 0,
toolpath: {}
toolpath: {},
macro_tab: null // Currently selected macro tab
}
},

Expand Down Expand Up @@ -169,7 +170,58 @@ module.exports = {
if (!this.toolpath.time || this.is_ready) return 0
let p = this.plan_time / this.toolpath.time
return p < 1 ? p : 1
}
},


// Get macro tabs from config, with default fallback
macro_tabs() {
let tabs = this.config.macro_tabs
if (!tabs || !tabs.length) {
return [{id: 'default', name: 'Macros'}]
}
return tabs
},


// Get the currently selected macro tab ID
current_macro_tab() {
if (this.macro_tab) return this.macro_tab
return this.macro_tabs.length ? this.macro_tabs[0].id : 'default'
},


// Get macros filtered by current tab and visibility
visible_macros() {
let macros = this.config.macros || []
let currentTab = this.current_macro_tab
let defaultTab = this.macro_tabs.length ? this.macro_tabs[0].id : 'default'

let result = []
for (let i = 0; i < macros.length; i++) {
let macro = macros[i]
// Check visibility
if (macro.visible === false) continue
// Check tab assignment
let macroTab = macro.tab || defaultTab
if (macroTab === currentTab) {
result.push({
name: macro.name,
path: macro.path,
color: macro.color,
confirm: macro.confirm,
originalIndex: i
})
}
}
return result
},


// Check if there are multiple tabs to show
has_multiple_tabs() {
return this.macro_tabs.length > 1
},

},


Expand All @@ -183,6 +235,12 @@ module.exports = {

step(axis, value) {
this.send('M70\nG91\nG0' + axis + value + '\nM72')
},


// FIX #1: Handle program-cleared event to clear textarea
'program-cleared'() {
this.clear_display()
}
},

Expand Down Expand Up @@ -210,13 +268,64 @@ module.exports = {
goto(hash) {window.location.hash = hash},
send(msg) {this.$dispatch('send', msg)},
on_scroll(cm, e) {e.preventDefault()},


// Select a macro tab
select_macro_tab(tabId) {
this.macro_tab = tabId
},


async run_macro(macro) {
try {
return this.$api.put('macro/' + macro)
// macro is the 1-based index from the button
// originalIndex is the 0-based index in config.macros
let originalIndex = macro
if (typeof macro === 'object' && macro.originalIndex !== undefined) {
originalIndex = macro.originalIndex
}

let macros = this.config.macros || []
if (originalIndex < 0 || originalIndex >= macros.length) {
throw new Error('Invalid macro index: ' + originalIndex)
}

let macroConfig = macros[originalIndex]
if (!macroConfig || !macroConfig.path) {
throw new Error('Macro has no file configured')
}

// Build full path
let path = macroConfig.path
if (!path.startsWith('Home/')) {
path = 'Home/' + path
}

// Check if confirmation is required (default: true for safety)
let requiresConfirm = macroConfig.confirm !== false

if (requiresConfirm) {
// Show confirmation dialog before running macro
let macroName = macroConfig.name || ('Macro ' + (originalIndex + 1))
let confirmed = await this.$root.open_dialog({
header: 'Confirm Macro',
icon: 'question',
body: 'Run macro "' + macroName + '"?\n\nFile: ' + path,
buttons: [
{text: 'Cancel', class: 'button-default'},
{text: 'Run', class: 'button-success', action: 'run'}
]
})

// User cancelled - don't run macro
if (confirmed != 'run') return
}

// Call the macro API endpoint (uses 1-based index)
return this.$api.put('macro/' + (originalIndex + 1))

} catch (e) {
this.$root.error_dialog('Failed to run macro "' + macro + '":\n' + e)
this.$root.error_dialog('Failed to run macro:\n' + e)
}
},

Expand All @@ -236,9 +345,24 @@ module.exports = {
},


// FIX #1: Clear the display when no program is loaded
clear_display() {
if (typeof this.editor != 'undefined') {
this.editor.setValue('')
}
this.toolpath = {}
this.highlighted_line = 0
},


async load() {
let path = this.active.path
if (!path) return

// FIX #1: If no path, clear the display
if (!path) {
this.clear_display()
return
}

let data = await this.active.load()
if (this.active.path != path) return
Expand Down
Loading