Compare commits
No commits in common. "994a41f9e38ef418716fe20df2adc7cd140980f4" and "1a16556ec3eb2a5780823a8518d4dd2d6013a8b0" have entirely different histories.
994a41f9e3
...
1a16556ec3
21 changed files with 741 additions and 1861 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,6 +1,5 @@
|
||||||
# build output
|
# build output
|
||||||
dist/
|
dist/
|
||||||
dc09ru.zip
|
|
||||||
|
|
||||||
# generated types
|
# generated types
|
||||||
.astro/
|
.astro/
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
import { defineConfig } from 'astro/config';
|
import { defineConfig } from 'astro/config';
|
||||||
|
|
||||||
import compress from "astro-compress";
|
import compress from "astro-compress";
|
||||||
|
|
||||||
import remarkPostMeta from './remark-post-meta.mjs';
|
import remarkPostMeta from './remark-post-meta.mjs';
|
||||||
|
import remarkUnwrapImages from 'remark-unwrap-images';
|
||||||
import { rehypeHeadingIds } from '@astrojs/markdown-remark';
|
import { rehypeHeadingIds } from '@astrojs/markdown-remark';
|
||||||
import rehypeSlug from 'rehype-slug';
|
import rehypeSlug from 'rehype-slug';
|
||||||
import rehypeAutolinkHeadings from 'rehype-autolink-headings';
|
import rehypeAutolinkHeadings from 'rehype-autolink-headings';
|
||||||
|
|
||||||
import rehypeFigure from 'rehype-figure';
|
import rehypeFigure from 'rehype-figure';
|
||||||
|
|
||||||
|
// https://astro.build/config
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
integrations: [compress()],
|
integrations: [compress()],
|
||||||
build: {
|
build: {
|
||||||
|
@ -19,6 +18,7 @@ export default defineConfig({
|
||||||
syntaxHighlight: "shiki",
|
syntaxHighlight: "shiki",
|
||||||
remarkPlugins: [
|
remarkPlugins: [
|
||||||
remarkPostMeta,
|
remarkPostMeta,
|
||||||
|
remarkUnwrapImages,
|
||||||
],
|
],
|
||||||
rehypePlugins: [
|
rehypePlugins: [
|
||||||
rehypeSlug,
|
rehypeSlug,
|
||||||
|
@ -27,7 +27,10 @@ export default defineConfig({
|
||||||
rehypeAutolinkHeadings,
|
rehypeAutolinkHeadings,
|
||||||
{ behavior: "append" },
|
{ behavior: "append" },
|
||||||
],
|
],
|
||||||
|
[
|
||||||
rehypeFigure,
|
rehypeFigure,
|
||||||
|
{ className: "rehype-figure" },
|
||||||
|
],
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
});
|
});
|
2172
package-lock.json
generated
2172
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -13,12 +13,11 @@
|
||||||
"astro": "^2.3.0",
|
"astro": "^2.3.0",
|
||||||
"astro-compress": "^1.1.42",
|
"astro-compress": "^1.1.42",
|
||||||
"less": "^4.1.3",
|
"less": "^4.1.3",
|
||||||
|
"mdast-util-to-string": "^3.2.0",
|
||||||
"reading-time": "^1.5.0",
|
"reading-time": "^1.5.0",
|
||||||
"rehype-autolink-headings": "^6.1.1",
|
"rehype-autolink-headings": "^6.1.1",
|
||||||
"rehype-figure": "^1.0.1",
|
"rehype-figure": "^1.0.1",
|
||||||
"rehype-slug": "^5.1.0",
|
"rehype-slug": "^5.1.0",
|
||||||
"remark-unwrap-images": "^3.0.1",
|
"remark-unwrap-images": "^3.0.1"
|
||||||
"unist-util-select": "^4.0.3",
|
|
||||||
"unist-util-visit": "^4.1.2"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Before Width: | Height: | Size: 229 KiB After Width: | Height: | Size: 229 KiB |
|
@ -1,26 +1,16 @@
|
||||||
import { select } from 'unist-util-select'
|
import getReadingTime from 'reading-time';
|
||||||
import { visit } from 'unist-util-visit'
|
import { toString as mdToString } from 'mdast-util-to-string';
|
||||||
import getReadingTime from 'reading-time'
|
|
||||||
|
|
||||||
export default function remarkPostMeta() {
|
const ELLIPSIS = '\u2026';
|
||||||
return (tree, { data }) => {
|
|
||||||
const firstPara = select(':not(heading) paragraph:first-of-type', tree)
|
|
||||||
|
|
||||||
const firstParaText = mdToString(firstPara)
|
export default function() {
|
||||||
const articleText = mdToString(tree)
|
return function (tree, { data }) {
|
||||||
|
const textOnPage = mdToString(tree);
|
||||||
|
|
||||||
const readingTime = getReadingTime(articleText)
|
const readingTime = getReadingTime(textOnPage);
|
||||||
data.astro.frontmatter.readingTime = readingTime.minutes
|
data.astro.frontmatter.readingTime = readingTime.text;
|
||||||
|
|
||||||
data.astro.frontmatter.description = firstParaText
|
const description = textOnPage.slice(0, 100) + ELLIPSIS;
|
||||||
}
|
data.astro.frontmatter.description = description;
|
||||||
}
|
};
|
||||||
|
|
||||||
function mdToString(tree) {
|
|
||||||
let str = ''
|
|
||||||
visit(tree, 'text', node => {
|
|
||||||
str += node.value
|
|
||||||
str += ' '
|
|
||||||
})
|
|
||||||
return str.trimEnd()
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,44 +0,0 @@
|
||||||
---
|
|
||||||
import { AstroComponentFactory } from 'astro/dist/runtime/server';
|
|
||||||
|
|
||||||
export interface Props {
|
|
||||||
PostContent: AstroComponentFactory;
|
|
||||||
title: string;
|
|
||||||
description: string;
|
|
||||||
readingTime: number;
|
|
||||||
date: Date;
|
|
||||||
image: string;
|
|
||||||
preview?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
const {
|
|
||||||
PostContent, title, description,
|
|
||||||
readingTime, date, image,
|
|
||||||
preview = false,
|
|
||||||
} = Astro.props;
|
|
||||||
|
|
||||||
const locale = 'ru';
|
|
||||||
---
|
|
||||||
|
|
||||||
<header>
|
|
||||||
<h1>{title}</h1>
|
|
||||||
<address>
|
|
||||||
on
|
|
||||||
<time datetime={date?.toISOString()}>{date?.toLocaleDateString(locale)}</time>
|
|
||||||
by
|
|
||||||
<a href="https://t.me/dcat09" rel="author">DarkCat09</a>
|
|
||||||
//
|
|
||||||
<span id="reading-time">
|
|
||||||
{readingTime.toFixed(1)} min read
|
|
||||||
</span>
|
|
||||||
</address>
|
|
||||||
</header>
|
|
||||||
{
|
|
||||||
!preview ?
|
|
||||||
<article>
|
|
||||||
<PostContent />
|
|
||||||
</article> :
|
|
||||||
<div>
|
|
||||||
{description}
|
|
||||||
</div>
|
|
||||||
}
|
|
|
@ -1,113 +0,0 @@
|
||||||
---
|
|
||||||
import Article from './Article.astro';
|
|
||||||
import MenuItem from './MenuItem.astro';
|
|
||||||
|
|
||||||
import postRenderer from '../post-renderer.mjs';
|
|
||||||
import { postType } from '../post-renderer.mjs';
|
|
||||||
|
|
||||||
export interface Props {
|
|
||||||
post: postType;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { post } = Astro.props;
|
|
||||||
const {
|
|
||||||
PostContent, title, description,
|
|
||||||
readingTime, date, image,
|
|
||||||
} = await postRenderer(post);
|
|
||||||
const link = `/blog/${post.slug}`;
|
|
||||||
---
|
|
||||||
|
|
||||||
<div class="card card-elevated" id={post.slug}>
|
|
||||||
<Article
|
|
||||||
PostContent={PostContent}
|
|
||||||
title={title}
|
|
||||||
description={description}
|
|
||||||
readingTime={readingTime}
|
|
||||||
date={date}
|
|
||||||
image={image}
|
|
||||||
preview={true}
|
|
||||||
/>
|
|
||||||
<ul>
|
|
||||||
<MenuItem
|
|
||||||
name={/*Comments*/""}
|
|
||||||
link={`/blog/comments#${post.slug}`}
|
|
||||||
icon=""
|
|
||||||
atLeft={false}
|
|
||||||
/>
|
|
||||||
<MenuItem
|
|
||||||
name={/*Copy Link*/""}
|
|
||||||
link="javascript:void(0)"
|
|
||||||
icon=""
|
|
||||||
atLeft={false}>
|
|
||||||
<span slot="js-dataset"
|
|
||||||
data-btn="copylink"
|
|
||||||
data-url={link}>
|
|
||||||
</span>
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem
|
|
||||||
name={/*Share*/""}
|
|
||||||
link="javascript:void(0)"
|
|
||||||
icon=""
|
|
||||||
atLeft={false}>
|
|
||||||
<span slot="js-dataset"
|
|
||||||
data-btn="share"
|
|
||||||
data-title={title}
|
|
||||||
data-url={link}>
|
|
||||||
</span>
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem
|
|
||||||
name="Read"
|
|
||||||
link={link}
|
|
||||||
icon=""
|
|
||||||
atLeft={false}
|
|
||||||
/>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.card {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul {
|
|
||||||
list-style: none;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: end;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
const onclickCopy = (ev: Event) => {
|
|
||||||
const li = ev.currentTarget as HTMLLIElement
|
|
||||||
const js = li.querySelector('[data-btn]') as HTMLElement
|
|
||||||
navigator.clipboard.writeText(
|
|
||||||
location.origin + js.dataset.url
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const onclickShare = (ev: Event) => {
|
|
||||||
const li = ev.currentTarget as HTMLLIElement
|
|
||||||
const js = li.querySelector('[data-btn]') as HTMLElement
|
|
||||||
const data = js.dataset
|
|
||||||
navigator.share({
|
|
||||||
title: "Share the article",
|
|
||||||
text: data.title,
|
|
||||||
url: data.url,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const btnsCopy = document.querySelectorAll('[data-btn="copylink"]')
|
|
||||||
const btnsShare = document.querySelectorAll('[data-btn="share"]')
|
|
||||||
|
|
||||||
for (let el of btnsCopy) {
|
|
||||||
(el.parentElement as HTMLLIElement).addEventListener('click', onclickCopy)
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let el of btnsShare) {
|
|
||||||
(el.parentElement as HTMLLIElement).addEventListener('click', onclickShare)
|
|
||||||
}
|
|
||||||
</script>
|
|
|
@ -5,42 +5,44 @@ export interface Props {
|
||||||
name: string;
|
name: string;
|
||||||
link: string;
|
link: string;
|
||||||
icon: string;
|
icon: string;
|
||||||
atLeft?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const { name, link, icon, atLeft = true } = Astro.props;
|
const { name, link, icon } = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
<li>
|
<li>
|
||||||
<slot name="js-dataset"></slot>
|
|
||||||
<a href={link}>
|
<a href={link}>
|
||||||
{!atLeft ? name : ""}
|
<span class="icon-wrapper">
|
||||||
<span class:list={["icon-wrapper", {"highlight-icon": name == ""}]}>
|
|
||||||
<Icon code={icon} />
|
<Icon code={icon} />
|
||||||
</span>
|
</span>
|
||||||
{atLeft ? name : ""}
|
{name}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
|
li {
|
||||||
|
// 0.6rem = horizontal padding
|
||||||
|
padding: 0 0.6rem;
|
||||||
|
font-size: 1.15rem;
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
font-size: 1.15rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-wrapper {
|
.icon-wrapper {
|
||||||
margin-right: 0.2rem;
|
margin-right: 0.25rem;
|
||||||
width: 1.5rem;
|
|
||||||
|
|
||||||
font-size: 1.5rem;
|
font-size: 1.5rem;
|
||||||
color: var(--fg-sec);
|
color: var(--fg-sec);
|
||||||
|
|
||||||
&.highlight-icon:hover {
|
|
||||||
color: var(--fg);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|
|
@ -23,4 +23,5 @@ console.log(Object.keys({a:'a',b:'b'}))
|
||||||
|
|
||||||
> Quotation
|
> Quotation
|
||||||
|
|
||||||
![Forest](/assets/forest.jpg)
|
![Forest](https://dc09.ru/forest.jpg)
|
||||||
|
123
|
||||||
|
|
|
@ -14,10 +14,9 @@ const { title, description } = Astro.props;
|
||||||
<Layout title={"Blog | " + title} description={description}>
|
<Layout title={"Blog | " + title} description={description}>
|
||||||
<slot name="metadata" slot="metadata" />
|
<slot name="metadata" slot="metadata" />
|
||||||
<Menu>
|
<Menu>
|
||||||
<MenuItem name="Home" icon="" link="/" />
|
<MenuItem name="All articles" icon="" link="/blog" />
|
||||||
<MenuItem name="Articles" icon="" link="/blog" />
|
|
||||||
<MenuItem name="Shorts" icon="" link="/blog/shorts" />
|
<MenuItem name="Shorts" icon="" link="/blog/shorts" />
|
||||||
<MenuItem name="Random" icon="" link="/blog/random" />
|
<MenuItem name="Random post" icon="" link="/blog/random" />
|
||||||
<MenuItem name="Search" icon="" link="/blog/search" />
|
<MenuItem name="Search" icon="" link="/blog/search" />
|
||||||
</Menu>
|
</Menu>
|
||||||
<main>
|
<main>
|
||||||
|
|
|
@ -33,9 +33,6 @@ const { title, description } = Astro.props;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
||||||
box-sizing: border-box;
|
|
||||||
padding: 0 0.5rem;
|
|
||||||
|
|
||||||
background: var(--bg);
|
background: var(--bg);
|
||||||
color: var(--fg);
|
color: var(--fg);
|
||||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Liberation Sans', 'Helvetica Neue', sans-serif;
|
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Liberation Sans', 'Helvetica Neue', sans-serif;
|
||||||
|
@ -61,16 +58,9 @@ const { title, description } = Astro.props;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
|
border: 0.125rem dashed var(--bg-sec);
|
||||||
border-radius: var(--bdrs);
|
border-radius: var(--bdrs);
|
||||||
padding: 0.625rem;
|
padding: 0.625rem;
|
||||||
|
|
||||||
&.card-dashed {
|
|
||||||
border: 0.125rem dashed var(--bg-sec);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.card-elevated {
|
|
||||||
background: var(--accent-bg);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hgroup > * {
|
hgroup > * {
|
||||||
|
@ -94,10 +84,6 @@ const { title, description } = Astro.props;
|
||||||
font-size: 1.1rem;
|
font-size: 1.1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1, h2, h3, h4, h5, h6 {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
a, .link {
|
a, .link {
|
||||||
color: var(--accent-fg);
|
color: var(--accent-fg);
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
|
|
@ -8,7 +8,6 @@ import ThemeModal from "../components/ThemeModal.astro";
|
||||||
<ul>
|
<ul>
|
||||||
<slot />
|
<slot />
|
||||||
</ul>
|
</ul>
|
||||||
<ul class="menu-sep"></ul>
|
|
||||||
<ul>
|
<ul>
|
||||||
<MenuItem name="Theme" icon="" link="#theme" />
|
<MenuItem name="Theme" icon="" link="#theme" />
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -17,9 +16,6 @@ import ThemeModal from "../components/ThemeModal.astro";
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
@import "/src/styles/max_width.less";
|
@import "/src/styles/max_width.less";
|
||||||
|
|
||||||
// distance between menu items
|
|
||||||
@items-dist: 0.8rem;
|
|
||||||
|
|
||||||
nav {
|
nav {
|
||||||
margin: 0.4rem auto;
|
margin: 0.4rem auto;
|
||||||
max-width: @max-width;
|
max-width: @max-width;
|
||||||
|
@ -37,10 +33,5 @@ import ThemeModal from "../components/ThemeModal.astro";
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
column-gap: @items-dist;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.menu-sep {
|
|
||||||
min-width: @items-dist;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -25,7 +25,6 @@ const { id } = Astro.props;
|
||||||
display: none;
|
display: none;
|
||||||
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
|
@ -38,7 +37,7 @@ const { id } = Astro.props;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
a.modal-bg-close {
|
.modal-bg-close {
|
||||||
display: block;
|
display: block;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,24 @@
|
||||||
---
|
---
|
||||||
import BlogLayout from "../../layouts/BlogLayout.astro";
|
import BlogLayout from "../../layouts/BlogLayout.astro";
|
||||||
import Article from "../../components/Article.astro";
|
|
||||||
|
|
||||||
import { getCollection } from "astro:content";
|
import { getCollection } from "astro:content";
|
||||||
import postRenderer from "../../post-renderer.mjs";
|
|
||||||
|
import { AstroComponentFactory } from "astro/dist/runtime/server";
|
||||||
|
import { MarkdownHeading } from "astro";
|
||||||
|
|
||||||
|
type postType = {
|
||||||
|
id: string;
|
||||||
|
slug: string;
|
||||||
|
body: string;
|
||||||
|
collection: "blog";
|
||||||
|
data: any;
|
||||||
|
} & {
|
||||||
|
render(): Promise<{
|
||||||
|
Content: AstroComponentFactory;
|
||||||
|
headings: MarkdownHeading[];
|
||||||
|
remarkPluginFrontmatter: Record<string, any>;
|
||||||
|
}>;
|
||||||
|
};
|
||||||
|
|
||||||
export async function getStaticPaths() {
|
export async function getStaticPaths() {
|
||||||
const posts = await getCollection("blog");
|
const posts = await getCollection("blog");
|
||||||
|
@ -16,14 +31,29 @@ export async function getStaticPaths() {
|
||||||
}
|
}
|
||||||
|
|
||||||
const { slug } = Astro.params;
|
const { slug } = Astro.params;
|
||||||
const { post } = Astro.props;
|
const post: postType = Astro.props.post;
|
||||||
|
|
||||||
const {
|
const postObj = await post.render();
|
||||||
PostContent, title, description,
|
const fm = postObj.remarkPluginFrontmatter;
|
||||||
readingTime, date, image,
|
|
||||||
} = await postRenderer(post);
|
|
||||||
|
|
||||||
const locale = 'ru-RU';
|
const title = postObj.headings[0]?.text || '';
|
||||||
|
const description = fm.description;
|
||||||
|
const readingTime = fm.readingTime;
|
||||||
|
|
||||||
|
let dateStr = slug.split("-", 1)[0];
|
||||||
|
let date: Date | undefined;
|
||||||
|
if (isNaN(Number(dateStr))) {
|
||||||
|
dateStr = undefined;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
date = new Date(
|
||||||
|
`${dateStr.slice(0,4)}-` +
|
||||||
|
`${dateStr.slice(4,6)}-` +
|
||||||
|
`${dateStr.slice(6,8)}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const image = (post.body.match(/(?:\s|^)!\[.*\]\((.*?)\)/) || [])[1];
|
||||||
---
|
---
|
||||||
|
|
||||||
<BlogLayout title={title} description={description}>
|
<BlogLayout title={title} description={description}>
|
||||||
|
@ -39,18 +69,28 @@ const locale = 'ru-RU';
|
||||||
<meta property="twitter:description" content={description} />
|
<meta property="twitter:description" content={description} />
|
||||||
<meta property="twitter:image" content={image} />
|
<meta property="twitter:image" content={image} />
|
||||||
</Fragment>
|
</Fragment>
|
||||||
<Article
|
<header>
|
||||||
PostContent={PostContent}
|
<h1>{title}</h1>
|
||||||
title={title}
|
<address>
|
||||||
description={description}
|
on
|
||||||
readingTime={readingTime}
|
<time datetime={date?.toISOString()}>{date?.toLocaleDateString()}</time>
|
||||||
date={date}
|
by
|
||||||
image={image}
|
<a href="https://t.me/dcat09" rel="author">DarkCat09</a>
|
||||||
/>
|
//
|
||||||
|
<span id="reading-time">
|
||||||
|
{readingTime}
|
||||||
|
</span>
|
||||||
|
</address>
|
||||||
|
</header>
|
||||||
|
<article>
|
||||||
|
<postObj.Content />
|
||||||
|
</article>
|
||||||
</BlogLayout>
|
</BlogLayout>
|
||||||
|
|
||||||
<style lang="less" is:global>
|
<style lang="less" is:global>
|
||||||
article {
|
article {
|
||||||
|
//padding: 0 0.75rem;
|
||||||
|
|
||||||
& > h1:first-of-type {
|
& > h1:first-of-type {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,30 +1,14 @@
|
||||||
---
|
---
|
||||||
import BlogLayout from "../../layouts/BlogLayout.astro";
|
import BlogLayout from "../../layouts/BlogLayout.astro";
|
||||||
import ArticleCard from "../../components/ArticleCard.astro";
|
|
||||||
|
|
||||||
import { getCollection } from "astro:content";
|
|
||||||
|
|
||||||
const posts = await getCollection('blog');
|
|
||||||
|
|
||||||
const description =
|
const description =
|
||||||
"DarkCat09's blog. " +
|
"DarkCat09's blog about IT. " +
|
||||||
"Reading a QR code without a phone, " +
|
"Reading a QR code without smartphone, " +
|
||||||
"breaking Windows password, " +
|
"breaking a password on Windows, " +
|
||||||
"coding your own browser... " +
|
"coding your own browser... " +
|
||||||
"The simpliest things!";
|
"The simpliest things!";
|
||||||
---
|
---
|
||||||
|
|
||||||
<BlogLayout title="Homepage" description={description}>
|
<BlogLayout title="Homepage" description={description}>
|
||||||
<div id="articles">
|
//
|
||||||
{posts.map(post => <ArticleCard post={post} />)}
|
|
||||||
</div>
|
|
||||||
</BlogLayout>
|
</BlogLayout>
|
||||||
|
|
||||||
<style lang="less">
|
|
||||||
#articles {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.8rem;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
@ -7,5 +7,5 @@ const description =
|
||||||
---
|
---
|
||||||
|
|
||||||
<BlogLayout title="Blog | Shorts" description={description}>
|
<BlogLayout title="Blog | Shorts" description={description}>
|
||||||
// soon
|
//
|
||||||
</BlogLayout>
|
</BlogLayout>
|
||||||
|
|
|
@ -23,15 +23,15 @@ const description =
|
||||||
<Profile app="Discord" icon="" name="DarkCat09#5587" url="https://discord.com/app" copyName />
|
<Profile app="Discord" icon="" name="DarkCat09#5587" url="https://discord.com/app" copyName />
|
||||||
</section>
|
</section>
|
||||||
</article>
|
</article>
|
||||||
<article class="card card-dashed" id="about">
|
<article class="card" id="about">
|
||||||
<ul>
|
<ul>
|
||||||
<ListItem icon="">
|
<ListItem icon="">
|
||||||
<a href="https://t.me/dc09about" target="_blank">
|
<a href="https://t.me/dc09about" target="_blank">
|
||||||
https://t.me/dc09about
|
https://t.me/dc09about
|
||||||
</a>
|
</a>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
<ListItem icon=""><!-- Clock icon:  -->
|
<ListItem icon="">
|
||||||
Russia, GMT+4
|
Russia, Ulyanovsk
|
||||||
</ListItem>
|
</ListItem>
|
||||||
<ListItem icon="">
|
<ListItem icon="">
|
||||||
Favorite music bands:
|
Favorite music bands:
|
||||||
|
|
|
@ -3,5 +3,5 @@ import MainLayout from "../layouts/MainLayout.astro";
|
||||||
---
|
---
|
||||||
|
|
||||||
<MainLayout page="Projects & Skills" description="">
|
<MainLayout page="Projects & Skills" description="">
|
||||||
// soon
|
//
|
||||||
</MainLayout>
|
</MainLayout>
|
||||||
|
|
|
@ -7,5 +7,5 @@ const description =
|
||||||
---
|
---
|
||||||
|
|
||||||
<MainLayout page="Services" description={description}>
|
<MainLayout page="Services" description={description}>
|
||||||
// soon
|
//
|
||||||
</MainLayout>
|
</MainLayout>
|
||||||
|
|
|
@ -1,46 +0,0 @@
|
||||||
import { AstroComponentFactory } from "astro/dist/runtime/server"
|
|
||||||
import { MarkdownHeading } from "astro"
|
|
||||||
|
|
||||||
export type postType = {
|
|
||||||
id: string
|
|
||||||
slug: string
|
|
||||||
body: string
|
|
||||||
collection: "blog"
|
|
||||||
data: any
|
|
||||||
} & {
|
|
||||||
render(): Promise<{
|
|
||||||
Content: AstroComponentFactory
|
|
||||||
headings: MarkdownHeading[]
|
|
||||||
remarkPluginFrontmatter: Record<string, any>
|
|
||||||
}>
|
|
||||||
}
|
|
||||||
|
|
||||||
export default async function postRenderer(post: postType) {
|
|
||||||
const postObj = await post.render()
|
|
||||||
const fm = postObj.remarkPluginFrontmatter
|
|
||||||
|
|
||||||
const title = postObj.headings[0]?.text || ''
|
|
||||||
const description: string = fm.description
|
|
||||||
const readingTime: number = fm.readingTime
|
|
||||||
|
|
||||||
const dateStr = post.slug.split("-", 1)[0]
|
|
||||||
let date: Date | undefined
|
|
||||||
if (!isNaN(Number(dateStr))) {
|
|
||||||
date = new Date(
|
|
||||||
`${dateStr.slice(0,4)}-` +
|
|
||||||
`${dateStr.slice(4,6)}-` +
|
|
||||||
`${dateStr.slice(6,8)}`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const image = (post.body.match(/(?:\s|^)!\[.*\]\((.*?)\)/) || [])[1]
|
|
||||||
|
|
||||||
return {
|
|
||||||
PostContent: postObj.Content,
|
|
||||||
title: title,
|
|
||||||
description: description,
|
|
||||||
readingTime: readingTime,
|
|
||||||
date: date,
|
|
||||||
image: image,
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Add table
Reference in a new issue