openvibers/openvibe.community Commits

Paste kinds as chips with an icon, a tint and their count (#389)

OP OpenVibers committed 2 days ago · 4549b5c06d · 2 files, +30 −6

The owner, 11-11, on the home: "improve design and add icon". Each kind under the
newest pastes is an icon in its own tint (most viewed a flame, images a picture,
plain text a page, Markdown a book, shells a terminal, configs a gear, code the
code mark), its name, and its count beside it in a quieter weight. A language
never detected is "Other", not "Auto-detect".

Changes

ddiff --git a/public/css/community.css b/public/css/community.css
iindex e044f3b..4d30af4 100644
---- a/public/css/community.css
++++ b/public/css/community.css
@@@ -449,7 +449,23 @@ button.rx:hover { border-color: var(--accent); }
/* The kinds as quick filters: one strip that scrolls sideways on a phone (like the numbers), wrapping from 600 px. */
.cm-kinds { display: flex; gap: 8px; margin: 2px -16px 0; padding: 0 16px 2px; overflow-x: auto; scrollbar-width: none; }
.cm-kinds::-webkit-scrollbar { display: none; }
--.cm-kinds .ovk-btn { flex: none; min-height: 44px; }
++.cm-kind { flex: none; display: inline-flex; align-items: center; gap: 8px; min-height: 44px; padding: 0 14px 0 7px; box-sizing: border-box;
++ border-radius: 999px; border: 1px solid var(--ovk-line, rgba(255,255,255,.1)); background: var(--ovk-panel, rgba(255,255,255,.04)); color: var(--text-primary);
++ font-size: .9375rem; font-weight: 650; text-decoration: none; white-space: nowrap; transition: background-color .15s, border-color .15s, transform .12s; }
++.cm-kind:hover, .cm-kind:focus-visible { background: var(--ovk-panel-2, rgba(255,255,255,.07)); border-color: color-mix(in srgb, var(--cm-tint) 45%, var(--ovk-line, transparent)); }
++.cm-kind:active { transform: scale(.97); }
++.cm-kind-ic { display: grid; place-items: center; width: 30px; height: 30px; border-radius: 50%; flex: none; color: var(--cm-tint);
++ background: color-mix(in srgb, var(--cm-tint) 16%, transparent); }
++.cm-kind-ic .ovk-i { width: 17px; height: 17px; }
++.cm-kind-n { margin-left: 2px; color: var(--text-secondary); font-size: .8125rem; font-weight: 600; font-variant-numeric: tabular-nums; }
++.cm-kind { --cm-tint: var(--accent-light, #60a5fa); }
++.cm-kind[data-kind="top"] { --cm-tint: #f59e0b; }
++.cm-kind[data-kind="image"] { --cm-tint: #34d399; }
++.cm-kind[data-kind="file"] { --cm-tint: #94a3b8; }
++.cm-kind[data-kind="book"] { --cm-tint: #a78bfa; }
++.cm-kind[data-kind="terminal"] { --cm-tint: #2dd4bf; }
++.cm-kind[data-kind="gear"] { --cm-tint: #fbbf24; }
++@media (prefers-reduced-motion: reduce) { .cm-kind { transition: none; } .cm-kind:active { transform: none; } }
.cm-pair { display: grid; grid-auto-flow: row; gap: 0 28px; min-width: 0; }
.cm-fold-lede { margin: 0 0 12px; color: var(--text-secondary); }
.cm-snip-h { margin: 0 0 6px; color: var(--text-secondary); font-size: .8rem; font-weight: 700; }
ddiff --git a/server/render/pages.js b/server/render/pages.js
iindex 30d2f69..cdae472 100644
---- a/server/render/pages.js
++++ b/server/render/pages.js
@@@ -209,12 +209,20 @@ function shelfHead(id, title, more) {
return `<div class="ovk-rail-head"><h2 id="${esc(id)}">${esc(title)}</h2><a class="ovk-rail-more" href="${esc(more.href)}">${esc(more.label)}${kit.icon('chevron')}</a></div>`;
}
--/** The kinds of paste as quick filters under the newest: the most viewed, then the languages people use most. */
++/** The kinds of paste as quick filters under the newest: the most viewed, then the languages people use most. Each is
++ * an icon in its own tint, the name, and its count beside it (the owner, 11-11: "improve design and add icon"). */
++const KIND_ICON = { image: 'image', text: 'file', plaintext: 'file', markdown: 'book', md: 'book', bash: 'terminal', shell: 'terminal', sh: 'terminal',
++ powershell: 'terminal', log: 'terminal', console: 'terminal', json: 'gear', yaml: 'gear', yml: 'gear', toml: 'gear', ini: 'gear', xml: 'gear', env: 'gear' };
function kindChips(languages) {
-- const items = [{ href: '/pastes?sort=views', label: 'Most viewed' }].concat((languages || []).slice(0, 7).map((l) => (l.language === 'image'
-- ? { href: '/pastes?type=images', label: `Images ${num(l.count)}` }
-- : { href: `/pastes?lang=${encodeURIComponent(l.language)}`, label: `${languageLabel(l.language)} ${num(l.count)}` })));
-- return `<nav class="cm-kinds" aria-label="Browse pastes by kind">${items.map((x) => kit.button({ ...x, kind: 'quiet', small: true })).join('')}</nav>`;
++ const chip = ({ href, label, n, icon, kind }) => `<a class="cm-kind" data-kind="${esc(kind)}" href="${esc(href)}"><span class="cm-kind-ic">${kit.icon(icon)}</span><span class="cm-kind-l">${esc(label)}</span>${n != null ? `<span class="cm-kind-n">${esc(num(n))}</span>` : ''}</a>`;
++ const items = [{ href: '/pastes?sort=views', label: 'Most viewed', icon: 'flame', kind: 'top' }].concat((languages || []).slice(0, 7).map((l) => {
++ const lang = String(l.language || 'text').toLowerCase();
++ if (lang === 'image') return { href: '/pastes?type=images', label: 'Images', n: l.count, icon: 'image', kind: 'image' };
++ // A paste whose language was never detected is "Other", not the picker's "Auto-detect".
++ const icon = lang === 'auto' ? 'file' : KIND_ICON[lang] || 'code';
++ return { href: `/pastes?lang=${encodeURIComponent(l.language)}`, label: lang === 'auto' ? 'Other' : languageLabel(l.language), n: l.count, icon, kind: icon === 'code' ? 'code' : icon };
++ }));
++ return `<nav class="cm-kinds" aria-label="Browse pastes by kind">${items.map(chip).join('')}</nav>`;
}
function latestShelf(latest, languages) {