diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000..a9459b5 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,167 @@ + + + + + + + Conduit - Native Mobile Client for Open-WebUI + + + + + + + + + + + +
+
+
+

Your AI, Unleashed.

+

The premium native mobile client for Open-WebUI. Connect to your self-hosted AI + infrastructure with a beautiful, fast, and secure experience.

+ + + +
+
+ Open Source + GPLv3 Licensed +
+
+ Private + Your Data, Your Control +
+
+ Native + iOS & Android +
+
+
+
+
+ Conduit App Demo +
+
+
+
+ +
+
+

Why Conduit?

+
+
+
+

Real-time Chat

+

Stream responses from your AI models instantly with low latency WebSocket connections.

+
+
+
🎨
+

Premium Design

+

Beautifully crafted interface with light, dark, and system themes that feel right at home on your + device.

+
+
+
🔐
+

Secure Storage

+

Your credentials and API keys are stored securely using the platform's native keychain/keystore. +

+
+
+
🎙️
+

Voice Input

+

Speak naturally to your AI with integrated speech-to-text support for hands-free interaction.

+
+
+
📁
+

File Uploads

+

Upload images and documents for RAG (Retrieval-Augmented Generation) and vision capabilities.

+
+
+
🛠️
+

Tool Support

+

Full support for Open-WebUI tools and function calling with rich result rendering.

+
+
+
+
+ +
+
+

Experience Conduit

+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs/script.js b/docs/script.js new file mode 100644 index 0000000..8505aaa --- /dev/null +++ b/docs/script.js @@ -0,0 +1,58 @@ +document.addEventListener('DOMContentLoaded', () => { + // Smooth scrolling for anchor links + document.querySelectorAll('a[href^="#"]').forEach(anchor => { + anchor.addEventListener('click', function (e) { + e.preventDefault(); + const target = document.querySelector(this.getAttribute('href')); + if (target) { + target.scrollIntoView({ + behavior: 'smooth' + }); + } + }); + }); + + // Intersection Observer for fade-in animations + const observerOptions = { + threshold: 0.1, + rootMargin: '0px 0px -50px 0px' + }; + + const observer = new IntersectionObserver((entries) => { + entries.forEach(entry => { + if (entry.isIntersecting) { + entry.target.classList.add('visible'); + observer.unobserve(entry.target); + } + }); + }, observerOptions); + + // Add fade-in class to elements we want to animate + const animatedElements = document.querySelectorAll('.feature-card, .screenshot-item, .hero-content, .hero-image'); + animatedElements.forEach(el => { + el.classList.add('fade-in'); + observer.observe(el); + }); +}); + +// Add this CSS to styles.css dynamically or ensure it's in the CSS file +const style = document.createElement('style'); +style.textContent = ` + .fade-in { + opacity: 0; + transform: translateY(20px); + transition: opacity 0.6s ease-out, transform 0.6s ease-out; + } + + .fade-in.visible { + opacity: 1; + transform: translateY(0); + } + + .feature-card:nth-child(2) { transition-delay: 0.1s; } + .feature-card:nth-child(3) { transition-delay: 0.2s; } + .feature-card:nth-child(4) { transition-delay: 0.3s; } + .feature-card:nth-child(5) { transition-delay: 0.4s; } + .feature-card:nth-child(6) { transition-delay: 0.5s; } +`; +document.head.appendChild(style); diff --git a/docs/styles.css b/docs/styles.css new file mode 100644 index 0000000..8c9d96f --- /dev/null +++ b/docs/styles.css @@ -0,0 +1,467 @@ +:root { + --primary: #0A84FF; + --primary-dark: #0066CC; + --secondary: #1447E6; + --background: #0f0f13; + --surface: #1a1a20; + --text: #ffffff; + --text-secondary: #a0a0b0; + --border: #2a2a35; + --gradient: linear-gradient(135deg, #0A84FF 0%, #1447E6 100%); + --shadow: 0 10px 30px -10px rgba(10, 132, 255, 0.2); + --font-main: 'Inter', sans-serif; + --font-display: 'Outfit', sans-serif; +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: var(--font-main); + background-color: var(--background); + color: var(--text); + line-height: 1.6; + overflow-x: hidden; +} + +.container { + max-width: 1200px; + margin: 0 auto; + padding: 0 2rem; +} + +/* Navbar */ +.navbar { + position: fixed; + top: 0; + left: 0; + width: 100%; + background: rgba(15, 15, 19, 0.8); + backdrop-filter: blur(10px); + z-index: 1000; + border-bottom: 1px solid var(--border); + padding: 1rem 0; +} + +.navbar .container { + display: flex; + justify-content: space-between; + align-items: center; +} + +.logo { + font-family: var(--font-display); + font-size: 1.5rem; + font-weight: 700; + color: var(--text); + text-decoration: none; + background: var(--gradient); + background-clip: text; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; +} + +.nav-links { + display: flex; + gap: 2rem; + align-items: center; +} + +.nav-links a { + color: var(--text-secondary); + text-decoration: none; + font-weight: 500; + transition: color 0.3s ease; +} + +.nav-links a:hover { + color: var(--text); +} + +.github-link { + display: flex; + align-items: center; + gap: 0.5rem; + background: var(--surface); + padding: 0.5rem 1rem; + border-radius: 2rem; + border: 1px solid var(--border); + transition: all 0.3s ease; +} + +.github-link:hover { + border-color: var(--primary); + transform: translateY(-2px); +} + +/* Hero Section */ +.hero { + padding: 8rem 0 4rem; + position: relative; + overflow: hidden; +} + +.hero::before { + content: ''; + position: absolute; + top: -20%; + right: -10%; + width: 600px; + height: 600px; + background: var(--primary); + filter: blur(150px); + opacity: 0.15; + border-radius: 50%; + z-index: -1; +} + +.hero::after { + content: ''; + position: absolute; + bottom: -10%; + left: -10%; + width: 500px; + height: 500px; + background: var(--secondary); + filter: blur(150px); + opacity: 0.15; + border-radius: 50%; + z-index: -1; +} + +.hero .container { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 4rem; + align-items: center; +} + +.hero-title { + font-family: var(--font-display); + font-size: 4rem; + line-height: 1.1; + margin-bottom: 1.5rem; + font-weight: 700; +} + +.gradient-text { + background: var(--gradient); + background-clip: text; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; +} + +.hero-subtitle { + font-size: 1.25rem; + color: var(--text-secondary); + margin-bottom: 2.5rem; + max-width: 500px; +} + +.download-badges { + display: flex; + gap: 1rem; + margin-bottom: 3rem; +} + +.store-badge img { + height: 56px; + transition: transform 0.3s ease; +} + +.store-badge:hover img { + transform: scale(1.05); +} + +.hero-stats { + display: flex; + gap: 3rem; + border-top: 1px solid var(--border); + padding-top: 2rem; +} + +.stat-item { + display: flex; + flex-direction: column; +} + +.stat-value { + font-weight: 700; + font-size: 1.1rem; + color: var(--text); +} + +.stat-label { + font-size: 0.9rem; + color: var(--text-secondary); +} + +.hero-image { + position: relative; + display: flex; + justify-content: center; + z-index: 1; +} + +.hero-image::before { + content: ''; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 120%; + height: 120%; + background: radial-gradient(circle, rgba(10, 132, 255, 0.15) 0%, rgba(0, 0, 0, 0) 70%); + z-index: -1; + pointer-events: none; +} + +.phone-frame { + position: relative; + width: 300px; + /* Removed border and radius as image already has them */ + background: transparent; + box-shadow: + 0 20px 50px -10px rgba(0, 0, 0, 0.5), + 0 0 100px -20px rgba(10, 132, 255, 0.3); + transform: none; + transition: all 0.5s cubic-bezier(0.23, 1, 0.32, 1); + border: 1px solid rgba(10, 132, 255, 0.3); + box-shadow: + 0 0 15px -5px rgba(10, 132, 255, 0.5), + 0 20px 50px -10px rgba(0, 0, 0, 0.5); +} + +.phone-frame:hover { + transform: translateY(-10px); + border-color: rgba(10, 132, 255, 0.8); + box-shadow: + 0 0 25px -5px rgba(10, 132, 255, 0.6), + 0 30px 60px -10px rgba(0, 0, 0, 0.6); +} + +.phone-frame img { + width: 100%; + height: auto; + display: block; + /* Removed border-radius */ +} + +/* Features Section */ +.features { + padding: 6rem 0; + background: var(--surface); +} + +.section-title { + font-family: var(--font-display); + font-size: 2.5rem; + text-align: center; + margin-bottom: 4rem; +} + +.features-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + gap: 2rem; +} + +.feature-card { + background: rgba(255, 255, 255, 0.03); + padding: 2rem; + border-radius: 1rem; + border: 1px solid var(--border); + transition: all 0.3s ease; +} + +.feature-card:hover { + transform: translateY(-5px); + background: rgba(255, 255, 255, 0.05); + border-color: var(--primary); +} + +.feature-icon { + font-size: 2.5rem; + margin-bottom: 1.5rem; +} + +.feature-card h3 { + font-size: 1.25rem; + margin-bottom: 1rem; + font-family: var(--font-display); +} + +.feature-card p { + color: var(--text-secondary); + font-size: 0.95rem; +} + +/* Screenshots Section */ +.screenshots { + padding: 6rem 0; +} + +.screenshot-gallery { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 2rem; +} + +.screenshot-item { + text-align: center; +} + +.screenshot-item img { + width: 100%; + border-radius: 1rem; + box-shadow: var(--shadow); + margin-bottom: 1rem; + transition: transform 0.3s ease; + border: 1px solid var(--border); +} + +.screenshot-item:hover img { + transform: translateY(-10px); +} + +.screenshot-item p { + color: var(--text-secondary); + font-weight: 500; +} + +/* Footer */ +.footer { + background: var(--surface); + padding: 4rem 0 2rem; + border-top: 1px solid var(--border); +} + +.footer-content { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 4rem; + margin-bottom: 3rem; +} + +.footer-logo h3 { + font-family: var(--font-display); + font-size: 1.5rem; + margin-bottom: 1rem; + background: var(--gradient); + background-clip: text; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; +} + +.footer-logo p { + color: var(--text-secondary); + max-width: 300px; +} + +.footer-links { + display: flex; + flex-direction: column; + gap: 1rem; +} + +.footer-links h4 { + color: var(--text); + margin-bottom: 0.5rem; +} + +.footer-links a { + color: var(--text-secondary); + text-decoration: none; + transition: color 0.3s ease; +} + +.footer-links a:hover { + color: var(--primary); +} + +.footer-legal { + grid-column: 1 / -1; + border-top: 1px solid var(--border); + padding-top: 2rem; + display: flex; + justify-content: space-between; + color: var(--text-secondary); + font-size: 0.9rem; +} + +/* Responsive Design */ +@media (max-width: 968px) { + .hero { + padding: 8rem 0 4rem; + } + + .hero .container { + grid-template-columns: 1fr; + text-align: center; + gap: 3rem; + } + + .hero-content { + order: 1; + } + + .hero-image { + order: 2; + margin-top: 0; + } + + .hero-title { + font-size: 3rem; + } + + .hero-subtitle { + margin: 0 auto 2rem; + } + + .download-badges { + justify-content: center; + flex-wrap: wrap; + } + + .hero-stats { + justify-content: center; + flex-wrap: wrap; + gap: 2rem; + } + + .phone-frame { + width: 100%; + max-width: 300px; + } +} + +@media (max-width: 480px) { + .hero-title { + font-size: 2.5rem; + } + + .store-badge img { + height: 48px; + } +} + +@media (max-width: 768px) { + .nav-links { + display: none; + /* Simple mobile menu handling for now */ + } + + .footer-content { + grid-template-columns: 1fr; + gap: 2rem; + } + + .footer-legal { + flex-direction: column; + gap: 1rem; + text-align: center; + } +} \ No newline at end of file