diff --git a/.gitignore b/.gitignore
index 2f7896d..188ebd1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,3 @@
target/
+.vscode/
+.idea/
\ No newline at end of file
diff --git a/site/hex-color/index.html b/site/hex-color/index.html
new file mode 100644
index 0000000..921b912
--- /dev/null
+++ b/site/hex-color/index.html
@@ -0,0 +1,37 @@
+
+
+
+
+
+ Hex Color Converter - Tools
+
+
+
+
+
+
+
+
+
+
+
+
+ | Preview |
+ Hex |
+ Normalized (R, G, B, A) |
+ Action |
+
+
+
+
+
+
+
+
+
+
diff --git a/site/hex-color/script.js b/site/hex-color/script.js
new file mode 100644
index 0000000..07852ed
--- /dev/null
+++ b/site/hex-color/script.js
@@ -0,0 +1,116 @@
+async function addHexFromClipboard() {
+ try {
+ const text = await navigator.clipboard.readText();
+ const hex = text.trim();
+
+ // Parse hex color
+ const color = parseHexColor(hex);
+ if (!color) {
+ alert('Invalid hex color in clipboard. Expected format: #RRGGBB or #RRGGBBAA');
+ return;
+ }
+
+ // Add row to table
+ addColorRow(hex, color);
+
+ // Show table if hidden
+ document.getElementById('colorTable').classList.remove('hidden');
+ } catch (err) {
+ alert('Failed to read from clipboard: ' + err.message);
+ }
+}
+
+document.getElementById('addHexBtn').addEventListener('click', addHexFromClipboard);
+
+document.getElementById('showHiddenBtn').addEventListener('click', () => {
+ const rows = document.querySelectorAll('#colorTableBody tr.hidden');
+ rows.forEach(row => row.classList.remove('hidden'));
+ updateShowHiddenButton();
+});
+
+document.addEventListener('keydown', (e) => {
+ if (e.key === 'p' || e.key === 'P') {
+ addHexFromClipboard();
+ }
+});
+
+function parseHexColor(hex) {
+ // Remove leading hash if present
+ const cleanHex = hex.startsWith('#') ? hex.slice(1) : hex;
+
+ // Validate hex string (6 or 8 characters)
+ if (!/^[0-9A-Fa-f]{6}([0-9A-Fa-f]{2})?$/.test(cleanHex)) {
+ return null;
+ }
+
+ // Parse components
+ const r = parseInt(cleanHex.slice(0, 2), 16) / 255;
+ const g = parseInt(cleanHex.slice(2, 4), 16) / 255;
+ const b = parseInt(cleanHex.slice(4, 6), 16) / 255;
+ const a = cleanHex.length === 8 ? parseInt(cleanHex.slice(6, 8), 16) / 255 : 1.0;
+
+ return { r, g, b, a, hex: '#' + cleanHex };
+}
+
+function addColorRow(originalHex, color) {
+ const tbody = document.getElementById('colorTableBody');
+ const row = tbody.insertRow();
+
+ // Preview cell
+ const previewCell = row.insertCell();
+ const preview = document.createElement('div');
+ preview.style.width = '40px';
+ preview.style.height = '40px';
+ preview.style.backgroundColor = `rgba(${color.r * 255}, ${color.g * 255}, ${color.b * 255}, ${color.a})`;
+ preview.style.border = '1px solid #ccc';
+ preview.style.borderRadius = '4px';
+ previewCell.appendChild(preview);
+
+ // Hex cell
+ const hexCell = row.insertCell();
+ hexCell.textContent = color.hex;
+
+ // Normalized components cell
+ const normalizedCell = row.insertCell();
+ const normalizedText = `${color.r.toFixed(1)}, ${color.g.toFixed(1)}, ${color.b.toFixed(1)}, ${color.a.toFixed(1)}`;
+ normalizedCell.textContent = normalizedText;
+
+ // Action cell with copy and hide buttons
+ const actionCell = row.insertCell();
+
+ const copyBtn = document.createElement('button');
+ copyBtn.textContent = 'Copy';
+ copyBtn.className = 'secondary';
+ copyBtn.addEventListener('click', () => {
+ navigator.clipboard.writeText(normalizedText).then(() => {
+ const originalText = copyBtn.textContent;
+ copyBtn.textContent = 'Copied!';
+ setTimeout(() => {
+ copyBtn.textContent = originalText;
+ }, 1500);
+ }).catch(err => {
+ alert('Failed to copy: ' + err.message);
+ });
+ });
+ actionCell.appendChild(copyBtn);
+
+ const hideBtn = document.createElement('button');
+ hideBtn.textContent = 'Hide';
+ hideBtn.className = 'secondary';
+ hideBtn.addEventListener('click', () => {
+ row.classList.add('hidden');
+ updateShowHiddenButton();
+ });
+ actionCell.appendChild(hideBtn);
+}
+
+function updateShowHiddenButton() {
+ const hiddenRows = document.querySelectorAll('#colorTableBody tr.hidden');
+ const showHiddenBtn = document.getElementById('showHiddenBtn');
+
+ if (hiddenRows.length > 0) {
+ showHiddenBtn.classList.remove('hidden');
+ } else {
+ showHiddenBtn.classList.add('hidden');
+ }
+}
diff --git a/site/hex-color/style.css b/site/hex-color/style.css
new file mode 100644
index 0000000..a5b0997
--- /dev/null
+++ b/site/hex-color/style.css
@@ -0,0 +1,20 @@
+.hidden {
+ display: none;
+}
+
+#colorTable {
+ margin-top: 2rem;
+}
+
+#colorTable button {
+ margin: 0;
+ margin-right: 0.5rem;
+}
+
+#colorTable td {
+ vertical-align: middle;
+}
+
+#showHiddenBtn {
+ margin-top: 1rem;
+}
diff --git a/site/index.html b/site/index.html
index 55afdd4..23d989a 100644
--- a/site/index.html
+++ b/site/index.html
@@ -30,6 +30,7 @@
Finska Scorecard
Mental Arithmatic Game
Neon Snake: vibe-coded by Google Gemini
+ Hex Color Converter