feat(web): updated tab, inline errors, card craft
Adds the Updated tab (series with an unread chapter) with its own empty state, so "what's new" no longer means scanning the whole list. A failed favourite/chapter/delete previously looked like an ignored tap: htmx does not swap on a non-2xx response and nothing else said so. Errors now surface inline on the card. The two per-card panels (chapter edit, delete confirm) also close each other. Mobile fixes, all measured at 360/390px rather than eyeballed: .chapter-form and .confirm-row now wrap so the hint and the prompt take their own row, and the number input shrinks (flex-basis 0) instead of holding its ~20ch intrinsic width. Before this the Save button sat off screen — 97px of page overflow at 390px, 127px at 360px — which broke correcting a chapter number on the one device class this is built for. Emoji glyphs replaced with inline SVG on currentColor: each rendered in a different face at a different weight and colour, ignoring the card's own type and colour system. No icon font or library. Series with no scraped og:image render a title-initial monogram instead of a blank rectangle that read as a cover still loading. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+42
-10
@@ -1,37 +1,69 @@
|
||||
{{define "card"}}
|
||||
<article class="card" id="card-{{.Key}}" data-title="{{.Title}}">
|
||||
<a class="cover" href="{{.ContinueURL}}" target="_blank" rel="noopener noreferrer">
|
||||
{{if .Cover}}<img src="{{.Cover}}" alt="" loading="lazy">{{end}}
|
||||
{{if .Cover}}<img src="{{.Cover}}" alt="" loading="lazy">
|
||||
{{else}}<span class="cover-blank" aria-hidden="true">{{.Initial}}</span>{{end}}
|
||||
{{if .HasNewChapter}}<span class="new-ribbon">New</span>{{end}}
|
||||
</a>
|
||||
<div class="body">
|
||||
<h3 class="title">{{.Title}}</h3>
|
||||
<p class="meta">
|
||||
<span class="site site-{{.Site}}">{{.Site}}</span>
|
||||
<span class="chapter">Ch {{.LastChapter}}</span>
|
||||
{{if .HasNewChapter}}<span class="new">NEW {{.LatestChapter}}</span>{{end}}
|
||||
{{if .HasNewChapter}}<span class="new-chapter">Ch {{.LatestChapter}} out</span>{{end}}
|
||||
</p>
|
||||
<div class="actions">
|
||||
<a class="primary" href="{{.ContinueURL}}" target="_blank" rel="noopener noreferrer">Continue</a>
|
||||
<a class="primary" href="{{.ContinueURL}}" target="_blank" rel="noopener noreferrer"
|
||||
title="Continue reading" aria-label="Continue reading">
|
||||
<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" focusable="false">
|
||||
<path d="M8 4.8a1 1 0 0 1 1.52-.85l10 7.2a1 1 0 0 1 0 1.7l-10 7.2A1 1 0 0 1 8 19.2z"/>
|
||||
</svg>
|
||||
</a>
|
||||
<button class="icon {{if .Favorite}}on{{end}}"
|
||||
title="Favourite" aria-label="Toggle favourite"
|
||||
hx-post="/ui/bookmarks/{{.Key}}/favorite"
|
||||
hx-target="[id='card-{{.Key}}']" hx-swap="outerHTML">
|
||||
{{if .Favorite}}★{{else}}☆{{end}}
|
||||
hx-target="[id='card-{{.Key}}']" hx-swap="outerHTML"
|
||||
hx-indicator="[id='card-{{.Key}}']" hx-disabled-elt="this">
|
||||
<svg viewBox="0 0 24 24" fill="{{if .Favorite}}currentColor{{else}}none{{end}}"
|
||||
stroke="currentColor" stroke-width="1.7" stroke-linejoin="round"
|
||||
aria-hidden="true" focusable="false">
|
||||
<path d="M12 3.7l2.63 5.33 5.87.86-4.25 4.14 1 5.85L12 17.31l-5.25 2.57 1-5.85L3.5 9.89l5.87-.86z"/>
|
||||
</svg>
|
||||
</button>
|
||||
<button class="icon" title="Set chapter" aria-label="Set chapter"
|
||||
onclick="toggleChapterForm('{{.Key}}')">✎</button>
|
||||
onclick="toggleChapterForm('{{.Key}}')">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7"
|
||||
stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false">
|
||||
<path d="M4 20h4L19.3 8.7a1.5 1.5 0 0 0 0-2.12l-1.88-1.88a1.5 1.5 0 0 0-2.12 0L4 16z"/>
|
||||
<path d="M13.8 7.2l3 3"/>
|
||||
</svg>
|
||||
</button>
|
||||
<button class="icon danger" title="Remove" aria-label="Remove"
|
||||
hx-delete="/ui/bookmarks/{{.Key}}"
|
||||
hx-target="[id='card-{{.Key}}']" hx-swap="outerHTML"
|
||||
hx-confirm="Remove {{.Title}} from the list?">🗑</button>
|
||||
onclick="toggleConfirmRow('{{.Key}}')">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7"
|
||||
stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false">
|
||||
<path d="M4.5 6.8h15M9.8 6.8V4.6h4.4v2.2M7.3 6.8l.9 12.6h7.6l.9-12.6"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<form class="chapter-form" id="chapter-form-{{.Key}}" hidden
|
||||
hx-post="/ui/bookmarks/{{.Key}}/chapter"
|
||||
hx-target="[id='card-{{.Key}}']" hx-swap="outerHTML">
|
||||
hx-target="[id='card-{{.Key}}']" hx-swap="outerHTML"
|
||||
hx-indicator="[id='card-{{.Key}}']" hx-disabled-elt="input, button">
|
||||
{{if .LatestChapter}}<p class="hint">Latest known: Ch {{.LatestChapter}}</p>{{end}}
|
||||
<input name="chapter" type="number" step="0.1" min="0"
|
||||
value="{{.LastChapterNum}}" aria-label="Chapter number" required>
|
||||
<button type="submit">Save</button>
|
||||
</form>
|
||||
<div class="confirm-row" id="confirm-row-{{.Key}}" hidden>
|
||||
<span>Remove this?</span>
|
||||
<button class="danger-solid"
|
||||
hx-delete="/ui/bookmarks/{{.Key}}"
|
||||
hx-target="[id='card-{{.Key}}']" hx-swap="outerHTML"
|
||||
hx-indicator="[id='card-{{.Key}}']" hx-disabled-elt="this">Remove</button>
|
||||
<button type="button" onclick="toggleConfirmRow('{{.Key}}')">Cancel</button>
|
||||
</div>
|
||||
<p class="error-inline" hidden></p>
|
||||
</div>
|
||||
</article>
|
||||
{{end}}
|
||||
|
||||
Reference in New Issue
Block a user