From e63cd7b196c92e6d9f24d1ec37645797a4c364e0 Mon Sep 17 00:00:00 2001 From: "aary.sh" Date: Sun, 14 Dec 2025 11:13:55 +1100 Subject: [PATCH 01/35] Create HELP.md --- docs/HELP.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 docs/HELP.md diff --git a/docs/HELP.md b/docs/HELP.md new file mode 100644 index 0000000..4bcfe98 --- /dev/null +++ b/docs/HELP.md @@ -0,0 +1 @@ +d From 5fe434e8d874b48cc87127ee66f98fce4df8a966 Mon Sep 17 00:00:00 2001 From: "aary.sh" Date: Sun, 14 Dec 2025 11:14:04 +1100 Subject: [PATCH 02/35] Create FEATURES.md --- docs/FEATURES.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 docs/FEATURES.md diff --git a/docs/FEATURES.md b/docs/FEATURES.md new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/docs/FEATURES.md @@ -0,0 +1 @@ + From 1b40b54dda0803c356951a7d5bbe686b215c2a24 Mon Sep 17 00:00:00 2001 From: "aary.sh" Date: Sun, 14 Dec 2025 11:15:33 +1100 Subject: [PATCH 03/35] Create .placehold --- assets/.placehold | 1 + 1 file changed, 1 insertion(+) create mode 100644 assets/.placehold diff --git a/assets/.placehold b/assets/.placehold new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/assets/.placehold @@ -0,0 +1 @@ + From 8f085bd3f419e2354a01a66ee740b6b4644be458 Mon Sep 17 00:00:00 2001 From: "aary.sh" Date: Sun, 14 Dec 2025 11:16:28 +1100 Subject: [PATCH 04/35] The official logos of "crescent" these are one of the many versions of the logo. --- assets/crescent..svg | 4 ++++ assets/favicon.ico | Bin 0 -> 1150 bytes 2 files changed, 4 insertions(+) create mode 100644 assets/crescent..svg create mode 100644 assets/favicon.ico diff --git a/assets/crescent..svg b/assets/crescent..svg new file mode 100644 index 0000000..3c12839 --- /dev/null +++ b/assets/crescent..svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/favicon.ico b/assets/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..7ec9e297361ea71bcfca21b0274017c26318caa2 GIT binary patch literal 1150 zcmZQzU<5(|0R|wcz>vYhz#zuJz@P!dKp~(AL>x#lFaYI*xgi+LhYI!rHcLVAvHjo6GBlo`(XbyA+vFOWZ+WcbL^uf)gxzr|b@93R-+ sNJtE%KVM(szYqrtS@whC0~9ZW?7=0^%f|el85lpf Date: Sun, 14 Dec 2025 11:16:53 +1100 Subject: [PATCH 05/35] Rename crescent..svg to icon.svg --- assets/{crescent..svg => icon.svg} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename assets/{crescent..svg => icon.svg} (100%) diff --git a/assets/crescent..svg b/assets/icon.svg similarity index 100% rename from assets/crescent..svg rename to assets/icon.svg From b0f7f1ec7bba6100fc70809f7046751e6f7361b1 Mon Sep 17 00:00:00 2001 From: "aary.sh" Date: Sun, 14 Dec 2025 11:17:15 +1100 Subject: [PATCH 06/35] removing the placeholder in (assets/) --- assets/.placehold | 1 - 1 file changed, 1 deletion(-) delete mode 100644 assets/.placehold diff --git a/assets/.placehold b/assets/.placehold deleted file mode 100644 index 8b13789..0000000 --- a/assets/.placehold +++ /dev/null @@ -1 +0,0 @@ - From 0bca57d6d9ff3e1f20abec278f5ace74793c8b92 Mon Sep 17 00:00:00 2001 From: "aary.sh" Date: Sun, 14 Dec 2025 11:17:52 +1100 Subject: [PATCH 07/35] Create main.css --- css/main.css | 1 + 1 file changed, 1 insertion(+) create mode 100644 css/main.css diff --git a/css/main.css b/css/main.css new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/css/main.css @@ -0,0 +1 @@ + From c9a50bf7c2f374c22f71ef3a954dc655d48e29d0 Mon Sep 17 00:00:00 2001 From: "aary.sh" Date: Sun, 14 Dec 2025 11:18:08 +1100 Subject: [PATCH 08/35] Create components.css --- css/components.css | 1 + 1 file changed, 1 insertion(+) create mode 100644 css/components.css diff --git a/css/components.css b/css/components.css new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/css/components.css @@ -0,0 +1 @@ + From b3034f95119abacff7455774927be707481c7371 Mon Sep 17 00:00:00 2001 From: "aary.sh" Date: Sun, 14 Dec 2025 11:18:16 +1100 Subject: [PATCH 09/35] Create features.css --- css/features.css | 1 + 1 file changed, 1 insertion(+) create mode 100644 css/features.css diff --git a/css/features.css b/css/features.css new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/css/features.css @@ -0,0 +1 @@ + From b41b442793c108abf621263084e9d1a243bb88ee Mon Sep 17 00:00:00 2001 From: "aary.sh" Date: Sun, 14 Dec 2025 11:28:08 +1100 Subject: [PATCH 10/35] Create app.js --- js/app.js | 1 + 1 file changed, 1 insertion(+) create mode 100644 js/app.js diff --git a/js/app.js b/js/app.js new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/js/app.js @@ -0,0 +1 @@ + From b0c7bcd8a0694154ec475a37ff47c3eddea9d04e Mon Sep 17 00:00:00 2001 From: "aary.sh" Date: Sun, 14 Dec 2025 11:28:16 +1100 Subject: [PATCH 11/35] Create search.js --- js/search.js | 1 + 1 file changed, 1 insertion(+) create mode 100644 js/search.js diff --git a/js/search.js b/js/search.js new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/js/search.js @@ -0,0 +1 @@ + From eb0664841d3a7678ca40af7a1ce18e9b9df714ba Mon Sep 17 00:00:00 2001 From: "aary.sh" Date: Sun, 14 Dec 2025 11:28:24 +1100 Subject: [PATCH 12/35] Create ui.js --- js/ui.js | 1 + 1 file changed, 1 insertion(+) create mode 100644 js/ui.js diff --git a/js/ui.js b/js/ui.js new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/js/ui.js @@ -0,0 +1 @@ + From 3039002eef8925833b9156d35dfb2613963a7b62 Mon Sep 17 00:00:00 2001 From: "aary.sh" Date: Sun, 14 Dec 2025 11:28:40 +1100 Subject: [PATCH 13/35] Create notes.js --- js/features/notes.js | 1 + 1 file changed, 1 insertion(+) create mode 100644 js/features/notes.js diff --git a/js/features/notes.js b/js/features/notes.js new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/js/features/notes.js @@ -0,0 +1 @@ + From 3c344caefffeefa495dfff8295e9ffdaf9a0c9b8 Mon Sep 17 00:00:00 2001 From: "aary.sh" Date: Sun, 14 Dec 2025 11:28:50 +1100 Subject: [PATCH 14/35] Create timer.js --- js/features/timer.js | 1 + 1 file changed, 1 insertion(+) create mode 100644 js/features/timer.js diff --git a/js/features/timer.js b/js/features/timer.js new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/js/features/timer.js @@ -0,0 +1 @@ + From 403a71e51a27cf25221674156a7a60a554b7be51 Mon Sep 17 00:00:00 2001 From: "aary.sh" Date: Sun, 14 Dec 2025 11:28:57 +1100 Subject: [PATCH 15/35] Create history.js --- js/features/history.js | 1 + 1 file changed, 1 insertion(+) create mode 100644 js/features/history.js diff --git a/js/features/history.js b/js/features/history.js new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/js/features/history.js @@ -0,0 +1 @@ + From a656572b15a8f69d0e6675bf747cd9ba3fa02aa6 Mon Sep 17 00:00:00 2001 From: "aary.sh" Date: Sun, 14 Dec 2025 11:30:25 +1100 Subject: [PATCH 16/35] Delete script.js --- script.js | 181 ------------------------------------------------------ 1 file changed, 181 deletions(-) delete mode 100644 script.js diff --git a/script.js b/script.js deleted file mode 100644 index 3f669d2..0000000 --- a/script.js +++ /dev/null @@ -1,181 +0,0 @@ -function updateClock() { - const now = new Date(); - const hours = now.getHours().toString().padStart(2, '0'); - const minutes = now.getMinutes().toString().padStart(2, '0'); - document.getElementById('clock').textContent = `${hours}:${minutes}`; -} - -updateClock(); -setInterval(updateClock, 1000); - -function toggleSpotify() { - const panel = document.getElementById('spotifyPanel'); - panel.classList.toggle('open'); -} - -let selectedAPI = 'all'; - -document.querySelectorAll('.api-chip').forEach(chip => { - chip.addEventListener('click', () => { - document.querySelectorAll('.api-chip').forEach(c => c.classList.remove('active')); - chip.classList.add('active'); - selectedAPI = chip.dataset.api; - }); -}); - -document.getElementById('searchInput').addEventListener('keypress', (e) => { - if (e.key === 'Enter') { - performSearch(); - } -}); - -const apiConfigs = { - wikipedia: { - name: 'Wikipedia', - class: 'source-wikipedia', - search: async (query) => { - const response = await fetch(`https://en.wikipedia.org/api/rest_v1/page/summary/${encodeURIComponent(query)}`); - if (!response.ok) return []; - const data = await response.json(); - return [{ - title: data.title, - excerpt: data.extract, - source: 'Wikipedia', - sourceClass: 'source-wikipedia', - url: data.content_urls?.desktop?.page, - meta: { type: 'Article' } - }]; - } - }, - openlib: { - name: 'Open Library', - class: 'source-openlib', - search: async (query) => { - const response = await fetch(`https://openlibrary.org/search.json?q=${encodeURIComponent(query)}&limit=5`); - const data = await response.json(); - return (data.docs || []).map(book => ({ - title: book.title, - excerpt: `By ${book.author_name?.join(', ') || 'Unknown'} · First published ${book.first_publish_year || 'N/A'}`, - source: 'Open Library', - sourceClass: 'source-openlib', - url: `https://openlibrary.org${book.key}`, - meta: { type: 'Book' } - })); - } - }, - dictionary: { - name: 'Dictionary', - class: 'source-dictionary', - search: async (query) => { - const response = await fetch(`https://api.dictionaryapi.dev/api/v2/entries/en/${encodeURIComponent(query)}`); - if (!response.ok) return []; - const data = await response.json(); - return data.map(entry => ({ - title: entry.word, - excerpt: entry.meanings?.map(m => `(${m.partOfSpeech}) ${m.definitions?.[0]?.definition}`).join(' · ') || '', - source: 'Dictionary', - sourceClass: 'source-dictionary', - url: entry.sourceUrls?.[0], - meta: { type: 'Definition', phonetic: entry.phonetic } - })); - } - }, - nasa: { - name: 'NASA', - class: 'source-nasa', - search: async (query) => { - const response = await fetch(`https://images-api.nasa.gov/search?q=${encodeURIComponent(query)}&media_type=image`); - if (!response.ok) return []; - const data = await response.json(); - return (data.collection?.items || []).slice(0, 5).map(item => ({ - title: item.data?.[0]?.title || 'Untitled', - excerpt: item.data?.[0]?.description?.substring(0, 200) || '', - source: 'NASA', - sourceClass: 'source-nasa', - url: item.links?.[0]?.href, - meta: { type: 'Image', date: item.data?.[0]?.date_created?.split('T')[0] } - })); - } - } -}; - -async function performSearch() { - const query = document.getElementById('searchInput').value.trim(); - if (!query) return; - - const resultsSection = document.getElementById('resultsSection'); - const emptyState = document.getElementById('emptyState'); - const loading = document.getElementById('loading'); - const resultsGrid = document.getElementById('resultsGrid'); - const resultsCount = document.getElementById('resultsCount'); - - resultsSection.style.display = 'block'; - emptyState.style.display = 'none'; - loading.classList.add('active'); - resultsGrid.innerHTML = ''; - - try { - let results = []; - - if (selectedAPI === 'all') { - const promises = Object.values(apiConfigs).map(api => - api.search(query).catch(() => []) - ); - const allResults = await Promise.all(promises); - results = allResults.flat(); - } else if (apiConfigs[selectedAPI]) { - results = await apiConfigs[selectedAPI].search(query); - } - - loading.classList.remove('active'); - resultsCount.textContent = `${results.length} result${results.length !== 1 ? 's' : ''}`; - - if (results.length === 0) { - resultsGrid.innerHTML = ` -
-

No results found. Try a different search term or source.

-
- `; - return; - } - - results.forEach((result, index) => { - const card = document.createElement('div'); - card.className = 'result-card'; - card.style.animationDelay = `${index * 0.1}s`; - card.innerHTML = ` -
- - ${result.source} -
-

${result.title}

-

${result.excerpt}

-
- ${result.meta?.type || 'Result'} - ${result.meta?.phonetic ? `${result.meta.phonetic}` : ''} - ${result.meta?.date ? `${result.meta.date}` : ''} -
- `; - card.onclick = () => { - if (result.url) window.open(result.url, '_blank'); - }; - resultsGrid.appendChild(card); - }); - - } catch (error) { - loading.classList.remove('active'); - resultsGrid.innerHTML = ` -
-

Something went wrong. Please try again.

-
- `; - } -} - -document.addEventListener('DOMContentLoaded', () => { - document.body.style.opacity = '0'; - document.body.style.transition = 'opacity 0.5s ease'; - setTimeout(() => { - document.body.style.opacity = '1'; - }, 100); -}); From e7bff84612e26bbc6b9ff73621dc4d5beffc9525 Mon Sep 17 00:00:00 2001 From: "aary.sh" Date: Sun, 14 Dec 2025 11:30:35 +1100 Subject: [PATCH 17/35] Delete styles.css --- styles.css | 625 ----------------------------------------------------- 1 file changed, 625 deletions(-) delete mode 100644 styles.css diff --git a/styles.css b/styles.css deleted file mode 100644 index 62f5ec5..0000000 --- a/styles.css +++ /dev/null @@ -1,625 +0,0 @@ -:root { - --bg-primary: #f7f5f2; - --bg-secondary: #fffef9; - --bg-card: rgba(255, 255, 255, 0.7); - --text-primary: #2c2c2c; - --text-secondary: #6b6b6b; - --text-muted: #9a9a9a; - --accent: #c4a87c; - --accent-soft: #e8dcc8; - --border: rgba(0, 0, 0, 0.06); - --shadow: 0 4px 24px rgba(0, 0, 0, 0.04); - --shadow-hover: 0 8px 32px rgba(0, 0, 0, 0.08); - --spotify-green: #1db954; -} - -* { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -html { - scroll-behavior: smooth; -} - -body { - font-family: 'DM Sans', sans-serif; - background: var(--bg-primary); - color: var(--text-primary); - min-height: 100vh; - overflow-x: hidden; - position: relative; -} - -body::before { - content: ''; - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - pointer-events: none; - opacity: 0.03; - background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseFilter'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseFilter)'/%3E%3C/svg%3E"); - z-index: 9999; -} - -.orb { - position: fixed; - border-radius: 50%; - filter: blur(80px); - opacity: 0.4; - pointer-events: none; - animation: float 20s ease-in-out infinite; -} - -.orb-1 { - width: 400px; - height: 400px; - background: linear-gradient(135deg, #e8dcc8 0%, #f0e6d8 100%); - top: -100px; - right: -100px; - animation-delay: 0s; -} - -.orb-2 { - width: 300px; - height: 300px; - background: linear-gradient(135deg, #d4e5f7 0%, #e8f0f8 100%); - bottom: -50px; - left: -50px; - animation-delay: -7s; -} - -.orb-3 { - width: 250px; - height: 250px; - background: linear-gradient(135deg, #e8d4d4 0%, #f5e8e8 100%); - top: 50%; - left: 30%; - animation-delay: -14s; -} - -@keyframes float { - 0%, 100% { transform: translate(0, 0) scale(1); } - 25% { transform: translate(20px, -20px) scale(1.05); } - 50% { transform: translate(-10px, 10px) scale(0.95); } - 75% { transform: translate(-20px, -10px) scale(1.02); } -} - -nav { - position: fixed; - top: 0; - left: 0; - right: 0; - z-index: 100; - padding: 1.5rem 3rem; - display: flex; - justify-content: space-between; - align-items: center; - background: rgba(247, 245, 242, 0.8); - backdrop-filter: blur(20px); - border-bottom: 1px solid var(--border); -} - -.logo { - display: flex; - align-items: center; - gap: 0.75rem; - text-decoration: none; - color: var(--text-primary); -} - -.logo-icon { - width: 32px; - height: 32px; - position: relative; -} - -.crescent-moon { - fill: none; - stroke: var(--accent); - stroke-width: 2; - animation: glow 4s ease-in-out infinite; -} - -@keyframes glow { - 0%, 100% { opacity: 0.8; } - 50% { opacity: 1; } -} - -.logo-text { - font-family: 'Cormorant Garamond', serif; - font-size: 1.5rem; - font-weight: 400; - letter-spacing: 0.1em; -} - -.nav-center { - display: flex; - align-items: center; - gap: 2rem; -} - -.clock { - font-family: 'Cormorant Garamond', serif; - font-size: 1.1rem; - color: var(--text-secondary); - letter-spacing: 0.05em; - min-width: 100px; - text-align: center; -} - -.nav-links { - display: flex; - gap: 2rem; - list-style: none; -} - -.nav-links a { - text-decoration: none; - color: var(--text-secondary); - font-size: 0.85rem; - font-weight: 400; - letter-spacing: 0.05em; - transition: color 0.3s ease; - position: relative; -} - -.nav-links a::after { - content: ''; - position: absolute; - bottom: -4px; - left: 0; - width: 0; - height: 1px; - background: var(--accent); - transition: width 0.3s ease; -} - -.nav-links a:hover { - color: var(--text-primary); -} - -.nav-links a:hover::after { - width: 100%; -} - -.spotify-tab { - position: fixed; - right: 0; - top: 50%; - transform: translateY(-50%); - z-index: 90; -} - -.spotify-toggle { - background: var(--bg-card); - border: 1px solid var(--border); - border-right: none; - border-radius: 12px 0 0 12px; - padding: 1rem 0.75rem; - cursor: pointer; - display: flex; - flex-direction: column; - align-items: center; - gap: 0.5rem; - transition: all 0.3s ease; - box-shadow: var(--shadow); -} - -.spotify-toggle:hover { - background: var(--bg-secondary); - padding-left: 1rem; -} - -.spotify-icon { - width: 24px; - height: 24px; - fill: var(--spotify-green); -} - -.spotify-toggle span { - writing-mode: vertical-rl; - text-orientation: mixed; - font-size: 0.7rem; - color: var(--text-muted); - letter-spacing: 0.1em; -} - -.spotify-panel { - position: fixed; - right: -320px; - top: 0; - width: 320px; - height: 100vh; - background: var(--bg-card); - backdrop-filter: blur(20px); - border-left: 1px solid var(--border); - padding: 6rem 1.5rem 2rem; - transition: right 0.4s cubic-bezier(0.4, 0, 0.2, 1); - z-index: 85; - overflow-y: auto; -} - -.spotify-panel.open { - right: 0; -} - -.spotify-panel h3 { - font-family: 'Cormorant Garamond', serif; - font-size: 1.25rem; - font-weight: 400; - margin-bottom: 1.5rem; - color: var(--text-primary); -} - -.spotify-placeholder { - background: linear-gradient(135deg, #1a1a1a, #2a2a2a); - border-radius: 12px; - padding: 1.5rem; - text-align: center; -} - -.spotify-placeholder p { - color: #b3b3b3; - font-size: 0.85rem; - margin-bottom: 1rem; -} - -.spotify-connect { - background: var(--spotify-green); - color: white; - border: none; - padding: 0.75rem 1.5rem; - border-radius: 20px; - font-size: 0.85rem; - font-weight: 500; - cursor: pointer; - transition: transform 0.2s ease, opacity 0.2s ease; -} - -.spotify-connect:hover { - transform: scale(1.05); - opacity: 0.9; -} - -.close-spotify { - position: absolute; - top: 1.5rem; - right: 1.5rem; - background: none; - border: none; - font-size: 1.5rem; - color: var(--text-muted); - cursor: pointer; - transition: color 0.2s ease; -} - -.close-spotify:hover { - color: var(--text-primary); -} - -main { - padding-top: 120px; - min-height: 100vh; - display: flex; - flex-direction: column; - align-items: center; -} - -.hero { - text-align: center; - padding: 4rem 2rem; - animation: fadeIn 1s ease-out; -} - -@keyframes fadeIn { - from { opacity: 0; transform: translateY(20px); } - to { opacity: 1; transform: translateY(0); } -} - -.hero h1 { - font-family: 'Cormorant Garamond', serif; - font-size: 3.5rem; - font-weight: 300; - letter-spacing: 0.15em; - margin-bottom: 0.5rem; - color: var(--text-primary); -} - -.hero p { - font-size: 1rem; - color: var(--text-secondary); - font-weight: 300; - letter-spacing: 0.05em; -} - -.search-container { - width: 100%; - max-width: 700px; - padding: 0 2rem; - margin-top: 2rem; - animation: fadeIn 1s ease-out 0.2s backwards; -} - -.search-box { - position: relative; - width: 100%; -} - -.search-input { - width: 100%; - padding: 1.25rem 1.5rem; - padding-right: 3.5rem; - font-family: 'DM Sans', sans-serif; - font-size: 1rem; - border: 1px solid var(--border); - border-radius: 16px; - background: var(--bg-card); - backdrop-filter: blur(10px); - color: var(--text-primary); - transition: all 0.3s ease; - box-shadow: var(--shadow); -} - -.search-input::placeholder { - color: var(--text-muted); -} - -.search-input:focus { - outline: none; - border-color: var(--accent-soft); - box-shadow: var(--shadow-hover); -} - -.search-btn { - position: absolute; - right: 8px; - top: 50%; - transform: translateY(-50%); - background: none; - border: none; - padding: 0.75rem; - cursor: pointer; - color: var(--text-muted); - transition: color 0.2s ease; -} - -.search-btn:hover { - color: var(--accent); -} - -.search-btn svg { - width: 20px; - height: 20px; -} - -.api-categories { - display: flex; - flex-wrap: wrap; - justify-content: center; - gap: 0.5rem; - margin-top: 1.5rem; - animation: fadeIn 1s ease-out 0.4s backwards; -} - -.api-chip { - padding: 0.5rem 1rem; - font-size: 0.75rem; - border: 1px solid var(--border); - border-radius: 20px; - background: var(--bg-card); - color: var(--text-secondary); - cursor: pointer; - transition: all 0.2s ease; - letter-spacing: 0.02em; -} - -.api-chip:hover, .api-chip.active { - background: var(--accent-soft); - border-color: var(--accent); - color: var(--text-primary); -} - -.api-chip.active { - background: var(--accent); - color: white; -} - -.results-section { - width: 100%; - max-width: 900px; - padding: 2rem; - margin-top: 2rem; - display: none; -} - -.results-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 1.5rem; - padding-bottom: 1rem; - border-bottom: 1px solid var(--border); -} - -.results-header h2 { - font-family: 'Cormorant Garamond', serif; - font-size: 1.25rem; - font-weight: 400; - color: var(--text-secondary); -} - -.results-count { - font-size: 0.8rem; - color: var(--text-muted); -} - -.results-grid { - display: grid; - gap: 1rem; -} - -.result-card { - background: var(--bg-card); - border: 1px solid var(--border); - border-radius: 12px; - padding: 1.5rem; - transition: all 0.3s ease; - cursor: pointer; - animation: slideUp 0.5s ease-out backwards; -} - -.result-card:hover { - transform: translateY(-2px); - box-shadow: var(--shadow-hover); - border-color: var(--accent-soft); -} - -@keyframes slideUp { - from { opacity: 0; transform: translateY(20px); } - to { opacity: 1; transform: translateY(0); } -} - -.result-source { - display: inline-flex; - align-items: center; - gap: 0.5rem; - font-size: 0.7rem; - color: var(--text-muted); - text-transform: uppercase; - letter-spacing: 0.1em; - margin-bottom: 0.75rem; -} - -.source-dot { - width: 6px; - height: 6px; - border-radius: 50%; -} - -.source-wikipedia { background: #636363; } -.source-openlib { background: #d4a373; } -.source-wolfram { background: #dd1100; } -.source-nasa { background: #0b3d91; } -.source-weather { background: #4fc3f7; } -.source-news { background: #e57373; } -.source-dictionary { background: #81c784; } - -.result-title { - font-family: 'Cormorant Garamond', serif; - font-size: 1.25rem; - font-weight: 400; - color: var(--text-primary); - margin-bottom: 0.5rem; - line-height: 1.4; -} - -.result-excerpt { - font-size: 0.9rem; - color: var(--text-secondary); - line-height: 1.6; - display: -webkit-box; - -webkit-line-clamp: 3; - -webkit-box-orient: vertical; - overflow: hidden; -} - -.result-meta { - display: flex; - gap: 1rem; - margin-top: 1rem; - font-size: 0.75rem; - color: var(--text-muted); -} - -.loading { - display: none; - justify-content: center; - align-items: center; - padding: 3rem; -} - -.loading.active { - display: flex; -} - -.loader { - width: 40px; - height: 40px; - border: 2px solid var(--border); - border-top-color: var(--accent); - border-radius: 50%; - animation: spin 1s linear infinite; -} - -@keyframes spin { - to { transform: rotate(360deg); } -} - -.empty-state { - text-align: center; - padding: 4rem 2rem; - color: var(--text-muted); -} - -.empty-state svg { - width: 80px; - height: 80px; - margin-bottom: 1.5rem; - opacity: 0.3; -} - -.empty-state p { - font-size: 0.9rem; -} - -footer { - text-align: center; - padding: 3rem 2rem; - margin-top: auto; - color: var(--text-muted); - font-size: 0.75rem; -} - -footer p + p { - margin-top: 0.5rem; -} - -footer a { - color: var(--text-secondary); - text-decoration: none; - transition: color 0.2s ease; -} - -footer a:hover { - color: var(--accent); -} - -@media (max-width: 768px) { - nav { - padding: 1rem 1.5rem; - } - - .nav-links { - display: none; - } - - .hero h1 { - font-size: 2.5rem; - } - - .search-container { - padding: 0 1rem; - } - - .api-categories { - padding: 0 1rem; - } - - .results-section { - padding: 1rem; - } -} From dfb86162fb40a1f622525a57854aa2019e609263 Mon Sep 17 00:00:00 2001 From: "aary.sh" Date: Sun, 14 Dec 2025 11:32:45 +1100 Subject: [PATCH 18/35] Update index.html --- index.html | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/index.html b/index.html index f32891d..9c67a24 100644 --- a/index.html +++ b/index.html @@ -3,11 +3,17 @@ - Crescent - Calm Search + Crescent - Calm Search for Students + - + + + + + +
@@ -36,7 +42,7 @@
-
- +

Now Playing

@@ -71,7 +77,7 @@

Crescent

placeholder="What would you like to explore?" autocomplete="off" > - - - - - +
@@ -120,6 +123,6 @@

Results

- + From 1f08ac72671e139ab60fd3400e3b8fc8412e6659 Mon Sep 17 00:00:00 2001 From: "aary.sh" Date: Sun, 14 Dec 2025 11:34:30 +1100 Subject: [PATCH 19/35] Define CSS variables and base styles Added CSS variables for theming and layout styles. --- css/main.css | 401 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 401 insertions(+) diff --git a/css/main.css b/css/main.css index 8b13789..fd45589 100644 --- a/css/main.css +++ b/css/main.css @@ -1 +1,402 @@ +:root { + --bg-primary: #f7f5f2; + --bg-secondary: #fffef9; + --bg-card: rgba(255, 255, 255, 0.7); + --text-primary: #2c2c2c; + --text-secondary: #6b6b6b; + --text-muted: #9a9a9a; + --accent: #c4a87c; + --accent-soft: #e8dcc8; + --border: rgba(0, 0, 0, 0.06); + --shadow: 0 4px 24px rgba(0, 0, 0, 0.04); + --shadow-hover: 0 8px 32px rgba(0, 0, 0, 0.08); + --spotify-green: #1db954; +} +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +html { + scroll-behavior: smooth; +} + +body { + font-family: 'DM Sans', sans-serif; + background: var(--bg-primary); + color: var(--text-primary); + min-height: 100vh; + overflow-x: hidden; + position: relative; +} + +body::before { + content: ''; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + pointer-events: none; + opacity: 0.03; + background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseFilter'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseFilter)'/%3E%3C/svg%3E"); + z-index: 9999; +} + +.orb { + position: fixed; + border-radius: 50%; + filter: blur(80px); + opacity: 0.4; + pointer-events: none; + animation: float 20s ease-in-out infinite; +} + +.orb-1 { + width: 400px; + height: 400px; + background: linear-gradient(135deg, #e8dcc8 0%, #f0e6d8 100%); + top: -100px; + right: -100px; + animation-delay: 0s; +} + +.orb-2 { + width: 300px; + height: 300px; + background: linear-gradient(135deg, #d4e5f7 0%, #e8f0f8 100%); + bottom: -50px; + left: -50px; + animation-delay: -7s; +} + +.orb-3 { + width: 250px; + height: 250px; + background: linear-gradient(135deg, #e8d4d4 0%, #f5e8e8 100%); + top: 50%; + left: 30%; + animation-delay: -14s; +} + +@keyframes float { + 0%, 100% { transform: translate(0, 0) scale(1); } + 25% { transform: translate(20px, -20px) scale(1.05); } + 50% { transform: translate(-10px, 10px) scale(0.95); } + 75% { transform: translate(-20px, -10px) scale(1.02); } +} + +nav { + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: 100; + padding: 1.5rem 3rem; + display: flex; + justify-content: space-between; + align-items: center; + background: rgba(247, 245, 242, 0.8); + backdrop-filter: blur(20px); + border-bottom: 1px solid var(--border); +} + +.logo { + display: flex; + align-items: center; + gap: 0.75rem; + text-decoration: none; + color: var(--text-primary); +} + +.logo-icon { + width: 32px; + height: 32px; + position: relative; +} + +.crescent-moon { + fill: none; + stroke: var(--accent); + stroke-width: 2; + animation: glow 4s ease-in-out infinite; +} + +@keyframes glow { + 0%, 100% { opacity: 0.8; } + 50% { opacity: 1; } +} + +.logo-text { + font-family: 'Cormorant Garamond', serif; + font-size: 1.5rem; + font-weight: 400; + letter-spacing: 0.1em; +} + +.nav-center { + display: flex; + align-items: center; + gap: 2rem; +} + +.clock { + font-family: 'Cormorant Garamond', serif; + font-size: 1.1rem; + color: var(--text-secondary); + letter-spacing: 0.05em; + min-width: 100px; + text-align: center; +} + +.nav-links { + display: flex; + gap: 2rem; + list-style: none; +} + +.nav-links a { + text-decoration: none; + color: var(--text-secondary); + font-size: 0.85rem; + font-weight: 400; + letter-spacing: 0.05em; + transition: color 0.3s ease; + position: relative; +} + +.nav-links a::after { + content: ''; + position: absolute; + bottom: -4px; + left: 0; + width: 0; + height: 1px; + background: var(--accent); + transition: width 0.3s ease; +} + +.nav-links a:hover { + color: var(--text-primary); +} + +.nav-links a:hover::after { + width: 100%; +} + +main { + padding-top: 120px; + min-height: 100vh; + display: flex; + flex-direction: column; + align-items: center; +} + +.hero { + text-align: center; + padding: 4rem 2rem; + animation: fadeIn 1s ease-out; +} + +@keyframes fadeIn { + from { opacity: 0; transform: translateY(20px); } + to { opacity: 1; transform: translateY(0); } +} + +.hero h1 { + font-family: 'Cormorant Garamond', serif; + font-size: 3.5rem; + font-weight: 300; + letter-spacing: 0.15em; + margin-bottom: 0.5rem; + color: var(--text-primary); +} + +.hero p { + font-size: 1rem; + color: var(--text-secondary); + font-weight: 300; + letter-spacing: 0.05em; +} + +.empty-state { + text-align: center; + padding: 4rem 2rem; + color: var(--text-muted); +} + +.empty-state svg { + width: 80px; + height: 80px; + margin-bottom: 1.5rem; + opacity: 0.3; +} + +.empty-state p { + font-size: 0.9rem; +} + +footer { + text-align: center; + padding: 3rem 2rem; + margin-top: auto; + color: var(--text-muted); + font-size: 0.75rem; +} + +footer p + p { + margin-top: 0.5rem; +} + +footer a { + color: var(--text-secondary); + text-decoration: none; + transition: color 0.2s ease; +} + +footer a:hover { + color: var(--accent); +} + +.spotify-tab { + position: fixed; + right: 0; + top: 50%; + transform: translateY(-50%); + z-index: 90; +} + +.spotify-toggle { + background: var(--bg-card); + border: 1px solid var(--border); + border-right: none; + border-radius: 12px 0 0 12px; + padding: 1rem 0.75rem; + cursor: pointer; + display: flex; + flex-direction: column; + align-items: center; + gap: 0.5rem; + transition: all 0.3s ease; + box-shadow: var(--shadow); +} + +.spotify-toggle:hover { + background: var(--bg-secondary); + padding-left: 1rem; +} + +.spotify-icon { + width: 24px; + height: 24px; + fill: var(--spotify-green); +} + +.spotify-toggle span { + writing-mode: vertical-rl; + text-orientation: mixed; + font-size: 0.7rem; + color: var(--text-muted); + letter-spacing: 0.1em; +} + +.spotify-panel { + position: fixed; + right: -320px; + top: 0; + width: 320px; + height: 100vh; + background: var(--bg-card); + backdrop-filter: blur(20px); + border-left: 1px solid var(--border); + padding: 6rem 1.5rem 2rem; + transition: right 0.4s cubic-bezier(0.4, 0, 0.2, 1); + z-index: 85; + overflow-y: auto; +} + +.spotify-panel.open { + right: 0; +} + +.spotify-panel h3 { + font-family: 'Cormorant Garamond', serif; + font-size: 1.25rem; + font-weight: 400; + margin-bottom: 1.5rem; + color: var(--text-primary); +} + +.spotify-placeholder { + background: linear-gradient(135deg, #1a1a1a, #2a2a2a); + border-radius: 12px; + padding: 1.5rem; + text-align: center; +} + +.spotify-placeholder p { + color: #b3b3b3; + font-size: 0.85rem; + margin-bottom: 1rem; +} + +.spotify-connect { + background: var(--spotify-green); + color: white; + border: none; + padding: 0.75rem 1.5rem; + border-radius: 20px; + font-size: 0.85rem; + font-weight: 500; + cursor: pointer; + transition: transform 0.2s ease, opacity 0.2s ease; +} + +.spotify-connect:hover { + transform: scale(1.05); + opacity: 0.9; +} + +.close-spotify { + position: absolute; + top: 1.5rem; + right: 1.5rem; + background: none; + border: none; + font-size: 1.5rem; + color: var(--text-muted); + cursor: pointer; + transition: color 0.2s ease; +} + +.close-spotify:hover { + color: var(--text-primary); +} + +@media (max-width: 768px) { + nav { + padding: 1rem 1.5rem; + } + + .nav-links { + display: none; + } + + .hero h1 { + font-size: 2.5rem; + } + + .search-container { + padding: 0 1rem; + } + + .api-categories { + padding: 0 1rem; + } + + .results-section { + padding: 1rem; + } + } From f5a09228ee79482d1d187ea1b424709889d39f7c Mon Sep 17 00:00:00 2001 From: "aary.sh" Date: Sun, 14 Dec 2025 11:35:32 +1100 Subject: [PATCH 20/35] Add styles for search and results components --- css/components.css | 219 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 219 insertions(+) diff --git a/css/components.css b/css/components.css index 8b13789..f0e5873 100644 --- a/css/components.css +++ b/css/components.css @@ -1 +1,220 @@ +.search-container { + width: 100%; + max-width: 700px; + padding: 0 2rem; + margin-top: 2rem; + animation: fadeIn 1s ease-out 0.2s backwards; +} +.search-box { + position: relative; + width: 100%; +} + +.search-input { + width: 100%; + padding: 1.25rem 1.5rem; + padding-right: 3.5rem; + font-family: 'DM Sans', sans-serif; + font-size: 1rem; + border: 1px solid var(--border); + border-radius: 16px; + background: var(--bg-card); + backdrop-filter: blur(10px); + color: var(--text-primary); + transition: all 0.3s ease; + box-shadow: var(--shadow); +} + +.search-input::placeholder { + color: var(--text-muted); +} + +.search-input:focus { + outline: none; + border-color: var(--accent-soft); + box-shadow: var(--shadow-hover); +} + +.search-btn { + position: absolute; + right: 8px; + top: 50%; + transform: translateY(-50%); + background: none; + border: none; + padding: 0.75rem; + cursor: pointer; + color: var(--text-muted); + transition: color 0.2s ease; +} + +.search-btn:hover { + color: var(--accent); +} + +.search-btn svg { + width: 20px; + height: 20px; +} + +.api-categories { + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: 0.5rem; + margin-top: 1.5rem; + animation: fadeIn 1s ease-out 0.4s backwards; +} + +.api-chip { + padding: 0.5rem 1rem; + font-size: 0.75rem; + border: 1px solid var(--border); + border-radius: 20px; + background: var(--bg-card); + color: var(--text-secondary); + cursor: pointer; + transition: all 0.2s ease; + letter-spacing: 0.02em; +} + +.api-chip:hover { + background: var(--accent-soft); + border-color: var(--accent); + color: var(--text-primary); +} + +.api-chip.active { + background: var(--accent); + border-color: var(--accent); + color: white; +} + +.results-section { + width: 100%; + max-width: 900px; + padding: 2rem; + margin-top: 2rem; + display: none; +} + +.results-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 1.5rem; + padding-bottom: 1rem; + border-bottom: 1px solid var(--border); +} + +.results-header h2 { + font-family: 'Cormorant Garamond', serif; + font-size: 1.25rem; + font-weight: 400; + color: var(--text-secondary); +} + +.results-count { + font-size: 0.8rem; + color: var(--text-muted); +} + +.results-grid { + display: grid; + gap: 1rem; +} + +.result-card { + background: var(--bg-card); + border: 1px solid var(--border); + border-radius: 12px; + padding: 1.5rem; + transition: all 0.3s ease; + cursor: pointer; + animation: slideUp 0.5s ease-out backwards; +} + +.result-card:hover { + transform: translateY(-2px); + box-shadow: var(--shadow-hover); + border-color: var(--accent-soft); +} + +@keyframes slideUp { + from { opacity: 0; transform: translateY(20px); } + to { opacity: 1; transform: translateY(0); } +} + +.result-source { + display: inline-flex; + align-items: center; + gap: 0.5rem; + font-size: 0.7rem; + color: var(--text-muted); + text-transform: uppercase; + letter-spacing: 0.1em; + margin-bottom: 0.75rem; +} + +.source-dot { + width: 6px; + height: 6px; + border-radius: 50%; +} + +.source-wikipedia { background: #636363; } +.source-openlib { background: #d4a373; } +.source-dictionary { background: #81c784; } +.source-nasa { background: #0b3d91; } + +.result-title { + font-family: 'Cormorant Garamond', serif; + font-size: 1.25rem; + font-weight: 400; + color: var(--text-primary); + margin-bottom: 0.5rem; + line-height: 1.4; +} + +.result-excerpt { + font-size: 0.9rem; + color: var(--text-secondary); + line-height: 1.6; + display: -webkit-box; + -webkit-line-clamp: 3; + -webkit-box-orient: vertical; + overflow: hidden; +} + +.result-meta { + display: flex; + gap: 1rem; + margin-top: 1rem; + font-size: 0.75rem; + color: var(--text-muted); +} + +.loading { + display: none; + justify-content: center; + align-items: center; + padding: 3rem; +} + +.loading.active { + display: flex; +} + +.loader { + width: 40px; + height: 40px; + border: 2px solid var(--border); + border-top-color: var(--accent); + border-radius: 50%; + animation: spin 1s linear infinite; +} + +@keyframes spin { + to { transform: rotate(360deg); } +} From a1c3c6c81368e52ede9a7f63f1be59d59fd026a6 Mon Sep 17 00:00:00 2001 From: "aary.sh" Date: Sun, 14 Dec 2025 11:37:04 +1100 Subject: [PATCH 21/35] Add 'coming soon -luvaary' message to features.css --- css/features.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/css/features.css b/css/features.css index 8b13789..f053e69 100644 --- a/css/features.css +++ b/css/features.css @@ -1 +1 @@ - +coming soon -luvaary From 52975da4e5a09081a923e3eaed2fa466c165445e Mon Sep 17 00:00:00 2001 From: "aary.sh" Date: Sun, 14 Dec 2025 11:37:25 +1100 Subject: [PATCH 22/35] Initialize app features on DOMContentLoaded --- js/app.js | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/js/app.js b/js/app.js index 8b13789..0631e02 100644 --- a/js/app.js +++ b/js/app.js @@ -1 +1,49 @@ +import { initSearch, performSearch } from './search.js'; +import { updateClock, toggleSpotify, fadeInPage } from './ui.js'; +document.addEventListener('DOMContentLoaded', () => { + console.log('🌙 Crescent initialized'); + + updateClock(); + setInterval(updateClock, 1000); + + initSearch(); + + setupEventListeners(); + + fadeInPage(); + + loadFeatures(); +}); + +function setupEventListeners() { + const searchBtn = document.getElementById('searchBtn'); + if (searchBtn) { + searchBtn.addEventListener('click', performSearch); + } + + const spotifyToggle = document.getElementById('spotifyToggle'); + const closeSpotify = document.getElementById('closeSpotify'); + + if (spotifyToggle) { + spotifyToggle.addEventListener('click', toggleSpotify); + } + + if (closeSpotify) { + closeSpotify.addEventListener('click', toggleSpotify); + } +} + +function loadFeatures() { + if (typeof window.loadNotes === 'function') { + window.loadNotes(); + } + + if (typeof window.loadSearchHistory === 'function') { + window.loadSearchHistory(); + } + + if (typeof window.initTimer === 'function') { + window.initTimer(); + } +} From 8bf8fcfd1857d7f5a129658feb0b4ff84e193924 Mon Sep 17 00:00:00 2001 From: "aary.sh" Date: Sun, 14 Dec 2025 11:37:42 +1100 Subject: [PATCH 23/35] Add clock update, Spotify toggle, and fade-in functions --- js/ui.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/js/ui.js b/js/ui.js index 8b13789..349ee89 100644 --- a/js/ui.js +++ b/js/ui.js @@ -1 +1,19 @@ +export function updateClock() { + const now = new Date(); + const hours = now.getHours().toString().padStart(2, '0'); + const minutes = now.getMinutes().toString().padStart(2, '0'); + document.getElementById('clock').textContent = `${hours}:${minutes}`; +} +export function toggleSpotify() { + const panel = document.getElementById('spotifyPanel'); + panel.classList.toggle('open'); +} + +export function fadeInPage() { + document.body.style.opacity = '0'; + document.body.style.transition = 'opacity 0.5s ease'; + setTimeout(() => { + document.body.style.opacity = '1'; + }, 100); +} From 16c888abdd81f1880859cdc564f7891d7e793af6 Mon Sep 17 00:00:00 2001 From: "aary.sh" Date: Sun, 14 Dec 2025 11:40:17 +1100 Subject: [PATCH 24/35] Update search.js --- js/search.js | 194 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 194 insertions(+) diff --git a/js/search.js b/js/search.js index 8b13789..5b17384 100644 --- a/js/search.js +++ b/js/search.js @@ -1 +1,195 @@ +export const apiConfigs = { + wikipedia: { + name: 'Wikipedia', + class: 'source-wikipedia', + search: async (query) => { + try { + const response = await fetch( + `https://en.wikipedia.org/api/rest_v1/page/summary/${encodeURIComponent(query)}` + ); + if (!response.ok) return []; + + const data = await response.json(); + return [{ + title: data.title, + excerpt: data.extract, + source: 'Wikipedia', + sourceClass: 'source-wikipedia', + url: data.content_urls?.desktop?.page, + meta: { type: 'Article' } + }]; + } catch (error) { + console.error('Wikipedia API error:', error); + return []; + } + } + }, + + openlib: { + name: 'Open Library', + class: 'source-openlib', + search: async (query) => { + try { + const response = await fetch( + `https://openlibrary.org/search.json?q=${encodeURIComponent(query)}&limit=5` + ); + const data = await response.json(); + + return (data.docs || []).map(book => ({ + title: book.title, + excerpt: `By ${book.author_name?.join(', ') || 'Unknown'} · First published ${book.first_publish_year || 'N/A'}`, + source: 'Open Library', + sourceClass: 'source-openlib', + url: `https://openlibrary.org${book.key}`, + meta: { type: 'Book' } + })); + } catch (error) { + console.error('Open Library API error:', error); + return []; + } + } + }, + + dictionary: { + name: 'Dictionary', + class: 'source-dictionary', + search: async (query) => { + try { + const response = await fetch( + `https://api.dictionaryapi.dev/api/v2/entries/en/${encodeURIComponent(query)}` + ); + if (!response.ok) return []; + + const data = await response.json(); + return data.map(entry => ({ + title: entry.word, + excerpt: entry.meanings?.map(m => + `(${m.partOfSpeech}) ${m.definitions?.[0]?.definition}` + ).join(' · ') || '', + source: 'Dictionary', + sourceClass: 'source-dictionary', + url: entry.sourceUrls?.[0], + meta: { type: 'Definition', phonetic: entry.phonetic } + })); + } catch (error) { + console.error('Dictionary API error:', error); + return []; + } + } + }, + + nasa: { + name: 'NASA', + class: 'source-nasa', + search: async (query) => { + try { + const response = await fetch( + `https://images-api.nasa.gov/search?q=${encodeURIComponent(query)}&media_type=image` + ); + if (!response.ok) return []; + + const data = await response.json(); + return (data.collection?.items || []).slice(0, 5).map(item => ({ + title: item.data?.[0]?.title || 'Untitled', + excerpt: item.data?.[0]?.description?.substring(0, 200) || '', + source: 'NASA', + sourceClass: 'source-nasa', + url: item.links?.[0]?.href, + meta: { type: 'Image', date: item.data?.[0]?.date_created?.split('T')[0] } + })); + } catch (error) { + console.error('NASA API error:', error); + return []; + } + } + } +}; +let selectedAPI = 'all'; + +export function initSearch() { + document.querySelectorAll('.api-chip').forEach(chip => { + chip.addEventListener('click', () => { + document.querySelectorAll('.api-chip').forEach(c => c.classList.remove('active')); + chip.classList.add('active'); + selectedAPI = chip.dataset.api; + }); + }); + + document.getElementById('searchInput').addEventListener('keypress', (e) => { + if (e.key === 'Enter') performSearch(); + }); +} + +export async function performSearch() { + const query = document.getElementById('searchInput').value.trim(); + if (!query) return; + + const resultsSection = document.getElementById('resultsSection'); + const emptyState = document.getElementById('emptyState'); + const loading = document.getElementById('loading'); + const resultsGrid = document.getElementById('resultsGrid'); + const resultsCount = document.getElementById('resultsCount'); + + resultsSection.style.display = 'block'; + emptyState.style.display = 'none'; + loading.classList.add('active'); + resultsGrid.innerHTML = ''; + + try { + let results = []; + + if (selectedAPI === 'all') { + const promises = Object.values(apiConfigs).map(api => + api.search(query).catch(() => []) + ); + const allResults = await Promise.all(promises); + results = allResults.flat(); + } else if (apiConfigs[selectedAPI]) { + results = await apiConfigs[selectedAPI].search(query); + } + + loading.classList.remove('active'); + resultsCount.textContent = `${results.length} result${results.length !== 1 ? 's' : ''}`; + + if (results.length === 0) { + resultsGrid.innerHTML = ` +
+

No results found. Try a different search term or source.

+
+ `; + return; + } + + results.forEach((result, index) => { + const card = document.createElement('div'); + card.className = 'result-card'; + card.style.animationDelay = `${index * 0.1}s`; + card.innerHTML = ` +
+ + ${result.source} +
+

${result.title}

+

${result.excerpt}

+
+ ${result.meta?.type || 'Result'} + ${result.meta?.phonetic ? `${result.meta.phonetic}` : ''} + ${result.meta?.date ? `${result.meta.date}` : ''} +
+ `; + card.onclick = () => { + if (result.url) window.open(result.url, '_blank'); + }; + resultsGrid.appendChild(card); + }); + + } catch (error) { + loading.classList.remove('active'); + resultsGrid.innerHTML = ` +
+

Something went wrong. Please try again.

+
+ `; + } + } From 3a59878a9283cf6a22156e60aa67ba87b5586f65 Mon Sep 17 00:00:00 2001 From: "aary.sh" Date: Sun, 14 Dec 2025 11:41:00 +1100 Subject: [PATCH 25/35] Implement note management functions with notifications --- js/features/notes.js | 66 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/js/features/notes.js b/js/features/notes.js index 8b13789..e799f3d 100644 --- a/js/features/notes.js +++ b/js/features/notes.js @@ -1 +1,67 @@ +export function initNotes() { + const notesPanel = document.getElementById('notesPanel'); + if (!notesPanel) return; + + loadNotes(); + + const saveBtn = document.getElementById('saveNotesBtn'); + if (saveBtn) { + saveBtn.addEventListener('click', saveNotes); + } + + const textarea = document.getElementById('notesTextarea'); + if (textarea) { + textarea.addEventListener('input', () => { + localStorage.setItem('crescentNotes', textarea.value); + }); + } +} +export function saveNotes() { + const textarea = document.getElementById('notesTextarea'); + if (!textarea) return; + + localStorage.setItem('crescentNotes', textarea.value); + showNotification('Notes saved!'); +} + +export function loadNotes() { + const textarea = document.getElementById('notesTextarea'); + if (!textarea) return; + + const saved = localStorage.getItem('crescentNotes'); + if (saved) { + textarea.value = saved; + } +} + +export function clearNotes() { + const textarea = document.getElementById('notesTextarea'); + if (!textarea) return; + + if (confirm('Clear all notes?')) { + textarea.value = ''; + localStorage.removeItem('crescentNotes'); + showNotification('Notes cleared'); + } +} + +function showNotification(message) { + const notification = document.createElement('div'); + notification.className = 'notification'; + notification.textContent = message; + document.body.appendChild(notification); + + setTimeout(() => { + notification.classList.add('show'); + }, 10); + + setTimeout(() => { + notification.classList.remove('show'); + setTimeout(() => notification.remove(), 300); + }, 2000); +} + +window.loadNotes = loadNotes; +window.saveNotes = saveNotes; +window.clearNotes = clearNotes; From 0e11868e0b74235cbb8ee84ad7cec2f0a07c154f Mon Sep 17 00:00:00 2001 From: "aary.sh" Date: Sun, 14 Dec 2025 11:41:21 +1100 Subject: [PATCH 26/35] Implement search history management functions --- js/features/history.js | 75 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/js/features/history.js b/js/features/history.js index 8b13789..bbb16ef 100644 --- a/js/features/history.js +++ b/js/features/history.js @@ -1 +1,76 @@ +export function initHistory() { + loadSearchHistory(); +} +export function addToSearchHistory(query, resultCount) { + const history = getHistory(); + + const entry = { + query: query, + results: resultCount, + timestamp: new Date().toISOString() + }; + + history.unshift(entry); + + if (history.length > 20) { + history.pop(); + } + + localStorage.setItem('crescentHistory', JSON.stringify(history)); + updateHistoryDisplay(); +} + +export function loadSearchHistory() { + updateHistoryDisplay(); +} + +export function clearHistory() { + if (confirm('Clear all search history?')) { + localStorage.removeItem('crescentHistory'); + updateHistoryDisplay(); + } +} + +function getHistory() { + const saved = localStorage.getItem('crescentHistory'); + return saved ? JSON.parse(saved) : []; +} + +function updateHistoryDisplay() { + const historyList = document.getElementById('historyList'); + if (!historyList) return; + + const history = getHistory(); + + if (history.length === 0) { + historyList.innerHTML = '

No search history yet

'; + return; + } + + historyList.innerHTML = history.map(entry => { + const date = new Date(entry.timestamp); + const timeStr = date.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' }); + + return ` +
+
${entry.query}
+
${entry.results} results · ${timeStr}
+
+ `; + }).join(''); +} + +window.searchFromHistory = function(query) { + const searchInput = document.getElementById('searchInput'); + if (searchInput) { + searchInput.value = query; + if (typeof window.performSearch === 'function') { + window.performSearch(); + } + } +}; + +window.loadSearchHistory = loadSearchHistory; +window.addToSearchHistory = addToSearchHistory; +window.clearHistory = clearHistory; From 294c1e482f61d8e01b0eda842a5454afb0b253df Mon Sep 17 00:00:00 2001 From: "aary.sh" Date: Sun, 14 Dec 2025 11:41:49 +1100 Subject: [PATCH 27/35] Create darkmode.js --- js/features/darkmode.js | 1 + 1 file changed, 1 insertion(+) create mode 100644 js/features/darkmode.js diff --git a/js/features/darkmode.js b/js/features/darkmode.js new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/js/features/darkmode.js @@ -0,0 +1 @@ + From 1f74ea478f05aa116ac6424e3e444ebbd3673bfe Mon Sep 17 00:00:00 2001 From: "aary.sh" Date: Sun, 14 Dec 2025 11:42:04 +1100 Subject: [PATCH 28/35] Add dark mode feature with toggle functionality Implement dark mode functionality with toggle and persistence. --- js/features/darkmode.js | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/js/features/darkmode.js b/js/features/darkmode.js index 8b13789..f8784af 100644 --- a/js/features/darkmode.js +++ b/js/features/darkmode.js @@ -1 +1,34 @@ +export function initDarkMode() { + loadDarkMode(); + + const toggle = document.getElementById('darkModeToggle'); + if (toggle) { + toggle.addEventListener('click', toggleDarkMode); + } +} +export function toggleDarkMode() { + document.body.classList.toggle('dark-mode'); + const isDark = document.body.classList.contains('dark-mode'); + localStorage.setItem('crescentDarkMode', isDark); + updateToggleIcon(); +} + +export function loadDarkMode() { + const isDark = localStorage.getItem('crescentDarkMode') === 'true'; + if (isDark) { + document.body.classList.add('dark-mode'); + } + updateToggleIcon(); +} + +function updateToggleIcon() { + const toggle = document.getElementById('darkModeToggle'); + if (!toggle) return; + + const isDark = document.body.classList.contains('dark-mode'); + toggle.textContent = isDark ? '☀️' : '🌙'; +} + +window.loadDarkMode = loadDarkMode; +window.toggleDarkMode = toggleDarkMode; From 9c4ed7e27ea185b24130723a1ee3d846fd552cbe Mon Sep 17 00:00:00 2001 From: "aary.sh" Date: Sun, 14 Dec 2025 11:42:39 +1100 Subject: [PATCH 29/35] Implement timer functionality with start, stop, reset --- js/features/timer.js | 107 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) diff --git a/js/features/timer.js b/js/features/timer.js index 8b13789..a7bb2c4 100644 --- a/js/features/timer.js +++ b/js/features/timer.js @@ -1 +1,108 @@ +let timerInterval = null; +let timeLeft = 25 * 60; +let isRunning = false; +export function initTimer() { + updateTimerDisplay(); + + const startBtn = document.getElementById('startTimer'); + const stopBtn = document.getElementById('stopTimer'); + const resetBtn = document.getElementById('resetTimer'); + + if (startBtn) { + startBtn.addEventListener('click', startTimer); + } + + if (stopBtn) { + stopBtn.addEventListener('click', stopTimer); + } + + if (resetBtn) { + resetBtn.addEventListener('click', resetTimer); + } +} + +export function startTimer() { + if (isRunning) return; + + isRunning = true; + timerInterval = setInterval(() => { + timeLeft--; + updateTimerDisplay(); + + if (timeLeft === 0) { + stopTimer(); + playTimerSound(); + showNotification('Time for a break! 🎉'); + timeLeft = 5 * 60; + updateTimerDisplay(); + } + }, 1000); + + updateTimerButtons(); +} + +export function stopTimer() { + isRunning = false; + if (timerInterval) { + clearInterval(timerInterval); + timerInterval = null; + } + updateTimerButtons(); +} + +export function resetTimer() { + stopTimer(); + timeLeft = 25 * 60; + updateTimerDisplay(); +} + +function updateTimerDisplay() { + const display = document.getElementById('timerDisplay'); + if (!display) return; + + const minutes = Math.floor(timeLeft / 60); + const seconds = timeLeft % 60; + display.textContent = `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`; +} + +function updateTimerButtons() { + const startBtn = document.getElementById('startTimer'); + const stopBtn = document.getElementById('stopTimer'); + + if (startBtn) { + startBtn.disabled = isRunning; + startBtn.style.opacity = isRunning ? '0.5' : '1'; + } + + if (stopBtn) { + stopBtn.disabled = !isRunning; + stopBtn.style.opacity = !isRunning ? '0.5' : '1'; + } +} + +function playTimerSound() { + const audio = new Audio('data:audio/wav;base64,UklGRnoGAABXQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0YQoGAACBhYqFbF1fdJivrJBhNjVgodDbq2EcBj+a2/LDciUFLIHO8tiJNwgZaLvt559NEAxQp+PwtmMcBjiR1/LMeSwFJHfH8N2QQAoUXrTp66hVFApGn+DyvmwhBSuBzvLZiTYIF2m98OScTgwOUKfj8LZjHAU5k9nzyn0vBSF0yPLaizsKElyx6OyrWBUIQ5vd8sFuJAUuhM/z2Ik3CRdqvfDjnE0MElCn4+y/aB0GNpPZ88p9LwUgdMny2Ys7ChJcsevsq1gVCEGZ2/G/biQFL4TP89mJNwgZa77w45xNDBJQqOPtv2gdBTaU2vPKfC8FIHbJ8diLOwkSW7Tr7KpYFghDmdvyv24jBS+Dz/PZiTYIGWu/8eSbTgwST6jk7cBoHAU2lNrzynwvBSB2yfPZizsKElux6+urWBQJQpjb8b9uIwUug9Dz2Yk2CBlrv/HkmkoME0+o4+y/aBwFNpTa88p8LgUfdsnx2Io6CRFbsOvqq1cUCEGY2/G+bSIFL4PR89iHNgcZarztopxODRNPp+PsvmYcBTaU2fPKfS4FH3fK8diKOggRWq/s66pYFAk/l9nyv24jBS+E0fPYhzUHGGm+8d+aTQ0UT6fj7L5mHAU2lNn0yXwuBR95yfHYizoJEVqv7OuqWRUJP5fZ8r5tIwYug9DzWYk2Bxdpv/HfmE8MFE+o4+y+ZhwFN5Ta88l8LgQefcny2Yo6CRFar+ztqldUCT+X2fK+biMFLoPQ81mJNgcZab/x4ZhODBRPpuPsvmYcBjGU2vLJfC4EH37K8NiJOwkRWrDr7KpYFQlBmNrxv24jBS6E0PPYiTUHGWu+8eCZTgwTT6jj7b5mHAYxldrzyHwuAx98yvDYijsJEFux6+yrWBUJQJfZ8b9uIwUugM/z2Ik1Bxlqv/HhmU4MFE+o4+y+ZhwGMZTa88h9LwQffsrw2Io7ChFaset9Z2YnDw=='); + audio.play().catch(() => {}); +} + +function showNotification(message) { + const notification = document.createElement('div'); + notification.className = 'notification'; + notification.textContent = message; + document.body.appendChild(notification); + + setTimeout(() => { + notification.classList.add('show'); + }, 10); + + setTimeout(() => { + notification.classList.remove('show'); + setTimeout(() => notification.remove(), 300); + }, 3000); +} + +window.initTimer = initTimer; +window.startTimer = startTimer; +window.stopTimer = stopTimer; +window.resetTimer = resetTimer; From c86cefc14990ced0ecfac98f721547743afa21f0 Mon Sep 17 00:00:00 2001 From: "aary.sh" Date: Sun, 14 Dec 2025 11:43:41 +1100 Subject: [PATCH 30/35] Implement bookmark management functions --- js/features/bookmarks.js | 97 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 js/features/bookmarks.js diff --git a/js/features/bookmarks.js b/js/features/bookmarks.js new file mode 100644 index 0000000..c99dd64 --- /dev/null +++ b/js/features/bookmarks.js @@ -0,0 +1,97 @@ +export function initBookmarks() { + loadBookmarks(); +} + +export function addBookmark(result) { + const bookmarks = getBookmarks(); + + const bookmark = { + id: Date.now(), + title: result.title, + excerpt: result.excerpt, + source: result.source, + sourceClass: result.sourceClass, + url: result.url, + timestamp: new Date().toISOString() + }; + + bookmarks.unshift(bookmark); + + if (bookmarks.length > 50) { + bookmarks.pop(); + } + + localStorage.setItem('crescentBookmarks', JSON.stringify(bookmarks)); + updateBookmarksDisplay(); + showNotification('Bookmarked!'); +} + +export function removeBookmark(id) { + const bookmarks = getBookmarks(); + const filtered = bookmarks.filter(b => b.id !== id); + localStorage.setItem('crescentBookmarks', JSON.stringify(filtered)); + updateBookmarksDisplay(); +} + +export function clearBookmarks() { + if (confirm('Clear all bookmarks?')) { + localStorage.removeItem('crescentBookmarks'); + updateBookmarksDisplay(); + } +} + +export function loadBookmarks() { + updateBookmarksDisplay(); +} + +function getBookmarks() { + const saved = localStorage.getItem('crescentBookmarks'); + return saved ? JSON.parse(saved) : []; +} + +function updateBookmarksDisplay() { + const bookmarksList = document.getElementById('bookmarksList'); + if (!bookmarksList) return; + + const bookmarks = getBookmarks(); + + if (bookmarks.length === 0) { + bookmarksList.innerHTML = '

No bookmarks yet

'; + return; + } + + bookmarksList.innerHTML = bookmarks.map(bookmark => ` +
+
+
+ + ${bookmark.source} +
+
${bookmark.title}
+
${bookmark.excerpt.substring(0, 100)}...
+
+ +
+ `).join(''); +} + +function showNotification(message) { + const notification = document.createElement('div'); + notification.className = 'notification'; + notification.textContent = message; + document.body.appendChild(notification); + + setTimeout(() => { + notification.classList.add('show'); + }, 10); + + setTimeout(() => { + notification.classList.remove('show'); + setTimeout(() => notification.remove(), 300); + }, 2000); +} + +window.loadBookmarks = loadBookmarks; +window.addBookmark = addBookmark; +window.removeBookmark = removeBookmark; +window.clearBookmarks = clearBookmarks; From 9226380fd6960c5c4331b40220bae1ef3af21f96 Mon Sep 17 00:00:00 2001 From: "aary.sh" Date: Sun, 14 Dec 2025 11:43:57 +1100 Subject: [PATCH 31/35] Update app.js --- js/app.js | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/js/app.js b/js/app.js index 0631e02..72442d9 100644 --- a/js/app.js +++ b/js/app.js @@ -34,16 +34,29 @@ function setupEventListeners() { } } -function loadFeatures() { - if (typeof window.loadNotes === 'function') { - window.loadNotes(); - } - - if (typeof window.loadSearchHistory === 'function') { - window.loadSearchHistory(); - } - - if (typeof window.initTimer === 'function') { - window.initTimer(); - } +async function loadFeatures() { + try { + const { initNotes } = await import('./features/notes.js'); + initNotes(); + } catch (e) {} + + try { + const { initHistory } = await import('./features/history.js'); + initHistory(); + } catch (e) {} + + try { + const { initDarkMode } = await import('./features/darkmode.js'); + initDarkMode(); + } catch (e) {} + + try { + const { initTimer } = await import('./features/timer.js'); + initTimer(); + } catch (e) {} + + try { + const { initBookmarks } = await import('./features/bookmarks.js'); + initBookmarks(); + } catch (e) {} } From aca98f5552d9e1b4d220ab6ad6dbc18e14eba165 Mon Sep 17 00:00:00 2001 From: "aary.sh" Date: Sun, 14 Dec 2025 11:44:14 +1100 Subject: [PATCH 32/35] Update search.js --- js/search.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/js/search.js b/js/search.js index 5b17384..9f382c1 100644 --- a/js/search.js +++ b/js/search.js @@ -177,12 +177,17 @@ export async function performSearch() { ${result.meta?.phonetic ? `${result.meta.phonetic}` : ''} ${result.meta?.date ? `${result.meta.date}` : ''} + `; card.onclick = () => { if (result.url) window.open(result.url, '_blank'); }; resultsGrid.appendChild(card); }); + + if (typeof window.addToSearchHistory === 'function') { + window.addToSearchHistory(query, results.length); + } } catch (error) { loading.classList.remove('active'); @@ -192,4 +197,4 @@ export async function performSearch() { `; } - } +} From 7b6144d17c78b79f1f0abffb90849f4018d0f82f Mon Sep 17 00:00:00 2001 From: "aary.sh" Date: Sun, 14 Dec 2025 11:45:14 +1100 Subject: [PATCH 33/35] Update index.html --- index.html | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/index.html b/index.html index 9c67a24..f2fe8f6 100644 --- a/index.html +++ b/index.html @@ -39,6 +39,8 @@
  • GitHub
  • About
  • + +
    @@ -50,6 +52,37 @@
    +
    +

    Notes

    + +
    + + +
    +
    + +
    +

    Study Timer

    +
    25:00
    +
    + + + +
    +
    + +
    +

    Search History

    +
    + +
    + +
    +

    Bookmarks

    +
    + +
    +

    Now Playing

    From db145b49fac4b47dca0b2fa16c41ef82e7c329a8 Mon Sep 17 00:00:00 2001 From: "aary.sh" Date: Sun, 14 Dec 2025 11:45:46 +1100 Subject: [PATCH 34/35] Update features.css --- css/features.css | 332 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 331 insertions(+), 1 deletion(-) diff --git a/css/features.css b/css/features.css index f053e69..40e5be4 100644 --- a/css/features.css +++ b/css/features.css @@ -1 +1,331 @@ -coming soon -luvaary +.notes-panel { + position: fixed; + left: 20px; + top: 120px; + width: 280px; + background: var(--bg-card); + padding: 1.5rem; + border-radius: 12px; + box-shadow: var(--shadow); + border: 1px solid var(--border); + backdrop-filter: blur(10px); +} + +.notes-panel h3 { + font-family: 'Cormorant Garamond', serif; + font-size: 1.1rem; + margin-bottom: 1rem; + color: var(--text-primary); +} + +.notes-panel textarea { + width: 100%; + height: 200px; + border: 1px solid var(--border); + border-radius: 8px; + padding: 0.75rem; + font-family: inherit; + font-size: 0.9rem; + resize: vertical; + background: var(--bg-secondary); + color: var(--text-primary); +} + +.notes-actions { + display: flex; + gap: 0.5rem; + margin-top: 0.75rem; +} + +.notes-actions button { + flex: 1; + padding: 0.5rem; + background: var(--accent); + color: white; + border: none; + border-radius: 6px; + cursor: pointer; + font-size: 0.85rem; + transition: all 0.2s ease; +} + +.notes-actions button:hover { + background: var(--accent-soft); + color: var(--text-primary); +} + +.timer-widget { + position: fixed; + bottom: 20px; + right: 20px; + background: var(--bg-card); + padding: 1.5rem; + border-radius: 12px; + box-shadow: var(--shadow); + border: 1px solid var(--border); + backdrop-filter: blur(10px); + min-width: 200px; + text-align: center; +} + +.timer-widget h3 { + font-family: 'Cormorant Garamond', serif; + font-size: 1rem; + margin-bottom: 1rem; + color: var(--text-primary); +} + +.timer-display { + font-family: 'Cormorant Garamond', serif; + font-size: 2.5rem; + color: var(--accent); + margin-bottom: 1rem; +} + +.timer-controls { + display: flex; + gap: 0.5rem; +} + +.timer-controls button { + flex: 1; + padding: 0.5rem; + background: var(--accent); + color: white; + border: none; + border-radius: 6px; + cursor: pointer; + font-size: 0.75rem; + transition: all 0.2s ease; +} + +.timer-controls button:hover { + background: var(--accent-soft); + color: var(--text-primary); +} + +.timer-controls button:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.history-panel { + position: fixed; + left: 20px; + bottom: 20px; + width: 280px; + max-height: 400px; + background: var(--bg-card); + padding: 1.5rem; + border-radius: 12px; + box-shadow: var(--shadow); + border: 1px solid var(--border); + backdrop-filter: blur(10px); + overflow-y: auto; +} + +.history-panel h3 { + font-family: 'Cormorant Garamond', serif; + font-size: 1.1rem; + margin-bottom: 1rem; + color: var(--text-primary); +} + +.history-item { + padding: 0.75rem; + background: var(--bg-secondary); + border-radius: 8px; + margin-bottom: 0.5rem; + cursor: pointer; + transition: all 0.2s ease; +} + +.history-item:hover { + background: var(--accent-soft); +} + +.history-query { + font-size: 0.9rem; + color: var(--text-primary); + margin-bottom: 0.25rem; +} + +.history-meta { + font-size: 0.7rem; + color: var(--text-muted); +} + +.history-empty { + text-align: center; + color: var(--text-muted); + font-size: 0.85rem; +} + +.clear-history-btn { + width: 100%; + padding: 0.5rem; + background: var(--text-muted); + color: white; + border: none; + border-radius: 6px; + cursor: pointer; + font-size: 0.75rem; + margin-top: 0.75rem; + transition: all 0.2s ease; +} + +.clear-history-btn:hover { + background: var(--text-secondary); +} + +.bookmarks-panel { + position: fixed; + right: 20px; + top: 120px; + width: 300px; + max-height: 500px; + background: var(--bg-card); + padding: 1.5rem; + border-radius: 12px; + box-shadow: var(--shadow); + border: 1px solid var(--border); + backdrop-filter: blur(10px); + overflow-y: auto; +} + +.bookmarks-panel h3 { + font-family: 'Cormorant Garamond', serif; + font-size: 1.1rem; + margin-bottom: 1rem; + color: var(--text-primary); +} + +.bookmark-item { + display: flex; + gap: 0.5rem; + padding: 0.75rem; + background: var(--bg-secondary); + border-radius: 8px; + margin-bottom: 0.5rem; + transition: all 0.2s ease; +} + +.bookmark-item:hover { + background: var(--accent-soft); +} + +.bookmark-content { + flex: 1; + cursor: pointer; +} + +.bookmark-source { + display: flex; + align-items: center; + gap: 0.5rem; + font-size: 0.65rem; + color: var(--text-muted); + text-transform: uppercase; + margin-bottom: 0.25rem; +} + +.bookmark-title { + font-size: 0.9rem; + color: var(--text-primary); + margin-bottom: 0.25rem; + font-weight: 500; +} + +.bookmark-excerpt { + font-size: 0.75rem; + color: var(--text-secondary); + line-height: 1.4; +} + +.bookmark-remove { + background: none; + border: none; + color: var(--text-muted); + font-size: 1.5rem; + cursor: pointer; + transition: color 0.2s ease; + padding: 0; + width: 24px; + height: 24px; + display: flex; + align-items: center; + justify-content: center; +} + +.bookmark-remove:hover { + color: var(--accent); +} + +.bookmarks-empty { + text-align: center; + color: var(--text-muted); + font-size: 0.85rem; +} + +.clear-bookmarks-btn { + width: 100%; + padding: 0.5rem; + background: var(--text-muted); + color: white; + border: none; + border-radius: 6px; + cursor: pointer; + font-size: 0.75rem; + margin-top: 0.75rem; + transition: all 0.2s ease; +} + +.clear-bookmarks-btn:hover { + background: var(--text-secondary); +} + +.notification { + position: fixed; + top: 100px; + right: -300px; + background: var(--accent); + color: white; + padding: 1rem 1.5rem; + border-radius: 8px; + box-shadow: var(--shadow-hover); + z-index: 1000; + transition: right 0.3s ease; + font-size: 0.9rem; +} + +.notification.show { + right: 20px; +} + +body.dark-mode { + --bg-primary: #1a1a1a; + --bg-secondary: #2a2a2a; + --bg-card: rgba(40, 40, 40, 0.9); + --text-primary: #ffffff; + --text-secondary: #cccccc; + --text-muted: #888888; + --border: rgba(255, 255, 255, 0.1); + --shadow: 0 4px 24px rgba(0, 0, 0, 0.3); + --shadow-hover: 0 8px 32px rgba(0, 0, 0, 0.4); +} + +body.dark-mode .orb { + opacity: 0.2; +} + +@media (max-width: 1200px) { + .notes-panel, + .history-panel, + .bookmarks-panel { + display: none; + } + + .timer-widget { + bottom: 80px; + } +} From 0964a4e119bd049dab5a17cc0c0aee297b33df01 Mon Sep 17 00:00:00 2001 From: "aary.sh" Date: Sun, 14 Dec 2025 11:46:10 +1100 Subject: [PATCH 35/35] Update components.css --- css/components.css | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/css/components.css b/css/components.css index f0e5873..63d644d 100644 --- a/css/components.css +++ b/css/components.css @@ -133,6 +133,7 @@ transition: all 0.3s ease; cursor: pointer; animation: slideUp 0.5s ease-out backwards; + position: relative; } .result-card:hover { @@ -195,6 +196,28 @@ color: var(--text-muted); } +.bookmark-btn { + position: absolute; + top: 1rem; + right: 1rem; + background: var(--bg-secondary); + border: 1px solid var(--border); + border-radius: 50%; + width: 32px; + height: 32px; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + font-size: 1rem; + transition: all 0.2s ease; +} + +.bookmark-btn:hover { + background: var(--accent); + transform: scale(1.1); +} + .loading { display: none; justify-content: center;