diff --git a/public/app.js b/public/app.js index d91fa50..acae557 100644 --- a/public/app.js +++ b/public/app.js @@ -102,6 +102,9 @@ const app = { 'PA-CF': { minTemp: 250, maxTemp: 280, bedTempMin: 70, bedTempMax: 90 } }, + // Primary color plus up to 4 extra colors + MAX_COLORS: 5, + palettes: { material: { paletteId: 'materialPalette', @@ -123,21 +126,29 @@ const app = { items: [{label: 'None', value: ''}, 'Basic', 'Matte', 'SnapSpeed', 'Silk', 'Support', 'HF', '95A', '95A HF'], defaultValue: '', }, + // Multi-color style, using OrcaSlicer's filament_colour_type values + colorType: { + paletteId: 'colorTypePalette', + inputId: 'colorType', + items: [{label: 'Gradient', value: '0'}, {label: 'Segments', value: '1'}], + defaultValue: '1', + }, }, init() { this.checkNFC(); // Removed preset swatch palette; using canvas pickers only if (typeof ColorPicker !== 'undefined' && ColorPicker && typeof ColorPicker.init === 'function') { - ColorPicker.init(this); + ColorPicker.init(this, this.MAX_COLORS); } for (const name in this.palettes) this.initPalette(name); this.populateFormats(); this.initEventListeners(); + this.updateColorSlots(); this.updateFormat(); this.updateVisibility(); - // Initialize all four colors - for (let i = 1; i <= 4; i++) { + // Initialize all colors + for (let i = 1; i <= this.MAX_COLORS; i++) { this.updateColor('#FFFFFF', i); } this.applyTemperaturePreset(); @@ -310,8 +321,8 @@ const app = { this.handleFileUpload(e.target.files[0]); }); - // Add event listeners for all four color inputs - for (let i = 1; i <= 4; i++) { + // Add event listeners for all color inputs + for (let i = 1; i <= this.MAX_COLORS; i++) { document.getElementById(`colorHex${i}`).addEventListener('input', (e) => { this.updateColor('#' + e.target.value, i); this.updateRecordSize(); @@ -323,9 +334,12 @@ const app = { this.updateVisibility(); }); - document.getElementById('showAdditionalColors').addEventListener('change', (e) => { - this.toggleAdditionalColors(e.target.checked); - }); + // Extra colors (2..MAX_COLORS) must be enabled before they can be set + for (let i = 2; i <= this.MAX_COLORS; i++) { + document.getElementById(`colorEnable${i}`).addEventListener('change', (e) => { + this.setColorEnabled(i, e.target.checked); + }); + } document.getElementById('brandInput').addEventListener('input', () => { document.getElementById('brandValue').value = document.getElementById('brandInput').value || ''; @@ -390,25 +404,23 @@ const app = { document.getElementById('formatSelect').value = data.format || format; this.setPaletteValue('material', data.materialType || 'PLA'); - // Populate all four color fields - document.getElementById('colorHex1').value = data.colorHex || 'FFFFFF'; - this.updateColor('#' + (data.colorHex || 'FFFFFF'), 1); - - document.getElementById('colorHex2').value = data.colorHex2 || 'FFFFFF'; - this.updateColor('#' + (data.colorHex2 || 'FFFFFF'), 2); - - document.getElementById('colorHex3').value = data.colorHex3 || 'FFFFFF'; - this.updateColor('#' + (data.colorHex3 || 'FFFFFF'), 3); - - document.getElementById('colorHex4').value = data.colorHex4 || 'FFFFFF'; - this.updateColor('#' + (data.colorHex4 || 'FFFFFF'), 4); + // Populate all color fields + for (let i = 1; i <= this.MAX_COLORS; i++) { + const hex = data[i === 1 ? 'colorHex' : `colorHex${i}`] || 'FFFFFF'; + document.getElementById(`colorHex${i}`).value = hex; + this.updateColor('#' + hex, i); + } - // Show additional colors if any are present - const hasAdditionalColors = (data.colorHex2 && data.colorHex2 !== 'FFFFFF') || - (data.colorHex3 && data.colorHex3 !== 'FFFFFF') || - (data.colorHex4 && data.colorHex4 !== 'FFFFFF'); - document.getElementById('showAdditionalColors').checked = hasAdditionalColors; - this.toggleAdditionalColors(hasAdditionalColors); + // Enable each extra color present in the data. Colors are enabled in + // order, so a later color also enables any earlier gaps. + let enableUpTo = 1; + for (let i = 2; i <= this.MAX_COLORS; i++) { + if (data[`colorHex${i}`]) enableUpTo = i; + } + for (let i = 2; i <= this.MAX_COLORS; i++) { + document.getElementById(`colorEnable${i}`).checked = i <= enableUpTo; + } + this.updateColorSlots(); if (data.brand && this.palettes.brand.items.includes(data.brand)) { this.setPaletteValue('brand', data.brand); @@ -426,6 +438,7 @@ const app = { document.getElementById('spoolmanId').value = data.spoolmanId || ''; document.getElementById('lotNr').value = data.lotNr || ''; this.setPaletteValue('variant', data.extendedSubType ?? ''); + this.setPaletteValue('colorType', data.colorType === '0' ? '0' : '1'); // Advanced fields document.getElementById('materialName').value = data.materialName || ''; @@ -464,9 +477,10 @@ const app = { materialType: document.getElementById('materialType').value, brand: brand || 'Generic', colorHex: document.getElementById('colorHex1').value.replace('#', ''), - colorHex2: document.getElementById('colorHex2').value.replace('#', ''), - colorHex3: document.getElementById('colorHex3').value.replace('#', ''), - colorHex4: document.getElementById('colorHex4').value.replace('#', ''), + colorHex2: this.getExtraColor(2), + colorHex3: this.getExtraColor(3), + colorHex4: this.getExtraColor(4), + colorHex5: this.getExtraColor(5), // Temps/IDs minTemp: document.getElementById('minTemp').value, @@ -476,6 +490,7 @@ const app = { spoolmanId: document.getElementById('spoolmanId').value, lotNr: document.getElementById('lotNr').value, extendedSubType: document.getElementById('extendedSubType').value, + colorType: document.getElementById('colorType').value, // Advanced materialName: document.getElementById('materialName').value, @@ -839,11 +854,36 @@ const app = { return '#' + result.slice(0, 3).map(x => parseInt(x).toString(16).padStart(2, '0')).join(''); }, - toggleAdditionalColors(show) { - const additionalColorFields = document.querySelectorAll('.additional-colors'); - additionalColorFields.forEach(field => { - field.style.display = show ? 'block' : 'none'; - }); + // Extra color i's hex, or '' when it isn't enabled (so it isn't written) + getExtraColor(i) { + if (!document.getElementById(`colorEnable${i}`).checked) return ''; + return document.getElementById(`colorHex${i}`).value.replace('#', ''); + }, + + setColorEnabled(i, enabled) { + if (enabled) { + // Enabling a color also enables any earlier ones, keeping them in order + for (let j = 2; j <= i; j++) document.getElementById(`colorEnable${j}`).checked = true; + } else { + // Disabling a color also disables every later one + for (let j = i; j <= this.MAX_COLORS; j++) document.getElementById(`colorEnable${j}`).checked = false; + } + this.updateColorSlots(); + this.updateRecordSize(); + }, + + // Show each enabled color's fields, and offer the next color's checkbox + // only once the one before it is enabled. + updateColorSlots() { + for (let i = 2; i <= this.MAX_COLORS; i++) { + const enabled = document.getElementById(`colorEnable${i}`).checked; + const prevEnabled = i === 2 || document.getElementById(`colorEnable${i - 1}`).checked; + document.querySelector(`.extra-color[data-color-index="${i}"]`).style.display = prevEnabled ? '' : 'none'; + document.getElementById(`colorFields${i}`).style.display = enabled ? '' : 'none'; + } + // Gradient/Segments only matters once there's more than one color + const typeGroup = document.getElementById('colorTypeGroup'); + if (typeGroup) typeGroup.style.display = document.getElementById('colorEnable2').checked ? '' : 'none'; }, showStatus(id, type, message) { diff --git a/public/index.html b/public/index.html index d2c871b..f3ab2fd 100644 --- a/public/index.html +++ b/public/index.html @@ -573,6 +573,101 @@