blog/src/routes/(DefaultStyle)/post/[slug]/+page.svelte

243 lines
No EOL
5.6 KiB
Svelte

<script lang="ts">
const { data } = $props();
import Toc from './toc.svelte'
import Share from './share.svelte'
import Profile from './profile.svelte'
import Metadata from './metadata.svelte'
import { base } from '$app/paths';
import { PUBLIC_HOSTNAME, PUBLIC_POST_REPO } from '$env/static/public';
import { onMount } from 'svelte';
import '$lib/components/Markdown/rainbow.css';
import '$lib/components/Markdown/Markdown.css';
const baseURL = `https://${PUBLIC_HOSTNAME}${base}`
const canonical = `${baseURL}/post/${data.id}/`
const thumbnail = data.metadata.thumbnail
// toggle code frame
onMount(()=>{
let toggleHide = function(e:MessageEvent){
if (e.data.message === 'toggleHide') {
let iframes = document.querySelectorAll('.markdown iframe')
for ( let i = 0; i < iframes.length; i++ ){
let frame = iframes[i]
if (frame instanceof HTMLIFrameElement && new URL(frame.src).origin === window.origin && frame.contentWindow?.name == e.data.id){
iframes[i].classList.toggle('hide')
}
}
}
}
window.addEventListener('message', toggleHide)
return ()=>{window.removeEventListener('message', toggleHide)}
})
// header marker
onMount(()=>{
function setMarkerClass(id:string) {
let hash = document.querySelector("#blog .marker");
if (hash instanceof HTMLElement) {
hash.classList.remove("marker")
}
let element = document.querySelector(id);
if(element instanceof HTMLElement){
element.classList.add("marker")
}
}
if(location.hash) {
setMarkerClass(decodeURI(location.hash));
}
function hashChange(){
let urlHash = decodeURI(location.hash);
setMarkerClass(urlHash);
}
window.addEventListener('hashchange', hashChange)
return ()=>{window.removeEventListener('hashchange', hashChange)}
})
// copy code
onMount(()=>{
document.getElementById('blog')?.addEventListener('click',(e:MouseEvent)=>{
if (e.target instanceof HTMLElement && e.target.matches('pre')) {
let code = e.target.textContent || '';
navigator.clipboard.writeText(code)
}
})
})
</script>
<svelte:head>
<title>{data.metadata.title} | moris.day Blog</title>
<link rel="canonical" href="{canonical}"/>
<meta name="description" content="{data.metadata.description}"/>
<meta property="og:url" content="{canonical}"/>
<meta property="og:title" content="{data.metadata.title}"/>
<meta property="og:description" content="{data.metadata.description}"/>
<meta property="og:type" content="article"/>
<meta property="og:locale" content="ja_JP"/>
{#if URL.parse(thumbnail)?.protocol == 'https:' && !thumbnail.endsWith('svg')}
<meta property="og:image" content="{thumbnail}"/>
{:else}
<meta property="og:image" content="https://moris.day/apple-touch-icon.png"/>
{/if}
{#if !data.metadata.index}
<meta name="robots" content="noindex"/>
{/if}
</svelte:head>
<div id='blog'>
<article>
<h1 class="title">{data.metadata.title}</h1>
<Metadata meta={data.metadata}/>
{#if thumbnail}
<img class="thumbnail image" alt="thumbnail" src="{thumbnail}"/>
{:else if data.metadata.emoji}
<img class="thumbnail emoji" alt="thumbnail" src="{data.metadata.emoji}"/>
{/if}
<div class='markdown'>
{@html data.post}
</div>
<div class="data">
{#if PUBLIC_POST_REPO}
<a class="dataItem" target="_blank" href="{PUBLIC_POST_REPO}/commits/branch/main/Posts/{data.id}.md">History</a>
<div class="spacer"></div>
{/if}
<a class="dataItem" target="_blank" href="raw.md">Raw file</a>
</div>
</article>
<aside>
<div id='side'>
<div class='sidecontent'>
<Profile/>
</div>
<div class='sidecontent'>
<Share url={canonical} title={data.metadata.title} />
</div>
{#if data.toc.length}
<div class='sidecontent'>
<Toc toclist={data.toc}></Toc>
</div>
{/if}
<iframe class='miframe sidecontent js' src="https://mi.moris.day/embed/user-timeline/9w7bhjzt2b5z0001?maxHeight=320&rounded=false&border=false" title="moris's posts" loading="lazy" style="border:none;"></iframe>
</aside>
</div>
<style>
#blog {
display: flex;
gap: 20px;
@media(width>999px) {
margin: 25px 2.5%;
}
}
article {
min-width: 0;
max-width: 60rem;
margin-inline: auto;
flex-grow: 1;
padding: 0 20px;
@media(width<640px){
padding: 0;
}
}
.title {
font-size: 2em;
font-weight: 600;
margin: 8px;
padding: 8px;
border-bottom: 1px solid;
@media(width<480px) {
font-size: 1.5em;
}
}
.thumbnail {
display: block;
margin-inline: auto;
max-height: 50vh;
}
.image {
max-width: 100%;
object-fit: contain;
}
.emoji {
width: 30%;
max-width: 25vh;
margin-block: 5vh;
}
.markdown {
margin: 1em;
@media (width<480px) {margin: 8px;}
}
.data {
display: flex;
padding: 8px;
margin: 12px;
border-top: solid 1px;
font-weight: 200;
font-size: 1.2em;
}
.dataItem {
color: currentColor;
text-decoration-thickness: 1px;
}
.spacer {
width: 1px;
margin: 2px .5em;
background-color: currentColor;
}
aside {
@media (width<1000px) {
display: none;
}
min-width: 280px;
max-width: 320px;
margin: 25px 0;
}
#side {
display: flex;
flex-direction: column;
row-gap: 20px;
position: sticky;
top: 60px;
}
.sidecontent {
background-color: var(--grid-color);
transition: background-color 1s;
border-radius: 8px;
box-shadow: 0 0 6px #1111;
overflow: hidden;
}
.miframe {
height: 320px;
@media(prefers-color-scheme: dark) {
border: solid #444 1px;
}
}
</style>