Tap the dock to expand; open فهرست for the current surah list with ±15s skips and remaining time, without interrupting the single audio session. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -368,3 +368,178 @@ button, input { font: inherit; }
|
||||
}
|
||||
.resume-label { font-weight: 700; color: var(--brand-deep); }
|
||||
.resume-meta { color: var(--muted); font-size: 0.9rem; }
|
||||
|
||||
/* Expanded player sheet */
|
||||
.player-expanded-host {
|
||||
position: fixed; inset: 0; z-index: 60;
|
||||
background: #f7f5f0;
|
||||
}
|
||||
.player-sheet {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
max-width: none;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
background: #f7f5f0;
|
||||
padding: calc(0.65rem + env(safe-area-inset-top)) 1.15rem calc(1.1rem + env(safe-area-inset-bottom));
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
direction: rtl;
|
||||
text-align: right;
|
||||
overflow: auto;
|
||||
}
|
||||
.sheet-header {
|
||||
display: grid;
|
||||
grid-template-columns: 44px 1fr 44px;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 0.85rem;
|
||||
}
|
||||
.sheet-header-title {
|
||||
text-align: center;
|
||||
font-weight: 700;
|
||||
font-size: 1rem;
|
||||
color: var(--ink);
|
||||
}
|
||||
.sheet-icon-btn {
|
||||
width: 44px; height: 44px; border-radius: 12px;
|
||||
border: 1px solid var(--line); background: #fff;
|
||||
cursor: pointer; font-size: 1.35rem; color: var(--brand-deep);
|
||||
display: grid; place-items: center;
|
||||
}
|
||||
.player-hero {
|
||||
width: min(100%, 360px);
|
||||
aspect-ratio: 1;
|
||||
margin: 0.25rem auto 1rem;
|
||||
border-radius: 22px;
|
||||
background:
|
||||
linear-gradient(145deg, rgba(19, 70, 50, 0.92), rgba(31, 107, 74, 0.78)),
|
||||
url('/background.png') center/cover;
|
||||
box-shadow: 0 18px 40px rgba(28, 43, 36, 0.18);
|
||||
display: grid;
|
||||
place-items: center;
|
||||
color: #fff;
|
||||
}
|
||||
.player-hero-inner {
|
||||
display: grid; gap: 0.45rem; text-align: center; padding: 1.25rem;
|
||||
}
|
||||
.player-hero-kicker {
|
||||
font-size: 0.9rem; opacity: 0.85;
|
||||
}
|
||||
.player-hero-inner strong {
|
||||
font-size: 1.45rem; font-weight: 800; line-height: 1.5;
|
||||
}
|
||||
.sheet-track-meta {
|
||||
text-align: center;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.sheet-track-name {
|
||||
font-size: 1.15rem; font-weight: 700; color: var(--ink);
|
||||
}
|
||||
.sheet-track-index {
|
||||
margin-top: 0.25rem; color: var(--muted); font-size: 0.95rem;
|
||||
}
|
||||
.sheet-actions {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
.sheet-action {
|
||||
border: 0; background: transparent; cursor: pointer;
|
||||
display: grid; gap: 0.3rem; justify-items: center;
|
||||
color: var(--ink); font-size: 0.85rem; padding: 0.35rem;
|
||||
}
|
||||
.sheet-action.on { color: var(--brand); font-weight: 700; }
|
||||
.sheet-action-icon {
|
||||
width: 44px; height: 44px; border-radius: 12px;
|
||||
border: 1px solid rgba(28, 43, 36, 0.18); background: #fff;
|
||||
display: grid; place-items: center; font-size: 1.25rem;
|
||||
}
|
||||
.sheet-action.on .sheet-action-icon {
|
||||
background: var(--brand); color: #fff; border-color: var(--brand);
|
||||
}
|
||||
.sheet-seek-block { margin-top: auto; padding-top: 0.5rem; }
|
||||
.sheet-seek { width: 100%; accent-color: var(--brand); }
|
||||
.sheet-times {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto 1fr;
|
||||
gap: 0.5rem;
|
||||
margin-top: 0.45rem;
|
||||
font-variant-numeric: tabular-nums;
|
||||
font-size: 0.85rem;
|
||||
color: var(--muted);
|
||||
}
|
||||
.sheet-times > span:first-child { text-align: left; }
|
||||
.sheet-times > span:last-child { text-align: right; }
|
||||
.sheet-remain { text-align: center; color: var(--brand-deep); }
|
||||
.player-main-controls {
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
gap: 1.4rem; margin: 1.35rem 0 0.4rem;
|
||||
}
|
||||
.play-btn-lg {
|
||||
width: 72px; height: 72px; font-size: 1.35rem;
|
||||
background: #1c2b24;
|
||||
}
|
||||
.skip-btn {
|
||||
width: 52px; height: 52px; border-radius: 50%;
|
||||
border: 1px solid rgba(28, 43, 36, 0.22); background: #fff;
|
||||
cursor: pointer; font-weight: 700; color: var(--brand-deep);
|
||||
}
|
||||
|
||||
/* TOC sheet */
|
||||
.toc-sheet {
|
||||
position: fixed; inset: 0; z-index: 70;
|
||||
background: #fff;
|
||||
padding: calc(0.65rem + env(safe-area-inset-top)) 0 calc(0.5rem + env(safe-area-inset-bottom));
|
||||
display: flex; flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
.toc-header {
|
||||
display: flex; align-items: center; gap: 0.65rem;
|
||||
padding: 0 1rem 0.85rem;
|
||||
border-bottom: 1px solid var(--line);
|
||||
}
|
||||
.toc-header-text {
|
||||
display: grid; gap: 0.15rem;
|
||||
}
|
||||
.toc-header-text strong { font-size: 1.15rem; color: var(--ink); }
|
||||
.toc-skeleton { padding: 1rem; }
|
||||
.toc-list {
|
||||
list-style: none; margin: 0; padding: 0;
|
||||
overflow: auto; flex: 1;
|
||||
}
|
||||
.toc-row {
|
||||
border-bottom: 1px solid rgba(28, 43, 36, 0.08);
|
||||
}
|
||||
.toc-row.active { background: rgba(28, 43, 36, 0.06); }
|
||||
.toc-row-btn {
|
||||
width: 100%; border: 0; background: transparent; cursor: pointer;
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr auto;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
padding: 0.95rem 1rem;
|
||||
text-align: right;
|
||||
color: inherit;
|
||||
}
|
||||
.toc-play-dot {
|
||||
width: 36px; height: 36px; border-radius: 50%;
|
||||
background: var(--brand); color: #fff;
|
||||
display: grid; place-items: center; font-size: 0.85rem;
|
||||
}
|
||||
.toc-row:not(.active) .toc-row-btn { grid-template-columns: 1fr auto; }
|
||||
.toc-row-main { display: grid; gap: 0.2rem; min-width: 0; }
|
||||
.toc-row-title { font-weight: 700; color: var(--ink); }
|
||||
.toc-row-preview {
|
||||
color: var(--muted); font-size: 0.88rem;
|
||||
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
||||
}
|
||||
.toc-row-flags {
|
||||
display: flex; gap: 0.4rem; font-size: 0.85rem; color: var(--brand-deep);
|
||||
}
|
||||
.toc-row-num {
|
||||
font-weight: 700; color: var(--muted); min-width: 1.5rem; text-align: center;
|
||||
}
|
||||
|
||||
@@ -59,5 +59,24 @@ window.tqAudio = {
|
||||
seek: (audio, value) => {
|
||||
if (!audio) return;
|
||||
audio.currentTime = Number(value) || 0;
|
||||
},
|
||||
|
||||
seekBy: (audio, deltaSeconds) => {
|
||||
if (!audio) return 0;
|
||||
const dur = Number.isFinite(audio.duration) ? audio.duration : 0;
|
||||
let next = (audio.currentTime || 0) + (Number(deltaSeconds) || 0);
|
||||
if (next < 0) next = 0;
|
||||
if (dur > 0 && next > dur) next = dur;
|
||||
audio.currentTime = next;
|
||||
return next;
|
||||
},
|
||||
|
||||
getState: (audio) => {
|
||||
if (!audio) return { time: 0, duration: 0, playing: false };
|
||||
return {
|
||||
time: audio.currentTime || 0,
|
||||
duration: audio.duration || 0,
|
||||
playing: !audio.paused
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user