This commit is contained in:
Mrrp 2025-01-03 21:50:12 -08:00
parent 328a6bd178
commit 1dbf1e4074
21 changed files with 1046 additions and 545 deletions

View file

@ -1,11 +1,3 @@
<script setup lang="ts">
import { Engine } from '@tsparticles/engine';
const onLoad = (container: Container) => {
}
</script>
<template>
<div>
<NuxtParticles

View file

@ -1,5 +1,5 @@
<template>
<div class="m-1 p-5 rounded-lg container bg-opacity-90 bg-slate-900 border-purple-600 border-2">
<div class="m-1 p-5 rounded-lg container bg-opacity-90 bg-slate-900 border-primary border-2">
<slot></slot>
</div>
</template>
@ -8,9 +8,8 @@
/* Glow effect on border, plus a slight response to hover by changing the border color near the cursor */
.container {
@apply border-primary border-2 shadow-primary shadow-md;
transition: border-color 0.5s;
border-color: #A020F0;
box-shadow: 0 0 10px #A020F0;
}
</style>

View file

@ -1,306 +1,63 @@
<script setup lang="ts">
import configured_markdown from '~/assets/markdown_conf';
import { globalMarkdown, MarkdownInput } from '~/assets/markdown_conf';
import { useSlots } from 'vue';
import fm, { type FrontMatterResult } from 'front-matter';
import '~/assets/style/markdown.scss';
const text = ref("");
const loading = ref(true);
const props = defineProps({
text: String,
// External inputs (reactive)
const input = defineProps({
input: {
type: String,
required: true
},
type: {
type: String,
default: "markdown",
validator: (value: string) => ["markdown", "html"].includes(value)
}
});
function render_markdown(data: string | undefined) {
// Validate that the data is a string
if (typeof data !== 'string') {
loading.value = false;
// 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;
}
text.value = configured_markdown().render(data);
loading.value = false
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(() => props.text, (newVal) => {
render_markdown(newVal);
}, {
immediate: true
});
watch(() => input.input, (newVal) => {
updateContents(newVal, input.type as "markdown" | "html");
}, { immediate: true });
</script>
<template>
<div v-if="loading" class="text-center animate-pulse">
<h2>Loading...</h2>
</div>
<div class="md-contents" v-html="text"></div>
</template>
<style>
/* Note: Use `md-override` to override the default markdown styling */
/* Headers (1-3) get a small visual upgrade */
.md-contents h1:not(.md-override), .md-contents h2:not(.md-override), .md-contents h3:not(.md-override) {
border-bottom: 1px solid #A020F0;
padding-bottom: 0.25rem;
margin-bottom: 0.5rem;
margin-top: 1rem;
}
/* Markdown alerts get a box, a shadow, and a background+border color */
.md-contents .markdown-alert:not(.md-override) {
@apply rounded-lg;
border: 1px solid #A020F0;
background-color: #333;
box-shadow: 0 0 7px #A020F0;
padding: 0.5rem;
margin: 1rem 0;
}
.md-contents .markdown-alert-title {
font-weight: bold;
margin-bottom: 0.5rem;
}
.md-contents .markdown-alert-warning:not(.md-override) {
border: 1px solid #FFA500;
background-color: #aa6600;
box-shadow: 0 0 7px #FFA500;
}
.md-contents .markdown-alert-warning .markdown-alert-title:not(.md-override):before {
content: "⚠️ ";
text-shadow: 1px 1px 1px #000;
}
.md-contents .markdown-alert-danger:not(.md-override) {
border: 1px solid #FF0000;
background-color: #aa0000;
box-shadow: 0 0 7px #FF0000;
}
.md-contents .markdown-alert-danger .markdown-alert-title:not(.md-override):before {
content: "❌ ";
text-shadow: 1px 1px 1px #000;
}
.md-contents .markdown-alert-success:not(.md-override) {
border: 1px solid #00FF00;
box-shadow: 0 0 7px #00FF00;
}
.md-contents .markdown-alert-success .markdown-alert-title:not(.md-override):before {
content: "✅ ";
text-shadow: 1px 1px 1px #000;
}
.md-contents .markdown-alert-info:not(.md-override) {
border: 1px solid #00FFFF;
box-shadow: 0 0 7px #00FFFF;
}
.md-contents .markdown-alert-info .markdown-alert-title:not(.md-override):before {
content: " ";
text-shadow: 1px 1px 1px #000;
}
.md-contents .markdown-alert-important:not(.md-override) {
border: 1px solid #FF00FF;
box-shadow: 0 0 7px #FF00FF;
}
.md-contents .markdown-alert-important .markdown-alert-title:not(.md-override):before {
content: "❗ ";
text-shadow: 1px 1px 1px #000;
}
.md-contents .markdown-alert-caution:not(.md-override) {
border: 1px solid #FFFF00;
box-shadow: 0 0 7px #FFFF00;
}
.md-contents .markdown-alert-caution .markdown-alert-title:not(.md-override):before {
content: "⚠️ ";
text-shadow: 1px 1px 1px #000;
}
.md-contents .markdown-alert-note:not(.md-override) {
border: 1px solid #0000FF;
box-shadow: 0 0 7px #0000FF;
}
.md-contents .markdown-alert-note .markdown-alert-title:not(.md-override):before {
content: "📝 ";
text-shadow: 1px 1px 1px #000;
}
.md-contents .markdown-alert-tip:not(.md-override) {
border: 1px solid #00FF00;
box-shadow: 0 0 7px #00FF00;
}
.md-contents .markdown-alert-tip .markdown-alert-title:not(.md-override):before {
content: "💡 ";
text-shadow: 1px 1px 1px #000;
}
.md-contents .markdown-alert-question:not(.md-override) {
border: 1px solid #ccc;
box-shadow: 0 0 7px #ccc;
}
.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) {
border: 1px dashed #ccc;
box-shadow: 0 0 7px #ccc;
}
.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) {
border: 1px solid indigo;
box-shadow: 0 0 7px indigo;
}
.md-contents .markdown-alert-deprecated .markdown-alert-title:not(.md-override):before {
content: "🚫 ";
text-shadow: 1px 1px 1px #000;
}
.md-contents .markdown-alert-example:not(.md-override) {
border: 1px solid #ccc;
box-shadow: 0 0 7px #ccc;
}
.md-contents .markdown-alert-example .markdown-alert-title:not(.md-override):before {
content: "💡 ";
text-shadow: 1px 1px 1px #000;
}
.md-contents .markdown-alert-todo:not(.md-override) {
border: 1px solid skyblue;
box-shadow: 0 0 7px skyblue;
}
.md-contents .markdown-alert-todo .markdown-alert-title:not(.md-override):before {
content: "📝 ";
text-shadow: 1px 1px 1px #000;
}
.md-contents .markdown-alert-done:not(.md-override) {
border: 1px solid #00FF00;
box-shadow: 0 0 7px #00FF00;
}
.md-contents .markdown-alert-done .markdown-alert-title:not(.md-override):before {
content: "✅ ";
text-shadow: 1px 1px 1px #000;
}
/* Footnotes, ensure they are by default white with no transition if they have no href */
.md-contents .footnote-anchor:not([href]) {
color: white;
transition: none;
}
/* Apply margin to code blocks */
.md-contents pre:not(.md-override) {
margin: 1rem 0;
}
/* Apply styling to lists and list items with nesting */
.md-contents ul:not(.md-override), .md-contents ol:not(.md-override) {
margin: 1rem 0;
padding-left: 1rem;
}
/* Apply different bullet types to nested lists as they are nested */
.md-contents ul:not(.md-override) ul:not(.md-override), .md-contents ol:not(.md-override) ul:not(.md-override) {
list-style-type: circle;
}
/* Apply table styling, with a pleasant glowing border effect */
.md-contents table:not(.md-override) {
border-collapse: collapse;
width: 100%;
border: 1px solid #333;
box-shadow: 0 0 7px #A020F0
}
.md-contents th:not(.md-override), .md-contents td:not(.md-override) {
border: 1px solid #A020F0;
padding: 0.5rem;
}
.md-contents th:not(.md-override) {
background-color: #333;
color: white;
}
Hosted
.md-contents button:not(.md-override) {
background-color: #333;
color: white;
border: 1px solid #A020F0;
padding: 0.5rem;
border-radius: 0.25rem;
box-shadow: 0 0 7px #A020F0;
}
.md-contents button:hover:not(.md-override) {
background-color: #555;
box-shadow: 0 0 10px #A020F0;
color: white;
}
/* transition for button hover effect */
.md-contents button:not(.md-override) {
transition: background-color 0.5s;
}
/* iframes get a border and a shadow */
.md-contents iframe:not(.md-override) {
@apply border-2 border-purple-600 rounded-lg;
box-shadow: 0 0 7px #A020F0;
}
/* Identify if the iframe links to a YouTube video - If so, red border */
.md-contents iframe[src*="youtube.com"]:not(.md-override) {
@apply border-2 border-red-600;
box-shadow: 0 0 7px red;
}
/* Identify if the iframe links to a Twitch video - If so, purple border */
.md-contents iframe[src*="twitch.tv"]:not(.md-override) {
@apply border-2 border-purple-600;
box-shadow: 0 0 7px #A020F0;
}
/* Identify if the iframe links to a Spotify playlist - If so, green border */
.md-contents iframe[src*="spotify.com"]:not(.md-override) {
@apply border-2 border-green-600 rounded-xl;
box-shadow: 0 0 7px green;
}
/* Identify if the iframe links to a SoundCloud track - If so, orange border */
.md-contents iframe[src*="soundcloud.com"]:not(.md-override) {
@apply border-2 border-orange-600;
box-shadow: 0 0 7px orange;
}
/* Identify if the iframe links to a Bandcamp track - If so, blue border */
.md-contents iframe[src*="bandcamp.com"]:not(.md-override) {
@apply border-2 border-blue-600;
box-shadow: 0 0 7px blue;
}
/* Identify if the iframe links to a GitHub Gist - If so, gray border */
.md-contents iframe[src*="gist.github.com"]:not(.md-override) {
@apply border-2 border-gray-600;
box-shadow: 0 0 7px gray;
}
/* And if it links to Wikipedia, the free online encyclopedia, give it a white border */
.md-contents iframe[src*="wikipedia.org"]:not(.md-override) {
@apply border-2 border-white;
box-shadow: 0 0 7px white;
}
/* If the iframe links to a Google Maps location */
.md-contents iframe[src*="google.com/maps"]:not(.md-override) {
border-left: 2px solid #4285F4;
border-right: 2px solid #0F9D58;
border-top: 2px solid #F4B400;
border-bottom: 2px solid #DB4437;
box-shadow: 0 0 7px #4285F4;
}
/* Redo the audio element styling */
.md-contents audio:not(.md-override) {
@apply border-2 border-purple-600 rounded-lg;
box-shadow: 0 0 7px #A020F0;
}
/* Images get a border and a shadow */
.md-contents img:not(.md-override) {
@apply border-2 border-purple-600;
box-shadow: 0 0 7px #A020F0;
}
</style>
<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>

View file

@ -4,11 +4,26 @@ import { ref } from 'vue';
import siteConfig from '../assets/config';
const props = defineProps({
title: String,
description: String,
date: String,
tags: Array<String>,
background: String
title: {
type: String,
default: 'Untitled'
},
description: {
type: String,
default: 'No description provided'
},
date: {
type: String,
default: 'No date provided'
},
tags: {
type: Array<String>,
default: []
},
background: {
type: String,
default: ''
}
});
const tags: Ref<String[]> = ref(props.tags || []);
@ -39,7 +54,7 @@ useSeoMeta({
ogSiteName: siteConfig.siteTitle,
ogLocale: 'en_US',
ogLocaleAlternate: 'en_GB',
themeColor: siteConfig.sitePrimaryColor,
themeColor: siteConfig.siteColor,
twitterCard: 'summary',
twitterTitle: fullTitle,
twitterDescription: description,
@ -54,7 +69,7 @@ useHead({
{ name: 'keywords', content: tagsToString(tags.value) },
{ name: 'author', content: siteConfig.siteAuthor },
{ name: 'date', content: date },
{ name: 'theme-color', content: siteConfig.sitePrimaryColor },
{ name: 'theme-color', content: siteConfig.siteColor },
{ name: 'twitter:card', content: 'summary' },
{ name: 'twitter:title', content: fullTitle },
{ name: 'twitter:description', content: description },
@ -72,4 +87,31 @@ useHead({
]
})
</script>
watch(() => props.title, (newVal) => {
title.value = newVal;
fullTitle.value = title.value + ' | ' + siteConfig.siteTitle;
});
watch(() => props.description, (newVal) => {
description.value = newVal;
});
watch(() => props.date, (newVal) => {
date.value = newVal;
});
watch(() => props.tags, (newVal) => {
tags.value = newVal;
});
watch(() => props.background, (newVal) => {
background.value = newVal;
});
</script>
<template>
<div>
<slot></slot>
</div>
</template>