diff --git a/client/components/announcementBanner/index.js b/client/components/announcementBanner/index.js index 3ae7edb..6169e7e 100644 --- a/client/components/announcementBanner/index.js +++ b/client/components/announcementBanner/index.js @@ -19,8 +19,8 @@ const BANNERS = [ url: 'https://www.hangingpiece.com?utm_source=acode_announcement_banner', alt: 'Hanging Piece', badge: 'NEW', - title: 'Hanging Piece - AI Chess Coach', - subtitle: 'Understand WHY you blundered. Stop repeating mistakes!', + title: 'Hanging Piece - Claude Code for Chess', + subtitle: 'Understand WHY you blundered and HOW to improve!', cta: 'Try Now', theme: 'hangingpiece', }, diff --git a/client/lib/background.js b/client/lib/background.js index e5fe37c..081f0cb 100644 --- a/client/lib/background.js +++ b/client/lib/background.js @@ -7,22 +7,39 @@ class Particle { * @param {number} dx * @param {number} dy * @param {string} color + * @param {number} opacity */ - constructor(canvas, radius, x, y, dx, dy, color) { + constructor(canvas, radius, x, y, dx, dy, color, opacity) { this.radius = radius; this.x = x; this.y = y; this.dx = dx; this.dy = dy; this.color = color; + this.opacity = opacity; this.canvas = canvas; } draw(ctx) { + // Draw glow effect + const gradient = ctx.createRadialGradient(this.x, this.y, 0, this.x, this.y, this.radius * 3); + gradient.addColorStop(0, this.color); + gradient.addColorStop(0.5, `${this.color}40`); // Add transparency to hex + gradient.addColorStop(1, `${this.color}00`); + + ctx.globalAlpha = this.opacity * 0.3; + ctx.fillStyle = gradient; ctx.beginPath(); - ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI); + ctx.arc(this.x, this.y, this.radius * 3, 0, 2 * Math.PI); + ctx.fill(); + + // Draw main particle + ctx.globalAlpha = this.opacity; ctx.fillStyle = this.color; + ctx.beginPath(); + ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI); ctx.fill(); + ctx.globalAlpha = 1; } update() { @@ -67,14 +84,26 @@ export default function background(canvas) { particles = []; + // Dramatic color palette: electric blues, cyans, and deep purples + const colors = [ + '#00d4ff', // Electric cyan + '#0099ff', // Bright blue + '#3366ff', // Deep blue + '#6b5bff', // Purple-blue + '#00ffcc', // Aqua + '#1a8fff', // Ocean blue + ]; + for (let i = 0; i < numParticles; ++i) { - const r = Math.random() * 3 + 1; + const r = Math.random() * 2.5 + 1.5; // Slightly larger particles const x = Math.random() * (canvas.width - r) + r; const y = Math.random() * (canvas.height - r) + r; - const dx = (Math.random() > 0.5 ? 1 : -1) * Math.random() * 1.5; - const dy = (Math.random() > 0.5 ? 1 : -1) * Math.random() * 1.5; + const dx = (Math.random() > 0.5 ? 1 : -1) * Math.random() * 1.2; + const dy = (Math.random() > 0.5 ? 1 : -1) * Math.random() * 1.2; + const color = colors[Math.floor(Math.random() * colors.length)]; + const opacity = Math.random() * 0.5 + 0.4; // 0.4 to 0.9 - more visible - particles.push(new Particle(canvas, r, x, y, dx, dy, '#606060')); + particles.push(new Particle(canvas, r, x, y, dx, dy, color, opacity)); } if (reqId != null) cancelAnimationFrame(reqId); @@ -106,13 +135,23 @@ export default function background(canvas) { for (const p2 of particles) { const distance = Math.sqrt((p2.x - p1.x) ** 2 + (p2.y - p1.y) ** 2); - if (distance < 100) { + if (distance < 140) { + // Create gradient line from p1 to p2 + const gradient = ctx.createLinearGradient(p1.x, p1.y, p2.x, p2.y); + gradient.addColorStop(0, p1.color); + gradient.addColorStop(0.5, '#00aaff'); // Electric blue middle + gradient.addColorStop(1, p2.color); + + ctx.strokeStyle = gradient; + // More visible connections with better opacity curve + const opacityFactor = 1 - distance / 140; + ctx.globalAlpha = opacityFactor * opacityFactor * 0.5; // Quadratic falloff + ctx.lineWidth = 1.5; ctx.beginPath(); - ctx.strokeStyle = '#606060'; - ctx.lineWidth = 1; ctx.moveTo(p1.x, p1.y); ctx.lineTo(p2.x, p2.y); ctx.stroke(); + ctx.globalAlpha = 1; } } } diff --git a/client/main.scss b/client/main.scss index e3f55d8..e986a26 100644 --- a/client/main.scss +++ b/client/main.scss @@ -10,11 +10,14 @@ html { width: 100%; overflow: hidden; margin: 0; - background: linear-gradient(to bottom right, - var(--secondary-color), - var(--primary-color), - var(--primary-color), - var(--primary-color)); + background: linear-gradient( + 135deg, + #1a1f2e 0%, + #2a2f3e 25%, + var(--primary-color) 50%, + #252a38 75%, + var(--secondary-color) 100% + ); color: var(--primary-text-color); font-family: 'Montserrat', sans-serif; } @@ -88,7 +91,7 @@ header { align-items: center; transition: all 1s ease-in-out; - >.icon { + > .icon { height: 60px; width: 60px; display: flex; @@ -118,7 +121,7 @@ header { justify-content: center; } - #menu-toggler~.mask { + #menu-toggler ~ .mask { position: fixed; top: 0; left: 0; @@ -129,11 +132,11 @@ header { transition: all 0.3s ease-in-out; } - #menu-toggler:checked~nav:nth-of-type(2) { + #menu-toggler:checked ~ nav:nth-of-type(2) { transform: translateX(0); } - #menu-toggler:checked~.mask { + #menu-toggler:checked ~ .mask { pointer-events: all; background-color: rgba($color: #000000, $alpha: 0.5); } @@ -424,27 +427,199 @@ code { width: fit-content; } -.footer-nav { - display: flex; - gap: 14px; - justify-content: center; - padding: 10px; - flex-wrap: wrap; +footer { + background: linear-gradient( + 180deg, + rgba(0, 0, 0, 0.05) 0%, + rgba(0, 0, 0, 0.3) 100% + ); + backdrop-filter: blur(30px); + border-top: 1px solid rgba(51, 153, 255, 0.15); + position: relative; + z-index: 0; + padding: 60px 20px 40px; + margin-top: 100px; + + .footer-content { + max-width: 1000px; + margin: 0 auto; + display: grid; + grid-template-columns: 2fr 1fr 1fr 1.2fr; + gap: 40px; + margin-bottom: 40px; + + @media screen and (max-width: 768px) { + grid-template-columns: 1fr 1fr; + gap: 32px; + } + + @media screen and (max-width: 480px) { + grid-template-columns: 1fr; + text-align: center; + } + } - a { - color: white; + .footer-section { + display: flex; + flex-direction: column; + align-items: flex-start; + + @media screen and (max-width: 480px) { + align-items: center; + } + + h4 { + font-family: 'Lexend', sans-serif; + font-size: 0.8rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.1em; + color: #fff; + margin-bottom: 16px; + } + + &.footer-brand { + .footer-description { + font-size: 0.85rem; + line-height: 1.5; + color: rgba(255, 255, 255, 0.6); + margin-bottom: 20px; + max-width: 240px; + + @media screen and (max-width: 480px) { + margin-left: auto; + margin-right: auto; + } + } + + .footer-logo { + display: flex; + align-items: center; + gap: 8px; + margin-bottom: 16px; + + @media screen and (max-width: 768px) { + justify-content: center; + } + + img { + height: 24px; + } + + span { + font-family: 'Lexend', sans-serif; + font-size: 1.2rem; + font-weight: 700; + background: linear-gradient(135deg, #3399ff, #66b3ff); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + } + } + + .footer-social { + display: flex; + gap: 12px; + + @media screen and (max-width: 768px) { + justify-content: center; + } + + a { + width: 32px; + height: 32px; + display: flex; + align-items: center; + justify-content: center; + background: rgba(255, 255, 255, 0.03); + border: 1px solid rgba(255, 255, 255, 0.1); + border-radius: 8px; + color: rgba(255, 255, 255, 0.6); + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + + &:hover { + background: rgba(51, 153, 255, 0.1); + border-color: rgba(51, 153, 255, 0.4); + color: #3399ff; + transform: translateY(-2px); + } + } + } + } + } + + .footer-links { + display: flex; + flex-direction: column; + gap: 10px; + + a { + color: rgba(255, 255, 255, 0.5); + text-decoration: none; + font-size: 0.85rem; + transition: all 0.2s ease; + margin: 0; + + &:hover { + color: #fff; + padding-left: 2px; + } + } + } + + .digitalocean-badge { + display: inline-flex; + align-items: center; + gap: 8px; + padding: 8px 12px; + background: rgba(255, 255, 255, 0.03); + border: 1px solid rgba(255, 255, 255, 0.1); + border-radius: 8px; + color: rgba(255, 255, 255, 0.7); + text-decoration: none; + transition: all 0.2s ease; + font-size: 0.8rem; margin: 0; - white-space: nowrap; + + &:hover { + background: rgba(255, 255, 255, 0.08); + border-color: rgba(255, 255, 255, 0.2); + color: #fff; + } + + img { + opacity: 0.8; + } + + span { + font-weight: 500; + } } -} -footer { - background-color: rgba(0, 0, 0, 0.1); - backdrop-filter: blur(20px); - position: relative; - z-index: 0; + .footer-bottom { + max-width: 1000px; + margin: 0 auto; + padding-top: 32px; + border-top: 1px solid rgba(255, 255, 255, 0.05); + display: flex; + justify-content: space-between; + align-items: center; + gap: 12px; + + @media screen and (max-width: 768px) { + flex-direction: column; + text-align: center; + padding-top: 24px; + } + + p { + color: rgba(255, 255, 255, 0.4); + font-size: 0.8rem; + margin: 0; + } + } } .text-center { text-align: center; -} \ No newline at end of file +} diff --git a/client/main.view.js b/client/main.view.js index 4021834..7ef4304 100644 --- a/client/main.view.js +++ b/client/main.view.js @@ -33,26 +33,72 @@ export default ({ routes }) => (
); diff --git a/client/pages/home/index.js b/client/pages/home/index.js index d5f8cb0..710eba0 100644 --- a/client/pages/home/index.js +++ b/client/pages/home/index.js @@ -1,6 +1,5 @@ import './style.scss'; -import { render } from 'github-buttons'; import Reactive from 'html-tag-js/reactive'; import Ref from 'html-tag-js/ref'; import background from 'lib/background'; @@ -13,9 +12,22 @@ import tabletImageWebp from 'res/tablet.webp'; export default async function home() { const canvas = Ref(); const pluginCount = Reactive('...'); + const stars = Reactive('...'); + const forks = Reactive('...'); showLoading(); + fetch('https://api.github.com/repos/acode-foundation/acode') + .then((res) => res.json()) + .then((data) => { + stars.value = `${(data.stargazers_count / 1000).toFixed(1)}k`; + forks.value = data.forks_count.toLocaleString(); + }) + .catch(() => { + stars.value = '1.2k+'; + forks.value = '200+'; + }); + let plugins = []; try { const { count } = await (await fetch('/api/plugins/count')).json(); @@ -46,69 +58,71 @@ export default async function home() {

An extensible, powerful and open-source code editor for Android

-
- - +
+ 1M+ + downloads
+
+ + +
-
- - Download from Google Play store - - - Download from F-Droid - -
-
-

Featured Plugins

+
+

Featured Plugins

+ + Browse all + +
); } -function GhButton({ url, title, icon }) { - const el =
; - render( - - {title} - , - (button) => { - el.replaceWith(button); - }, +function GhButton({ url, title, count }) { + return ( + +
+ + GitHub + + + {title} +
+
{count}
+
); - return el; } function Plugin({ data }) { diff --git a/client/pages/home/style.scss b/client/pages/home/style.scss index e487455..a2f411b 100644 --- a/client/pages/home/style.scss +++ b/client/pages/home/style.scss @@ -1,3 +1,5 @@ +@import url('https://fonts.googleapis.com/css2?family=Lexend:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap'); + #home { max-width: none; width: 100%; @@ -24,28 +26,41 @@ .download-buttons { gap: 10px; display: none; - margin-top: 10px; + margin-top: 20px; align-items: center; justify-content: center; a { - height: 60px; - width: 160px; + display: inline-flex; + align-items: center; + gap: 8px; + height: 44px; + padding: 0 20px; text-decoration: none; - background-repeat: no-repeat; - background-position: center; - background-size: contain; + background: rgba(255, 255, 255, 0.05); + border: 1px solid rgba(255, 255, 255, 0.15); + border-radius: 12px; + color: rgba(255, 255, 255, 0.9); + font-size: 0.9rem; + font-weight: 500; + transition: all 0.2s ease; - .hidden { - display: none; + &:hover { + background: rgba(255, 255, 255, 0.1); + border-color: rgba(255, 255, 255, 0.25); + transform: translateY(-1px); } &.play-store { - background-image: url(https://play.google.com/intl/en_us/badges/static/images/badges/en_badge_web_generic.png); + svg { + color: #00d4ff; + } } &.f-droid { - background-image: url(https://f-droid.org/badge/get-it-on.png); + svg { + color: #33ff99; + } } } } @@ -53,17 +68,20 @@ .intro { display: flex; flex-wrap: wrap-reverse; - justify-content: center; + justify-content: space-between; width: 100%; - height: 400px; - max-width: unset; + min-height: 500px; + max-width: 1300px; align-items: center; - margin-bottom: 60px; + margin-bottom: 80px; + padding: 80px 40px 40px; + position: relative; + gap: 40px; .preview-image { position: relative; display: flex; - width: 60%; + width: 45%; max-width: 500px; picture { @@ -71,19 +89,18 @@ &:hover { img { - box-shadow: 0 0 16px rgba(0, 0, 0, 0.7); - border: 1px solid rgba(255, 255, 255, 0.2); - transform: scale(1.05); + transform: scale(1.02); + box-shadow: 0 20px 50px rgba(0, 0, 0, 0.6); } } img { width: 100%; - border-radius: 4px; + border-radius: 12px; object-fit: contain; - transition: all 0.3s ease-in-out; + transition: all 0.3s ease; border: 1px solid rgba(255, 255, 255, 0.1); - box-shadow: 0 0 16px rgba(0, 0, 0, 0.5); + box-shadow: 0 12px 40px rgba(0, 0, 0, 0.5); } &.phone { @@ -101,93 +118,358 @@ } .heading { - width: 40%; - padding: 0 16px; - white-space: pre-wrap; + flex: 1; + padding: 0 20px; + text-align: left; + display: flex; + flex-direction: column; + align-items: flex-start; + + > p { + font-family: 'Lexend', sans-serif; + font-size: clamp(1.8rem, 5vw, 3rem); + line-height: 1.1; + margin-bottom: 24px; + font-weight: 800; + letter-spacing: -0.03em; + color: #ffffff; + max-width: 600px; + } + + .stat-badge { + display: inline-flex; + align-items: center; + gap: 8px; + margin-bottom: 18px; + padding: 8px 16px; + background: rgba(0, 212, 255, 0.1); + border: 1px solid rgba(0, 212, 255, 0.3); + border-radius: 24px; + + .stat-number { + font-family: 'Lexend', sans-serif; + font-size: 1.4rem; + font-weight: 700; + color: #00d4ff; + line-height: 1; + } + + .stat-label { + font-size: 0.9rem; + color: rgba(255, 255, 255, 0.7); + font-weight: 500; + text-transform: lowercase; + letter-spacing: 0.02em; + } + } .gh-buttons { - margin: 0 auto; - width: 170px; display: flex; - justify-content: space-between; - gap: 10px; + gap: 12px; + align-items: center; + margin-bottom: 0; + + .gh-button-modern { + display: inline-flex; + align-items: center; + text-decoration: none; + background: rgba(255, 255, 255, 0.03); + border: 1px solid rgba(255, 255, 255, 0.1); + border-radius: 8px; + overflow: hidden; + font-size: 0.75rem; + height: 28px; + transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); + + &:hover { + border-color: rgba(0, 212, 255, 0.4); + background: rgba(0, 212, 255, 0.05); + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); + + .gh-button-main { + background: rgba(255, 255, 255, 0.05); + color: #00d4ff; + } + + .gh-button-count { + color: #fff; + } + } + + .gh-button-main { + display: flex; + align-items: center; + gap: 6px; + padding: 0 10px; + height: 100%; + background: rgba(255, 255, 255, 0.03); + color: rgba(255, 255, 255, 0.8); + font-weight: 600; + border-right: 1px solid rgba(255, 255, 255, 0.1); + transition: all 0.2s ease; + } + + .gh-button-count { + padding: 0 10px; + height: 100%; + display: flex; + align-items: center; + color: rgba(255, 255, 255, 0.6); + font-weight: 500; + transition: all 0.2s ease; + } + } } .download-buttons { display: flex; + gap: 12px; + align-items: center; + flex-wrap: wrap; + margin-bottom: 32px; + + a { + display: inline-flex; + align-items: center; + gap: 10px; + height: 52px; + padding: 0 24px; + text-decoration: none; + background: rgba(255, 255, 255, 0.05); + border: 1px solid rgba(255, 255, 255, 0.1); + border-radius: 14px; + color: rgba(255, 255, 255, 0.9); + font-size: 1rem; + font-weight: 600; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + + &:hover { + background: rgba(255, 255, 255, 0.08); + border-color: rgba(255, 255, 255, 0.2); + transform: translateY(-2px); + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3); + } + + &.play-store { + background: #00d4ff; + color: #000; + border: none; + + svg { + color: #000; + width: 22px; + height: 22px; + } + + &:hover { + background: #00c2eb; + box-shadow: 0 10px 30px rgba(0, 212, 255, 0.35); + } + } + + &.f-droid { + svg { + color: #33ff99; + width: 20px; + height: 20px; + } + } + } } } - @media screen and (max-width: 700px) { - height: auto; + @media screen and (max-width: 900px) { + flex-direction: column; + padding: 40px 20px; .preview-image { width: 100%; max-width: 400px; + margin-bottom: 40px; } .heading { width: 100%; padding: 0 16px; + align-items: center; + text-align: center; + + > p { + font-size: clamp(1.6rem, 8vw, 2.2rem); + } + + .downloads-badge { + justify-content: center; + } + + .gh-buttons { + justify-content: center; + } + } + } + + @media screen and (max-width: 600px) { + padding: 30px 20px; + + .heading { + padding: 0 10px; + + > p { + font-size: 1.5rem; + } + + .downloads-badge { + .downloads-number { + font-size: 1.7rem; + } + } .download-buttons { - display: none; + a { + height: 40px; + padding: 0 16px; + font-size: 0.85rem; + + svg { + width: 16px; + height: 16px; + } + } } } } } - @media screen and (max-width: 700px) { + @media screen and (max-width: 900px) { .download-buttons { display: flex; } } - .featured-plugins__list { - display: flex; - justify-content: center; - flex-wrap: wrap; - list-style-type: none; + .featured-plugins { + margin-top: 80px; + width: 100%; + max-width: 1000px; - li { + .section-header { display: flex; - padding: 10px; - border: 1px solid #333; - border-radius: 5px; - flex-direction: column; - align-items: center; - justify-content: center; - flex-shrink: 0; - background-color: rgba(0, 0, 0, 0.2); - backdrop-filter: blur(10px); - - &:hover { - box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.7); + justify-content: space-between; + align-items: flex-end; + margin-bottom: 32px; + padding: 0 10px; + + h2 { + font-family: 'Lexend', sans-serif; + font-size: 1.8rem; + font-weight: 700; + color: #ffffff; } - } - } - .featured-plugins { - margin-top: 32px; + .see-all { + color: #00d4ff; + text-decoration: none; + font-size: 0.9rem; + font-weight: 600; + display: flex; + align-items: center; + gap: 4px; + transition: opacity 0.2s ease; - &__list li { - width: 160px; - min-height: 160px; - margin: 10px 10px 0; - padding: 10px; - text-align: center; - cursor: pointer; + &:hover { + opacity: 0.8; + text-decoration: underline; + } - img { - width: 90px; + .icon { + font-size: 0.8rem; + } } - h4 { - margin-top: 10px; + @media screen and (max-width: 600px) { + flex-direction: column; + align-items: center; + gap: 12px; + text-align: center; } + } - small { - color: #b9b9b9; + &__list { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); + gap: 20px; + padding: 10px; + list-style: none; + + li { + background: rgba(255, 255, 255, 0.03); + border: 1px solid rgba(255, 255, 255, 0.08); + border-radius: 20px; + padding: 32px 24px; + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + cursor: pointer; + transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1); + position: relative; + overflow: hidden; + + &::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: radial-gradient( + circle at top right, + rgba(0, 212, 255, 0.1), + transparent 70% + ); + opacity: 0; + transition: opacity 0.4s ease; + } + + img { + width: 80px; + height: 80px; + border-radius: 16px; + margin-bottom: 20px; + object-fit: contain; + transition: all 0.4s ease; + filter: drop-shadow(0 8px 16px rgba(0, 0, 0, 0.3)); + } + + h4 { + font-family: 'Lexend', sans-serif; + font-size: 1.1rem; + font-weight: 600; + color: #fff; + margin-bottom: 8px; + } + + small { + font-size: 0.85rem; + color: rgba(255, 255, 255, 0.5); + font-weight: 500; + } + + &:hover { + transform: translateY(-8px); + background: rgba(255, 255, 255, 0.06); + border-color: rgba(0, 212, 255, 0.3); + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.4); + + &::before { + opacity: 1; + } + + img { + transform: scale(1.1) rotate(5deg); + filter: drop-shadow(0 12px 24px rgba(0, 212, 255, 0.3)); + } + } } } } @@ -198,7 +480,7 @@ } footer { - margin-top: 16px; - padding: 16px; + margin-top: 40px; + padding: 0; } -} \ No newline at end of file +}