From f6d89144019b8f56cb1cab1d510f109feb074d95 Mon Sep 17 00:00:00 2001 From: chris Date: Mon, 1 Dec 2025 09:16:14 -0500 Subject: [PATCH] chore: snapshot current root state --- classic.js | 571 ----------------------------------------------------- index.html | 5 +- 2 files changed, 3 insertions(+), 573 deletions(-) delete mode 100644 classic.js diff --git a/classic.js b/classic.js deleted file mode 100644 index a400d62..0000000 --- a/classic.js +++ /dev/null @@ -1,571 +0,0 @@ -(() => { - 'use strict'; - - // -------- helpers ---------- - const log = (...a) => console.log('[Classic]', ...a); - const fail = (msg) => { - console.error('[Classic ERROR]', msg); - const d = document.getElementById('classic-display'); - if (d) d.innerHTML = `
- Classic failed: ${String(msg)} -
`; - }; - const normHex = (h) => (String(h || '')).trim().toLowerCase(); - - // -------- persistent color selection (now supports image textures) ---------- - const PALETTE_KEY = 'classic:colors:v2'; - const TOPPER_COLOR_KEY = 'classic:topperColor:v2'; - - const defaultColors = () => [ - { hex: '#d92e3a', image: null }, { hex: '#ffffff', image: null }, - { hex: '#0055a4', image: null }, { hex: '#40e0d0', image: null }, - { hex: '#fcd34d', image: null } - ]; - const defaultTopper = () => ({ hex: '#a18b67', image: 'images/chrome-gold.webp' }); - - function getClassicColors() { - let arr = defaultColors(); - try { - const savedJSON = localStorage.getItem(PALETTE_KEY); - if (!savedJSON) return arr; - const saved = JSON.parse(savedJSON); - if (Array.isArray(saved) && saved.length > 0) { - if (typeof saved[0] === 'string') { - arr = saved.slice(0, 5).map(hex => ({ hex: normHex(hex), image: null })); - } else if (typeof saved[0] === 'object' && saved[0] !== null) { - arr = saved.slice(0, 5); - } - while (arr.length < 5) arr.push({ hex: '#ffffff', image: null }); - } - } catch (e) { console.error('Failed to parse classic colors:', e); } - return arr; - } - - function setClassicColors(arr) { - const clean = (arr || []).slice(0, 5).map(c => ({ - hex: normHex(c.hex), image: c.image || null - })); - while (clean.length < 5) clean.push({ hex: '#ffffff', image: null }); - try { localStorage.setItem(PALETTE_KEY, JSON.stringify(clean)); } catch {} - return clean; - } - - function getTopperColor() { - try { - const saved = JSON.parse(localStorage.getItem(TOPPER_COLOR_KEY)); - return (saved && saved.hex) ? saved : defaultTopper(); - } catch { return defaultTopper(); } - } - - function setTopperColor(colorObj) { - const clean = { hex: normHex(colorObj.hex), image: colorObj.image || null }; - try { localStorage.setItem(TOPPER_COLOR_KEY, JSON.stringify(clean)); } catch {} - } - - function buildClassicPalette() { - const colors = getClassicColors(); - const palette = { 0: { colour: '#FFFFFF', name: 'No Colour', image: null } }; - colors.forEach((c, i) => { - palette[i + 1] = { colour: c.hex, image: c.image }; - }); - return palette; - } - - function flattenPalette() { - const out = []; - if (Array.isArray(window.PALETTE)) { - window.PALETTE.forEach(group => { - (group.colors || []).forEach(c => { - if (!c?.hex) return; - out.push({ - hex: normHex(c.hex), name: c.name || c.hex, - family: group.family || '', image: c.image || null - }); - }); - }); - } - const seen = new Set(); - return out.filter(c => (seen.has(c.hex) ? false : (seen.add(c.hex), true))); - } - - // -------- tiny grid engine (Mithril) ---------- - function GridCalculator() { - if (typeof window.m === 'undefined') throw new Error('Mithril (m) not loaded'); - - let pxUnit = 10; - let clusters = 10; - let reverse = false; - let topperEnabled = false; - let topperType = 'round'; - let topperOffsetX_Px = 0; - let topperOffsetY_Px = 0; - let topperSizeMultiplier = 1; - let shineEnabled = true; - - const patterns = {}; - const api = { - patterns, - initialPattern: 'Arch 4', - controller: (el) => makeController(el), - setClusters(n) { clusters = Math.max(1, (Number(n)|0) || 10); }, - setReverse(on){ reverse = !!on; }, - setTopperEnabled(on) { topperEnabled = !!on; }, - setTopperType(type) { topperType = type || 'round'; }, - setTopperOffsetX(val) { topperOffsetX_Px = (Number(val) || 0) * 5; }, - setTopperOffsetY(val) { topperOffsetY_Px = (Number(val) || 0) * -5; }, - setTopperSize(multiplier) { topperSizeMultiplier = Number(multiplier) || 1; }, - setShineEnabled(on) { shineEnabled = !!on; } - }; - - const svg = (tag, attrs, children) => m(tag, attrs, children); - - function extend(p){ - const parentName = p.deriveFrom; if (!parentName) return; - const base = patterns[parentName]; if (!base) return; - if (base.deriveFrom) extend(base); - Object.keys(base).forEach(k => { if (!(k in p)) p[k] = base[k]; }); - p.parent = base; - } - - function BBox(){ this.min={x:Infinity,y:Infinity}; this.max={x:-Infinity,y:-Infinity}; } - BBox.prototype.add = function(x,y){ if(isNaN(x)||isNaN(y)) return this; - this.min.x=Math.min(this.min.x,x); this.min.y=Math.min(this.min.y,y); - this.max.x=Math.max(this.max.x,x); this.max.y=Math.max(this.max.y,y); return this; }; - BBox.prototype.w=function(){return this.max.x-this.min.x;}; - BBox.prototype.h=function(){return this.max.y-this.min.y;}; - - const balloonSize = (cell)=> (cell.shape.size ?? 1); - const cellScale = (cell)=> balloonSize(cell) * pxUnit; - - function cellView(cell, id, explicitFill, model){ - const shape = cell.shape; - const scale = cellScale(cell); - const transform = [(shape.base.transform||''), `scale(${scale})`].join(' '); - const commonAttrs = { - 'vector-effect': 'non-scaling-stroke', stroke: '#111827', - 'stroke-width': 2, 'paint-order': 'stroke fill', class: 'balloon', - fill: explicitFill || '#cccccc' - }; - if (cell.isTopper) { - commonAttrs['data-is-topper'] = true; - } else { - commonAttrs['data-color-code'] = cell.colorCode || 0; - commonAttrs['data-quad-number'] = cell.y + 1; - } - - let shapeEl; - if (shape.base.type === 'path') shapeEl = svg('path', { ...commonAttrs, d: shape.base.d }); - else shapeEl = svg('ellipse', { ...commonAttrs, cx:0, cy:0, rx:0.5, ry:0.5 }); - - const kids = [shapeEl]; - const applyShine = model.shineEnabled && (!cell.isTopper || (cell.isTopper && model.topperType === 'round')); - if (applyShine) { - kids.push(svg('ellipse', { - class: 'shine', cx: -0.15, cy: -0.15, rx: 0.22, ry: 0.13, - fill: '#ffffff', opacity: 0.45, transform: 'rotate(-25)', 'pointer-events': 'none' - })); - } - return svg('g', { id, transform }, kids); - } - - function gridPos(x,y,z,inflate,pattern,model){ - const base = patterns[model.patternName].parent || patterns[model.patternName]; - const rel = (pattern.baseBalloonSize && base.baseBalloonSize) ? pattern.baseBalloonSize/base.baseBalloonSize : 1; - let p = { x: pattern.gridX(model.pattern.cellsPerRow > 1 ? y : x, x), y: pattern.gridY(y,x) }; - if (pattern.transform) p = pattern.transform(p,x,y,model); - return { x: p.x * rel * pxUnit, y: p.y * rel * pxUnit }; - } - - // === Spiral coloring helpers (shared by 4- and 5-balloon clusters) === -function distinctPaletteSlots(palette) { - // Collapse visually identical slots so 3-color spirals work even if you filled 5 slots. - const seen = new Set(), out = []; - for (let s = 1; s <= 5; s++) { - const c = palette[s]; - if (!c) continue; - const key = (c.image || '') + '|' + String(c.colour || '').toLowerCase(); - if (!seen.has(key)) { seen.add(key); out.push(s); } - } - return out.length ? out : [1,2,3,4,5]; -} - - - - function newGrid(pattern, cells, container, model){ - const kids = [], layers = [], bbox = new BBox(); - const balloonsPerCluster = pattern.balloonsPerCluster || 4; - const reversed = !!(pattern._reverse || (pattern.parent && pattern.parent._reverse)); - const rowColorPatterns = {}; - - const colorBlock4 = [[1, 2, 3, 4], [3, 1, 4, 2], [4, 3, 2, 1], [2, 4, 1, 3]]; - const colorBlock5 = - [ - [5, 2, 3, 4, 1], - [2, 3, 4, 5, 1], - [2, 4, 5, 1, 3], - [4, 5, 1, 2, 3], - [4, 1, 2, 3, 5], - ]; - - for (let cell of cells) { - let c, fill; - if (cell.isTopper) { - const topRowYIndex = 0, topClusterY = pattern.gridY(topRowYIndex, 0) * pxUnit; - const regularBalloonRadius = (pattern.balloonShapes['front'] || pattern.balloonShapes['penta'] || pattern.balloonShapes['middle']).size * pxUnit * 0.5; - const highestPoint = topClusterY - regularBalloonRadius; - const topperRadius = cell.shape.size * pxUnit * cell.shape.base.radius; - const topperY = highestPoint - topperRadius - (pxUnit * 0.5) + topperOffsetY_Px; - c = { x: topperOffsetX_Px, y: topperY }; - fill = model.topperColor.image ? `url(#classic-pattern-topper)` : model.topperColor.hex; - } else { - c = gridPos(cell.x, cell.y, cell.shape.zIndex, cell.inflate, pattern, model); - - const rowIndex = cell.y; - if (!rowColorPatterns[rowIndex]) { - const qEff = rowIndex + 1; - let pat; - - if (balloonsPerCluster === 5) { - const base = (qEff - 1) % 5; - pat = colorBlock5[base].slice(); - } else { - const base = Math.floor((qEff - 1) / 2); - pat = colorBlock4[base % 4].slice(); - if (qEff % 2 === 0) { - pat = [pat[0], pat[2], pat[1], pat[3]]; - } - } - - if (reversed && pat.length > 1) { - const first = pat.shift(); - pat.reverse(); - pat.unshift(first); - } - - // --- NEW: swap left/right after every 5 clusters --- -const SWAP_EVERY = 5; // clusters per block -const blockIndex = Math.floor(rowIndex / SWAP_EVERY); - -// swap on blocks #2, #4, #6, ... (i.e., rows 6–10, 16–20, ...) -if (blockIndex % 2 === 1) { - if (balloonsPerCluster === 5) { - // [leftMid, leftBack, front, rightBack, rightMid] - [pat[0], pat[4]] = [pat[4], pat[0]]; - // [pat[1], pat[3]] = [pat[3], pat[1]]; - } -} - rowColorPatterns[rowIndex] = pat; - } - - const colorCode = rowColorPatterns[rowIndex][cell.balloonIndexInCluster]; - cell.colorCode = colorCode; - const colorInfo = model.palette[colorCode]; - fill = colorInfo ? (colorInfo.image ? `url(#classic-pattern-slot-${colorCode})` : colorInfo.colour) : 'transparent'; - } - - const scale = cellScale(cell), shapeRadius = cell.shape.base.radius || 0.5, size = shapeRadius * scale; - bbox.add(c.x - size, c.y - size); - bbox.add(c.x + size, c.y + size); - const v = cellView(cell, `balloon_${cell.x}_${cell.y}`, fill, model); - v.attrs.transform = `translate(${c.x},${c.y}) ${v.attrs.transform || ''}`; - const zi = cell.isTopper ? 100 + 2 : (100 + (cell.shape.zIndex || 0)); - (layers[zi] ||= []).push(v); - }; - - layers.forEach(layer => layer && layer.forEach(v => kids.push(v))); - const margin = 20; - const vb = [ bbox.min.x - margin, bbox.min.y - margin, Math.max(1,bbox.w()) + margin*2, Math.max(1,bbox.h()) + margin*2 ].join(' '); - - const patternsDefs = []; - const SVG_PATTERN_ZOOM = 2.5; - const offset = (1 - SVG_PATTERN_ZOOM) / 2; - - Object.entries(model.palette).forEach(([slot, colorInfo]) => { - if (colorInfo.image) { - patternsDefs.push(svg('pattern', {id: `classic-pattern-slot-${slot}`, patternContentUnits: 'objectBoundingBox', width: 1, height: 1}, - [ svg('image', { href: colorInfo.image, x: offset, y: offset, width: SVG_PATTERN_ZOOM, height: SVG_PATTERN_ZOOM, preserveAspectRatio: 'xMidYMid slice' }) ] - )); - } - }); - if (model.topperColor.image) { - patternsDefs.push(svg('pattern', {id: 'classic-pattern-topper', patternContentUnits: 'objectBoundingBox', width: 1, height: 1}, - [ svg('image', { href: model.topperColor.image, x: offset, y: offset, width: SVG_PATTERN_ZOOM, height: SVG_PATTERN_ZOOM, preserveAspectRatio: 'xMidYMid slice' }) ] - )); - } - const svgDefs = svg('defs', {}, patternsDefs); - - const mainGroup = svg('g', null, kids); - m.render(container, svg('svg', { xmlns: 'http://www.w3.org/2000/svg', width:'100%', height:'100%', viewBox: vb, preserveAspectRatio:'xMidYMid meet', style: 'isolation:isolate' }, [svgDefs, mainGroup])); - } - - function makeController(displayEl){ - const models = []; - function buildModel(name){ - const pattern = patterns[name]; - if (patterns['Column 4']) patterns['Column 4']._reverse = reverse; - if (patterns['Arch 4']) patterns['Arch 4']._reverse = reverse; - if (patterns['Column 5']) patterns['Column 5']._reverse = reverse; - if (patterns['Arch 5']) patterns['Arch 5']._reverse = reverse; - - const model = { patternName: name, pattern, cells: [], rowCount: clusters, palette: buildClassicPalette(), topperColor: getTopperColor(), topperType, shineEnabled }; - const rows = pattern.cellsPerRow * model.rowCount, cols = pattern.cellsPerColumn; - for (let y=0; y [a[0] + (b[0] - a[0]) * u, a[1] + (b[1] - a[1]) * u]; - let v0 = verts[0], v1 = verts[1]; let p0 = lerp(v0, v1, t); - let d = `M ${p0[0].toFixed(4)} ${p0[1].toFixed(4)}`; - for (let i = 0; i < verts.length; i++) { const v = verts[(i + 1) % verts.length], vNext = verts[(i + 2) % verts.length]; const p = lerp(v, vNext, t); d += ` Q ${v[0].toFixed(4)} ${v[1].toFixed(4)} ${p[0].toFixed(4)} ${p[1].toFixed(4)}`; } - return d + ' Z'; - } - - // --- Column 4: This is the existing logic from classic.js, which matches your template file --- - patterns['Column 4'] = { - baseBalloonSize: 25, _reverse: false, balloonsPerCluster: 4, - balloonShapes: { - 'front':{zIndex:4, base:{radius:0.5}, size:3}, 'front-inner':{zIndex:3, base:{radius:0.5}, size:3}, 'back-inner':{zIndex:2, base:{radius:0.5}, size:3}, 'back':{zIndex:1, base:{radius:0.5}, size:3}, - 'topper-round':{base:{type:'ellipse', radius:0.5}, size:8}, 'topper-star':{base:{type:'path', d:roundedStarPath({}), radius:0.5}, size:8}, 'topper-heart':{base:{type:'path', d:'M0,0.35 C-0.5,0, -0.14,-0.35, 0,-0.14 C0.14,-0.35, 0.5,0, 0,0.35 Z', radius:0.5}, size:20} - }, - tile: { size:{x:5,y:1} }, cellsPerRow: 1, cellsPerColumn: 5, - gridX(row, col){ return col + [0, -0.12, -0.24, -0.36, -0.48][col % 5]; }, - gridY(row, col){ return 2.2 * (1 - 1/5) * (Math.floor(row/2) + Math.floor((row+1)/2)); }, - createCell(x, y) { - const odd = !!(y % 2); - const A = ['front-inner','back','', 'front','back-inner'], B = ['back-inner', 'front','', 'back', 'front-inner']; - const arr = this._reverse ? (odd ? B : A) : (odd ? A : B); - const shapeName = arr[x % 5]; - const shape = this.balloonShapes[shapeName]; - return shape ? { shape:{...shape} } : null; - } - }; - - // --- Arch 4: This is the existing logic from classic.js, which matches your template file --- - patterns['Arch 4'] = { - deriveFrom: 'Column 4', - transform(point, col, row, model){ - const len = this.gridY(model.rowCount*this.tile.size.y, 0) - this.gridY(0, 0); - const r = (len / Math.PI) + point.x; - const y = point.y - this.gridY(0, 0); - const a = Math.PI * (y / len); - return { x: -r*Math.cos(a), y: -r*Math.sin(a) }; - } - }; - - // --- START: MODIFIED SECTION --- - // This is the new 'Column 5' definition, adapted from your template file. - patterns['Column 5'] = { - baseBalloonSize: 25, - _reverse: false, - balloonsPerCluster: 5, // Kept this from classic.js to ensure 5-color spiral - tile: { size: { x: 5, y: 1 } }, - cellsPerRow: 1, - cellsPerColumn: 5, - - // Balloon shapes from your template, converted to classic.js format - // (type: "qlink" is approx size: 3.0) - balloonShapes: { - "front": { zIndex:5, base:{radius:0.5}, size:3.0 }, - "front2": { zIndex:4, base:{radius:0.5}, size:3.0 }, - "middle": { zIndex:3, base:{radius:0.5}, size:3.0 }, - "middle2": { zIndex:2, base:{radius:0.5}, size:3.0 }, - "back": { zIndex:1, base:{radius:0.5}, size:3.0 }, - "back2": { zIndex:0, base:{radius:0.5}, size:3.0 } - }, - - // gridX function from your template - // (I've hard-coded `this.exploded` to false, as it's not in classic.js) - gridX(row, col) { - var mid = 0.6; // this.exploded ? 0.2 : 0.6 - return (0.9) * (col + (0 === col % 5 && -0.5) + (1 === col % 5 && -mid) + (3 === col % 5 && mid) + (4 === col % 5 && 0.5) - 0.5); - }, - - // gridY function is inherited from Column 4 via `deriveFrom` in your template. - // So, we use the gridY function from this file's 'Column 4'. - gridY(row, col){ - return 2.2 * (1 - 1/5) * (Math.floor(row/2) + Math.floor((row+1)/2)); - }, - - // createCell function from your template, adapted for classic.js - createCell(x, y) { - var yOdd = !!(y % 2); - - // Re-created logic from template's createCell - const shapePattern = yOdd ? - ['middle', 'back', 'front', 'back', 'middle'] : - ['middle2', 'front2', 'back2', 'front2', 'middle2']; - - var shapeName = shapePattern[x % 5]; - var shape = this.balloonShapes[shapeName]; - - // Return in classic.js format - return shape ? { shape: {...shape} } : null; - } - }; - - // This is the new 'Arch 5' definition. - // It derives from the new 'Column 5' and uses the same arching logic as 'Arch 4'. - patterns['Arch 5'] = { - deriveFrom: 'Column 5', - transform(point, col, row, model){ - // This transform logic is standard and will work with the new Column 5's gridY - const len = this.gridY(model.rowCount * this.tile.size.y, 0) - this.gridY(0, 0); - const r = (len / Math.PI) + point.x; - const y = point.y - this.gridY(0, 0); - const a = Math.PI * (y / len); - return { x: -r * Math.cos(a), y: -r * Math.sin(a) }; - } - }; - // --- END: MODIFIED SECTION --- - - - Object.keys(patterns).forEach(n => extend(patterns[n])); - return api; - } - - function initClassicColorPicker(onColorChange) { - const slots = Array.from(document.querySelectorAll('#classic-slots .slot-btn')), topperSwatch = document.getElementById('classic-topper-color-swatch'), swatchGrid = document.getElementById('classic-swatch-grid'), activeLabel = document.getElementById('classic-active-label'), randomizeBtn = document.getElementById('classic-randomize-colors'); - if (!slots.length || !topperSwatch || !swatchGrid || !activeLabel) return; - topperSwatch.classList.add('tab-btn'); - let classicColors = getClassicColors(), activeTarget = '1'; - - function updateUI() { - [...slots, topperSwatch].forEach(el => { const id = el.dataset.slot || 'T'; el.classList.toggle('tab-active', activeTarget === id); el.classList.toggle('tab-idle', activeTarget !== id); }); - - slots.forEach((slot, i) => { - const color = classicColors[i]; - if (!color) return; // Safeguard against errors - slot.style.backgroundImage = color.image ? `url("${color.image}")` : 'none'; - slot.style.backgroundColor = color.hex; - slot.style.backgroundSize = '200%'; - slot.style.backgroundPosition = 'center'; - }); - - const topperColor = getTopperColor(); - topperSwatch.style.backgroundImage = topperColor.image ? `url("${topperColor.image}")` : 'none'; - topperSwatch.style.backgroundColor = topperColor.hex; - topperSwatch.style.backgroundSize = '200%'; - topperSwatch.style.backgroundPosition = 'center'; - - activeLabel.textContent = activeTarget === 'T' ? 'Topper' : `Slot #${activeTarget}`; - } - - const allPaletteColors = flattenPalette(); swatchGrid.innerHTML = ''; - (window.PALETTE || []).forEach(group => { - const title = document.createElement('div'); title.className = 'family-title'; title.textContent = group.family; swatchGrid.appendChild(title); - const row = document.createElement('div'); row.className = 'swatch-row'; - (group.colors || []).forEach(colorItem => { - const sw = document.createElement('div'); sw.className = 'swatch'; sw.title = colorItem.name; - - sw.style.backgroundImage = colorItem.image ? `url("${colorItem.image}")` : 'none'; - sw.style.backgroundColor = colorItem.hex; - sw.style.backgroundSize = '500%'; - sw.style.backgroundPosition = 'center'; - - sw.addEventListener('click', () => { - const selectedColor = { hex: colorItem.hex, image: colorItem.image }; - if (activeTarget === 'T') setTopperColor(selectedColor); - else { - const index = parseInt(activeTarget, 10) - 1; - if (index >= 0 && index < 5) { classicColors[index] = selectedColor; setClassicColors(classicColors); } - } - updateUI(); onColorChange(); - if (window.updateExportButtonVisibility) window.updateExportButtonVisibility(); - }); - row.appendChild(sw); - }); - swatchGrid.appendChild(row); - }); - slots.forEach(slot => { slot.addEventListener('click', () => { activeTarget = slot.dataset.slot; updateUI(); }); }); - topperSwatch.addEventListener('click', () => { activeTarget = 'T'; updateUI(); }); - randomizeBtn?.addEventListener('click', () => { - const pool = allPaletteColors.slice(); const picks = []; - const colorCount = (patterns[document.getElementById('classic-pattern')?.value] || {}).balloonsPerCluster || 5; - for (let i = 0; i < colorCount && pool.length; i++) { picks.push(pool.splice(Math.floor(Math.random() * pool.length), 1)[0]); } - classicColors = setClassicColors(picks.map(c => ({ hex: c.hex, image: c.image }))); - updateUI(); onColorChange(); - if (window.updateExportButtonVisibility) window.updateExportButtonVisibility(); - }); - updateUI(); - } - - function initClassic() { - try { - if (typeof window.m === 'undefined') return fail('Mithril not loaded'); - const display = document.getElementById('classic-display'), patSel = document.getElementById('classic-pattern'), lengthInp = document.getElementById('classic-length-ft'), clusterHint = document.getElementById('classic-cluster-hint'), reverseCb = document.getElementById('classic-reverse'), rebuildBtn = document.getElementById('classic-rerender'), topperControls = document.getElementById('topper-controls'), topperEnabledCb = document.getElementById('classic-topper-enabled'), topperTypeSelect = document.getElementById('classic-topper-type'), topperOffsetX_Inp = document.getElementById('classic-topper-offset-x'), topperOffsetY_Inp = document.getElementById('classic-topper-offset-y'), topperSizeInp = document.getElementById('classic-topper-size'), shineEnabledCb = document.getElementById('classic-shine-enabled'); - if (!display) return fail('#classic-display not found'); - if (window.setupAccordionPanel) { window.setupAccordionPanel({ panelId: 'classic-controls-panel', expandBtnId: 'classic-expand-all', collapseBtnId: 'classic-collapse-all', reorderBtnId: 'classic-toggle-reorder', storagePrefix: 'cbd' }); } - const GC = GridCalculator(), ctrl = GC.controller(display); - - function updateClassicDesign() { - if (!lengthInp || !patSel) return; - const patternName = patSel.value || 'Arch 4'; - const isColumn = patternName.toLowerCase().includes('column'); - const hasTopper = patternName.includes('4'); - - topperControls.classList.toggle('hidden', !isColumn || !hasTopper); - topperTypeSelect.disabled = !topperEnabledCb.checked; - - GC.setTopperEnabled(isColumn && hasTopper && topperEnabledCb.checked); - GC.setClusters(Math.round((parseFloat(lengthInp.value) || 0) * 2)); - GC.setReverse(!!reverseCb?.checked); - GC.setTopperType(topperTypeSelect.value); - GC.setTopperOffsetX(topperOffsetX_Inp?.value); - GC.setTopperOffsetY(topperOffsetY_Inp?.value); - GC.setTopperSize(topperSizeInp?.value); - GC.setShineEnabled(!!shineEnabledCb?.checked); - if(clusterHint) clusterHint.textContent = `≈ ${Math.round((parseFloat(lengthInp.value) || 0) * 2)} clusters (rule: 2 clusters/ft)`; - ctrl.selectPattern(patternName); - } - - window.ClassicDesigner = window.ClassicDesigner || {}; - window.ClassicDesigner.api = GC; - window.ClassicDesigner.redraw = updateClassicDesign; - window.ClassicDesigner.getColors = getClassicColors; - window.ClassicDesigner.getTopperColor = getTopperColor; - - document.querySelector('#mode-tabs')?.addEventListener('click', () => setTimeout(() => { if (window.updateExportButtonVisibility) window.updateExportButtonVisibility() }, 50)); - patSel?.addEventListener('change', () => { - const isArch = patSel.value.toLowerCase().includes('arch'); - lengthInp.value = isArch ? 25 : 5; - updateClassicDesign(); - }); - [lengthInp, reverseCb, topperEnabledCb, topperTypeSelect, topperOffsetX_Inp, topperOffsetY_Inp, topperSizeInp, rebuildBtn] - .forEach(el => { if (!el) return; const eventType = (el.type === 'range' || el.type === 'number') ? 'input' : 'change'; el.addEventListener((el === rebuildBtn) ? 'click' : eventType, updateClassicDesign); }); - shineEnabledCb?.addEventListener('change', (e) => { const on = !!e.target.checked; GC.setShineEnabled(on); updateClassicDesign(); window.syncAppShine?.(on); }); - initClassicColorPicker(updateClassicDesign); - try { const saved = localStorage.getItem('app:shineEnabled:v1'); if (saved !== null && shineEnabledCb) shineEnabledCb.checked = JSON.parse(saved); } catch {} - updateClassicDesign(); - if (window.updateExportButtonVisibility) window.updateExportButtonVisibility(); - log('Classic ready'); - } catch (e) { fail(e.message || e); } - } - - window.ClassicDesigner = window.ClassicDesigner || { init: initClassic, api: null, redraw: null }; - document.addEventListener('DOMContentLoaded', () => { if (document.getElementById('classic-display') && !window.__classicInit) { window.__classicInit = true; initClassic(); } }); -})(); \ No newline at end of file diff --git a/index.html b/index.html index 63410f0..302f52f 100644 --- a/index.html +++ b/index.html @@ -325,8 +325,9 @@ - - + + +