Compare commits
35 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 582051ba20 | |||
| a25500bb91 | |||
| c3e05b44ea | |||
| b4f8a42e05 | |||
| ff486c01e3 | |||
| 33ead9d968 | |||
| 20f8b38c8a | |||
| 7f0b65fa20 | |||
| 2fdaf64b47 | |||
| 4bad9e095e | |||
| facb2b0f99 | |||
| ef0c8dd981 | |||
| 32b9a29028 | |||
| 1da77d6074 | |||
| 64ecc338be | |||
| 66623a64b4 | |||
| 6997e313e6 | |||
| 435ca0f2d7 | |||
| 3e742ba952 | |||
| caad696191 | |||
| 51419e4c7c | |||
| 4310c90f48 | |||
| 3378844823 | |||
| 02c7e2f45a | |||
| f0d58a1a33 | |||
| a37955cbec | |||
| 834e176841 | |||
| bad4457ad7 | |||
| b2657962e1 | |||
| 4992379487 | |||
| 1f499739fb | |||
| 7972acb40f | |||
| e06bb64645 | |||
| 3918171086 | |||
| 1e9489bb14 |
20
web/package-lock.json
generated
20
web/package-lock.json
generated
@@ -12,6 +12,7 @@
|
||||
"@tailwindcss/vite": "^4.1.17",
|
||||
"astro": "^5.15.4",
|
||||
"pocketbase": "^0.26.3",
|
||||
"swiper": "^12.0.3",
|
||||
"tailwindcss": "^4.1.17"
|
||||
}
|
||||
},
|
||||
@@ -4966,6 +4967,25 @@
|
||||
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/swiper": {
|
||||
"version": "12.0.3",
|
||||
"resolved": "https://registry.npmjs.org/swiper/-/swiper-12.0.3.tgz",
|
||||
"integrity": "sha512-BHd6U1VPEIksrXlyXjMmRWO0onmdNPaTAFduzqR3pgjvi7KfmUCAm/0cj49u2D7B0zNjMw02TSeXfinC1hDCXg==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/swiperjs"
|
||||
},
|
||||
{
|
||||
"type": "open_collective",
|
||||
"url": "http://opencollective.com/swiper"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 4.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tailwindcss": {
|
||||
"version": "4.1.17",
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.17.tgz",
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"@tailwindcss/vite": "^4.1.17",
|
||||
"astro": "^5.15.4",
|
||||
"pocketbase": "^0.26.3",
|
||||
"swiper": "^12.0.3",
|
||||
"tailwindcss": "^4.1.17"
|
||||
}
|
||||
}
|
||||
|
||||
18
web/src/components/albums/albumCard.astro
Normal file
18
web/src/components/albums/albumCard.astro
Normal file
@@ -0,0 +1,18 @@
|
||||
---
|
||||
import { authPB } from "@utils/pocketbase"
|
||||
const { a } = Astro.props
|
||||
const pb = await authPB()
|
||||
---
|
||||
|
||||
<div class="swiper-slide flex flex-col md:flex-row items-center justify-center">
|
||||
<a class="b1-no-underline" href={`/albums/${encodeURI(a.title)}`}>
|
||||
<h1 class="text-center pb-0 md:flex-1">{a.title}</h1>
|
||||
<p class="text-center">{a.date}</p>
|
||||
<img
|
||||
src={pb.files.getURL(a, a.images[0])}
|
||||
class="w-full h-full object-contain"
|
||||
loading="lazy"
|
||||
alt={a.title}
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
@@ -12,10 +12,14 @@ const links = [
|
||||
txt: "email",
|
||||
lnk: "mailto:contact@breadone.net"
|
||||
},
|
||||
{
|
||||
txt: "copyright",
|
||||
lnk: "/copyright"
|
||||
}
|
||||
// {
|
||||
// txt: "about",
|
||||
// lnk: "/about"
|
||||
// },
|
||||
// {
|
||||
// txt: "copyright",
|
||||
// lnk: "/copyright"
|
||||
// }
|
||||
]
|
||||
---
|
||||
|
||||
|
||||
@@ -4,9 +4,13 @@ const links = [
|
||||
txt: 'now',
|
||||
lnk: '/now'
|
||||
},
|
||||
// {
|
||||
// txt: 'photos',
|
||||
// lnk: '/photos'
|
||||
// },
|
||||
{
|
||||
txt: 'photos',
|
||||
lnk: '/photos'
|
||||
txt: 'albums',
|
||||
lnk: '/albums'
|
||||
},
|
||||
{
|
||||
txt: 'posts',
|
||||
|
||||
36
web/src/components/index/recentAlbums.astro
Normal file
36
web/src/components/index/recentAlbums.astro
Normal file
@@ -0,0 +1,36 @@
|
||||
---
|
||||
import SummaryCard from "./summaryCard.astro";
|
||||
import { authPB } from "@utils/pocketbase";
|
||||
import { getFormattedDate } from "@utils/date";
|
||||
|
||||
const pb = await authPB()
|
||||
|
||||
const albums = await pb.collection('albums').getList(1, 3, {
|
||||
sort: '-created',
|
||||
})
|
||||
|
||||
const postImages = await Promise.all( albums.items.map(a => pb.files.getURL(a, a.images[0])) )
|
||||
|
||||
---
|
||||
<SummaryCard
|
||||
title="Recent Albums",
|
||||
titleLink="/albums"
|
||||
>
|
||||
<div class="flex flex-col md:flex-row gap-4 md:gap-4">
|
||||
{
|
||||
albums.items.map((p, i) => (
|
||||
<div class="flex md:flex-col flex-row gap-3 md:gap-2 flex-1">
|
||||
<a href={`/albums/${encodeURI(p.title)}`} class="shrink-0">
|
||||
<img class="w-24 h-24 md:w-full md:h-48 object-cover rounded-sm" src={postImages[i]} alt={p.title}/>
|
||||
</a>
|
||||
|
||||
<div class="flex flex-col justify-start md:justify-start">
|
||||
<a href={`/albums/${encodeURI(p.title)}`} class="b1-no-underline line-clamp-2 md:line-clamp-none">
|
||||
{p.title}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</SummaryCard>
|
||||
39
web/src/components/index/recentPosts.astro
Normal file
39
web/src/components/index/recentPosts.astro
Normal file
@@ -0,0 +1,39 @@
|
||||
---
|
||||
import SummaryCard from "./summaryCard.astro";
|
||||
import { authPB } from "@utils/pocketbase";
|
||||
import { getFormattedDate } from "@utils/date";
|
||||
|
||||
const pb = await authPB()
|
||||
|
||||
const posts = await pb.collection('posts').getList(1, 3, {
|
||||
sort: '-publishDate',
|
||||
filter: 'published=true',
|
||||
})
|
||||
|
||||
const postImages = await Promise.all( posts.items.map(p => pb.files.getURL(p, p.headerImage)) )
|
||||
|
||||
---
|
||||
<SummaryCard
|
||||
title="Recent Posts",
|
||||
titleLink="/posts"
|
||||
>
|
||||
<div class="flex flex-col md:flex-row gap-4 md:gap-4">
|
||||
{
|
||||
posts.items.map((p, i) => (
|
||||
<div class="flex md:flex-col flex-row gap-3 md:gap-2 flex-1">
|
||||
<time class="hidden md:block text-sm text-gray-500" datetime={p.publishDate}>{getFormattedDate(p.publishDate)}</time>
|
||||
<a href={`/posts/${p.slug}`} class="shrink-0">
|
||||
<img class="w-24 h-24 md:w-full md:h-48 object-cover rounded-sm" src={postImages[i]} alt={p.title}/>
|
||||
</a>
|
||||
|
||||
<div class="flex flex-col justify-start md:justify-start">
|
||||
<a href={`/posts/${p.slug}`} class="b1-no-underline line-clamp-2 md:line-clamp-none">
|
||||
{p.title}
|
||||
</a>
|
||||
<time class="md:hidden text-sm text-gray-500" datetime={p.publishDate}>{getFormattedDate(p.publishDate)}</time>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</SummaryCard>
|
||||
14
web/src/components/index/summaryCard.astro
Normal file
14
web/src/components/index/summaryCard.astro
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
|
||||
const { title, titleLink } = Astro.props
|
||||
|
||||
---
|
||||
|
||||
<div>
|
||||
<a href={titleLink} rel="prefetch" class="b1-no-underline">
|
||||
<h2 class="text-xl">{title}</h2>
|
||||
</a>
|
||||
<div class="flex flex-row gap-2">
|
||||
<slot/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,73 +1,102 @@
|
||||
---
|
||||
import { authPB } from "src/utils/pocketbase";
|
||||
import type { RecordModel } from 'pocketbase'
|
||||
|
||||
// export const prerender = false
|
||||
|
||||
const pb = await authPB()
|
||||
const photos = await pb.collection('photos').getFullList({
|
||||
sort: '-created'
|
||||
sort: '-created',
|
||||
filter: 'published=true'
|
||||
})
|
||||
|
||||
const getImageLink = async (record: any) => {
|
||||
return pb.files.getURL(record, record.image)
|
||||
}
|
||||
const photoLinks = await Promise.all(
|
||||
photos.map(p => pb.files.getURL(p, p.image, { thumb: '0x1800' }))
|
||||
)
|
||||
---
|
||||
|
||||
<script>
|
||||
let pos = 0;
|
||||
import Swiper from 'swiper';
|
||||
import { Navigation, Keyboard, EffectFade } from 'swiper/modules';
|
||||
import 'swiper/css';
|
||||
import 'swiper/css/effect-fade';
|
||||
|
||||
const dataElement = document.getElementById('carousel-data');
|
||||
const photos = dataElement ? JSON.parse(dataElement.textContent || '[]') : [];
|
||||
const cap = photos.length - 1;
|
||||
const img = document.getElementById('carousel-img') as HTMLImageElement;
|
||||
const photoLinksElement = document.getElementById('photo-links-data');
|
||||
const photoLinks = photoLinksElement ? JSON.parse(photoLinksElement.textContent || '[]') : [];
|
||||
const titleEl = document.getElementById('photo-title');
|
||||
const cameraEl = document.getElementById('photo-camera');
|
||||
const filmEl = document.getElementById('photo-film');
|
||||
const locationEl = document.getElementById('photo-location');
|
||||
const currentPosIndicator = document.getElementById('current-pos');
|
||||
const maxPosIndicator = document.getElementById('max-pos');
|
||||
|
||||
maxPosIndicator!.innerText = (cap + 1) as string
|
||||
currentPosIndicator!.innerText = (pos + 1) as string
|
||||
|
||||
function updatePhoto() {
|
||||
const currentPhoto = photos[pos];
|
||||
|
||||
currentPosIndicator!.innerText = (pos + 1) as string
|
||||
|
||||
// Update image src
|
||||
if (img && currentPhoto) {
|
||||
const imageUrl = `/api/files/photos/${currentPhoto.id}/${currentPhoto.image}`;
|
||||
img.src = imageUrl;
|
||||
if (maxPosIndicator) {
|
||||
maxPosIndicator.innerText = String(photos.length);
|
||||
}
|
||||
if (currentPosIndicator) {
|
||||
currentPosIndicator.innerText = '1';
|
||||
}
|
||||
|
||||
// Update metadata
|
||||
if (titleEl) {
|
||||
|
||||
const swiper = new Swiper('.swiper', {
|
||||
modules: [Navigation, Keyboard, EffectFade],
|
||||
spaceBetween: 20,
|
||||
effect: 'cards',
|
||||
fadeEffect: {
|
||||
crossFade: true
|
||||
},
|
||||
speed: 250,
|
||||
navigation: {
|
||||
nextEl: '#inc-button',
|
||||
prevEl: '#dec-button',
|
||||
},
|
||||
keyboard: {
|
||||
enabled: true,
|
||||
onlyInViewport: false,
|
||||
},
|
||||
loop: true,
|
||||
on: {
|
||||
slideChange: function() {
|
||||
const realIndex = this.realIndex;
|
||||
const currentPhoto = photos[realIndex];
|
||||
|
||||
if (currentPosIndicator) {
|
||||
currentPosIndicator.innerText = String(realIndex + 1);
|
||||
}
|
||||
|
||||
if (titleEl && currentPhoto) {
|
||||
titleEl.textContent = currentPhoto.title || "Untitled";
|
||||
}
|
||||
if (cameraEl) {
|
||||
cameraEl.textContent = `📸 ${currentPhoto.camera}`
|
||||
if (cameraEl && currentPhoto) {
|
||||
cameraEl.textContent = `📸 ${currentPhoto.camera}`;
|
||||
}
|
||||
if (locationEl) {
|
||||
locationEl.textContent = `📌 ${currentPhoto.location}`
|
||||
if (filmEl && currentPhoto) {
|
||||
filmEl.textContent = currentPhoto.film !== "" ? `🎞️ ${currentPhoto.film}` : "";
|
||||
}
|
||||
if (locationEl && currentPhoto) {
|
||||
locationEl.textContent = `📌 ${currentPhoto.location}`;
|
||||
}
|
||||
}
|
||||
|
||||
function inc() {
|
||||
pos = pos === cap ? 0 : pos + 1;
|
||||
updatePhoto();
|
||||
}
|
||||
|
||||
function dec() {
|
||||
pos = pos === 0 ? cap : pos - 1;
|
||||
updatePhoto();
|
||||
}
|
||||
|
||||
// make functions globally accessible
|
||||
(window as any).inc = inc;
|
||||
(window as any).dec = dec;
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- Hidden element to pass server data to client -->
|
||||
<div class="hidden" id="carousel-data">{JSON.stringify(photos)}</div>
|
||||
<div class="hidden" id="photo-links-data">{JSON.stringify(photoLinks)}</div>
|
||||
|
||||
<img id="carousel-img" class="w-full md:h-[calc(100vh-7rem)] object-contain" src={ pb.files.getURL(photos[0], photos[0].image) } />
|
||||
<div class="swiper w-full h-[70vh] md:h-[calc(100vh-7rem)]">
|
||||
<div class="swiper-wrapper">
|
||||
{photos.map((photo, i) => (
|
||||
<div class="swiper-slide flex items-center justify-center">
|
||||
<img
|
||||
src={photoLinks[i]}
|
||||
class="w-full h-full object-contain"
|
||||
loading="lazy"
|
||||
alt={photo.title || 'Photo'}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
@@ -3,8 +3,15 @@ import { getFormattedDate } from '@utils/date'
|
||||
const { publishDate, wordCount, readTime, tags } = Astro.props
|
||||
---
|
||||
|
||||
<time datetime={publishDate}>{getFormattedDate(publishDate)}</time> |
|
||||
<span title={`${wordCount} words`}>{readTime} min. read</span> |
|
||||
<div class="flex flex-row gap-1">
|
||||
<time datetime={publishDate}>{getFormattedDate(publishDate)}</time>
|
||||
|
||||
<span class="tag-separator"/>
|
||||
|
||||
<span title={`${wordCount} words`}>{readTime} min. read</span>
|
||||
|
||||
<span class="tag-separator"/>
|
||||
|
||||
{
|
||||
tags.map((tag, i) => (
|
||||
<>
|
||||
@@ -15,3 +22,4 @@ const { publishDate, wordCount, readTime, tags } = Astro.props
|
||||
</>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
|
||||
@@ -16,6 +16,20 @@
|
||||
lnk.className = 'b1-no-underline'
|
||||
lnk.innerHTML = `<span class="mr-1">${"#".repeat(depth)}</span>${tag.innerHTML}`
|
||||
|
||||
// Add smooth scroll with offset
|
||||
lnk.addEventListener('click', (e) => {
|
||||
e.preventDefault()
|
||||
const target = document.getElementById(tag.id)
|
||||
if (target) {
|
||||
const offset = 20 // pixels from top
|
||||
const targetPosition = target.getBoundingClientRect().top + window.pageYOffset - offset
|
||||
window.scrollTo({
|
||||
top: targetPosition,
|
||||
behavior: 'smooth'
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
let li = document.createElement('li')
|
||||
li.className = 'line-clamp-1'
|
||||
li.appendChild(lnk)
|
||||
@@ -24,14 +38,11 @@
|
||||
}
|
||||
|
||||
// get all titles in the page
|
||||
const titles = document.querySelectorAll('h2, h3')
|
||||
const titles = document.querySelectorAll('h1, h2, h3')
|
||||
const toc = document.getElementById('toc') as Element
|
||||
const toc_cnt = document.getElementById('toc-container') as Element
|
||||
|
||||
console.log(titles)
|
||||
|
||||
// if there's more than two titles (ie, there's more than just the article title and TOC title, then appropriate to show)
|
||||
if (titles.length > 2) {
|
||||
if (titles.length > 0) {
|
||||
toc_cnt.classList.add('lg:block')
|
||||
}
|
||||
|
||||
@@ -40,19 +51,7 @@
|
||||
|
||||
// Assign IDs in case they haven't been (by me)
|
||||
t.id = t.innerHTML
|
||||
|
||||
let depth = 1
|
||||
|
||||
// determine what depth to use
|
||||
switch (t.tagName) {
|
||||
case 'H2':
|
||||
depth = 1
|
||||
break
|
||||
case 'H3':
|
||||
depth = 2
|
||||
break
|
||||
}
|
||||
|
||||
let depth = t.tagName.substring(1) as number
|
||||
addLink(t, depth)
|
||||
}
|
||||
</script>
|
||||
@@ -1,18 +1,42 @@
|
||||
---
|
||||
import { getFormattedDate } from "@utils/date"
|
||||
import { calcReadTime } from "@utils/post"
|
||||
import { authPB } from "@utils/pocketbase"
|
||||
import PostInfo from "@components/post/postInfo"
|
||||
|
||||
const pb = await authPB()
|
||||
const { p } = Astro.props
|
||||
|
||||
const wordCount = p.content.split(' ').length;
|
||||
const readTime = calcReadTime(wordCount);
|
||||
---
|
||||
|
||||
<div class="mb-5 w-2/3">
|
||||
<time datetime={p['publishDate']} class=" text-gray-500">{getFormattedDate(p['publishDate'])}</time>
|
||||
<div>
|
||||
<a href={`/posts/${p['slug']}/`} rel="prefetch">
|
||||
<img src={ pb.files.getURL(p, p.headerImage) } class="rounded-xl border-12 border-white"/>
|
||||
{p['title']}
|
||||
<div class="mb-8 pb-7 md:pb-8 border-b border-gray-200 last:border-b-0 flex flex-col md:flex-row gap-4 md:gap-3">
|
||||
<!-- Text content on left -->
|
||||
<div class="flex-1 flex flex-col justify-start order-2 md:order-1">
|
||||
|
||||
<a href={`/posts/${p.slug}/`} rel="prefetch" class="no-underline hover:underline">
|
||||
<h2 class="text-xl ">{p.title}</h2>
|
||||
</a>
|
||||
{
|
||||
p.description !== "" &&
|
||||
<p class="line-clamp-3 block italic text-gray-600">{p.description}</p>
|
||||
}
|
||||
|
||||
|
||||
<PostInfo
|
||||
publishDate={p.publishDate},
|
||||
wordCount={wordCount},
|
||||
readTime={readTime},
|
||||
tags={p.tags}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<!-- Image on right -->
|
||||
<div class="md:max-w-1/2 order-1 md:order-2">
|
||||
<a href={`/posts/${p.slug}/`} rel="prefetch">
|
||||
<img src={ pb.files.getURL(p, p.headerImage) } class="rounded-sm w-full h-48 md:h-full object-cover" alt={p['title']}/>
|
||||
</a>
|
||||
</div>
|
||||
<q class="line-clamp-3 block italic">{p['description']}</q>
|
||||
</div>
|
||||
@@ -3,11 +3,27 @@ import BaseHead from "./BaseHead.astro"
|
||||
import Sidebar from "@components/sidebar"
|
||||
import Navbar from "@components/index/navbar"
|
||||
import Footer from "@components/footer"
|
||||
|
||||
const {
|
||||
meta = {}
|
||||
} = Astro.props;
|
||||
|
||||
const {
|
||||
title = "",
|
||||
description = "",
|
||||
ogImage = "",
|
||||
articleDate = ""
|
||||
} = meta;
|
||||
---
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<BaseHead/>
|
||||
<BaseHead
|
||||
title={title}
|
||||
description={description}
|
||||
ogImage={ogImage}
|
||||
articleDate={articleDate}
|
||||
/>
|
||||
|
||||
</head>
|
||||
|
||||
|
||||
27
web/src/pages/albums/[title].astro
Normal file
27
web/src/pages/albums/[title].astro
Normal file
@@ -0,0 +1,27 @@
|
||||
---
|
||||
import Base from "@layout/Base";
|
||||
import { authPB } from "@utils/pocketbase";
|
||||
export const prerender = false
|
||||
|
||||
const { title } = Astro.params
|
||||
const pb = await authPB()
|
||||
const album = await pb.collection('albums').getFirstListItem(`title ~ "${decodeURI(title!)}"`)
|
||||
const images = await Promise.all(
|
||||
album.images.map(i => pb.files.getURL(album, i))
|
||||
)
|
||||
---
|
||||
|
||||
<Base meta={{title: "album", description: album.title, ogImage: images[0]}}>
|
||||
|
||||
<div slot="sidebar">
|
||||
<h1>{album.title}</h1>
|
||||
<p>{album.date}</p>
|
||||
<p>{album.location}</p><br>
|
||||
|
||||
<Fragment set:html={album.remarks}/>
|
||||
</div>
|
||||
|
||||
<div slot="content">
|
||||
{ images.map(img => <img class="pb-2" src={img}/> ) }
|
||||
</div>
|
||||
</Base>
|
||||
49
web/src/pages/albums/index.astro
Normal file
49
web/src/pages/albums/index.astro
Normal file
@@ -0,0 +1,49 @@
|
||||
---
|
||||
import Base from "@layout/Base";
|
||||
import { authPB } from "@utils/pocketbase";
|
||||
import AlbumCard from "@components/albums/albumCard";
|
||||
export const prerender = false
|
||||
|
||||
const pb = await authPB()
|
||||
|
||||
const albums = await pb.collection('albums').getFullList({
|
||||
sort: '-created'
|
||||
})
|
||||
---
|
||||
|
||||
<script>
|
||||
import Swiper from "swiper";
|
||||
import { Keyboard } from 'swiper/modules';
|
||||
import 'swiper/css';
|
||||
|
||||
const s = new Swiper('.swiper-album', {
|
||||
modules: [Keyboard],
|
||||
slidesPerView: 1,
|
||||
spaceBetween: 0,
|
||||
speed: 400,
|
||||
loop: true,
|
||||
keyboard: {
|
||||
enabled: true,
|
||||
onlyInViewport: false,
|
||||
}
|
||||
}
|
||||
)
|
||||
</script>
|
||||
|
||||
|
||||
<Base meta={{title: "albums"}}>
|
||||
<div slot="sidebar">
|
||||
<p class="text-left"> More focused, themed collections of photos compared to the more miscellaneous images of the (now defunct) <a href="/photos">photos page.</a> Best viewed on a large screen!</p>
|
||||
<!-- <br>
|
||||
← / → -->
|
||||
</div>
|
||||
|
||||
<div slot="content">
|
||||
<div class=" w-full">
|
||||
<div class="">
|
||||
{ albums.map(a => <AlbumCard a={a}/> ) }
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</Base>
|
||||
@@ -3,7 +3,7 @@ import Base from "src/layout/Base.astro"
|
||||
import Sidebar from "src/components/sidebar.astro"
|
||||
---
|
||||
|
||||
<Base>
|
||||
<Base meta={{title: "copyright"}}>
|
||||
<div slot="content">
|
||||
<p class="text-xl">Copyright & License</p>
|
||||
|
||||
|
||||
@@ -1,13 +1,25 @@
|
||||
---
|
||||
import Base from "src/layout/Base.astro"
|
||||
import RecentPosts from "@components/index/recentPosts"
|
||||
import RecentAlbums from "@components/index/recentAlbums"
|
||||
|
||||
export const prerender = false
|
||||
|
||||
---
|
||||
|
||||
<Base>
|
||||
<div slot="sidebar">
|
||||
sidebar stuff
|
||||
<div slot="sidebar" class="text-left">
|
||||
<p>New website new me!</p> <br>
|
||||
|
||||
<p>This is the third major iteration of the website, and by far the most flexible and best designed (imo!)</p><br>
|
||||
|
||||
<p>I'm... really not sure what else to put on this landing page, make sure to check out some of the photos and posts I've added recently, I've been totally revamping those too and I'm really proud of this batch.</p><br>
|
||||
|
||||
<p>Hope you enjoy your time here :)</p>
|
||||
</div>
|
||||
|
||||
<div slot="content">
|
||||
ya
|
||||
<div slot="content" class="md:py-10 space-y-4">
|
||||
<RecentAlbums/>
|
||||
<RecentPosts/>
|
||||
</div>
|
||||
</Base>
|
||||
|
||||
37
web/src/pages/now.astro
Normal file
37
web/src/pages/now.astro
Normal file
@@ -0,0 +1,37 @@
|
||||
---
|
||||
import Base from "@layout/Base";
|
||||
import { authPB } from "@utils/pocketbase";
|
||||
import { getFormattedDate } from '@utils/date'
|
||||
export const prerender = false
|
||||
|
||||
const pb = await authPB()
|
||||
|
||||
const records = await pb.collection('now').getFullList({
|
||||
sort: '-updated'
|
||||
})
|
||||
|
||||
const lastUpdated = getFormattedDate(records[0].updated)
|
||||
---
|
||||
|
||||
<Base meta={{title: "now"}}>
|
||||
|
||||
<div slot="sidebar" class="text-left">
|
||||
<h2>The Now!</h2>
|
||||
<p>Trying out a <a href="https://nownownow.com/about">Now Page</a>, inspired by some other personal sites such as <a href="https://lai.nz">this great one.</a></p><br>
|
||||
|
||||
<p>I'll update this every now and then; the gist is "this is what you would tell someone that you haven't seem in a year, what you've been up to". Love that concept!</p>
|
||||
</div>
|
||||
|
||||
<div slot="content" class="">
|
||||
<p class="mb-1 md:mt-5 italic ">Last Updated: {lastUpdated}</p>
|
||||
{
|
||||
records.map(r => (
|
||||
<div class="mb-3">
|
||||
<h3>{r.heading}</h3>
|
||||
<Fragment set:html={r.content}/>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
|
||||
</Base>
|
||||
@@ -11,19 +11,16 @@ const photos = await pb.collection('photos').getFullList({
|
||||
})
|
||||
---
|
||||
|
||||
<Base>
|
||||
<Base meta={{title: "photos"}}>
|
||||
<div slot="sidebar">
|
||||
<p id="photo-title" class="bold text-2xl">{photos[0].title === "" ? "Untitled" : photos[0].title}</p>
|
||||
<p id="photo-camera" class="text-sm">📸 {photos[0].camera}</p>
|
||||
<p id="photo-film" class="text-sm">{photos[0].film !== "" ? `🎞️ ${photos[0].film}` : ""}</p>
|
||||
<p id="photo-location" class="text-sm">📌 {photos[0].location}</p>
|
||||
|
||||
<button id="dec-button" onclick="dec()"><</button>
|
||||
<span id="current-pos" /> of <span id="max-pos" />
|
||||
<button id="inc-button" onclick="inc()">></button>
|
||||
|
||||
|
||||
<!-- <p class="text-2xl">Photography :)</p>
|
||||
<p class="text-left">Chuck me an email if you'd like a print of these, I'll see what I can do</p> -->
|
||||
</div>
|
||||
|
||||
<div slot="content" class="">
|
||||
|
||||
@@ -20,12 +20,11 @@ const readTime = calcReadTime(wordCount);
|
||||
|
||||
---
|
||||
|
||||
<Base>
|
||||
<Base meta={{title: "post", description: title, ogImage: headerImage, articleDate: publishDate}}>
|
||||
<div slot="sidebar" class="text-left">
|
||||
<p class="text-2xl">{title}</p>
|
||||
|
||||
<!-- <img src={headerImage}/> -->
|
||||
<img src={headerImage}/>
|
||||
|
||||
<p class="text-2xl pt-2">{title}</p>
|
||||
<p class="italic">{description}</p>
|
||||
|
||||
<PostInfo
|
||||
@@ -39,11 +38,15 @@ const readTime = calcReadTime(wordCount);
|
||||
|
||||
</div>
|
||||
|
||||
<div id="post-content" slot="content" class="py-12">
|
||||
<div id="post-content" slot="content" class="md:py-12">
|
||||
<Fragment set:html={content} />
|
||||
</div>
|
||||
|
||||
<style is:global>
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
#post-content img {
|
||||
width: 90%;
|
||||
margin-inline: auto;
|
||||
|
||||
@@ -17,14 +17,14 @@ const tags = posts.flatMap(p => p.tags)
|
||||
const uniqueTags = tags.filter((v, i, a) => a.indexOf(v) === i)
|
||||
---
|
||||
|
||||
<Base>
|
||||
<Base meta={{title: "posts"}}>
|
||||
<div slot="sidebar" class="text-left">
|
||||
A few thoughts I've had, whenever I feel like it as you can see!
|
||||
|
||||
<TagList tags={uniqueTags}/>
|
||||
</div>
|
||||
|
||||
<div slot="content" class="py-12">
|
||||
<div slot="content" class="md:py-12">
|
||||
{ posts.map(p => <PostCard p={p}/>) }
|
||||
</div>
|
||||
</Base>
|
||||
@@ -1,4 +1,33 @@
|
||||
---
|
||||
import Base from "@layout/Base"
|
||||
import PostCard from '@components/postList/postCard'
|
||||
import { authPB } from "@utils/pocketbase"
|
||||
export const prerender = false
|
||||
|
||||
const { tag } = Astro.params
|
||||
const pb = await authPB()
|
||||
|
||||
const posts = await pb.collection('posts').getFullList({
|
||||
sort: '-publishDate',
|
||||
filter: `tags ~ "${tag}" && published=true`
|
||||
})
|
||||
|
||||
const postCount = posts.length
|
||||
const plural = postCount === 1 ? "post" : "posts"
|
||||
---
|
||||
|
||||
export const prerender = false
|
||||
---
|
||||
<Base>
|
||||
|
||||
<div slot="sidebar">
|
||||
<a href="javascript:history.back()">Back</a>
|
||||
|
||||
<p class="text-xl">{postCount} {plural} with tag <code class="bg-gray-300 py-1 px-2 text-sm rounded-md">{tag}</code></p>
|
||||
</div>
|
||||
|
||||
<div slot="content" class="md:py-12">
|
||||
{
|
||||
posts.map(p => <PostCard p={p}/>)
|
||||
}
|
||||
</div>
|
||||
|
||||
</Base>
|
||||
@@ -42,9 +42,22 @@
|
||||
@apply hover:decoration-(--accent);
|
||||
}
|
||||
|
||||
h2 {
|
||||
@apply text-xl;
|
||||
h1 {
|
||||
font-size: 28px;
|
||||
@apply py-4;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.tag-separator {
|
||||
@apply border-l border-gray-400;
|
||||
}
|
||||
|
||||
.content-panel {
|
||||
|
||||
Reference in New Issue
Block a user