Skip to content

Commit 042f8a9

Browse files
style: fix mobile ui for navbar header (#53)
1 parent d44b5aa commit 042f8a9

File tree

3 files changed

+112
-19
lines changed

3 files changed

+112
-19
lines changed

app/static/index.html

Lines changed: 54 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
<head>
55
<meta charset="UTF-8">
6-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
77
<title>Watchly - Personalized Stremio Recommendations</title>
88
<meta name="description"
99
content="Stremio catalog addon that provides personalized movie and series recommendations based on your library.">
@@ -21,7 +21,6 @@
2121
900: '#0f172a',
2222
950: '#020617'
2323
},
24-
},
2524
stremio: {
2625
DEFAULT: '#3b2667',
2726
hover: '#4e3286',
@@ -74,11 +73,15 @@
7473
}
7574

7675
.nav-item.active {
77-
@apply bg-white/10 text-white border-l-2 border-white;
76+
background-color: rgba(255, 255, 255, 0.1);
77+
color: #ffffff;
78+
border-left: 2px solid #ffffff;
7879
}
7980

8081
.nav-item.disabled {
81-
@apply opacity-50 cursor-not-allowed pointer-events-none;
82+
opacity: 0.5;
83+
cursor: not-allowed;
84+
pointer-events: none;
8285
}
8386

8487
/* Announcement link styling to ensure visibility (neutral theme) */
@@ -91,27 +94,63 @@
9194
color: #ffffff;
9295
text-decoration: underline;
9396
}
97+
98+
/* Improve mobile scrolling behavior */
99+
main {
100+
-webkit-overflow-scrolling: touch;
101+
overscroll-behavior: contain;
102+
}
103+
104+
/* Animated hamburger icon */
105+
.hamburger {
106+
position: relative;
107+
width: 40px;
108+
height: 40px;
109+
}
110+
.hamburger .bar {
111+
position: absolute;
112+
left: 9px;
113+
right: 9px;
114+
height: 2px;
115+
background: #e5e7eb; /* slate-200 */
116+
border-radius: 2px;
117+
transform-origin: center;
118+
transition: transform 200ms ease, opacity 180ms ease;
119+
}
120+
.hamburger .bar.top { top: 12px; }
121+
.hamburger .bar.middle { top: 19px; }
122+
.hamburger .bar.bottom { top: 26px; }
123+
124+
.hamburger.is-active .bar.top { transform: translateY(7px) rotate(45deg); }
125+
.hamburger.is-active .bar.middle { opacity: 0; }
126+
.hamburger.is-active .bar.bottom { transform: translateY(-7px) rotate(-45deg); }
127+
128+
@media (prefers-reduced-motion: reduce) {
129+
.hamburger .bar { transition: none; }
130+
}
94131
</style>
95132
</head>
96133

97-
<body class="min-h-screen text-slate-200 font-sans antialiased overflow-hidden flex flex-col md:flex-row">
134+
<body class="min-h-screen text-slate-200 font-sans antialiased overflow-x-hidden flex flex-col md:flex-row">
98135

99136
<!-- Mobile Header -->
100-
<div class="md:hidden p-4 bg-neutral-950 border-b border-slate-800 flex items-center gap-3">
101-
<button id="mobileNavToggle" aria-label="Open navigation" class="p-2 rounded-md hover:bg-slate-800/50">
102-
<svg class="w-6 h-6 text-slate-200" fill="none" stroke="currentColor" viewBox="0 0 24 24">
103-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path>
104-
</svg>
137+
<div id="mobileHeader"
138+
class="md:hidden fixed top-0 left-0 right-0 z-50 px-4 py-3 bg-neutral-950 border-b border-slate-800 flex items-center gap-4">
139+
<button id="mobileNavToggle" aria-label="Open navigation" aria-expanded="false"
140+
class="hamburger p-2 rounded-md hover:bg-slate-800/50">
141+
<span class="bar top"></span>
142+
<span class="bar middle"></span>
143+
<span class="bar bottom"></span>
105144
</button>
106145
<img src="/app/static/logo.png" alt="Watchly" class="w-8 h-8 rounded-lg ring-1 ring-white/15 bg-black">
107-
<h1 class="font-bold text-lg text-white">Watchly</h1>
146+
<h1 class="font-bold text-xl text-white">Watchly</h1>
108147

109148
</div>
110149
<!-- Mobile Nav Backdrop -->
111150
<div id="mobileNavBackdrop" class="fixed inset-0 bg-black/50 z-30 hidden md:hidden"></div>
112151
<!-- Sidebar Navigation -->
113152
<aside id="mainSidebar"
114-
class="fixed inset-y-0 left-0 z-40 w-72 transform -translate-x-full transition-transform duration-300 ease-out bg-neutral-950/50 backdrop-blur-xl border-b md:border-b-0 md:border-r border-slate-800 flex flex-col flex-shrink-0 md:relative md:translate-x-0 md:flex">
153+
class="fixed inset-y-0 left-0 z-40 w-72 transform -translate-x-full transition-transform duration-300 ease-out bg-neutral-950/50 backdrop-blur-xl border-b md:border-b-0 md:border-r border-slate-800 flex flex-col flex-shrink-0 md:relative md:translate-x-0 md:flex pt-[env(safe-area-inset-top)] pb-[env(safe-area-inset-bottom)]">
115154
<div class="p-6 md:p-8 flex-col items-start gap-4 hidden md:flex">
116155
<div class="flex items-center gap-3">
117156
<img src="/app/static/logo.png" alt="Watchly"
@@ -235,8 +274,8 @@ <h1 class="font-bold text-2xl text-transparent bg-clip-text bg-gradient-to-r fro
235274
</aside>
236275

237276
<!-- Main Content Area -->
238-
<main class="flex-grow h-screen overflow-y-auto relative no-scrollbar">
239-
<div class="max-w-2xl mx-auto px-4 py-8 md:py-16 pb-32">
277+
<main class="flex-grow min-h-[100dvh] overflow-y-auto relative no-scrollbar">
278+
<div class="max-w-2xl mx-auto px-4 py-8 md:py-16 pb-[calc(env(safe-area-inset-bottom)+8rem)]">
240279

241280
<form id="configForm">
242281
<!-- SECTION 0: WELCOME -->
@@ -414,7 +453,7 @@ <h3 class="text-base font-bold text-white mb-1">Based on Your Loves</h3>
414453
<path
415454
d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387 0.6.111 0.82-.25 0.82-.557 0-.276-0.01-1.003-0.015-1.972-3.337 0.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-0.744 0.084-0.73 0.084-0.73 1.205 0.084 1.838 1.237 1.838 1.237 1.07 1.835 2.809 1.305 3.492 0.998 0.108-0.776 0.419-1.305 0.762-1.604-2.665-0.3-5.466-1.332-5.466-5.93 0-1.31 0.465-2.38 1.235-3.221-0.135-0.303-0.54-1.523 0.116-3.176 0 0 1.008-0.322 3.301 1.23A11.026 11.026 0 0112 5.86c1.02 0 2.047 0.137 3.013 0.403 2.291-1.552 3.297-1.23 3.297-1.23 0.656 1.653 0.24 2.873 0.116 3.176 0.77 0.84 1.235 1.91 1.235 3.221 0 4.61-2.805 5.621-5.476 5.922 0.43 0.37 0.81 1.096 0.81 2.22 0 1.606-0.015 2.899-0.015 3.286 0 0.308 0.219 0.678 0.825 0.557C20.565 21.79 24 17.29 24 12c0-6.627-5.372-12-12-12z" />
416455
</svg>
417-
<span class="font-medium">View Source Code</span>
456+
<span class="font-medium">Source Code</span>
418457
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
419458
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
420459
d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" />

app/static/script.js

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,31 @@ const sections = {
4949
success: document.getElementById('sect-success')
5050
};
5151

52+
// Main scroll container
53+
const mainEl = document.querySelector('main');
54+
const sidebarEl = document.getElementById('mainSidebar');
55+
const headerEl = document.getElementById('mobileHeader');
56+
57+
function updateMobileLayout() {
58+
try {
59+
const isMobile = window.matchMedia('(max-width: 767.98px)').matches;
60+
const header = headerEl;
61+
if (!header || !mainEl || !sidebarEl) return;
62+
const h = header.offsetHeight || 0;
63+
document.documentElement.style.setProperty('--mobile-header', `${h}px`);
64+
65+
if (isMobile) {
66+
mainEl.style.paddingTop = `${h}px`;
67+
sidebarEl.style.top = `${h}px`;
68+
sidebarEl.style.height = `calc(100dvh - ${h}px)`;
69+
} else {
70+
mainEl.style.paddingTop = '';
71+
sidebarEl.style.top = '';
72+
sidebarEl.style.height = '';
73+
}
74+
} catch (e) { /* noop */ }
75+
}
76+
5277
// Welcome Elements
5378
const btnGetStarted = document.getElementById('btn-get-started');
5479

@@ -73,6 +98,11 @@ document.addEventListener('DOMContentLoaded', () => {
7398
initializeKofi();
7499
initializeAnnouncement();
75100

101+
// Layout adjustments for fixed mobile header
102+
updateMobileLayout();
103+
window.addEventListener('resize', updateMobileLayout);
104+
window.addEventListener('orientationchange', updateMobileLayout);
105+
76106
// Next Buttons
77107
if (configNextBtn) configNextBtn.addEventListener('click', () => switchSection('catalogs'));
78108
if (catalogsNextBtn) catalogsNextBtn.addEventListener('click', () => switchSection('install'));
@@ -137,15 +167,28 @@ function initializeMobileNav() {
137167
sidebar.classList.add('translate-x-0');
138168
backdrop.classList.remove('hidden');
139169
document.body.classList.add('overflow-hidden');
170+
// Animate hamburger to X
171+
mobileToggle.classList.add('is-active');
172+
mobileToggle.setAttribute('aria-expanded', 'true');
173+
mobileToggle.setAttribute('aria-label', 'Close navigation');
140174
};
141175
const closeNav = () => {
142176
sidebar.classList.remove('translate-x-0');
143177
sidebar.classList.add('-translate-x-full');
144178
backdrop.classList.add('hidden');
145179
document.body.classList.remove('overflow-hidden');
180+
// Reset hamburger
181+
mobileToggle.classList.remove('is-active');
182+
mobileToggle.setAttribute('aria-expanded', 'false');
183+
mobileToggle.setAttribute('aria-label', 'Open navigation');
146184
};
147185

148-
mobileToggle.addEventListener('click', (e) => { e.preventDefault(); openNav(); });
186+
mobileToggle.addEventListener('click', (e) => {
187+
e.preventDefault();
188+
// Toggle open/close for convenience
189+
const isOpen = sidebar.classList.contains('translate-x-0');
190+
if (isOpen) closeNav(); else openNav();
191+
});
149192
backdrop.addEventListener('click', closeNav);
150193

151194
// Auto-close when a nav item is selected (mobile)
@@ -176,6 +219,16 @@ function switchSection(sectionKey) {
176219
if (navItems[sectionKey]) {
177220
navItems[sectionKey].classList.add('active');
178221
}
222+
223+
// Ensure new section starts at top in the scroll container
224+
try {
225+
if (mainEl) {
226+
// Using scrollTo with behavior auto to avoid jank on iOS toolbars
227+
mainEl.scrollTo({ top: 0, behavior: 'auto' });
228+
} else {
229+
window.scrollTo({ top: 0, behavior: 'auto' });
230+
}
231+
} catch (e) { /* noop */ }
179232
}
180233

181234

scripts/generate_release_notes.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -213,8 +213,9 @@ def generate_release_notes(commits, last_release_tag):
213213
" Features, Bug Fixes, Improvements, etc. with markdown formatting. Include PR requests with (#123) at"
214214
" the end of each change. Include refactor commits only if they contain meaningful architectural"
215215
" changes. Exclude trivial changes like formatting, linting, merge commits, or dependency updates"
216-
" unless they're significant. Be concise but informative. Format with proper markdown. Do not include"
217-
" commit hashes. Do not output anything other than the release notes. Keep it to a reasonable length"
216+
" unless they're significant. Format with proper markdown. Do not include commit hashes. When"
217+
" generating release notes, do not just write commit messages. Describe them. Try to make them like"
218+
" release change.Do not output anything other than the release notes. Keep it to a reasonable length"
218219
" that helps developers and engineers understand the changes. This is directly attached to GitHub"
219220
" release notes, so please do not include anything other than required.\n\nAdditionally, suggest a"
220221
" unique version name inspired by something beautiful and unique to Nepal (such as a place, temple,"
@@ -224,7 +225,7 @@ def generate_release_notes(commits, last_release_tag):
224225
)
225226
try:
226227
response = oai_client.responses.parse(
227-
model="gpt-5-nano",
228+
model="gpt-5-mini",
228229
input=[
229230
{"role": "system", "content": prompt},
230231
{"role": "user", "content": f"## Last release tag: {last_release_tag}\n ## Commits: {commits}"},

0 commit comments

Comments
 (0)