180 lines
4.1 KiB
TypeScript
180 lines
4.1 KiB
TypeScript
import { render as renderMarkdown } from "@deno/gfm";
|
|
import { log } from "./log.ts";
|
|
|
|
export function htmlLayout(title: string, body: string) {
|
|
return `
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
|
<title>${title}</title>
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link href="https://fonts.googleapis.com/css2?family=Merriweather:ital,wght@0,400;0,700;0,900;1,400;1,700&display=swap" rel="stylesheet">
|
|
<style>
|
|
body {
|
|
background-color: #f5f2eb;
|
|
color: #3b3a38;
|
|
font-size: 18px;
|
|
font-family: "Merriweather", serif;
|
|
}
|
|
|
|
img {
|
|
max-width: 100%;
|
|
}
|
|
|
|
img.avatar {
|
|
height: 48px;
|
|
width: 48px;
|
|
border-radius: 50%;
|
|
}
|
|
|
|
.profile-page img.avatar {
|
|
height: 128px;
|
|
width: 128px;
|
|
}
|
|
|
|
a.anchor {
|
|
display: none;
|
|
}
|
|
|
|
h1 {
|
|
margin: 2em 0 0 0;
|
|
}
|
|
|
|
h2, h3, h4 {
|
|
margin-top: 2em;
|
|
margin-bottom: 2rem;
|
|
}
|
|
|
|
h1, h2, h3, h4 {
|
|
color: #191818;
|
|
}
|
|
|
|
p, pre, ul, ol, blockquote {
|
|
line-height: 1.6em;
|
|
margin-bottom: 1.6em;
|
|
}
|
|
|
|
a {
|
|
color: #023b77
|
|
}
|
|
|
|
a:visited {
|
|
color: #3b0277
|
|
}
|
|
|
|
code {
|
|
font-size: 1rem;
|
|
background-color: #e8e3da;
|
|
color: #027739;
|
|
padding: 0.1em 0.3em;
|
|
}
|
|
|
|
pre code {
|
|
display: block;
|
|
padding: 0.6rem 1rem;
|
|
background-color: #333;
|
|
color: #ccc;
|
|
}
|
|
|
|
main {
|
|
display: block;
|
|
max-width: 728px;
|
|
margin: 12rem auto;
|
|
}
|
|
|
|
main header {
|
|
display: block;
|
|
margin-bottom: 3rem;
|
|
}
|
|
|
|
main header h1 {
|
|
margin-bottom: 1.6rem;
|
|
}
|
|
|
|
p.meta {
|
|
display: flex;
|
|
column-gap: 1rem;
|
|
}
|
|
|
|
p.meta .content {
|
|
display: flex;
|
|
flex-direction: column;
|
|
font-size: 0.875rem;
|
|
line-height: 1.6rem;
|
|
}
|
|
|
|
p.meta .date {
|
|
color: #888;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
${body}
|
|
</body>
|
|
</html>
|
|
`;
|
|
}
|
|
|
|
export function articleHtml(articleEvent: object, profile: object) {
|
|
const titleTag = articleEvent.tags.find(t => t[0] === "title");
|
|
const title = titleTag ? titleTag[1] : "Untitled";
|
|
const content = renderMarkdown(articleEvent.content);
|
|
const date = new Date(articleEvent.created_at * 1000);
|
|
const formattedDate = date.toLocaleDateString("en-US", {
|
|
year: "numeric",
|
|
month: "long",
|
|
day: "numeric",
|
|
});
|
|
|
|
const body = `
|
|
<main>
|
|
<header>
|
|
<h1>${title}</h1>
|
|
<p class="meta">
|
|
<img class="avatar" src="${profile.picture}" alt="User Avatar" />
|
|
<span class="content">
|
|
<span class="name">${profile.name}</span>
|
|
<span class="date">${formattedDate}</span>
|
|
</span>
|
|
</p>
|
|
</header>
|
|
${content}
|
|
</main>
|
|
`;
|
|
|
|
return htmlLayout(title, body);
|
|
}
|
|
|
|
export function profilePageHtml(profileEvent: object) {
|
|
const profile = JSON.parse(profileEvent.content);
|
|
const name = profile.name || "Anonymous";
|
|
const title = `${name} on Nostr`
|
|
// const date = new Date(articleEvent.created_at * 1000);
|
|
// const formattedDate = date.toLocaleDateString("en-US", {
|
|
// year: "numeric",
|
|
// month: "long",
|
|
// day: "numeric",
|
|
// });
|
|
|
|
const body = `
|
|
<main class="profile-page">
|
|
<header>
|
|
<img class="avatar" src="${profile.picture}" alt="User Avatar" />
|
|
<div class="bio">
|
|
<h1>${profile.name}</h1>
|
|
<p class="about">
|
|
${profile.about}
|
|
</p>
|
|
</div>
|
|
</header>
|
|
<h2>Articles</h2>
|
|
</main>
|
|
`;
|
|
|
|
return htmlLayout(title, body);
|
|
}
|