fix(ui): mobile layout overflows across admin, reader, and book pages
Every fix is a minimal class-level change; desktop layouts are untouched because each fixed row already fit on one line there. - progress: CFI string is one unbreakable token — it inflated the page layout width to 398-501px (whole-page zoom-out on phones) and rendered debug-looking text edge to edge. min-w-0 + break-all lets it wrap. - audio reader: settings strip min-content (~470px) exceeded the 390px viewport, clipping Skip/Sleep and — because the page ICB inflated — pushing the chapters drawer (w-full of the viewport) past the screen edge so its timestamps clipped too. flex-wrap fixes both symptoms. - book detail edit-metadata modal footer: Rescan/Reset/Write-to-file row overflowed the modal; clipped buttons were unreachable. flex-wrap. - bookshelf save-filter modal footer: three buttons overflowed both screen edges. flex-wrap. - admin libraries: rows overprinted (badge chips shrink-0 vs actions shrink-0, no wrap) — 'manga⟳Scan▸1 folders⌄Manage'. flex-wrap stacks actions under the name on phones. - admin podcasts: maintenance table forced a 892px page (44% zoom). overflow-x-auto on the card scrolls the table inside it instead. - stats: 4-pill range row forced 421px; page permanently ~7% zoomed out. flex-wrap. - podcast feed detail: label+select pairs now travel in their own flex pairs so flex-wrap can never split 'Order' from its select; ids and htmx change-triggers preserved. - cosmetics: conflicts stat labels tracking-wide→normal (UNRESOLVED clipped), archived file path line-clamp-3, rules-page Smart badge wraps cleanly, dashboard View All shrink-0 nowrap (was wrapping to two lines).
This commit is contained in:
1 parent
e3ca3426ba
commit
88adf30aa9
24 files changed
+109
-103
No files matched your search
@@ -120,21 +120,25 @@ templ PodcastDetailPage(user User, data PodcastDetailData) {
|
||||
<span>Refresh</span>
|
||||
</button>
|
||||
|
||||
<!-- Order override (D5): per-user; refreshes the episode list -->
|
||||
<label class="text-sm ml-2" for="podcast-order-select" style="color: var(--text-secondary)">Order</label>
|
||||
<select
|
||||
id="podcast-order-select"
|
||||
name="order"
|
||||
class="input w-auto py-1.5 cursor-pointer"
|
||||
hx-put={ "/api/podcasts/feeds/" + data.Feed.ID + "/order" }
|
||||
hx-target="#episode-list"
|
||||
hx-swap="outerHTML"
|
||||
>
|
||||
<option value="native" selected?={ data.Order == "native" }>Feed order</option>
|
||||
<option value="reverse" selected?={ data.Order == "reverse" }>Feed order (reversed)</option>
|
||||
<option value="oldest" selected?={ data.Order == "oldest" }>Oldest first (pub date)</option>
|
||||
<option value="newest" selected?={ data.Order == "newest" }>Newest first (pub date)</option>
|
||||
</select>
|
||||
<!-- Order override (D5): per-user; refreshes the episode list.
|
||||
Label+select travel as a pair so flex-wrap can never
|
||||
separate them on narrow screens. -->
|
||||
<div class="flex items-center gap-1.5">
|
||||
<label class="text-sm" for="podcast-order-select" style="color: var(--text-secondary)">Order</label>
|
||||
<select
|
||||
id="podcast-order-select"
|
||||
name="order"
|
||||
class="input w-auto py-1.5 cursor-pointer"
|
||||
hx-put={ "/api/podcasts/feeds/" + data.Feed.ID + "/order" }
|
||||
hx-target="#episode-list"
|
||||
hx-swap="outerHTML"
|
||||
>
|
||||
<option value="native" selected?={ data.Order == "native" }>Feed order</option>
|
||||
<option value="reverse" selected?={ data.Order == "reverse" }>Feed order (reversed)</option>
|
||||
<option value="oldest" selected?={ data.Order == "oldest" }>Oldest first (pub date)</option>
|
||||
<option value="newest" selected?={ data.Order == "newest" }>Newest first (pub date)</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Serial suggestion (D5, §0 item 3 rename): one-click,
|
||||
never silent. "reverse" sorts by FEED POSITION DESC —
|
||||
@@ -152,21 +156,23 @@ templ PodcastDetailPage(user User, data PodcastDetailData) {
|
||||
</button>
|
||||
}
|
||||
|
||||
<!-- Auto-download policy (D6) -->
|
||||
<label class="text-sm ml-2" for="podcast-autodl-select" style="color: var(--text-secondary)">Auto-download</label>
|
||||
@PodcastAutoDownloadSelect(data.Feed)
|
||||
if data.Feed.AutoDownload == "newest" {
|
||||
<input
|
||||
type="number"
|
||||
id="podcast-autodl-count"
|
||||
name="auto_download_count"
|
||||
min="1"
|
||||
max="100"
|
||||
value={ podcastInt(data.Feed.AutoDownloadCount) }
|
||||
class="input w-20 py-1.5"
|
||||
aria-label="Newest N episodes"
|
||||
/>
|
||||
}
|
||||
<!-- Auto-download policy (D6) — label+controls as one pair -->
|
||||
<div class="flex items-center gap-1.5">
|
||||
<label class="text-sm" for="podcast-autodl-select" style="color: var(--text-secondary)">Auto-download</label>
|
||||
@PodcastAutoDownloadSelect(data.Feed)
|
||||
if data.Feed.AutoDownload == "newest" {
|
||||
<input
|
||||
type="number"
|
||||
id="podcast-autodl-count"
|
||||
name="auto_download_count"
|
||||
min="1"
|
||||
max="100"
|
||||
value={ podcastInt(data.Feed.AutoDownloadCount) }
|
||||
class="input w-20 py-1.5"
|
||||
aria-label="Newest N episodes"
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
|
||||
<!-- Unsubscribe (issues #4/#7): the sole subscriber who is
|
||||
an admin gets the three-choice modal (unsubscribe /
|
||||
|
||||
Reference in new issue
Block a user