feat: manpage, ?compact, and some changes to formatting

This commit is contained in:
Lewis Wynne 2026-03-26 19:07:37 +00:00
parent 51e33844b1
commit 306e26da46
8 changed files with 120 additions and 21 deletions

View file

@ -5,15 +5,6 @@ export function formatDate(date: Date): string {
return `${d}/${m}/${y}`;
}
export function extractDomain(url: string): string {
try {
const parsed = new URL(url);
return parsed.hostname.replace(/^www\./, '');
} catch {
return url;
}
}
export function formatListItem(
date: Date,
url: string,
@ -23,7 +14,7 @@ export function formatListItem(
const pinnedBadge = options?.pinned ? ' [pinned]' : '';
const suffix = options?.suffix ? ` ${options.suffix}` : '';
const prefix = options?.prefix ?? '';
return `${prefix}<span class="muted">${formatDate(date)}</span> <a href="${url}">${title}</a>${pinnedBadge}${suffix}`;
return `<span class="list-meta">${prefix}<span class="muted">${formatDate(date)}</span> </span><a href="${url}">${title}</a>${pinnedBadge}${suffix}`;
}
interface Sortable {

View file

@ -8,10 +8,12 @@ export interface TxtFile {
name: string;
date: Date;
pinned: boolean;
description?: string;
}
export interface TxtConfig {
pinned?: string[];
descriptions?: Record<string, string>;
}
export function getTxtDir(): string {
@ -32,12 +34,14 @@ export function getTxtFiles(): TxtFile[] {
const config = loadTxtConfig();
const pinnedSet = new Set(config.pinned || []);
const descriptions = config.descriptions || {};
const files = fs.readdirSync(txtDir)
.filter(file => file.endsWith('.txt'))
.map(name => ({
name,
date: getGitLastModifiedDate(path.join(txtDir, name)),
pinned: pinnedSet.has(name),
description: descriptions[name],
}));
return sortByPinnedThenDate(files);
}

View file

@ -2,7 +2,7 @@
import { getCollection } from 'astro:content';
import Layout from '../layouts/Layout.astro';
import { getApprovedEntries, type GuestbookEntry } from '../lib/db';
import { formatDate, extractDomain, formatListItem } from '../lib/format';
import { formatDate, formatListItem } from '../lib/format';
import { organizePostsByCategory, getSlug, enrichPostsWithDates } from '../lib/md';
import { getTxtFiles } from '../lib/txt';
import { DEFAULT_CATEGORY, SECTIONS, SUBDOMAINS } from '../lib/consts';
@ -16,6 +16,7 @@ const bookmarks = bookmarksCollection
.sort((a, b) => b.data.date.getTime() - a.data.date.getTime());
const txtFiles = getTxtFiles();
const txtMaxNameLen = Math.max(...txtFiles.map(f => f.name.replace(/\.txt$/, '').length));
const visibleLabels = [...sortedCategories.filter(c => c !== DEFAULT_CATEGORY), ...Object.values(SECTIONS)];
const labelWidth = Math.max(...visibleLabels.map(l => l.length));
@ -48,18 +49,23 @@ const urls = [
})}
<section data-section={SECTIONS.plaintext}>
<pre set:html={txtFiles.map((f, i) => formatListItem(f.date, `/${f.name}`, f.name, { pinned: f.pinned, prefix: i === 0 ? labelPrefix(SECTIONS.plaintext, `?just=${SECTIONS.plaintext}`) : blankPrefix })).join('\n')} />
<pre set:html={txtFiles.map((f, i) => {
const name = f.name.replace(/\.txt$/, '');
const pad = ' '.repeat(txtMaxNameLen - name.length);
const suffix = f.description ? `${pad} <span class="muted">(${f.description})</span>` : undefined;
return formatListItem(f.date, `/${f.name}`, name, { pinned: f.pinned, suffix, prefix: i === 0 ? labelPrefix(SECTIONS.plaintext, `?just=${SECTIONS.plaintext}`) : blankPrefix });
}).join('\n')} />
</section>
<section data-section={SECTIONS.bookmarks}>
<pre set:html={bookmarks.map((b, i) => formatListItem(b.data.date, b.data.url, b.data.title, { suffix: `<span class="muted">(${extractDomain(b.data.url)})</span>`, prefix: i === 0 ? labelPrefix(SECTIONS.bookmarks, `?just=${SECTIONS.bookmarks}`) : blankPrefix })).join('\n')} />
<pre set:html={bookmarks.map((b, i) => formatListItem(b.data.date, b.data.url, b.data.title, { prefix: i === 0 ? labelPrefix(SECTIONS.bookmarks, `?just=${SECTIONS.bookmarks}`) : blankPrefix })).join('\n')} />
</section>
<section data-section={SECTIONS.guestbook}>
<pre class="guestbook-entries" set:html={guestbookEntries.map((e, i) => {
const prefix = i === 0 ? labelPrefix(SECTIONS.guestbook, `?just=${SECTIONS.guestbook}`) : blankPrefix;
const nameHtml = e.url ? `<a href="${e.url}"><b>${e.name}</b></a>` : `<b>${e.name}</b>`;
return `<span class="guestbook-entry" style="padding-left: ${labelWidth + 12}ch; text-indent: -${labelWidth + 12}ch;">${prefix}<span class="muted">${formatDate(e.createdAt)}</span> ${nameHtml} ${e.message.replace(/\n/g, ' ')}</span>`;
return `<span class="guestbook-entry" style="padding-left: ${labelWidth + 12}ch; text-indent: -${labelWidth + 12}ch;"><span class="list-meta">${prefix}<span class="muted">${formatDate(e.createdAt)}</span> </span>${nameHtml} ${e.message.replace(/\n/g, ' ')}</span>`;
}).join('')} />
<form id="guestbook-form" class="guestbook-form" style={`margin-left: ${labelWidth + 12}ch`}>
<input type="text" name="name" placeholder="name" required maxlength="100" /><br />

View file

@ -1,4 +1,5 @@
body {
box-sizing: border-box;
max-width: 38rem;
margin: 0 auto;
padding: 1rem;
@ -112,3 +113,14 @@ section pre {
margin-top: 0.5rem;
font-family: monospace;
}
html[data-compact] .list-meta {
display: none;
}
html[data-compact] .guestbook-entry {
padding-left: 0 !important;
text-indent: 0 !important;
}
html[data-compact] .guestbook-form {
margin-left: 0 !important;
}