feat: removed pins, and added right-aligned suffixes
This commit is contained in:
parent
c647fd62c3
commit
20811f107b
8 changed files with 51 additions and 25 deletions
|
|
@ -1,7 +1,6 @@
|
|||
---
|
||||
title: hello
|
||||
date: 2023-02-26
|
||||
pinned: true
|
||||
---
|
||||
|
||||
i've always had some sort of homepage. it was originally on bebo, then that died and i didn't ever get into other social media, so i made a website
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
pinned: []
|
||||
descriptions:
|
||||
cv.txt: curriculum vitae
|
||||
now.txt: what i'm doing now
|
||||
|
|
|
|||
|
|
@ -3,13 +3,12 @@ import { glob, file } from 'astro/loaders';
|
|||
import { z } from 'astro/zod';
|
||||
import yaml from 'js-yaml';
|
||||
|
||||
const md = defineCollection({
|
||||
const posts = defineCollection({
|
||||
loader: glob({ pattern: '**/*.md', base: './content' }),
|
||||
schema: z.object({
|
||||
title: z.string(),
|
||||
date: z.coerce.date(),
|
||||
updated: z.coerce.date().optional(),
|
||||
pinned: z.boolean().optional(),
|
||||
category: z.string().optional(),
|
||||
related: z.array(z.string()).optional(),
|
||||
})
|
||||
|
|
|
|||
|
|
@ -26,28 +26,45 @@ export function formatDate(date: Date): string {
|
|||
return `${d}/${m}/${y}`;
|
||||
}
|
||||
|
||||
export function wordCount(markdown: string | undefined): string {
|
||||
if (!markdown) return '';
|
||||
const words = markdown
|
||||
.replace(/^---[\s\S]*?---/m, '')
|
||||
.replace(/^#+\s+.*$/gm, '')
|
||||
.replace(/!?\[([^\]]*)\]\([^)]*\)/g, '$1')
|
||||
.replace(/[*_~`]/g, '')
|
||||
.replace(/:[a-z]+\[([^\]]*)\]/g, '$1')
|
||||
.trim()
|
||||
.split(/\s+/)
|
||||
.filter(Boolean).length;
|
||||
if (words < 100) return `${words} words`;
|
||||
const mins = Math.ceil(words / 200);
|
||||
return `${mins} min`;
|
||||
}
|
||||
|
||||
export function extractDomain(url: string): string {
|
||||
try {
|
||||
return new URL(url).hostname.replace(/^www\./, '');
|
||||
} catch {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
export function formatListItem(
|
||||
date: Date,
|
||||
url: string,
|
||||
title: string,
|
||||
options?: { pinned?: boolean; suffix?: string }
|
||||
options?: { suffix?: string }
|
||||
): string {
|
||||
const pinnedBadge = options?.pinned ? ' [pinned]' : '';
|
||||
const suffix = options?.suffix ? ` ${options.suffix}` : '';
|
||||
return `<span class="list-meta"><span class="muted">${formatDate(date)}</span></span><span class="entry-content"><a href="${url}" title="${title}">${title}</a>${pinnedBadge}${suffix}</span>`;
|
||||
const suffixHtml = options?.suffix ? `<span class="entry-suffix muted">${options.suffix}</span>` : '';
|
||||
return `<span class="list-meta"><span class="muted">${formatDate(date)}</span></span><span class="entry-content"><a href="${url}" title="${title}">${title}</a>${suffixHtml}</span>`;
|
||||
}
|
||||
|
||||
interface Sortable {
|
||||
date: Date;
|
||||
pinned?: boolean;
|
||||
}
|
||||
|
||||
export function sortEntries<T>(items: T[], key?: (item: T) => Sortable): T[] {
|
||||
const get = key ?? (item => item as unknown as Sortable);
|
||||
return items.slice().sort((a, b) => {
|
||||
const ak = get(a), bk = get(b);
|
||||
if (ak.pinned && !bk.pinned) return -1;
|
||||
if (!ak.pinned && bk.pinned) return 1;
|
||||
return bk.date.getTime() - ak.date.getTime();
|
||||
});
|
||||
return items.slice().sort((a, b) => get(b).date.getTime() - get(a).date.getTime());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,12 +6,10 @@ import { sortEntries } from './format';
|
|||
export interface TxtFile {
|
||||
name: string;
|
||||
date: Date;
|
||||
pinned: boolean;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export interface TxtConfig {
|
||||
pinned?: string[];
|
||||
descriptions?: Record<string, string>;
|
||||
dates?: Record<string, string>;
|
||||
}
|
||||
|
|
@ -32,7 +30,6 @@ export function getTxtFiles(): TxtFile[] {
|
|||
if (!fs.existsSync(txtDir)) return [];
|
||||
|
||||
const config = loadTxtConfig();
|
||||
const pinnedSet = new Set(config.pinned || []);
|
||||
const descriptions = config.descriptions || {};
|
||||
const dates = config.dates || {};
|
||||
|
||||
|
|
@ -41,7 +38,6 @@ export function getTxtFiles(): TxtFile[] {
|
|||
.map(name => ({
|
||||
name,
|
||||
date: dates[name] ? new Date(dates[name]) : new Date(0),
|
||||
pinned: pinnedSet.has(name),
|
||||
description: descriptions[name],
|
||||
}));
|
||||
return sortEntries(files);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
import { getCollection, render } from 'astro:content';
|
||||
import Layout from '../layouts/Layout.astro';
|
||||
import { formatDate, formatListItem, excerpt } from '../lib/format';
|
||||
import { formatDate, formatListItem, excerpt, wordCount } from '../lib/format';
|
||||
import { getSlug, resolveRelatedPosts, type Post } from '../lib/md';
|
||||
|
||||
export async function getStaticPaths() {
|
||||
|
|
@ -21,7 +21,7 @@ const description = excerpt((post as Post).body) || undefined;
|
|||
|
||||
<article>
|
||||
<h1>{post.data.title}</h1>
|
||||
<p class="muted" style="margin-top: 0;">{formatDate(post.data.date)}{post.data.updated && ` (updated ${formatDate(post.data.updated)})`}</p>
|
||||
<p class="muted" style="margin-top: 0;">{formatDate(post.data.date)}{post.data.updated && ` (updated ${formatDate(post.data.updated)})`} · {wordCount((post as Post).body)}{post.data.category && ` · ${post.data.category}`}</p>
|
||||
<Content />
|
||||
</article>
|
||||
{related.length > 0 && (
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
import { getCollection } from 'astro:content';
|
||||
import Layout from '../layouts/Layout.astro';
|
||||
import { getApprovedEntries, type GuestbookEntry } from '../lib/db';
|
||||
import { formatDate, formatListItem, escapeHtml } from '../lib/format';
|
||||
import { formatDate, formatListItem, extractDomain, wordCount, escapeHtml } from '../lib/format';
|
||||
import { organizePostsByCategory, getSlug } from '../lib/md';
|
||||
import { getTxtFiles } from '../lib/txt';
|
||||
import { DEFAULT_CATEGORY, SECTIONS, SUBDOMAINS } from '../lib/consts';
|
||||
|
|
@ -37,7 +37,7 @@ const urls = [
|
|||
<section data-section={category}>
|
||||
{!isDefault && <a class="section-label" href={`?just=${category}`}>{category}</a>}
|
||||
<div class="entry-list" set:html={categoryPosts.map(post =>
|
||||
`<span class="entry">${formatListItem(post.data.date, `/${getSlug(post.id)}`, post.data.title, { pinned: post.data.pinned })}</span>`
|
||||
`<span class="entry">${formatListItem(post.data.date, `/${getSlug(post.id)}`, post.data.title, { suffix: wordCount(post.body) })}</span>`
|
||||
).join('')} />
|
||||
</section>
|
||||
);
|
||||
|
|
@ -47,14 +47,14 @@ const urls = [
|
|||
<a class="section-label" href={`?just=${SECTIONS.plaintext}`}>{SECTIONS.plaintext}</a>
|
||||
<div class="entry-list" set:html={txtFiles.map(f => {
|
||||
const name = f.name.replace(/\.txt$/, '');
|
||||
return `<span class="entry">${formatListItem(f.date, `/${f.name}`, name, { pinned: f.pinned })}</span>`;
|
||||
return `<span class="entry">${formatListItem(f.date, `/${f.name}`, name, { suffix: f.description })}</span>`;
|
||||
}).join('')} />
|
||||
</section>
|
||||
|
||||
<section data-section={SECTIONS.bookmarks}>
|
||||
<a class="section-label" href={`?just=${SECTIONS.bookmarks}`}>{SECTIONS.bookmarks}</a>
|
||||
<div class="entry-list" set:html={bookmarks.map(b =>
|
||||
`<span class="entry">${formatListItem(b.data.date, b.data.url, b.data.title)}</span>`
|
||||
`<span class="entry">${formatListItem(b.data.date, b.data.url, b.data.title, { suffix: extractDomain(b.data.url) })}</span>`
|
||||
).join('')} />
|
||||
</section>
|
||||
|
||||
|
|
|
|||
|
|
@ -116,11 +116,27 @@ section pre {
|
|||
}
|
||||
|
||||
.entry-content {
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.entry-content > a {
|
||||
flex: 0 1 auto;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.entry-suffix {
|
||||
flex: 1 10000 0%;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
text-align: right;
|
||||
padding-left: 1ch;
|
||||
}
|
||||
|
||||
.guestbook-entries {
|
||||
font-family: monospace;
|
||||
white-space: pre;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue