openvibers/openvibe.exchange Commits
Characters: a tidy search row and a Make or import button (#88)
Changes
@@@ -267,6 +267,7 @@ body.ex-app .ovf { display: none; }/* ── Companions: the gallery, a character page, the card form ───────────── */.ex-companions-head { padding: 30px 0 14px; }.ex-companions-head h1 { margin: 0 0 8px; font-size: clamp(1.7rem, 5vw, 2.4rem); letter-spacing: -.02em; }++.ex-head-actions { margin: 12px 0 0; }.ex-lede { margin: 0; max-width: 62ch; color: var(--ovk-muted); font-size: .95rem; line-height: 1.55; }.ex-gallery-bar { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; justify-content: space-between; margin: 18px 0 14px; }.ex-search { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; flex: 1 1 320px; }@@@ -67,19 +67,21 @@ function galleryPage({ items = [], total = 0, q = '', tags = [], sort = 'new', t+ '</form>';const searchForm = '<form class="ex-search" method="get" action="/companions">'+ '<input type="hidden" name="sort" value="' + esc(sort) + '">'-- + '<label class="ovt-sr" for="ex-q">Search characters</label>'++ + '<label class="ovk-sr" for="ex-q">Search characters</label>'+ '<input id="ex-q" type="search" name="q" value="' + esc(q) + '" placeholder="Search characters" maxlength="80">'-- + '<label class="ovt-sr" for="ex-tags">Tags</label>'++ + '<label class="ovk-sr" for="ex-tags">Tags</label>'+ '<input id="ex-tags" type="text" name="tags" value="' + esc(tags.join(',')) + '" placeholder="Tags, comma separated" maxlength="120">'+ '<button type="submit" class="ex-btn">Search</button>'+ '</form>';const tagChips = tagList.length? '<ul class="ex-taglist">' + tagList.map((t) => '<li><a class="ex-chip' + (tags.includes(t.tag) ? ' is-on' : '') + '" href="/companions?tags=' + encodeURIComponent(t.tag) + '&sort=' + esc(sort) + '">' + esc(t.tag) + ' <span class="ex-chip-n">' + t.n + '</span></a></li>').join('') + '</ul>': '';-- const empty = items.length ? '' : '<div class="ex-empty">' + kit.empty({ icon: 'person', title: 'No characters yet', text: 'The gallery is empty. Make the first one, or import a card you brought from elsewhere.' }) + '</div>';++ const empty = items.length ? '' : '<div class="ex-empty">' + kit.empty({ icon: 'person', title: 'No characters yet', text: 'The gallery is empty. Make the first one above, or import a Character Card V2 or V3 you brought from elsewhere.' }) + '</div>';return '<div class="ex-wrap">'+ '<header class="ex-companions-head"><h1>Characters</h1>'-- + '<p class="ex-lede">Cards people made, on OpenVibe.Exchange. Bring one in as a Character Card V2 or V3, talk to it, and take it with you again.</p></header>'++ + '<p class="ex-lede">Cards people made, on OpenVibe.Exchange. Bring one in as a Character Card V2 or V3, talk to it, and take it with you again.</p>'++ // Making or importing a card is always one tap away, not only from "Your characters" once you have one.++ + '<p class="ex-head-actions"><a class="ovk-btn ovk-btn--primary" href="/companions/new">Make or import a character</a></p></header>'+ (problem ? kit.notice({ kind: 'error', text: problem }) : '')+ '<div class="ex-gallery-bar">' + searchForm + sortForm + '</div>'+ (tagChips ? '<div class="ex-gallery-tags">' + tagChips + '</div>' : '')
@@ -267,6 +267,7 @@ body.ex-app .ovf { display: none; }
@@ -267,6 +267,7 @@ body.ex-app .ovf { display: none; }
/* ── Companions: the gallery, a character page, the card form ───────────── */
/* ── Companions: the gallery, a character page, the card form ───────────── */
.ex-companions-head { padding: 30px 0 14px; }
.ex-companions-head { padding: 30px 0 14px; }
.ex-companions-head h1 { margin: 0 0 8px; font-size: clamp(1.7rem, 5vw, 2.4rem); letter-spacing: -.02em; }
.ex-companions-head h1 { margin: 0 0 8px; font-size: clamp(1.7rem, 5vw, 2.4rem); letter-spacing: -.02em; }
+.ex-head-actions { margin: 12px 0 0; }
.ex-lede { margin: 0; max-width: 62ch; color: var(--ovk-muted); font-size: .95rem; line-height: 1.55; }
.ex-lede { margin: 0; max-width: 62ch; color: var(--ovk-muted); font-size: .95rem; line-height: 1.55; }
.ex-gallery-bar { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; justify-content: space-between; margin: 18px 0 14px; }
.ex-gallery-bar { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; justify-content: space-between; margin: 18px 0 14px; }
.ex-search { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; flex: 1 1 320px; }
.ex-search { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; flex: 1 1 320px; }
@@ -67,19 +67,21 @@ function galleryPage({ items = [], total = 0, q = '', tags = [], sort = 'new', t
@@ -67,19 +67,21 @@ function galleryPage({ items = [], total = 0, q = '', tags = [], sort = 'new', t
+ '</form>';
+ '</form>';
const searchForm = '<form class="ex-search" method="get" action="/companions">'
const searchForm = '<form class="ex-search" method="get" action="/companions">'
+ '<input type="hidden" name="sort" value="' + esc(sort) + '">'
+ '<input type="hidden" name="sort" value="' + esc(sort) + '">'
- + '<label class="ovt-sr" for="ex-q">Search characters</label>'
+ + '<label class="ovk-sr" for="ex-q">Search characters</label>'
+ '<input id="ex-q" type="search" name="q" value="' + esc(q) + '" placeholder="Search characters" maxlength="80">'
+ '<input id="ex-q" type="search" name="q" value="' + esc(q) + '" placeholder="Search characters" maxlength="80">'
- + '<label class="ovt-sr" for="ex-tags">Tags</label>'
+ + '<label class="ovk-sr" for="ex-tags">Tags</label>'
+ '<input id="ex-tags" type="text" name="tags" value="' + esc(tags.join(',')) + '" placeholder="Tags, comma separated" maxlength="120">'
+ '<input id="ex-tags" type="text" name="tags" value="' + esc(tags.join(',')) + '" placeholder="Tags, comma separated" maxlength="120">'
+ '<button type="submit" class="ex-btn">Search</button>'
+ '<button type="submit" class="ex-btn">Search</button>'
+ '</form>';
+ '</form>';
const tagChips = tagList.length
const tagChips = tagList.length
? '<ul class="ex-taglist">' + tagList.map((t) => '<li><a class="ex-chip' + (tags.includes(t.tag) ? ' is-on' : '') + '" href="/companions?tags=' + encodeURIComponent(t.tag) + '&sort=' + esc(sort) + '">' + esc(t.tag) + ' <span class="ex-chip-n">' + t.n + '</span></a></li>').join('') + '</ul>'
? '<ul class="ex-taglist">' + tagList.map((t) => '<li><a class="ex-chip' + (tags.includes(t.tag) ? ' is-on' : '') + '" href="/companions?tags=' + encodeURIComponent(t.tag) + '&sort=' + esc(sort) + '">' + esc(t.tag) + ' <span class="ex-chip-n">' + t.n + '</span></a></li>').join('') + '</ul>'
: '';
: '';
- const empty = items.length ? '' : '<div class="ex-empty">' + kit.empty({ icon: 'person', title: 'No characters yet', text: 'The gallery is empty. Make the first one, or import a card you brought from elsewhere.' }) + '</div>';
+ const empty = items.length ? '' : '<div class="ex-empty">' + kit.empty({ icon: 'person', title: 'No characters yet', text: 'The gallery is empty. Make the first one above, or import a Character Card V2 or V3 you brought from elsewhere.' }) + '</div>';
return '<div class="ex-wrap">'
return '<div class="ex-wrap">'
+ '<header class="ex-companions-head"><h1>Characters</h1>'
+ '<header class="ex-companions-head"><h1>Characters</h1>'
- + '<p class="ex-lede">Cards people made, on OpenVibe.Exchange. Bring one in as a Character Card V2 or V3, talk to it, and take it with you again.</p></header>'
+ + '<p class="ex-lede">Cards people made, on OpenVibe.Exchange. Bring one in as a Character Card V2 or V3, talk to it, and take it with you again.</p>'
+ // Making or importing a card is always one tap away, not only from "Your characters" once you have one.
+ + '<p class="ex-head-actions"><a class="ovk-btn ovk-btn--primary" href="/companions/new">Make or import a character</a></p></header>'
+ (problem ? kit.notice({ kind: 'error', text: problem }) : '')
+ (problem ? kit.notice({ kind: 'error', text: problem }) : '')
+ '<div class="ex-gallery-bar">' + searchForm + sortForm + '</div>'
+ '<div class="ex-gallery-bar">' + searchForm + sortForm + '</div>'
+ (tagChips ? '<div class="ex-gallery-tags">' + tagChips + '</div>' : '')
+ (tagChips ? '<div class="ex-gallery-tags">' + tagChips + '</div>' : '')