meow
This commit is contained in:
parent
2c66a9b678
commit
9aa81ef675
28 changed files with 1735 additions and 242 deletions
2
.github/workflows/nuxtjs.yml
vendored
2
.github/workflows/nuxtjs.yml
vendored
|
@ -41,7 +41,7 @@ jobs:
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pip install python-frontmatter && deno install
|
run: pip install python-frontmatter && deno install
|
||||||
- name: Update Page list
|
- name: Update Page list
|
||||||
run: deno --allow-all utils/pageupdater/update_pagelist.ts
|
run: deno --allow-all utils/page_updater/update_pagelist.ts
|
||||||
- name: Update Page history
|
- name: Update Page history
|
||||||
run: python utils/pageupdater/commit_post_history.py
|
run: python utils/pageupdater/commit_post_history.py
|
||||||
- name: Generate RSS feed
|
- name: Generate RSS feed
|
||||||
|
|
2
app.vue
2
app.vue
|
@ -1,10 +1,8 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { main } from "@popperjs/core";
|
import { main } from "@popperjs/core";
|
||||||
import MainPage from "./pages/index.vue"
|
|
||||||
import backgroundCalm from "./components/BackgroundCalm.vue";
|
import backgroundCalm from "./components/BackgroundCalm.vue";
|
||||||
import Navbar from "./components/Navbar.vue"
|
import Navbar from "./components/Navbar.vue"
|
||||||
import './assets/style/style.css'
|
import './assets/style/style.css'
|
||||||
import siteConfig from '~/assets/config'
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"last_generated": "2025-01-03 21:53:59",
|
"last_generated": "2025-01-03 23:39:05",
|
||||||
"posts": [
|
"posts": [
|
||||||
{
|
{
|
||||||
"metadata": {
|
"metadata": {
|
||||||
|
|
|
@ -9,115 +9,142 @@
|
||||||
margin-top: 1rem;
|
margin-top: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Markdown alerts get a box, a shadow, and a background+border color */
|
// reset all alert styles
|
||||||
.md-contents .markdown-alert:not(.md-override) {
|
.md-contents alert {
|
||||||
@apply rounded-lg border-highlight border-b-2 shadow-primary shadow-md;
|
all: unset;
|
||||||
background-color: #333;
|
@apply border-2 border-primary shadow-primary shadow-md rounded-md w-fit h-fit p-2 block m-4;
|
||||||
padding: 0.5rem;
|
|
||||||
margin: 1rem 0;
|
|
||||||
}
|
}
|
||||||
.md-contents .markdown-alert-title {
|
|
||||||
font-weight: bold;
|
/* Different alert types get different colors */
|
||||||
margin-bottom: 0.5rem;
|
.md-contents alert[type="warning"]:not(.md-override) {
|
||||||
}
|
|
||||||
.md-contents .markdown-alert-warning:not(.md-override) {
|
|
||||||
@apply border-2 border-warning shadow-warning shadow-md;
|
@apply border-2 border-warning shadow-warning shadow-md;
|
||||||
}
|
}
|
||||||
|
|
||||||
.md-contents .markdown-alert-warning .markdown-alert-title:not(.md-override):before {
|
|
||||||
content: "⚠️ ";
|
.md-contents alert[type="warning"]:not(.md-override)::before {
|
||||||
text-shadow: 1px 1px 1px #000;
|
content: "⚠️ Warning ";
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
text-shadow: 0 0 7px black;
|
||||||
}
|
}
|
||||||
.md-contents .markdown-alert-danger:not(.md-override) {
|
|
||||||
|
.md-contents alert[type="danger"]:not(.md-override) {
|
||||||
@apply border-2 border-danger shadow-danger shadow-md;
|
@apply border-2 border-danger shadow-danger shadow-md;
|
||||||
}
|
}
|
||||||
.md-contents .markdown-alert-danger .markdown-alert-title:not(.md-override):before {
|
|
||||||
content: "❌ ";
|
.md-contents alert[type="danger"]:not(.md-override)::before {
|
||||||
text-shadow: 1px 1px 1px #000;
|
content: "❌ Danger ";
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
text-shadow: 0 0 7px black;
|
||||||
}
|
}
|
||||||
.md-contents .markdown-alert-success:not(.md-override) {
|
|
||||||
|
.md-contents alert[type="success"]:not(.md-override) {
|
||||||
@apply border-2 border-success shadow-success shadow-md;
|
@apply border-2 border-success shadow-success shadow-md;
|
||||||
}
|
}
|
||||||
.md-contents .markdown-alert-success .markdown-alert-title:not(.md-override):before {
|
|
||||||
content: "✅ ";
|
.md-contents alert[type="success"]:not(.md-override)::before {
|
||||||
text-shadow: 1px 1px 1px #000;
|
content: "✅ Success ";
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
text-shadow: 0 0 7px black;
|
||||||
}
|
}
|
||||||
.md-contents .markdown-alert-info:not(.md-override) {
|
|
||||||
|
.md-contents alert[type="info"]:not(.md-override) {
|
||||||
@apply border-2 border-info shadow-info shadow-md;
|
@apply border-2 border-info shadow-info shadow-md;
|
||||||
}
|
}
|
||||||
.md-contents .markdown-alert-info .markdown-alert-title:not(.md-override):before {
|
|
||||||
content: "ℹ️ ";
|
.md-contents alert[type="info"]:not(.md-override)::before {
|
||||||
text-shadow: 1px 1px 1px #000;
|
content: "ℹ️ Info ";
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
text-shadow: 0 0 7px black;
|
||||||
}
|
}
|
||||||
.md-contents .markdown-alert-important:not(.md-override) {
|
|
||||||
|
.md-contents alert[type="important"]:not(.md-override) {
|
||||||
@apply border-2 border-important shadow-important shadow-md;
|
@apply border-2 border-important shadow-important shadow-md;
|
||||||
}
|
}
|
||||||
.md-contents .markdown-alert-important .markdown-alert-title:not(.md-override):before {
|
|
||||||
content: "❗ ";
|
.md-contents alert[type="important"]:not(.md-override)::before {
|
||||||
text-shadow: 1px 1px 1px #000;
|
content: "🔥 Important ";
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
text-shadow: 0 0 7px black;
|
||||||
}
|
}
|
||||||
.md-contents .markdown-alert-caution:not(.md-override) {
|
|
||||||
|
.md-contents alert[type="caution"]:not(.md-override) {
|
||||||
@apply border-2 border-warning shadow-warning shadow-md;
|
@apply border-2 border-warning shadow-warning shadow-md;
|
||||||
}
|
}
|
||||||
.md-contents .markdown-alert-caution .markdown-alert-title:not(.md-override):before {
|
|
||||||
content: "⚠️ ";
|
.md-contents alert[type="caution"]:not(.md-override)::before {
|
||||||
text-shadow: 1px 1px 1px #000;
|
content: "⚠️ Caution ";
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
text-shadow: 0 0 7px black;
|
||||||
}
|
}
|
||||||
.md-contents .markdown-alert-note:not(.md-override) {
|
|
||||||
|
.md-contents alert[type="note"]:not(.md-override) {
|
||||||
@apply border-2 border-note shadow-note shadow-md;
|
@apply border-2 border-note shadow-note shadow-md;
|
||||||
}
|
}
|
||||||
.md-contents .markdown-alert-note .markdown-alert-title:not(.md-override):before {
|
|
||||||
content: "📝 ";
|
.md-contents alert[type="note"]:not(.md-override)::before {
|
||||||
text-shadow: 1px 1px 1px #000;
|
content: "📝 Note ";
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
text-shadow: 0 0 7px black;
|
||||||
}
|
}
|
||||||
.md-contents .markdown-alert-tip:not(.md-override) {
|
|
||||||
|
.md-contents alert[type="tip"]:not(.md-override) {
|
||||||
@apply border-2 border-tip shadow-tip shadow-md;
|
@apply border-2 border-tip shadow-tip shadow-md;
|
||||||
}
|
}
|
||||||
.md-contents .markdown-alert-tip .markdown-alert-title:not(.md-override):before {
|
|
||||||
content: "💡 ";
|
.md-contents alert[type="tip"]:not(.md-override)::before {
|
||||||
text-shadow: 1px 1px 1px #000;
|
content: "💡 Tip ";
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
text-shadow: 0 0 7px black;
|
||||||
}
|
}
|
||||||
.md-contents .markdown-alert-question:not(.md-override) {
|
|
||||||
@apply border-2 border-note shadow-note shadow-md;
|
.md-contents alert[type="deprecated"]:not(.md-override) {
|
||||||
}
|
|
||||||
.md-contents .markdown-alert-question .markdown-alert-title:not(.md-override):before {
|
|
||||||
content: "❓ ";
|
|
||||||
text-shadow: 1px 1px 1px #000;
|
|
||||||
}
|
|
||||||
.md-contents .markdown-alert-quote:not(.md-override) {
|
|
||||||
@apply border-2 border-note shadow-note shadow-md border-dashed;
|
|
||||||
}
|
|
||||||
.md-contents .markdown-alert-quote .markdown-alert-title:not(.md-override):before {
|
|
||||||
content: "❝ ";
|
|
||||||
text-shadow: 1px 1px 1px #000;
|
|
||||||
}
|
|
||||||
.md-contents .markdown-alert-deprecated:not(.md-override) {
|
|
||||||
@apply border-2 border-danger shadow-danger shadow-md;
|
@apply border-2 border-danger shadow-danger shadow-md;
|
||||||
}
|
}
|
||||||
.md-contents .markdown-alert-deprecated .markdown-alert-title:not(.md-override):before {
|
|
||||||
content: "🚫 ";
|
.md-contents alert[type="deprecated"]:not(.md-override)::before {
|
||||||
text-shadow: 1px 1px 1px #000;
|
content: "🚫 Deprecated ";
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
text-shadow: 0 0 7px black;
|
||||||
}
|
}
|
||||||
.md-contents .markdown-alert-example:not(.md-override) {
|
|
||||||
|
.md-contents alert[type="example"]:not(.md-override) {
|
||||||
@apply border-2 border-info shadow-info shadow-md;
|
@apply border-2 border-info shadow-info shadow-md;
|
||||||
}
|
}
|
||||||
.md-contents .markdown-alert-example .markdown-alert-title:not(.md-override):before {
|
|
||||||
content: "💡 ";
|
.md-contents alert[type="example"]:not(.md-override)::before {
|
||||||
text-shadow: 1px 1px 1px #000;
|
content: "💡 Example ";
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
text-shadow: 0 0 7px black;
|
||||||
}
|
}
|
||||||
.md-contents .markdown-alert-todo:not(.md-override) {
|
|
||||||
|
.md-contents alert[type="todo"]:not(.md-override) {
|
||||||
@apply border-2 border-warning shadow-warning shadow-md;
|
@apply border-2 border-warning shadow-warning shadow-md;
|
||||||
}
|
}
|
||||||
.md-contents .markdown-alert-todo .markdown-alert-title:not(.md-override):before {
|
|
||||||
content: "📝 ";
|
.md-contents alert[type="todo"]:not(.md-override)::before {
|
||||||
text-shadow: 1px 1px 1px #000;
|
content: "📝 Todo ";
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
text-shadow: 0 0 7px black;
|
||||||
}
|
}
|
||||||
.md-contents .markdown-alert-done:not(.md-override) {
|
|
||||||
|
.md-contents alert[type="done"]:not(.md-override) {
|
||||||
@apply border-2 border-success shadow-success shadow-md;
|
@apply border-2 border-success shadow-success shadow-md;
|
||||||
}
|
}
|
||||||
.md-contents .markdown-alert-done .markdown-alert-title:not(.md-override):before {
|
|
||||||
content: "✅ ";
|
.md-contents alert[type="done"]:not(.md-override)::before {
|
||||||
text-shadow: 1px 1px 1px #000;
|
content: "✅ Done ";
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
text-shadow: 0 0 7px black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.md-contents alert[type="quote"]:not(.md-override) {
|
||||||
|
@apply border-2 border-note shadow-note shadow-md border-dashed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.md-contents alert[type="quote"]:not(.md-override)::before {
|
||||||
|
content: "🗨️ Quote ";
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
text-shadow: 0 0 7px black;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Footnotes, ensure they are by default white with no transition if they have no href */
|
/* Footnotes, ensure they are by default white with no transition if they have no href */
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
<NuxtParticles
|
<NuxtParticles
|
||||||
class="w-full h-full z-0"
|
class="w-full h-full z-0"
|
||||||
id="tsparticles"
|
id="tsparticles"
|
||||||
@load="onLoad"
|
|
||||||
:particlesInit="particlesInit"
|
:particlesInit="particlesInit"
|
||||||
:options="{
|
:options="{
|
||||||
fullScreen: {
|
fullScreen: {
|
||||||
|
|
|
@ -1,63 +1,13 @@
|
||||||
<script setup lang="ts">
|
<script setup>
|
||||||
import { globalMarkdown, MarkdownInput } from '~/assets/markdown_conf';
|
|
||||||
import { useSlots } from 'vue';
|
|
||||||
import fm, { type FrontMatterResult } from 'front-matter';
|
|
||||||
import '~/assets/style/markdown.scss';
|
import '~/assets/style/markdown.scss';
|
||||||
|
|
||||||
// External inputs (reactive)
|
// External inputs (reactive)
|
||||||
const input = defineProps({
|
const input = defineProps({
|
||||||
input: {
|
input: Object
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
type: {
|
|
||||||
type: String,
|
|
||||||
default: "markdown",
|
|
||||||
validator: (value: string) => ["markdown", "html"].includes(value)
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Internal state
|
|
||||||
const contents = ref("");
|
|
||||||
const loading = ref(false);
|
|
||||||
const error = ref("");
|
|
||||||
|
|
||||||
const emit = defineEmits(["metadata"]);
|
|
||||||
|
|
||||||
function updateContents(input: string | undefined, type: "markdown" | "html" | undefined) {
|
|
||||||
if (input === "" || input === undefined) {
|
|
||||||
contents.value = "";
|
|
||||||
error.value = "";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const markdown_input: MarkdownInput = type ?
|
|
||||||
type === "markdown" ?
|
|
||||||
MarkdownInput.from_markdown(input) : type == "html" ?
|
|
||||||
MarkdownInput.from_html(input) :
|
|
||||||
MarkdownInput.from_markdown(input) : MarkdownInput.from_markdown(input);
|
|
||||||
|
|
||||||
console.log("Rendering...")
|
|
||||||
const render = globalMarkdown.render(markdown_input);
|
|
||||||
console.log("Metadata: ", render.metadata);
|
|
||||||
contents.value = render.contents;
|
|
||||||
emit("metadata", render.metadata);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
watch(() => input.input, (newVal) => {
|
|
||||||
updateContents(newVal, input.type as "markdown" | "html");
|
|
||||||
}, { immediate: true });
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div v-if="loading" class="text-center animate-pulse">
|
<ContentRendererMarkdown :value="input.input" class="md-contents" />
|
||||||
<h2>Loading...</h2>
|
|
||||||
</div>
|
|
||||||
<div v-else-if="error" class="text-center">
|
|
||||||
<h2>Error: {{ error }}</h2>
|
|
||||||
</div>
|
|
||||||
<div v-else class="md-contents">
|
|
||||||
<div class="md-contents" v-html="contents"></div>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
|
@ -59,7 +59,6 @@ useSeoMeta({
|
||||||
twitterTitle: fullTitle,
|
twitterTitle: fullTitle,
|
||||||
twitterDescription: description,
|
twitterDescription: description,
|
||||||
twitterImage: background
|
twitterImage: background
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
useHead({
|
useHead({
|
||||||
|
@ -87,6 +86,15 @@ useHead({
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
definePageMeta({
|
||||||
|
title: fullTitle,
|
||||||
|
description: description,
|
||||||
|
"og:title": fullTitle,
|
||||||
|
"og:description": description,
|
||||||
|
"og:image": background,
|
||||||
|
"og:url": siteConfig.siteUrl,
|
||||||
|
});
|
||||||
|
|
||||||
watch(() => props.title, (newVal) => {
|
watch(() => props.title, (newVal) => {
|
||||||
title.value = newVal;
|
title.value = newVal;
|
||||||
fullTitle.value = title.value + ' | ' + siteConfig.siteTitle;
|
fullTitle.value = title.value + ' | ' + siteConfig.siteTitle;
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
<script setup>
|
<script setup lang="ts">
|
||||||
import { ref, watch } from 'vue';
|
import { ref, watch } from 'vue';
|
||||||
import fm from 'front-matter';
|
import fm from 'front-matter';
|
||||||
import { auto } from '@popperjs/core';
|
import { auto } from '@popperjs/core';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
url: String,
|
url: String,
|
||||||
tagFilter: Array,
|
tagFilter: Array<String>,
|
||||||
// Allow sm, md, and lg
|
// Allow sm, md, and lg
|
||||||
size: {
|
size: {
|
||||||
type: String,
|
type: String,
|
||||||
|
@ -14,49 +14,59 @@ const props = defineProps({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const url = ref(props.url)
|
const url: Ref<string> = ref("")
|
||||||
url.value = props.url
|
url.value = props.url as string
|
||||||
|
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const data = ref(null)
|
const error: Ref<string | null> = ref(null)
|
||||||
const error = ref(null)
|
const markdown: Ref<any> = ref(null)
|
||||||
|
|
||||||
const background = ref('');
|
const background: Ref<string> = ref('');
|
||||||
const title = ref(null);
|
const title: Ref<string> = ref("Untitled");
|
||||||
const description = ref(null);
|
const description: Ref<string | null> = ref(null);
|
||||||
const date = ref(null);
|
const date: Ref<string | null> = ref(null);
|
||||||
const tags = ref(null);
|
const tags: Ref<string | null> = ref(null);
|
||||||
|
|
||||||
|
watch(url, (newVal) => {
|
||||||
|
fetchData(newVal)
|
||||||
|
}, { immediate: true })
|
||||||
|
|
||||||
// watch the params of the route to fetch the data again
|
watch(markdown, (newVal) => {
|
||||||
watch(url, async () => {
|
if (newVal) {
|
||||||
await fetchData()
|
title.value = newVal.title ? newVal.title : ""
|
||||||
}, {
|
description.value = newVal.description ? newVal.description : ""
|
||||||
immediate: true
|
date.value = newVal.date ? new Date(newVal.date).toLocaleDateString() : ""
|
||||||
});
|
tags.value = newVal.tags ? newVal.tags : []
|
||||||
|
background.value = newVal.background ? newVal.background : ""
|
||||||
|
|
||||||
async function fetchData() {
|
console.log("Title: " + title.value)
|
||||||
error.value = data.value = null
|
console.log("Description: " + description.value)
|
||||||
|
console.log("Date: " + date.value)
|
||||||
|
console.log("Tags: " + tags.value)
|
||||||
|
console.log("Background: " + background.value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
async function fetchData(url: string) {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
|
// remove all .md extensions
|
||||||
|
const temp_url = url.replace(/\.md$/, '')
|
||||||
|
console.log("Fetching article: " + temp_url)
|
||||||
try {
|
try {
|
||||||
data.value = await (await fetch(url.value)).text()
|
const {data} = await useAsyncData(temp_url, () => queryContent(temp_url).findOne())
|
||||||
console.log(url.value)
|
|
||||||
const processed = fm(data.value)
|
|
||||||
background.value = processed.attributes.background
|
|
||||||
title.value = processed.attributes.title
|
|
||||||
description.value = processed.attributes.description
|
|
||||||
console.log(JSON.stringify(processed.attributes.date))
|
|
||||||
date.value = processed.attributes.date
|
|
||||||
tags.value = processed.attributes.tags
|
|
||||||
|
|
||||||
date.value = new Date(date.value).toLocaleDateString()
|
markdown.value = data.value;
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
error.value = err.toString()
|
error.value = err.toString()
|
||||||
loading.value = false
|
loading.value = false
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
fetchData(url.value)
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -79,7 +89,7 @@ async function fetchData() {
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-center">
|
<div class="flex justify-center">
|
||||||
<div v-for="tag in tags" :key="tag" class="m-1 text-center">
|
<div v-for="tag in tags" :key="tag" class="m-1 text-center">
|
||||||
<div v-if="props.tagFilter.includes(tag)">
|
<div v-if="props.tagFilter?.includes(tag)">
|
||||||
<span class="text-xs bg-slate-700 border-white border-2 text-white p-1 rounded-md">{{ tag }}</span>
|
<span class="text-xs bg-slate-700 border-white border-2 text-white p-1 rounded-md">{{ tag }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
|
@ -111,13 +121,13 @@ async function fetchData() {
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-center">
|
<div class="flex justify-center">
|
||||||
<div v-for="tag in tags" :key="tag" class="m-1 text-center">
|
<div v-for="tag in tags" :key="tag" class="m-1 text-center">
|
||||||
<div v-if="props.tagFilter.includes(tag)">
|
<div v-if="props.tagFilter?.includes(tag)">
|
||||||
<span class="text-xs bg-slate-700 border-white border-2 text-white p-1 rounded-md">{{ tag }}</span>
|
<span class="text-xs bg-slate-700 border-white border-2 text-white p-1 rounded-md">{{ tag }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<div class="flex justify-center">
|
<div class="flex justify-center">
|
||||||
<div v-for="tag in tags" :key="tag" class="m-1 text-center">
|
<div v-for="tag in tags" :key="tag" class="m-1 text-center">
|
||||||
<div v-if="props.tagFilter.includes(tag)">
|
<div v-if="props.tagFilter?.includes(tag)">
|
||||||
<span class="text-xs bg-slate-700 border-white border-2 text-white p-1 rounded-md">{{ tag }}</span>
|
<span class="text-xs bg-slate-700 border-white border-2 text-white p-1 rounded-md">{{ tag }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
|
---
|
||||||
|
title: About Me
|
||||||
|
prop: true
|
||||||
|
---
|
||||||
|
|
||||||
<h1 class="md-override">Helau! :3</h1>
|
<h1 class="md-override">Helau! :3</h1>
|
||||||
|
|
||||||
## 🌙 Luna - She/Her
|
## 🌙 Luna - She/Her
|
|
@ -13,15 +13,9 @@ tags: ['lgbtq+', 'resources']
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
> [!important]
|
::alert{type="note"}
|
||||||
> If you are in severe crisis or thinking about doing *that*, please contact the Trevor Project or Trans Lifeline.
|
I will try not to provide medical advice, for I am not a doctor. Please consult a medical professional for any medical advice.
|
||||||
> The Trevor Project can be contacted through [the site](https://www.thetrevorproject.org/), or by calling `1-866-488-7386`.
|
::
|
||||||
> Trans Lifeline can be contacted via these phone numbers:
|
|
||||||
> - `877-565-8860` in the US
|
|
||||||
> - `877-330-6366` in Canada
|
|
||||||
|
|
||||||
> [!note]
|
|
||||||
> I will try not to provide medical advice, for I am not a doctor. Please consult a medical professional for any medical advice.
|
|
||||||
|
|
||||||
If you find that this list is lacking or inaccurate, please open a GitHub issue or pull request.
|
If you find that this list is lacking or inaccurate, please open a GitHub issue or pull request.
|
||||||
|
|
|
@ -134,9 +134,6 @@ $$
|
||||||
|
|
||||||
### Alerts
|
### Alerts
|
||||||
|
|
||||||
> [!warning]
|
|
||||||
> This is a warning alert.
|
|
||||||
|
|
||||||
> [!info]
|
> [!info]
|
||||||
> This is an info alert.
|
> This is an info alert.
|
||||||
|
|
||||||
|
@ -176,6 +173,48 @@ $$
|
||||||
> [!done]
|
> [!done]
|
||||||
> This is a done alert.
|
> This is a done alert.
|
||||||
|
|
||||||
|
<!-- new vers-->
|
||||||
|
> ::alert{type="warning"}
|
||||||
|
> The **alert** component - Warning
|
||||||
|
> ::
|
||||||
|
>
|
||||||
|
> ::alert{type="danger"}
|
||||||
|
> The **alert** component - Danger
|
||||||
|
> ::
|
||||||
|
>
|
||||||
|
> ::alert{type="info"}
|
||||||
|
> The **alert** component - Info
|
||||||
|
> ::
|
||||||
|
>
|
||||||
|
> ::alert{type="success"}
|
||||||
|
> The **alert** component - Success
|
||||||
|
> ::
|
||||||
|
>
|
||||||
|
> ::alert{type="example"}
|
||||||
|
> The **alert** component - Example
|
||||||
|
> ::
|
||||||
|
>
|
||||||
|
> ::alert{type="note"}
|
||||||
|
> The **alert** component - Note
|
||||||
|
> ::
|
||||||
|
>
|
||||||
|
> ::alert{type="tip"}
|
||||||
|
> The **alert** component - Tip
|
||||||
|
> ::
|
||||||
|
>
|
||||||
|
> ::alert{type="important"}
|
||||||
|
> The **alert** component - Important
|
||||||
|
> ::
|
||||||
|
>
|
||||||
|
> ::alert{type="caution"}
|
||||||
|
> The **alert** component - Caution
|
||||||
|
> ::
|
||||||
|
>
|
||||||
|
> ::alert{type="quote"}
|
||||||
|
> The **alert** component - Quote
|
||||||
|
> ::
|
||||||
|
|
||||||
|
|
||||||
### Iframes
|
### Iframes
|
||||||
|
|
||||||
<!-- The towwwn inside meeeeee :3 -->
|
<!-- The towwwn inside meeeeee :3 -->
|
|
@ -1,8 +1,28 @@
|
||||||
|
import * as pages from '~/utils/page_updater/update_pagelist';
|
||||||
|
|
||||||
|
const blog_list: pages.PageList = (await import('./assets/meta/blog_list.json')) as pages.PageList;
|
||||||
|
|
||||||
|
const blog_routes: any = blog_list.posts.map((post) => {
|
||||||
|
return {
|
||||||
|
['/blog?post=' + post.id]: {
|
||||||
|
prerender: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
blog_routes.push({
|
||||||
|
'/blog': {
|
||||||
|
prerender: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(blog_routes);
|
||||||
|
|
||||||
// https://nuxt.com/docs/api/configuration/nuxt-config
|
// https://nuxt.com/docs/api/configuration/nuxt-config
|
||||||
export default defineNuxtConfig({
|
export default defineNuxtConfig({
|
||||||
compatibilityDate: '2024-11-01',
|
compatibilityDate: '2024-11-01',
|
||||||
ssr: false,
|
ssr: true,
|
||||||
|
routeRules: blog_routes,
|
||||||
postcss: {
|
postcss: {
|
||||||
plugins: {
|
plugins: {
|
||||||
tailwindcss: {},
|
tailwindcss: {},
|
||||||
|
@ -16,8 +36,13 @@ export default defineNuxtConfig({
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
modules: [
|
modules: [
|
||||||
'nuxt-particles'
|
'nuxt-particles',
|
||||||
|
'@nuxt/content'
|
||||||
],
|
],
|
||||||
|
content: {
|
||||||
|
// ... options
|
||||||
|
|
||||||
|
},
|
||||||
particles: {
|
particles: {
|
||||||
mode: 'slim',
|
mode: 'slim',
|
||||||
lazy: true
|
lazy: true
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
"@mdit/plugin-tab": "^0.14.0",
|
"@mdit/plugin-tab": "^0.14.0",
|
||||||
"@mdit/plugin-tasklist": "^0.14.0",
|
"@mdit/plugin-tasklist": "^0.14.0",
|
||||||
"@mdit/plugin-tex": "^0.14.0",
|
"@mdit/plugin-tex": "^0.14.0",
|
||||||
|
"@nuxt/content": "^2.13.4",
|
||||||
"@popperjs/core": "^2.11.8",
|
"@popperjs/core": "^2.11.8",
|
||||||
"@tsparticles/slim": "^3.7.1",
|
"@tsparticles/slim": "^3.7.1",
|
||||||
"@types/markdown-it": "^14.1.2",
|
"@types/markdown-it": "^14.1.2",
|
||||||
|
|
|
@ -4,8 +4,9 @@ import fm from 'front-matter';
|
||||||
|
|
||||||
import PostCard from '../components/PostCard.vue';
|
import PostCard from '../components/PostCard.vue';
|
||||||
import { globalMarkdown, MarkdownInput, type MarkdownMetadata } from '~/assets/markdown_conf';
|
import { globalMarkdown, MarkdownInput, type MarkdownMetadata } from '~/assets/markdown_conf';
|
||||||
import * as pages from '~/utils/pageupdater/update_pagelist';
|
import * as pages from '~/utils/page_updater/update_pagelist';
|
||||||
import type { PageInfo, PageInfoMetdata } from '~/utils/pageupdater/pages';
|
import type { PageInfo, PageInfoMetdata } from '~/utils/page_updater/pages';
|
||||||
|
import type { ParsedContent } from '@nuxt/content';
|
||||||
|
|
||||||
// Automatically maintained is a blog_list.json in assets/meta. This file contains a list of all blog posts and their metadata.
|
// Automatically maintained is a blog_list.json in assets/meta. This file contains a list of all blog posts and their metadata.
|
||||||
// This file is generated by a script in the utils/pageupdater folder.
|
// This file is generated by a script in the utils/pageupdater folder.
|
||||||
|
@ -27,18 +28,27 @@ const tagList: Ref<any> = ref([])
|
||||||
const tagFilter: Ref<string[]> = ref([])
|
const tagFilter: Ref<string[]> = ref([])
|
||||||
tagFilter.value = []
|
tagFilter.value = []
|
||||||
|
|
||||||
const article_contents: Ref<string> = ref("")
|
const markdown: Ref<any> = ref(null)
|
||||||
|
|
||||||
const metadata: Ref<PageInfoMetdata> = ref({
|
const title: Ref<string> = ref("")
|
||||||
title: "",
|
const description: Ref<string> = ref("")
|
||||||
description: "",
|
const date: Ref<string> = ref("")
|
||||||
date: "",
|
const tags: Ref<string[]> = ref([])
|
||||||
tags: [],
|
const background: Ref<string> = ref("")
|
||||||
background: ""
|
const next: Ref<string> = ref("")
|
||||||
|
const previous: Ref<string> = ref("")
|
||||||
|
|
||||||
|
watch(markdown , (newVal) => {
|
||||||
|
if (newVal) {
|
||||||
|
title.value = newVal.title ? newVal.title : ""
|
||||||
|
description.value = newVal.description ? newVal.description : ""
|
||||||
|
date.value = newVal.date ? new Date(newVal.date).toLocaleDateString() : ""
|
||||||
|
tags.value = newVal.tags ? newVal.tags : []
|
||||||
|
background.value = newVal.background ? newVal.background : ""
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// watch the params of the route to fetch the data again
|
// watch the params of the route to fetch the data again
|
||||||
|
|
||||||
watch(route, async () => {
|
watch(route, async () => {
|
||||||
url.value = route.query.post as string
|
url.value = route.query.post as string
|
||||||
if (url.value) {
|
if (url.value) {
|
||||||
|
@ -65,14 +75,23 @@ async function fetchList() {
|
||||||
// Sort the posts by date, most recent first
|
// Sort the posts by date, most recent first
|
||||||
list.value.sort((a: any, b: any) => b.metadata.date.localeCompare(a.metadata.date))
|
list.value.sort((a: any, b: any) => b.metadata.date.localeCompare(a.metadata.date))
|
||||||
}
|
}
|
||||||
fetchList()
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await fetchList()
|
||||||
|
await fetchArticle(url.value)
|
||||||
|
})
|
||||||
|
|
||||||
// Fetch the article contents from the URL
|
// Fetch the article contents from the URL
|
||||||
async function fetchArticle(url: string) {
|
async function fetchArticle(url: string) {
|
||||||
const post = blog_list.posts.find(post => post.url === url)
|
const post = blog_list.posts.find(post => post.url === url)
|
||||||
if (post) {
|
if (post) {
|
||||||
const response = await fetch(post.url)
|
// Trim the .md extension
|
||||||
article_contents.value = await response.text()
|
var url = url.replace(/\.md$/, "")
|
||||||
|
console.log("Fetching article: " + url)
|
||||||
|
const { data } = await useAsyncData(url, () => queryContent(url).findOne())
|
||||||
|
console.log(data)
|
||||||
|
|
||||||
|
markdown.value = data.value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,17 +99,22 @@ function resetReadingPosition() {
|
||||||
window.scrollTo(0, 0)
|
window.scrollTo(0, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateMetadata(meta: MarkdownMetadata) {
|
fetchArticle(url.value)
|
||||||
metadata.value = meta
|
|
||||||
}
|
|
||||||
|
console.log("Prefetching blog")
|
||||||
|
await fetchList();
|
||||||
|
const temp_url = route.query.post as string
|
||||||
|
await fetchArticle(temp_url);
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="relative z-50 flex w-full justify-center text-white">
|
<div class="relative z-50 flex w-full justify-center text-white">
|
||||||
<!-- Metadata -->
|
<!-- Metadata -->
|
||||||
<MetaSet :title="metadata.title" :description="metadata.description" :date="metadata.date"
|
<MetaSet :title="title" :description="description" :date="date"
|
||||||
:background="metadata.background" :tags="metadata.tags" />
|
:background="background" :tags="tags" />
|
||||||
|
|
||||||
<!-- Main Content -->
|
<!-- Main Content -->
|
||||||
<div class="mt-8 flex-col text-center">
|
<div class="mt-8 flex-col text-center">
|
||||||
|
@ -123,10 +147,10 @@ function updateMetadata(meta: MarkdownMetadata) {
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<Transition name="list">
|
<Transition name="list">
|
||||||
<div class="flex flex-col" :key="url">
|
<div class="flex flex-col" :key="url">
|
||||||
<h1>{{ metadata.title }}</h1>
|
<h1>{{ title }}</h1>
|
||||||
<small>{{ metadata.date ? new Date(metadata.date).toLocaleDateString() : "" }}</small>
|
<small>{{ date }}</small>
|
||||||
<div class="max-w-50 flex flex-row justify-center">
|
<div class="max-w-50 flex flex-row justify-center">
|
||||||
<div v-for="tag in metadata.tags" :key="tag" class="m-1 text-center">
|
<div v-for="tag in tags" :key="tag" class="m-1 text-center">
|
||||||
<span
|
<span
|
||||||
class="text-xs bg-black border-purple-400 border-2 text-white p-1 rounded-md">{{
|
class="text-xs bg-black border-purple-400 border-2 text-white p-1 rounded-md">{{
|
||||||
tag }}</span>
|
tag }}</span>
|
||||||
|
@ -136,11 +160,11 @@ function updateMetadata(meta: MarkdownMetadata) {
|
||||||
<!-- Next/Prev controls, on the left and right side using PostCards -->
|
<!-- Next/Prev controls, on the left and right side using PostCards -->
|
||||||
<div class="flex max-w-4xl max-md:w-screen">
|
<div class="flex max-w-4xl max-md:w-screen">
|
||||||
<div class="justify-start">
|
<div class="justify-start">
|
||||||
<NuxtLink v-if="metadata.previous" :onclick="resetReadingPosition" :to="metadata.previous"
|
<NuxtLink v-if="previous" :onclick="resetReadingPosition" :to="previous"
|
||||||
class="m-2 text-white">Previous</NuxtLink>
|
class="m-2 text-white">Previous</NuxtLink>
|
||||||
</div>
|
</div>
|
||||||
<div class="justify-end">
|
<div class="justify-end">
|
||||||
<NuxtLink v-if="metadata.next" :onclick="resetReadingPosition" :to="metadata.next"
|
<NuxtLink v-if="next" :onclick="resetReadingPosition" :to="next"
|
||||||
class="m-2 text-white">Next</NuxtLink>
|
class="m-2 text-white">Next</NuxtLink>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -148,15 +172,17 @@ function updateMetadata(meta: MarkdownMetadata) {
|
||||||
<!-- Article Content -->
|
<!-- Article Content -->
|
||||||
<Card class="text-pretty max-w-4xl mt-4 max-md:w-screen text-left">
|
<Card class="text-pretty max-w-4xl mt-4 max-md:w-screen text-left">
|
||||||
<article>
|
<article>
|
||||||
<Markdown :input="article_contents" type="markdown" @metadata="updateMetadata" />
|
<div v-if="markdown != null">
|
||||||
|
<Markdown :input="markdown" />
|
||||||
|
</div>
|
||||||
<!-- Aligned next/prev controls -->
|
<!-- Aligned next/prev controls -->
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<div class="justify-start">
|
<div class="justify-start">
|
||||||
<NuxtLink v-if="metadata.previous" :onclick="resetReadingPosition" :to="metadata.previous"
|
<NuxtLink v-if="previous" :onclick="resetReadingPosition" :to="previous"
|
||||||
class="m-2 text-white">Previous</NuxtLink>
|
class="m-2 text-white">Previous</NuxtLink>
|
||||||
</div>
|
</div>
|
||||||
<div class="justify-end">
|
<div class="justify-end">
|
||||||
<NuxtLink v-if="metadata.next" :onclick="resetReadingPosition" :to="metadata.next"
|
<NuxtLink v-if="next" :onclick="resetReadingPosition" :to="next"
|
||||||
class="m-2 text-white">Next</NuxtLink>
|
class="m-2 text-white">Next</NuxtLink>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,23 +2,19 @@
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import Markdown from '~/components/Markdown.vue';
|
import Markdown from '~/components/Markdown.vue';
|
||||||
import Card from '~/components/Card.vue';
|
import Card from '~/components/Card.vue';
|
||||||
import MetaSet from '~/components/MetaSet.vue';
|
|
||||||
|
|
||||||
const aboutMe = ref("");
|
const aboutMe = ref('');
|
||||||
|
const test = ref('');
|
||||||
|
|
||||||
fetch("/about_me.md")
|
const { data } = await useAsyncData('about_me', () => queryContent('/about_me').findOne())
|
||||||
.then((res) => res.text())
|
|
||||||
.then((data) => {
|
|
||||||
console.log(data);
|
|
||||||
aboutMe.value = data;
|
|
||||||
});
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="relative flex w-full justify-center text-white">
|
<div class="relative flex w-full justify-center text-white">
|
||||||
<MetaSet title="Home" description="TheFelidae's personal site :3" tags="home, personal, author"/>
|
<!-- Metadata -->
|
||||||
|
<MetaSet title="TheFelidae" description="TheFelidae's personal site :3" background="https://avatars.githubusercontent.com/u/94077364?v=4"
|
||||||
|
tags="home, personal, author" />
|
||||||
<div class="mt-8 flex-col text-center">
|
<div class="mt-8 flex-col text-center">
|
||||||
<div class="flex justify-center">
|
<div class="flex justify-center">
|
||||||
<div id="PFP" class="shadow-md rounded-full shadow-highlight">
|
<div id="PFP" class="shadow-md rounded-full shadow-highlight">
|
||||||
|
@ -28,6 +24,7 @@ fetch("/about_me.md")
|
||||||
</div>
|
</div>
|
||||||
<Card class="max-w-4xl mt-4 max-md:w-screen">
|
<Card class="max-w-4xl mt-4 max-md:w-screen">
|
||||||
<Markdown :input="aboutMe" type="markdown"></Markdown>
|
<Markdown :input="aboutMe" type="markdown"></Markdown>
|
||||||
|
<ContentRendererMarkdown :value="data" class="md-content" />
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -101,7 +101,7 @@ def generate_post_history(pages_info, state):
|
||||||
return post_history_json
|
return post_history_json
|
||||||
|
|
||||||
# Get the pages info from the public/blog directory
|
# Get the pages info from the public/blog directory
|
||||||
pages_info = pages.get_pages_info("", "public/blog")
|
pages_info = pages.get_pages_info("", "assets/blog")
|
||||||
|
|
||||||
# Load the previous state from the assets/post_history.json file
|
# Load the previous state from the assets/post_history.json file
|
||||||
try:
|
try:
|
|
@ -52,7 +52,7 @@ def generate_page_list(pages_info):
|
||||||
return page_list_json
|
return page_list_json
|
||||||
|
|
||||||
# Print the page list
|
# Print the page list
|
||||||
post_list = generate_page_list(pages.get_pages_info("", "public/blog"));
|
post_list = generate_page_list(pages.get_pages_info("", "assets/blog"));
|
||||||
print(post_list)
|
print(post_list)
|
||||||
|
|
||||||
# Output to assets/blog_list.json (overwriting)
|
# Output to assets/blog_list.json (overwriting)
|
|
@ -23,7 +23,7 @@ function generatePageList(pagesInfo: Record<string, any>): PageList {
|
||||||
const pageDict: Page = {
|
const pageDict: Page = {
|
||||||
metadata: page.metadata,
|
metadata: page.metadata,
|
||||||
id: page.local_path,
|
id: page.local_path,
|
||||||
url: page.absolute_path.replace("public", ""),
|
url: page.absolute_path.replace("assets", ""),
|
||||||
hash: page.hash
|
hash: page.hash
|
||||||
};
|
};
|
||||||
pageList.push(pageDict);
|
pageList.push(pageDict);
|
||||||
|
@ -44,7 +44,7 @@ function generatePageList(pagesInfo: Record<string, any>): PageList {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the page list and print it
|
// Get the page list and print it
|
||||||
const postList = generatePageList(pages.getPagesInfo("", "public/blog"));
|
const postList = generatePageList(pages.getPagesInfo("", "assets/blog"));
|
||||||
console.log(JSON.stringify(postList, null, 2));
|
console.log(JSON.stringify(postList, null, 2));
|
||||||
|
|
||||||
// Output to assets/blog_list.json (overwriting)
|
// Output to assets/blog_list.json (overwriting)
|
Loading…
Add table
Reference in a new issue