From 42c93a7fc41cc05f36b37aaf5e4ab968d1ca8d46 Mon Sep 17 00:00:00 2001 From: neurokitti Date: Tue, 11 Feb 2025 17:39:45 -0600 Subject: [PATCH 01/14] this is a test --- src/browser/base/zen-components/ZenGradientGenerator.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/browser/base/zen-components/ZenGradientGenerator.mjs b/src/browser/base/zen-components/ZenGradientGenerator.mjs index 125c45da7..a5fc7ad48 100644 --- a/src/browser/base/zen-components/ZenGradientGenerator.mjs +++ b/src/browser/base/zen-components/ZenGradientGenerator.mjs @@ -1,3 +1,4 @@ +//this is a test { class ZenThemePicker extends ZenMultiWindowFeature { static GRADIENT_IMAGE_URL = 'chrome://browser/content/zen-images/gradient.png'; From 580e3596b2898a4009cd8e8fffe2e35a34f12924 Mon Sep 17 00:00:00 2001 From: neurokitti Date: Tue, 11 Feb 2025 17:42:12 -0600 Subject: [PATCH 02/14] test 2 --- src/browser/base/zen-components/ZenGradientGenerator.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/browser/base/zen-components/ZenGradientGenerator.mjs b/src/browser/base/zen-components/ZenGradientGenerator.mjs index a5fc7ad48..d8d0ae1c9 100644 --- a/src/browser/base/zen-components/ZenGradientGenerator.mjs +++ b/src/browser/base/zen-components/ZenGradientGenerator.mjs @@ -1,4 +1,4 @@ -//this is a test +//this is a test 1 { class ZenThemePicker extends ZenMultiWindowFeature { static GRADIENT_IMAGE_URL = 'chrome://browser/content/zen-images/gradient.png'; From 3b3d5292e51f53e223befe7b747506b927df04c0 Mon Sep 17 00:00:00 2001 From: neurokitti Date: Sat, 15 Feb 2025 01:52:12 -0600 Subject: [PATCH 03/14] removed test comment --- src/browser/base/zen-components/ZenGradientGenerator.mjs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/browser/base/zen-components/ZenGradientGenerator.mjs b/src/browser/base/zen-components/ZenGradientGenerator.mjs index 4a952703d..963f87f76 100644 --- a/src/browser/base/zen-components/ZenGradientGenerator.mjs +++ b/src/browser/base/zen-components/ZenGradientGenerator.mjs @@ -1,4 +1,3 @@ -//this is a test 1 { class ZenThemePicker extends ZenMultiWindowFeature { static GRADIENT_IMAGE_URL = 'chrome://browser/content/zen-images/gradient.png'; From fceb1e85a6fd2c65fcbf06f7caeaea3b74104678 Mon Sep 17 00:00:00 2001 From: neurokitti Date: Sat, 15 Feb 2025 17:02:53 -0600 Subject: [PATCH 04/14] added function to calculate compliments --- .../zen-components/ZenGradientGenerator.mjs | 66 ++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/src/browser/base/zen-components/ZenGradientGenerator.mjs b/src/browser/base/zen-components/ZenGradientGenerator.mjs index 963f87f76..f8b7f535e 100644 --- a/src/browser/base/zen-components/ZenGradientGenerator.mjs +++ b/src/browser/base/zen-components/ZenGradientGenerator.mjs @@ -6,7 +6,7 @@ currentOpacity = 0.5; currentRotation = 45; - + dots = []; numberOfDots = 0; constructor() { @@ -331,6 +331,70 @@ await this.updateCurrentWorkspace(); } + spawnDot() { + + } + + calculateCompliments(primaryDot, dots, colorHarmonyType) { + const colorHarmonies = { + complementary: [180], + splitComplementary: [150, 210], + analogous: [30, 330], + triadic: [120, 240], + }; + + function getColorHarmony(colorHarmonyType) { + return colorHarmonies[colorHarmonyType] || null; + } + + // rule: if the data will be inputed into an argument it should be stored as an object else not + function getAngleFromPosition(position, centerPosition) { + let deltaX = position.x - centerPosition.x; + let deltaY = position.y - centerPosition.y; + let angle = Math.atan2(deltaY, deltaX) * (180 / Math.PI); + return (angle + 360) % 360; + } + + const dotPad = this.panel.querySelector('.zen-theme-picker-gradient'); + const rect = dotPad.getBoundingClientRect(); + const padding = 90; + + // Calculate the center of the color wheel + const centerX = rect.left + rect.width / 2; + const centerY = rect.top + rect.height / 2; + const radius = (rect.width - padding) / 2; + const centerPosition = { x: rect.width / 2, y: rect.height / 2 }; + + const harmonyAngles = getColorHarmony(colorHarmonyType); + if (harmonyAngles.length === 0) return []; + + primaryDot = this.dots.find((dot) => {dot.Element === primaryDot}); + if (!primaryDot) return []; + const baseAngle = getAngleFromPosition(primaryDot.position, centerPosition) + + dots.sort((a, b) => a.ID - b.ID); + + let updatedDots = []; + + harmonyAngles.forEach((angleOffset, index) => { + const dot = dots[index + 1]; // Skip the primary dot + if (!dot) return; + + let newAngle = (baseAngle + angleOffset) % 360; + let radian = (newAngle * Math.PI) / 180; + + let newPosition = { + x: centerPosition.x + radius * Math.cos(radian), + y: centerPosition.y + radius * Math.sin(radian), + }; + + updatedDots.push({ dot, newPosition }); + }); + + return updatedDots; + } + + onThemePickerClick(event) { event.preventDefault(); From e6166c751253b90df48d61fc133640d7053ceb01 Mon Sep 17 00:00:00 2001 From: neurokitti Date: Sat, 15 Feb 2025 22:06:12 -0600 Subject: [PATCH 05/14] basic working version --- .../zen-components/ZenGradientGenerator.mjs | 202 +++++++++++++----- 1 file changed, 143 insertions(+), 59 deletions(-) diff --git a/src/browser/base/zen-components/ZenGradientGenerator.mjs b/src/browser/base/zen-components/ZenGradientGenerator.mjs index f8b7f535e..4886b55d4 100644 --- a/src/browser/base/zen-components/ZenGradientGenerator.mjs +++ b/src/browser/base/zen-components/ZenGradientGenerator.mjs @@ -171,12 +171,17 @@ } onCustomColorKeydown(event) { - //checks for enter key for custom colors + // Check for Enter key to add custom colors if (event.key === 'Enter') { event.preventDefault(); this.addCustomColor(); + + let colorPositions = this.calculateCompliments(this.dots); + console.log("colorPositions: ", colorPositions); + this.handleColorPositions(colorPositions); } } + initThemePicker() { const themePicker = this.panel.querySelector('.zen-theme-picker-gradient'); @@ -245,23 +250,6 @@ } } - onDotMouseDown(event) { - event.preventDefault(); - if (event.button === 2) { - return; - } - this.dragging = true; - this.draggedDot = event.target; - this.draggedDot.style.zIndex = 1; - this.draggedDot.classList.add('dragging'); - - // Store the starting position of the drag - this.dragStartPosition = { - x: event.clientX, - y: event.clientY, - }; - } - onDotMouseMove(event) { if (this.dragging) { event.preventDefault(); @@ -331,20 +319,53 @@ await this.updateCurrentWorkspace(); } - spawnDot() { + spawnDot(relativePosition, primary = false) { + const dotPad = this.panel.querySelector('.zen-theme-picker-gradient'); + const dot = document.createElement('div'); + dot.classList.add('zen-theme-picker-dot'); + + dot.style.left = `${relativePosition.x}px`; + dot.style.top = `${relativePosition.y}px`; + + dotPad.appendChild(dot); + + let id = this.dots.length + + if (primary === true) { + id = 0 + const existingPrimaryDot = this.dots.find((d) => d.ID === 0); + if (existingPrimaryDot) { + existingPrimaryDot.ID = this.dots.length; + } + } + const colorFromPos = this.getColorFromPosition(relativePosition.x, relativePosition.y); + dot.style.setProperty('--zen-theme-picker-dot-color', `rgb(${colorFromPos[0]}, ${colorFromPos[1]}, ${colorFromPos[2]})`); + + + this.dots.push({ + ID: id, + Element: dot, + Position: { x: parseFloat(dot.style.left), y: parseFloat(dot.style.top) } + }); + console.log("dotsss: ", this.dots); } - calculateCompliments(primaryDot, dots, colorHarmonyType) { - const colorHarmonies = { - complementary: [180], - splitComplementary: [150, 210], - analogous: [30, 330], - triadic: [120, 240], - }; + calculateCompliments(dots, dotRemoved = false) { + console.log("calculateCompliments"); + console.log("dots", dots); + const colorHarmonies = [ + { type: 'complementary', angles: [180] }, + { type: 'splitComplementary', angles: [150, 210] }, + { type: 'analogous', angles: [30, 330] }, + { type: 'triadic', angles: [120, 240] }, + ]; - function getColorHarmony(colorHarmonyType) { - return colorHarmonies[colorHarmonyType] || null; + function getColorHarmonyType(numDots) { + if (dotRemoved) { + return colorHarmonies.find((harmony) => harmony.angles.length === numDots - 1); + } + return colorHarmonies.find((harmony) => harmony.angles.length === numDots); } // rule: if the data will be inputed into an argument it should be stored as an object else not @@ -354,7 +375,13 @@ let angle = Math.atan2(deltaY, deltaX) * (180 / Math.PI); return (angle + 360) % 360; } - + + function getDistanceFromCenter(position, centerPosition) { + const deltaX = position.x - centerPosition.x; + const deltaY = position.y - centerPosition.y; + return Math.sqrt(deltaX * deltaX + deltaY * deltaY); + } + const dotPad = this.panel.querySelector('.zen-theme-picker-gradient'); const rect = dotPad.getBoundingClientRect(); const padding = 90; @@ -365,41 +392,70 @@ const radius = (rect.width - padding) / 2; const centerPosition = { x: rect.width / 2, y: rect.height / 2 }; - const harmonyAngles = getColorHarmony(colorHarmonyType); - if (harmonyAngles.length === 0) return []; + //check for null harmonys + console.log("DOTS LENGTH: ", dots.length) + const harmonyAngles = getColorHarmonyType(dots.length); + console.log("harmonyAngles",harmonyAngles); + if (!harmonyAngles || harmonyAngles.angles.length === 0) return []; + + let primaryDot = dots.find((dot) => dot.ID === 0); - primaryDot = this.dots.find((dot) => {dot.Element === primaryDot}); if (!primaryDot) return []; - const baseAngle = getAngleFromPosition(primaryDot.position, centerPosition) + + const baseAngle = getAngleFromPosition(primaryDot.Position, centerPosition) + + let distance = getDistanceFromCenter(primaryDot.Position, centerPosition); + console.log("distance", distance); + // If the dot is outside the radius, adjust it to stay inside + if (distance > radius) { + const angle = Math.atan2(dy, dx); + cursorPosition.x = centerX + Math.cos(angle) * radius; + cursorPosition.y = centerY + Math.sin(angle) * radius; + distance = radius; // Set distance to the constrained radius + } dots.sort((a, b) => a.ID - b.ID); let updatedDots = []; - - harmonyAngles.forEach((angleOffset, index) => { - const dot = dots[index + 1]; // Skip the primary dot - if (!dot) return; - + harmonyAngles.angles.forEach((angleOffset, index) => { let newAngle = (baseAngle + angleOffset) % 360; let radian = (newAngle * Math.PI) / 180; let newPosition = { - x: centerPosition.x + radius * Math.cos(radian), - y: centerPosition.y + radius * Math.sin(radian), + x: centerPosition.x + distance * Math.cos(radian), + y: centerPosition.y + distance * Math.sin(radian), }; - updatedDots.push({ dot, newPosition }); + updatedDots.push({ID: index + 1, Position: newPosition}); }); - return updatedDots; + return updatedDots; } - + + handleColorPositions(colorPositions) { + colorPositions.sort((a, b) => a.ID - b.ID); + + colorPositions.forEach(dotPosition => { + const existingDot = this.dots.find(dot => dot.ID === dotPosition.ID); + + if (existingDot) { + existingDot.Position = dotPosition.Position; + existingDot.Element.style.left = `${dotPosition.Position.x}px`; + existingDot.Element.style.top = `${dotPosition.Position.y}px`; + const colorFromPos = this.getColorFromPosition(dotPosition.Position.x, dotPosition.Position.y); + existingDot.Element.style.setProperty('--zen-theme-picker-dot-color', `rgb(${colorFromPos[0]}, ${colorFromPos[1]}, ${colorFromPos[2]})`); + } else { + this.spawnDot(dotPosition.Position); + } + }); + } + onThemePickerClick(event) { event.preventDefault(); if (event.button !== 0 || this.dragging) return; - + const gradient = this.panel.querySelector('.zen-theme-picker-gradient'); const rect = gradient.getBoundingClientRect(); const padding = 90; // each side @@ -417,26 +473,47 @@ } const clickedElement = event.target; - const isExistingDot = clickedElement.classList.contains('zen-theme-picker-dot'); + let clickedDot = null; + const existingPrimaryDot = this.dots.find((d) => d.ID === 0); + clickedDot = this.dots.find(dot => dot.Element === clickedElement); + if (clickedDot) { + existingPrimaryDot.ID = clickedDot.ID; + clickedDot.ID = 0; + console.log("clickedDot.ID", clickedDot.ID) + console.log("existingPrimaryDot.ID", existingPrimaryDot.ID) - if (!isExistingDot && this.numberOfDots < ZenThemePicker.MAX_DOTS) { - const relativeX = event.clientX - rect.left; - const relativeY = event.clientY - rect.top; + let colorPositions = this.calculateCompliments(this.dots, true); + this.handleColorPositions(colorPositions); + return; + } + + const relativeX = event.clientX - rect.left; + const relativeY = event.clientY - rect.top; - const color = this.getColorFromPosition(relativeX, relativeY); - - const dot = document.createElement('div'); - dot.classList.add('zen-theme-picker-dot'); - dot.addEventListener('mousedown', this.onDotMouseDown.bind(this)); - - dot.style.left = `${relativeX}px`; - dot.style.top = `${relativeY}px`; - dot.style.setProperty('--zen-theme-picker-dot-color', `rgb(${color[0]}, ${color[1]}, ${color[2]})`); - - gradient.appendChild(dot); + + if (!clickedDot && this.dots.length < 1) { + if (this.dots.length === 0) { + this.spawnDot({x: relativeX, y: relativeY}, true); + } + else{ + this.spawnDot({x: relativeX, y: relativeY}); + } + this.updateCurrentWorkspace(true); } + else if (!clickedDot && existingPrimaryDot) { + existingPrimaryDot.Element.style.left = `${relativeX}px`; + existingPrimaryDot.Element.style.top = `${relativeY}px`; + existingPrimaryDot.Position = { + x: relativeX, + y: relativeY + }; + let colorPositions = this.calculateCompliments(this.dots, true); + this.handleColorPositions(colorPositions); + } + + console.log(this.dots); } onDotMouseDown(event) { @@ -461,7 +538,14 @@ if (!event.target.classList.contains('zen-theme-picker-dot')) { return; } + this.dots = this.dots.filter(dot => dot.Element !== event.target); + console.log("this.dots: ", this.dots); event.target.remove(); + + let colorPositions = this.calculateCompliments(this.dots, true); + console.log("colorPositions: ", colorPositions); + this.handleColorPositions(colorPositions); + this.updateCurrentWorkspace(); this.numberOfDots--; return; From b068e9729a9126d531237f67c7096eb483cc12c9 Mon Sep 17 00:00:00 2001 From: neurokitti Date: Sat, 15 Feb 2025 23:11:39 -0600 Subject: [PATCH 06/14] made dots bigger and fixed primary dot color issue --- .../zen-styles/zen-gradient-generator.css | 25 ++-- .../zen-components/ZenGradientGenerator.mjs | 140 ++++++++++-------- 2 files changed, 89 insertions(+), 76 deletions(-) diff --git a/src/browser/base/content/zen-styles/zen-gradient-generator.css b/src/browser/base/content/zen-styles/zen-gradient-generator.css index c9a0844ae..7be09dc58 100644 --- a/src/browser/base/content/zen-styles/zen-gradient-generator.css +++ b/src/browser/base/content/zen-styles/zen-gradient-generator.css @@ -237,18 +237,19 @@ } & .zen-theme-picker-dot { - position: absolute; - z-index: 2; - width: 20px; - height: 20px; - border-radius: 50%; - background: var(--zen-theme-picker-dot-color); - box-shadow: 0 0 0 2px var(--zen-themed-toolbar-bg); - cursor: pointer; - border: 2px solid #fff; - animation: zen-theme-picker-dot-animation 0.5s; - transition: transform 0.2s; - transform: translate(-50%, -50%); + position: absolute; + z-index: 2; + width: 28px; + height: 28px; + border-radius: 50%; + background: var(--zen-theme-picker-dot-color); + box-shadow: 0 0 0 2px var(--zen-themed-toolbar-bg); + cursor: pointer; + border: 3px solid #fff; + animation: zen-theme-picker-dot-animation 0.5s; + transition: transform 0.2s; + transform: translate(-50%, -50%); + &[dragging='true'] { transform: scale(1.2) translate(-50%, -50%) !important; diff --git a/src/browser/base/zen-components/ZenGradientGenerator.mjs b/src/browser/base/zen-components/ZenGradientGenerator.mjs index 4886b55d4..f4591a419 100644 --- a/src/browser/base/zen-components/ZenGradientGenerator.mjs +++ b/src/browser/base/zen-components/ZenGradientGenerator.mjs @@ -7,7 +7,6 @@ currentOpacity = 0.5; currentRotation = 45; dots = []; - numberOfDots = 0; constructor() { super(); @@ -177,7 +176,6 @@ this.addCustomColor(); let colorPositions = this.calculateCompliments(this.dots); - console.log("colorPositions: ", colorPositions); this.handleColorPositions(colorPositions); } } @@ -188,6 +186,7 @@ themePicker.style.setProperty('--zen-theme-picker-gradient-image', `url(${ZenThemePicker.GRADIENT_DISPLAY_URL})`); themePicker.addEventListener('mousemove', this.onDotMouseMove.bind(this)); themePicker.addEventListener('mouseup', this.onDotMouseUp.bind(this)); + themePicker.addEventListener('mousedown', this.onDotMouseDown.bind(this)); themePicker.addEventListener('click', this.onThemePickerClick.bind(this)); } @@ -250,38 +249,6 @@ } } - onDotMouseMove(event) { - if (this.dragging) { - event.preventDefault(); - const rect = this.panel.querySelector('.zen-theme-picker-gradient').getBoundingClientRect(); - const padding = 90; // each side - // do NOT let the ball be draged outside of an imaginary circle. You can drag it anywhere inside the circle - // if the distance between the center of the circle and the dragged ball is bigger than the radius, then the ball - // should be placed on the edge of the circle. If it's inside the circle, then the ball just follows the mouse - - const centerX = rect.left + rect.width / 2; - const centerY = rect.top + rect.height / 2; - const radius = (rect.width - padding) / 2; - let pixelX = event.clientX; - let pixelY = event.clientY; - const distance = Math.sqrt((pixelX - centerX) ** 2 + (pixelY - centerY) ** 2); - if (distance > radius) { - const angle = Math.atan2(pixelY - centerY, pixelX - centerX); - pixelX = centerX + Math.cos(angle) * radius; - pixelY = centerY + Math.sin(angle) * radius; - } - - // set the location of the dot in pixels - const relativeX = pixelX - rect.left; - const relativeY = pixelY - rect.top; - this.draggedDot.style.left = `${relativeX}px`; - this.draggedDot.style.top = `${relativeY}px`; - const color = this.getColorFromPosition(relativeX, relativeY); - this.draggedDot.style.setProperty('--zen-theme-picker-dot-color', `rgb(${color[0]}, ${color[1]}, ${color[2]})`); - this.updateCurrentWorkspace(); - } - } - addColorToCustomList(color) { const listItems = window.MozXULElement.parseXULToFragment(` @@ -334,8 +301,10 @@ if (primary === true) { id = 0 + const existingPrimaryDot = this.dots.find((d) => d.ID === 0); if (existingPrimaryDot) { + existingPrimaryDot.Element.style.zIndex = 999; existingPrimaryDot.ID = this.dots.length; } } @@ -348,12 +317,9 @@ Element: dot, Position: { x: parseFloat(dot.style.left), y: parseFloat(dot.style.top) } }); - console.log("dotsss: ", this.dots); } calculateCompliments(dots, dotRemoved = false) { - console.log("calculateCompliments"); - console.log("dots", dots); const colorHarmonies = [ { type: 'complementary', angles: [180] }, { type: 'splitComplementary', angles: [150, 210] }, @@ -386,16 +352,12 @@ const rect = dotPad.getBoundingClientRect(); const padding = 90; - // Calculate the center of the color wheel const centerX = rect.left + rect.width / 2; const centerY = rect.top + rect.height / 2; const radius = (rect.width - padding) / 2; const centerPosition = { x: rect.width / 2, y: rect.height / 2 }; - //check for null harmonys - console.log("DOTS LENGTH: ", dots.length) const harmonyAngles = getColorHarmonyType(dots.length); - console.log("harmonyAngles",harmonyAngles); if (!harmonyAngles || harmonyAngles.angles.length === 0) return []; let primaryDot = dots.find((dot) => dot.ID === 0); @@ -405,13 +367,9 @@ const baseAngle = getAngleFromPosition(primaryDot.Position, centerPosition) let distance = getDistanceFromCenter(primaryDot.Position, centerPosition); - console.log("distance", distance); - // If the dot is outside the radius, adjust it to stay inside + if (distance > radius) { - const angle = Math.atan2(dy, dx); - cursorPosition.x = centerX + Math.cos(angle) * radius; - cursorPosition.y = centerY + Math.sin(angle) * radius; - distance = radius; // Set distance to the constrained radius + distance = radius; } dots.sort((a, b) => a.ID - b.ID); @@ -434,7 +392,14 @@ handleColorPositions(colorPositions) { colorPositions.sort((a, b) => a.ID - b.ID); - + + const existingPrimaryDot = this.dots.find((d) => d.ID === 0); + + if (existingPrimaryDot) { + existingPrimaryDot.Element.style.zIndex = 999; + const colorFromPos = this.getColorFromPosition(existingPrimaryDot.Position.x, existingPrimaryDot.Position.y); + existingPrimaryDot.Element.style.setProperty('--zen-theme-picker-dot-color', `rgb(${colorFromPos[0]}, ${colorFromPos[1]}, ${colorFromPos[2]})`); + } colorPositions.forEach(dotPosition => { const existingDot = this.dots.find(dot => dot.ID === dotPosition.ID); @@ -444,6 +409,21 @@ existingDot.Element.style.top = `${dotPosition.Position.y}px`; const colorFromPos = this.getColorFromPosition(dotPosition.Position.x, dotPosition.Position.y); existingDot.Element.style.setProperty('--zen-theme-picker-dot-color', `rgb(${colorFromPos[0]}, ${colorFromPos[1]}, ${colorFromPos[2]})`); + + if (!this.dragging) { + gZenUIManager.motion.animate( + existingDot.Element, + { + left: `${dotPosition.Position.x}px`, + top: `${dotPosition.Position.y}px`, + }, + { + duration: 0.4, + type: 'spring', + bounce: 0.3, + } + ); + } } else { this.spawnDot(dotPosition.Position); } @@ -458,7 +438,7 @@ const gradient = this.panel.querySelector('.zen-theme-picker-gradient'); const rect = gradient.getBoundingClientRect(); - const padding = 90; // each side + const padding = 90; const centerX = rect.left + rect.width / 2; const centerY = rect.top + rect.height / 2; @@ -479,8 +459,7 @@ if (clickedDot) { existingPrimaryDot.ID = clickedDot.ID; clickedDot.ID = 0; - console.log("clickedDot.ID", clickedDot.ID) - console.log("existingPrimaryDot.ID", existingPrimaryDot.ID) + clickedDot.Element.style.zIndex = 999; let colorPositions = this.calculateCompliments(this.dots, true); this.handleColorPositions(colorPositions); @@ -513,7 +492,6 @@ this.handleColorPositions(colorPositions); } - console.log(this.dots); } onDotMouseDown(event) { @@ -521,10 +499,13 @@ if (event.button === 2) { return; } - this.dragging = true; - this.draggedDot = event.target; - this.draggedDot.style.zIndex = 1; - this.draggedDot.classList.add('dragging'); + const draggedDot = this.dots.find(dot => dot.Element === event.target) + if (draggedDot) { + this.dragging = true; + this.draggedDot = event.target; + this.draggedDot.classList.add('dragging'); + } + // Store the starting position of the drag this.dragStartPosition = { @@ -539,15 +520,11 @@ return; } this.dots = this.dots.filter(dot => dot.Element !== event.target); - console.log("this.dots: ", this.dots); event.target.remove(); - let colorPositions = this.calculateCompliments(this.dots, true); - console.log("colorPositions: ", colorPositions); this.handleColorPositions(colorPositions); this.updateCurrentWorkspace(); - this.numberOfDots--; return; } @@ -555,14 +532,51 @@ event.preventDefault(); event.stopPropagation(); this.dragging = false; - this.draggedDot.style.zIndex = 1; this.draggedDot.classList.remove('dragging'); this.draggedDot = null; this.dragStartPosition = null; // Reset the drag start position return; } + } - this.numberOfDots = this.panel.querySelectorAll('.zen-theme-picker-dot').length; + + onDotMouseMove(event) { + if (this.dragging) { + event.preventDefault(); + const rect = this.panel.querySelector('.zen-theme-picker-gradient').getBoundingClientRect(); + const padding = 90; // each side + // do NOT let the ball be draged outside of an imaginary circle. You can drag it anywhere inside the circle + // if the distance between the center of the circle and the dragged ball is bigger than the radius, then the ball + // should be placed on the edge of the circle. If it's inside the circle, then the ball just follows the mouse + + const centerX = rect.left + rect.width / 2; + const centerY = rect.top + rect.height / 2; + const radius = (rect.width - padding) / 2; + let pixelX = event.clientX; + let pixelY = event.clientY; + const distance = Math.sqrt((pixelX - centerX) ** 2 + (pixelY - centerY) ** 2); + if (distance > radius) { + const angle = Math.atan2(pixelY - centerY, pixelX - centerX); + pixelX = centerX + Math.cos(angle) * radius; + pixelY = centerY + Math.sin(angle) * radius; + } + + // set the location of the dot in pixels + const relativeX = pixelX - rect.left; + const relativeY = pixelY - rect.top; + + const draggedDot = this.dots.find(dot => dot.Element === this.draggedDot) + draggedDot.Element.style.left = `${relativeX}px`; + draggedDot.Element.style.top = `${relativeY}px`; + draggedDot.Position = { + x: relativeX, + y: relativeY + }; + let colorPositions = this.calculateCompliments(this.dots, true); + this.handleColorPositions(colorPositions); + + this.updateCurrentWorkspace(); + } } themedColors(colors) { @@ -780,8 +794,6 @@ browser.gZenThemePicker.currentRotation = workspaceTheme.rotation ?? 45; browser.gZenThemePicker.currentTexture = workspaceTheme.texture ?? 0; - browser.gZenThemePicker.numberOfDots = workspaceTheme.gradientColors.length; - browser.document.getElementById('PanelUI-zen-gradient-generator-opacity').value = browser.gZenThemePicker.currentOpacity; browser.document.getElementById('PanelUI-zen-gradient-generator-texture').value = From 05a97971b155559b3f09658bc0818fe661a564c1 Mon Sep 17 00:00:00 2001 From: neurokitti Date: Sun, 16 Feb 2025 01:04:07 -0600 Subject: [PATCH 07/14] fixed creatDot issue --- .../zen-components/ZenGradientGenerator.mjs | 87 +++++++++++++------ 1 file changed, 59 insertions(+), 28 deletions(-) diff --git a/src/browser/base/zen-components/ZenGradientGenerator.mjs b/src/browser/base/zen-components/ZenGradientGenerator.mjs index 9a91c3023..f8318554b 100644 --- a/src/browser/base/zen-components/ZenGradientGenerator.mjs +++ b/src/browser/base/zen-components/ZenGradientGenerator.mjs @@ -237,13 +237,26 @@ dot.style.opacity = 0; dot.style.setProperty('--zen-theme-picker-dot-color', color.c); } else { - dot.style.setProperty('--zen-theme-picker-dot-color', `rgb(${r}, ${g}, ${b})`); const { x, y } = this.calculateInitialPosition(color); + const dotPad = this.panel.querySelector('.zen-theme-picker-gradient'); + + const dot = document.createElement('div'); + dot.classList.add('zen-theme-picker-dot'); + dot.style.left = `${x * 100}%`; dot.style.top = `${y * 100}%`; - dot.addEventListener('mousedown', this.onDotMouseDown.bind(this)); + + dotPad.appendChild(dot); + let id = this.dots.length; + + dot.style.setProperty('--zen-theme-picker-dot-color', `rgb(${r}, ${g}, ${b})`); + + this.dots.push({ + ID: id, + Element: dot, + Position: { x: parseFloat(dot.style.left), y: parseFloat(dot.style.top) } + }); } - this.panel.querySelector('.zen-theme-picker-gradient').appendChild(dot); if (!fromWorkspace) { this.updateCurrentWorkspace(true); } @@ -297,17 +310,16 @@ dotPad.appendChild(dot); - let id = this.dots.length + let id = this.dots.length; if (primary === true) { id = 0 - const existingPrimaryDot = this.dots.find((d) => d.ID === 0); if (existingPrimaryDot) { - existingPrimaryDot.Element.style.zIndex = 999; existingPrimaryDot.ID = this.dots.length; } } + const colorFromPos = this.getColorFromPosition(relativePosition.x, relativePosition.y); dot.style.setProperty('--zen-theme-picker-dot-color', `rgb(${colorFromPos[0]}, ${colorFromPos[1]}, ${colorFromPos[2]})`); @@ -364,7 +376,7 @@ if (!primaryDot) return []; - const baseAngle = getAngleFromPosition(primaryDot.Position, centerPosition) + const baseAngle = getAngleFromPosition(primaryDot.Position, centerPosition); let distance = getDistanceFromCenter(primaryDot.Position, centerPosition); @@ -434,8 +446,8 @@ onThemePickerClick(event) { event.preventDefault(); - if (event.button !== 0 || this.dragging) return; - + if (event.button !== 0 || this.dragging || this.recentlyDragged) return; + const gradient = this.panel.querySelector('.zen-theme-picker-gradient'); const rect = gradient.getBoundingClientRect(); const padding = 90; @@ -446,29 +458,28 @@ let pixelX = event.clientX; let pixelY = event.clientY; + const clickedElement = event.target; + let clickedDot = null; + const existingPrimaryDot = this.dots.find((d) => d.ID === 0); + clickedDot = this.dots.find((dot) => dot.Element === clickedElement); + console.log(clickedDot); + if (clickedDot) { + existingPrimaryDot.ID = clickedDot.ID; + clickedDot.ID = 0; + clickedDot.Element.style.zIndex = 999; + let colorPositions = this.calculateCompliments(this.dots, true); + this.handleColorPositions(colorPositions); + return; + } + // Check if the click is within the circle const distance = Math.sqrt((pixelX - centerX) ** 2 + (pixelY - centerY) ** 2); if (distance > radius) { return; } - - const clickedElement = event.target; - let clickedDot = null; - const existingPrimaryDot = this.dots.find((d) => d.ID === 0); - clickedDot = this.dots.find(dot => dot.Element === clickedElement); - if (clickedDot) { - existingPrimaryDot.ID = clickedDot.ID; - clickedDot.ID = 0; - clickedDot.Element.style.zIndex = 999; - - let colorPositions = this.calculateCompliments(this.dots, true); - this.handleColorPositions(colorPositions); - return; - } const relativeX = event.clientX - rect.left; const relativeY = event.clientY - rect.top; - if (!clickedDot && this.dots.length < 1) { @@ -488,10 +499,23 @@ x: relativeX, y: relativeY }; + let colorPositions = this.calculateCompliments(this.dots, true); this.handleColorPositions(colorPositions); - } + gZenUIManager.motion.animate( + existingPrimaryDot.Element, + { + left: `${relativeX}px`, + top: `${relativeY}px`, + }, + { + duration: 0.4, + type: 'spring', + bounce: 0.3, + } + ); + } } onDotMouseDown(event) { @@ -505,8 +529,10 @@ this.draggedDot = event.target; this.draggedDot.classList.add('dragging'); } - - + console.log("Dots: ", this.dots); + this.dots.forEach((dot) => { + console.log(dot.Element); + }); // Store the starting position of the drag this.dragStartPosition = { x: event.clientX, @@ -535,6 +561,11 @@ this.draggedDot.classList.remove('dragging'); this.draggedDot = null; this.dragStartPosition = null; // Reset the drag start position + + this.recentlyDragged = true; + setTimeout(() => { + this.recentlyDragged = false; + }, 100); return; } } @@ -822,6 +853,7 @@ } if (!skipUpdate) { + this.dots = []; browser.gZenThemePicker.recalculateDots(workspaceTheme.gradientColors); } }); @@ -862,7 +894,6 @@ } recalculateDots(colors) { - //THIS IS PART OF THE ISSUE for (const color of colors) { this.createDot(color, true); } From ff14d2b32e45ae1dd58b90ab6f78d00ce67c17c3 Mon Sep 17 00:00:00 2001 From: neurokitti Date: Sun, 16 Feb 2025 01:54:17 -0600 Subject: [PATCH 08/14] removed console logs --- src/browser/base/zen-components/ZenGradientGenerator.mjs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/browser/base/zen-components/ZenGradientGenerator.mjs b/src/browser/base/zen-components/ZenGradientGenerator.mjs index f8318554b..f3a20d0e2 100644 --- a/src/browser/base/zen-components/ZenGradientGenerator.mjs +++ b/src/browser/base/zen-components/ZenGradientGenerator.mjs @@ -462,7 +462,6 @@ let clickedDot = null; const existingPrimaryDot = this.dots.find((d) => d.ID === 0); clickedDot = this.dots.find((dot) => dot.Element === clickedElement); - console.log(clickedDot); if (clickedDot) { existingPrimaryDot.ID = clickedDot.ID; clickedDot.ID = 0; @@ -529,10 +528,7 @@ this.draggedDot = event.target; this.draggedDot.classList.add('dragging'); } - console.log("Dots: ", this.dots); - this.dots.forEach((dot) => { - console.log(dot.Element); - }); + // Store the starting position of the drag this.dragStartPosition = { x: event.clientX, From a0d2770d702da5c73e8ccda1ca9ca9d4d8bf5946 Mon Sep 17 00:00:00 2001 From: neurokitti Date: Sun, 16 Feb 2025 02:21:35 -0600 Subject: [PATCH 09/14] removed console log --- l10n | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/l10n b/l10n index 19e2af33c..83464a5d3 160000 --- a/l10n +++ b/l10n @@ -1 +1 @@ -Subproject commit 19e2af33c4d901a4edece2b95c4372b40d50a942 +Subproject commit 83464a5d3596558a8d15c7fd7f495f61726a0f05 From c71cea87ccc15ecba4bd231b56a399a958e15be5 Mon Sep 17 00:00:00 2001 From: neurokitti Date: Sun, 16 Feb 2025 02:24:05 -0600 Subject: [PATCH 10/14] fixed formatting --- .../zen-styles/zen-gradient-generator.css | 25 ++-- .../zen-components/ZenGradientGenerator.mjs | 121 +++++++++--------- 2 files changed, 72 insertions(+), 74 deletions(-) diff --git a/src/browser/base/content/zen-styles/zen-gradient-generator.css b/src/browser/base/content/zen-styles/zen-gradient-generator.css index 7be09dc58..20980305c 100644 --- a/src/browser/base/content/zen-styles/zen-gradient-generator.css +++ b/src/browser/base/content/zen-styles/zen-gradient-generator.css @@ -237,19 +237,18 @@ } & .zen-theme-picker-dot { - position: absolute; - z-index: 2; - width: 28px; - height: 28px; - border-radius: 50%; - background: var(--zen-theme-picker-dot-color); - box-shadow: 0 0 0 2px var(--zen-themed-toolbar-bg); - cursor: pointer; - border: 3px solid #fff; - animation: zen-theme-picker-dot-animation 0.5s; - transition: transform 0.2s; - transform: translate(-50%, -50%); - + position: absolute; + z-index: 2; + width: 28px; + height: 28px; + border-radius: 50%; + background: var(--zen-theme-picker-dot-color); + box-shadow: 0 0 0 2px var(--zen-themed-toolbar-bg); + cursor: pointer; + border: 3px solid #fff; + animation: zen-theme-picker-dot-animation 0.5s; + transition: transform 0.2s; + transform: translate(-50%, -50%); &[dragging='true'] { transform: scale(1.2) translate(-50%, -50%) !important; diff --git a/src/browser/base/zen-components/ZenGradientGenerator.mjs b/src/browser/base/zen-components/ZenGradientGenerator.mjs index f3a20d0e2..093e6c2b3 100644 --- a/src/browser/base/zen-components/ZenGradientGenerator.mjs +++ b/src/browser/base/zen-components/ZenGradientGenerator.mjs @@ -174,12 +174,11 @@ if (event.key === 'Enter') { event.preventDefault(); this.addCustomColor(); - + let colorPositions = this.calculateCompliments(this.dots); this.handleColorPositions(colorPositions); } } - initThemePicker() { const themePicker = this.panel.querySelector('.zen-theme-picker-gradient'); @@ -242,19 +241,19 @@ const dot = document.createElement('div'); dot.classList.add('zen-theme-picker-dot'); - + dot.style.left = `${x * 100}%`; dot.style.top = `${y * 100}%`; - + dotPad.appendChild(dot); let id = this.dots.length; dot.style.setProperty('--zen-theme-picker-dot-color', `rgb(${r}, ${g}, ${b})`); - + this.dots.push({ ID: id, Element: dot, - Position: { x: parseFloat(dot.style.left), y: parseFloat(dot.style.top) } + Position: { x: parseFloat(dot.style.left), y: parseFloat(dot.style.top) }, }); } if (!fromWorkspace) { @@ -307,13 +306,13 @@ dot.style.left = `${relativePosition.x}px`; dot.style.top = `${relativePosition.y}px`; - + dotPad.appendChild(dot); - + let id = this.dots.length; if (primary === true) { - id = 0 + id = 0; const existingPrimaryDot = this.dots.find((d) => d.ID === 0); if (existingPrimaryDot) { existingPrimaryDot.ID = this.dots.length; @@ -322,12 +321,11 @@ const colorFromPos = this.getColorFromPosition(relativePosition.x, relativePosition.y); dot.style.setProperty('--zen-theme-picker-dot-color', `rgb(${colorFromPos[0]}, ${colorFromPos[1]}, ${colorFromPos[2]})`); - this.dots.push({ ID: id, Element: dot, - Position: { x: parseFloat(dot.style.left), y: parseFloat(dot.style.top) } + Position: { x: parseFloat(dot.style.left), y: parseFloat(dot.style.top) }, }); } @@ -377,71 +375,76 @@ if (!primaryDot) return []; const baseAngle = getAngleFromPosition(primaryDot.Position, centerPosition); - + let distance = getDistanceFromCenter(primaryDot.Position, centerPosition); if (distance > radius) { distance = radius; } - dots.sort((a, b) => a.ID - b.ID); + dots.sort((a, b) => a.ID - b.ID); let updatedDots = []; harmonyAngles.angles.forEach((angleOffset, index) => { let newAngle = (baseAngle + angleOffset) % 360; let radian = (newAngle * Math.PI) / 180; - + let newPosition = { x: centerPosition.x + distance * Math.cos(radian), y: centerPosition.y + distance * Math.sin(radian), }; - updatedDots.push({ID: index + 1, Position: newPosition}); + updatedDots.push({ ID: index + 1, Position: newPosition }); }); - - return updatedDots; + + return updatedDots; } - + handleColorPositions(colorPositions) { colorPositions.sort((a, b) => a.ID - b.ID); const existingPrimaryDot = this.dots.find((d) => d.ID === 0); - + if (existingPrimaryDot) { existingPrimaryDot.Element.style.zIndex = 999; const colorFromPos = this.getColorFromPosition(existingPrimaryDot.Position.x, existingPrimaryDot.Position.y); - existingPrimaryDot.Element.style.setProperty('--zen-theme-picker-dot-color', `rgb(${colorFromPos[0]}, ${colorFromPos[1]}, ${colorFromPos[2]})`); + existingPrimaryDot.Element.style.setProperty( + '--zen-theme-picker-dot-color', + `rgb(${colorFromPos[0]}, ${colorFromPos[1]}, ${colorFromPos[2]})` + ); } - colorPositions.forEach(dotPosition => { - const existingDot = this.dots.find(dot => dot.ID === dotPosition.ID); - + colorPositions.forEach((dotPosition) => { + const existingDot = this.dots.find((dot) => dot.ID === dotPosition.ID); + if (existingDot) { existingDot.Position = dotPosition.Position; existingDot.Element.style.left = `${dotPosition.Position.x}px`; existingDot.Element.style.top = `${dotPosition.Position.y}px`; const colorFromPos = this.getColorFromPosition(dotPosition.Position.x, dotPosition.Position.y); - existingDot.Element.style.setProperty('--zen-theme-picker-dot-color', `rgb(${colorFromPos[0]}, ${colorFromPos[1]}, ${colorFromPos[2]})`); - - if (!this.dragging) { - gZenUIManager.motion.animate( - existingDot.Element, - { - left: `${dotPosition.Position.x}px`, - top: `${dotPosition.Position.y}px`, - }, - { - duration: 0.4, - type: 'spring', - bounce: 0.3, - } + existingDot.Element.style.setProperty( + '--zen-theme-picker-dot-color', + `rgb(${colorFromPos[0]}, ${colorFromPos[1]}, ${colorFromPos[2]})` ); - } + + if (!this.dragging) { + gZenUIManager.motion.animate( + existingDot.Element, + { + left: `${dotPosition.Position.x}px`, + top: `${dotPosition.Position.y}px`, + }, + { + duration: 0.4, + type: 'spring', + bounce: 0.3, + } + ); + } } else { this.spawnDot(dotPosition.Position); } }); } - onThemePickerClick(event) { event.preventDefault(); @@ -476,27 +479,24 @@ if (distance > radius) { return; } - + const relativeX = event.clientX - rect.left; const relativeY = event.clientY - rect.top; - - if (!clickedDot && this.dots.length < 1) { + if (!clickedDot && this.dots.length < 1) { if (this.dots.length === 0) { - this.spawnDot({x: relativeX, y: relativeY}, true); + this.spawnDot({ x: relativeX, y: relativeY }, true); + } else { + this.spawnDot({ x: relativeX, y: relativeY }); } - else{ - this.spawnDot({x: relativeX, y: relativeY}); - } - + this.updateCurrentWorkspace(true); - } - else if (!clickedDot && existingPrimaryDot) { + } else if (!clickedDot && existingPrimaryDot) { existingPrimaryDot.Element.style.left = `${relativeX}px`; existingPrimaryDot.Element.style.top = `${relativeY}px`; existingPrimaryDot.Position = { - x: relativeX, - y: relativeY + x: relativeX, + y: relativeY, }; let colorPositions = this.calculateCompliments(this.dots, true); @@ -513,8 +513,8 @@ type: 'spring', bounce: 0.3, } - ); - } + ); + } } onDotMouseDown(event) { @@ -522,7 +522,7 @@ if (event.button === 2) { return; } - const draggedDot = this.dots.find(dot => dot.Element === event.target) + const draggedDot = this.dots.find((dot) => dot.Element === event.target); if (draggedDot) { this.dragging = true; this.draggedDot = event.target; @@ -541,7 +541,7 @@ if (!event.target.classList.contains('zen-theme-picker-dot')) { return; } - this.dots = this.dots.filter(dot => dot.Element !== event.target); + this.dots = this.dots.filter((dot) => dot.Element !== event.target); event.target.remove(); let colorPositions = this.calculateCompliments(this.dots, true); this.handleColorPositions(colorPositions); @@ -566,7 +566,6 @@ } } - onDotMouseMove(event) { if (this.dragging) { event.preventDefault(); @@ -591,17 +590,17 @@ // set the location of the dot in pixels const relativeX = pixelX - rect.left; const relativeY = pixelY - rect.top; - - const draggedDot = this.dots.find(dot => dot.Element === this.draggedDot) + + const draggedDot = this.dots.find((dot) => dot.Element === this.draggedDot); draggedDot.Element.style.left = `${relativeX}px`; draggedDot.Element.style.top = `${relativeY}px`; draggedDot.Position = { - x: relativeX, - y: relativeY + x: relativeX, + y: relativeY, }; let colorPositions = this.calculateCompliments(this.dots, true); this.handleColorPositions(colorPositions); - + this.updateCurrentWorkspace(); } } From 3c323519a829e7f6df0f6dafaada442fe65757b9 Mon Sep 17 00:00:00 2001 From: neurokitti Date: Sun, 16 Feb 2025 02:38:55 -0600 Subject: [PATCH 11/14] fixed issue with removing dots in wrong order --- src/browser/base/zen-components/ZenGradientGenerator.mjs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/browser/base/zen-components/ZenGradientGenerator.mjs b/src/browser/base/zen-components/ZenGradientGenerator.mjs index 093e6c2b3..51091938b 100644 --- a/src/browser/base/zen-components/ZenGradientGenerator.mjs +++ b/src/browser/base/zen-components/ZenGradientGenerator.mjs @@ -543,6 +543,15 @@ } this.dots = this.dots.filter((dot) => dot.Element !== event.target); event.target.remove(); + + this.dots.sort((a, b) => a.ID - b.ID); + + // Reassign the IDs after sorting + this.dots.forEach((dot, index) => { + dot.ID = index; + }); + + console.log(this.dots); let colorPositions = this.calculateCompliments(this.dots, true); this.handleColorPositions(colorPositions); From 8783c469bedc0812973ed864a456dc909d464bc3 Mon Sep 17 00:00:00 2001 From: neurokitti Date: Sun, 16 Feb 2025 02:45:10 -0600 Subject: [PATCH 12/14] fixed white space issue --- src/browser/base/zen-components/ZenGradientGenerator.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/browser/base/zen-components/ZenGradientGenerator.mjs b/src/browser/base/zen-components/ZenGradientGenerator.mjs index 51091938b..6b5036c1b 100644 --- a/src/browser/base/zen-components/ZenGradientGenerator.mjs +++ b/src/browser/base/zen-components/ZenGradientGenerator.mjs @@ -548,7 +548,7 @@ // Reassign the IDs after sorting this.dots.forEach((dot, index) => { - dot.ID = index; + dot.ID = index; }); console.log(this.dots); From 944e7cb60a89cb4368bf35bebccf0d4d179c9fbf Mon Sep 17 00:00:00 2001 From: neurokitti Date: Sun, 16 Feb 2025 03:03:33 -0600 Subject: [PATCH 13/14] dot now moves inside circle if user clicks outside circle instead of not appearing --- src/browser/base/zen-components/ZenGradientGenerator.mjs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/browser/base/zen-components/ZenGradientGenerator.mjs b/src/browser/base/zen-components/ZenGradientGenerator.mjs index 6b5036c1b..8c3c4160b 100644 --- a/src/browser/base/zen-components/ZenGradientGenerator.mjs +++ b/src/browser/base/zen-components/ZenGradientGenerator.mjs @@ -474,14 +474,15 @@ return; } - // Check if the click is within the circle const distance = Math.sqrt((pixelX - centerX) ** 2 + (pixelY - centerY) ** 2); if (distance > radius) { - return; + const angle = Math.atan2(pixelY - centerY, pixelX - centerX); + pixelX = centerX + Math.cos(angle) * radius; + pixelY = centerY + Math.sin(angle) * radius; } - const relativeX = event.clientX - rect.left; - const relativeY = event.clientY - rect.top; + const relativeX = pixelX - rect.left; + const relativeY = pixelY - rect.top; if (!clickedDot && this.dots.length < 1) { if (this.dots.length === 0) { From 2140029a4a3557cc39af864e1c7bde247bba79e3 Mon Sep 17 00:00:00 2001 From: neurokitti Date: Sun, 16 Feb 2025 03:23:37 -0600 Subject: [PATCH 14/14] fixed custom color input button css --- .../base/content/zen-styles/zen-gradient-generator.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/browser/base/content/zen-styles/zen-gradient-generator.css b/src/browser/base/content/zen-styles/zen-gradient-generator.css index 20980305c..411523b32 100644 --- a/src/browser/base/content/zen-styles/zen-gradient-generator.css +++ b/src/browser/base/content/zen-styles/zen-gradient-generator.css @@ -97,8 +97,8 @@ #PanelUI-zen-gradient-generator-color-custom-add { position: absolute; - right: 5px; - top: 5px; + right: 0px; + top: 0px; cursor: pointer; }