diff --git a/assets/editor.png b/assets/editor.png index ab9616f..41d0473 100644 Binary files a/assets/editor.png and b/assets/editor.png differ diff --git a/public/index.html b/public/index.html index d5a097b..5b10abe 100644 --- a/public/index.html +++ b/public/index.html @@ -90,61 +90,80 @@
- -
-

Preset Info

+ +
+
+

Preset

+
+ + + + + +
+
+ +
-
- - + + +
+
+ +
- -
-

Add Layer

-
- - -
- -
-
-

Layers

+
+

Layers

+ +

No layers yet. Click "Add Layer" to get started!

- -
-

Manage

-
- - - -
-
- -
-
- - -
-
diff --git a/public/scripts/preset-editor.js b/public/scripts/preset-editor.js index 2716de5..27170b4 100644 --- a/public/scripts/preset-editor.js +++ b/public/scripts/preset-editor.js @@ -67,19 +67,12 @@ class PresetEditor { setupEventListeners() { // Preset metadata const nameInput = document.getElementById('editor-preset-name'); - const descInput = document.getElementById('editor-preset-desc'); if (nameInput) { nameInput.addEventListener('input', (e) => { this.configuration.name = e.target.value; }); } - - if (descInput) { - descInput.addEventListener('input', (e) => { - this.configuration.description = e.target.value; - }); - } // Add layer button const addLayerBtn = document.getElementById('editor-add-layer'); @@ -87,14 +80,6 @@ class PresetEditor { addLayerBtn.addEventListener('click', () => this.addLayer()); } - // Building block type selector - const typeSelector = document.getElementById('editor-layer-type'); - if (typeSelector) { - typeSelector.addEventListener('change', (e) => { - this.showLayerTypeOptions(e.target.value); - }); - } - // Save/Load buttons const saveBtn = document.getElementById('editor-save-preset'); if (saveBtn) { @@ -157,14 +142,8 @@ class PresetEditor { } addLayer() { - const layerType = document.getElementById('editor-layer-type')?.value || 'shape'; - - let layer; - if (layerType === 'pattern') { - layer = this.createPatternLayer(); - } else { - layer = this.createShapeLayer(); - } + // Default to shape layer type + const layer = this.createShapeLayer(); this.configuration.layers.push(layer); @@ -464,6 +443,14 @@ class PresetEditor { } renderShapeEditor(container, layer, index) { + // Layer type selector + this.addSelect(container, 'Layer Type', layer.type, + ['shape', 'pattern'], + (value) => { + this.changeLayerType(index, value); + } + ); + // Shape type this.addSelect(container, 'Shape', layer.shape, ['circle', 'rectangle', 'triangle', 'blob', 'point', 'line'], @@ -517,6 +504,14 @@ class PresetEditor { } renderPatternEditor(container, layer, index) { + // Layer type selector + this.addSelect(container, 'Layer Type', layer.type, + ['shape', 'pattern'], + (value) => { + this.changeLayerType(index, value); + } + ); + // Pattern type this.addSelect(container, 'Pattern', layer.pattern, ['trail', 'radial', 'spiral'], @@ -774,6 +769,31 @@ class PresetEditor { this.renderLayerList(); } + changeLayerType(index, newType) { + const layer = this.configuration.layers[index]; + + if (layer.type === newType) return; + + // Create a new layer of the specified type + let newLayer; + if (newType === 'pattern') { + newLayer = this.createPatternLayer(); + } else { + newLayer = this.createShapeLayer(); + } + + // Preserve some properties if they exist + if (layer.intensity !== undefined) { + newLayer.intensity = layer.intensity; + } + + // Replace the layer + this.configuration.layers[index] = newLayer; + + this.renderLayerList(); + this.refreshPreviewIfActive(); + } + deleteLayer(index) { if (confirm('Delete this layer?')) { this.configuration.layers.splice(index, 1); @@ -808,16 +828,11 @@ class PresetEditor { // Update UI const nameInput = document.getElementById('editor-preset-name'); - const descInput = document.getElementById('editor-preset-desc'); if (nameInput) { nameInput.value = this.configuration.name; } - if (descInput) { - descInput.value = this.configuration.description; - } - // Clear and re-render layer list this.renderLayerList(); @@ -850,7 +865,6 @@ class PresetEditor { this.configuration = JSON.parse(JSON.stringify(preset)); document.getElementById('editor-preset-name').value = this.configuration.name; - document.getElementById('editor-preset-desc').value = this.configuration.description; this.renderLayerList(); this.refreshPreviewIfActive(); @@ -868,7 +882,6 @@ class PresetEditor { this.configuration = this.getDefaultConfiguration(); document.getElementById('editor-preset-name').value = this.configuration.name; - document.getElementById('editor-preset-desc').value = this.configuration.description; this.loadSavedPresets(); this.renderLayerList(); @@ -924,7 +937,6 @@ class PresetEditor { this.configuration = imported; document.getElementById('editor-preset-name').value = this.configuration.name; - document.getElementById('editor-preset-desc').value = this.configuration.description; this.renderLayerList(); this.refreshPreviewIfActive(); diff --git a/public/styles/main.css b/public/styles/main.css index 5778748..8ddda33 100644 --- a/public/styles/main.css +++ b/public/styles/main.css @@ -955,23 +955,23 @@ body { } .btn-primary { - background: linear-gradient(135deg, var(--accent-primary) 0%, #22c55e 100%); + background: linear-gradient(135deg, rgba(74, 222, 128, 0.8) 0%, rgba(34, 197, 94, 0.8) 100%); color: white; - border-color: var(--accent-primary); - box-shadow: 0 2px 8px rgba(74, 222, 128, 0.25); + border-color: rgba(74, 222, 128, 0.6); + box-shadow: 0 2px 8px rgba(74, 222, 128, 0.15); } .btn-primary:hover { - background: linear-gradient(135deg, #22c55e 0%, var(--accent-primary) 100%); - border-color: #22c55e; + background: linear-gradient(135deg, rgba(34, 197, 94, 0.9) 0%, rgba(74, 222, 128, 0.9) 100%); + border-color: rgba(34, 197, 94, 0.8); color: white; - transform: translateY(-2px); - box-shadow: 0 4px 12px rgba(74, 222, 128, 0.35); + transform: translateY(-1px); + box-shadow: 0 3px 10px rgba(74, 222, 128, 0.2); } .btn-primary:active { transform: translateY(0); - box-shadow: 0 2px 6px rgba(74, 222, 128, 0.25); + box-shadow: 0 1px 4px rgba(74, 222, 128, 0.15); } .btn-stop { @@ -1004,15 +1004,18 @@ body { } .btn-secondary { - background: linear-gradient(135deg, rgba(255, 255, 255, 0.05) 0%, rgba(255, 255, 255, 0.02) 100%); + background: linear-gradient(135deg, rgba(255, 255, 255, 0.08) 0%, rgba(255, 255, 255, 0.04) 100%); color: var(--text-secondary); border-color: var(--border-secondary); + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1); } .btn-secondary:hover { - background: linear-gradient(135deg, rgba(255, 255, 255, 0.1) 0%, rgba(255, 255, 255, 0.05) 100%); + background: linear-gradient(135deg, rgba(255, 255, 255, 0.12) 0%, rgba(255, 255, 255, 0.08) 100%); color: var(--text-primary); border-color: var(--border-tertiary); + transform: translateY(-1px); + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15); } .btn-small { @@ -1806,6 +1809,17 @@ body { opacity: 0.85; } +.section-title-with-button { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 0.65rem; +} + +.section-title-with-button .section-title-compact { + margin-bottom: 0; +} + .editor-section { margin-bottom: 1.5rem; } @@ -2012,6 +2026,132 @@ body { margin-top: 0.15rem; } +.editor-preset-section { + background: var(--bg-secondary); + border: 1px solid var(--border-primary); + border-radius: 6px; + padding: 0.75rem; + margin-bottom: 0.75rem; +} + +.preset-actions { + display: flex; + flex-direction: column; + gap: 0.5rem; + margin-top: 0.75rem; +} + +.preset-actions-row { + display: flex; + gap: 0.375rem; + align-items: center; +} + +.preset-actions-row:first-child { + justify-content: stretch; +} + +.preset-actions-row .preset-select { + flex: 1; + margin: 0; +} + +.editor-preset-section .editor-input-wrapper { + margin-bottom: 0.5rem; +} + +.editor-preset-section .editor-input-wrapper input { + padding: 0.5rem 0.75rem; + font-size: 0.85rem; + min-height: 2rem; +} + +.editor-preset-section .section-title-compact { + margin-bottom: 0.5rem; + font-size: 0.8rem; +} + +.preset-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 0.5rem; +} + +.preset-header .section-title-compact { + margin-bottom: 0; +} + +.preset-header-buttons { + display: flex; + gap: 0.25rem; +} + +.btn-icon { + padding: 0.5rem; + min-width: 2.5rem; + width: 2.5rem; + height: 2.5rem; + display: flex; + align-items: center; + justify-content: center; + background: var(--bg-tertiary); + border: 1px solid var(--border-secondary); + border-radius: 6px; + color: var(--text-secondary); + transition: all 0.2s ease; + cursor: pointer; + position: relative; + overflow: hidden; +} + +.btn-icon:hover { + background: var(--bg-hover); + border-color: var(--border-hover); + color: var(--text-primary); + transform: translateY(-1px); + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); +} + +.btn-icon:active { + transform: translateY(0); + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1); +} + +.btn-icon svg { + transition: all 0.2s ease; +} + +.btn-icon:hover svg { + transform: scale(1.05); +} + +.btn-icon:active svg { + transform: scale(0.95); +} + +/* Icon button color variations */ +.btn-icon[id="editor-new-preset"]:hover { + border-color: rgba(74, 222, 128, 0.4); + color: var(--accent-primary); +} + +.btn-icon[id="editor-save-preset"]:hover { + border-color: rgba(76, 175, 80, 0.4); + color: var(--accent-success); +} + +.btn-icon[id="editor-delete-preset"]:hover { + border-color: rgba(248, 113, 113, 0.4); + color: var(--accent-error); +} + +.btn-icon[id="editor-export-json"]:hover, +.btn-icon[for="editor-import-json"]:hover { + border-color: rgba(96, 165, 250, 0.4); + color: var(--accent-secondary); +} + .editor-layer-buttons { display: flex; gap: 0.3rem; @@ -2103,21 +2243,31 @@ body { /* Button Variants */ .btn-success { - background: linear-gradient(135deg, var(--accent-success) 0%, #45a049 100%); + background: linear-gradient(135deg, rgba(76, 175, 80, 0.8) 0%, rgba(69, 160, 73, 0.8) 100%); color: white; + border-color: rgba(76, 175, 80, 0.6); + box-shadow: 0 2px 8px rgba(76, 175, 80, 0.15); } .btn-success:hover { - background: linear-gradient(135deg, #45a049 0%, #3d8b40 100%); + background: linear-gradient(135deg, rgba(69, 160, 73, 0.9) 0%, rgba(61, 139, 64, 0.9) 100%); + border-color: rgba(69, 160, 73, 0.8); + transform: translateY(-1px); + box-shadow: 0 3px 10px rgba(76, 175, 80, 0.2); } .btn-danger { - background: linear-gradient(135deg, var(--accent-error) 0%, #dc2626 100%); + background: linear-gradient(135deg, rgba(248, 113, 113, 0.8) 0%, rgba(220, 38, 38, 0.8) 100%); color: white; + border-color: rgba(248, 113, 113, 0.6); + box-shadow: 0 2px 8px rgba(248, 113, 113, 0.15); } .btn-danger:hover { - background: linear-gradient(135deg, #dc2626 0%, #b91c1c 100%); + background: linear-gradient(135deg, rgba(220, 38, 38, 0.9) 0%, rgba(185, 28, 28, 0.9) 100%); + border-color: rgba(220, 38, 38, 0.8); + transform: translateY(-1px); + box-shadow: 0 3px 10px rgba(248, 113, 113, 0.2); } /* Notifications */