diff --git a/package.json b/package.json index 4e8dfef..8bcaafa 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "@astrojs/node": "11.0.2", "@astrojs/solid-js": "^5.1.3", "astro": "^7.0.6", + "clipboard": "^2.0.11", "dayjs": "^1.11.21", "github-markdown-css": "^5.9.0", "jsdom": "^29.1.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 81afef9..0e43751 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,6 +17,9 @@ importers: astro: specifier: ^7.0.6 version: 7.0.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.2)(@types/node@26.1.0)(rollup@4.62.2) + clipboard: + specifier: ^2.0.11 + version: 2.0.11 dayjs: specifier: ^1.11.21 version: 1.11.21 @@ -1265,6 +1268,9 @@ packages: resolution: {integrity: sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==} engines: {node: '>=8'} + clipboard@2.0.11: + resolution: {integrity: sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==} + clsx@2.1.1: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} @@ -1337,6 +1343,9 @@ packages: defu@6.1.7: resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==} + delegate@3.2.0: + resolution: {integrity: sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==} + depd@2.0.0: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} @@ -1487,6 +1496,9 @@ packages: github-slugger@2.0.0: resolution: {integrity: sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==} + good-listener@1.2.2: + resolution: {integrity: sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw==} + h3@1.15.11: resolution: {integrity: sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg==} @@ -1877,6 +1889,9 @@ packages: resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} engines: {node: '>=v12.22.7'} + select@1.1.2: + resolution: {integrity: sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA==} + semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true @@ -1956,6 +1971,9 @@ packages: symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + tiny-emitter@2.1.0: + resolution: {integrity: sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==} + tiny-inflate@1.0.3: resolution: {integrity: sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==} @@ -3295,6 +3313,12 @@ snapshots: ci-info@4.4.0: {} + clipboard@2.0.11: + dependencies: + good-listener: 1.2.2 + select: 1.1.2 + tiny-emitter: 2.1.0 + clsx@2.1.1: {} comma-separated-tokens@2.0.3: {} @@ -3356,6 +3380,8 @@ snapshots: defu@6.1.7: {} + delegate@3.2.0: {} + depd@2.0.0: {} dequal@2.0.3: {} @@ -3515,6 +3541,10 @@ snapshots: github-slugger@2.0.0: {} + good-listener@1.2.2: + dependencies: + delegate: 3.2.0 + h3@1.15.11: dependencies: cookie-es: 1.2.3 @@ -3931,6 +3961,8 @@ snapshots: dependencies: xmlchars: 2.2.0 + select@1.1.2: {} + semver@6.3.1: {} semver@7.8.5: {} @@ -4048,6 +4080,8 @@ snapshots: symbol-tree@3.2.4: {} + tiny-emitter@2.1.0: {} + tiny-inflate@1.0.3: {} tinyclip@0.1.15: {} diff --git a/src/assets/icon/back-top.svg b/src/assets/icon/back-top.svg deleted file mode 100644 index 16aa9dd..0000000 --- a/src/assets/icon/back-top.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/assets/icon/calendar.png b/src/assets/icon/calendar.png deleted file mode 100644 index d16ac5b..0000000 Binary files a/src/assets/icon/calendar.png and /dev/null differ diff --git a/src/assets/icon/client.png b/src/assets/icon/client.png deleted file mode 100644 index da01127..0000000 Binary files a/src/assets/icon/client.png and /dev/null differ diff --git a/src/assets/icon/eye.png b/src/assets/icon/eye.png deleted file mode 100644 index 58f33e8..0000000 Binary files a/src/assets/icon/eye.png and /dev/null differ diff --git a/src/assets/icon/folder.png b/src/assets/icon/folder.png deleted file mode 100644 index ca84780..0000000 Binary files a/src/assets/icon/folder.png and /dev/null differ diff --git a/src/assets/icon/github.svg b/src/assets/icon/github.svg deleted file mode 100644 index 91ade14..0000000 --- a/src/assets/icon/github.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/assets/icon/tag.png b/src/assets/icon/tag.png deleted file mode 100644 index dccd2d2..0000000 Binary files a/src/assets/icon/tag.png and /dev/null differ diff --git a/src/components/BackTop.astro b/src/components/BackTop.astro deleted file mode 100644 index dbca6e5..0000000 --- a/src/components/BackTop.astro +++ /dev/null @@ -1,69 +0,0 @@ ---- -// 后面的 ?url 是 一个导入查询(import query),表示:不要把 SVG 当作组件或源码处理,而是返回这个资源的 URL 字符串。 -import backTopIcon from "@/assets/icon/back-top.svg?url"; ---- - - - - - - diff --git a/src/components/BaseFooter.astro b/src/components/BaseFooter.astro deleted file mode 100644 index 6bac2c8..0000000 --- a/src/components/BaseFooter.astro +++ /dev/null @@ -1,46 +0,0 @@ ---- -import { metadata } from "../consts"; - -const year = new Date().getFullYear(); ---- - - - - - diff --git a/src/components/BaseHeader.astro b/src/components/BaseHeader.astro deleted file mode 100644 index 73f3fc7..0000000 --- a/src/components/BaseHeader.astro +++ /dev/null @@ -1,113 +0,0 @@ ---- -import {metadata} from "../consts"; -import ThemeIcon from './ThemeIcon.astro' -import GitHubIcon from '@/assets/icon/github.svg?url' ---- - -
- -
- - diff --git a/src/components/IndexPage.astro b/src/components/IndexPage.astro new file mode 100644 index 0000000..49ea51c --- /dev/null +++ b/src/components/IndexPage.astro @@ -0,0 +1,107 @@ +--- +import {siteConfig} from "@/consts.ts"; +import PostList from "@/components/Post/List.astro"; +import Pagination from "@/components/Post/Pagination.astro"; +import type {Post} from "@/types"; + +const {siteTitle} = siteConfig; + +interface Props { + posts: Post[] + page: number + totalPage: number +} + +const {posts, page, totalPage} = Astro.props +--- + +
+
+
+

{siteTitle}

+ + + + + + + +
+
+

did you stand too close to the fire?
+ like a liar looking for forgiveness from a stone.

+
+
+ +
+ + +
+ + +
+ + diff --git a/src/components/IndexSidebar.astro b/src/components/IndexSidebar.astro deleted file mode 100644 index c672f71..0000000 --- a/src/components/IndexSidebar.astro +++ /dev/null @@ -1,152 +0,0 @@ ---- -import Folder from "@/assets/icon/folder.png?url"; -import TagIcon from "@/assets/icon/tag.png?url"; -import type { CategoriesStats,Tag } from "@/types"; -import { createHttp } from "@/lib/request"; -const http = createHttp(Astro.request.headers); - -const { data: categories } = - await http.get>("categories/stats"); - -const { data: tags } = - await http.get>("posts/tags"); ---- - - - - diff --git a/src/components/IndexView.astro b/src/components/IndexView.astro deleted file mode 100644 index 0a21d5d..0000000 --- a/src/components/IndexView.astro +++ /dev/null @@ -1,46 +0,0 @@ ---- -import type {Post} from "@/types/post"; -import IndexSidebar from "@/components/IndexSidebar.astro"; -import PostList from "@/components/Post/List.astro"; -import Pagination from "@/components/Post/Pagination.astro"; - -interface Props { - posts: Post[]; - page: number; - totalPage: number; -} - -const {posts, page, totalPage} = Astro.props; ---- - -
-
-
- - -
- -
-
- - diff --git a/src/components/LazyImage.astro b/src/components/LazyImage.astro index 8ec026b..45d19a2 100644 --- a/src/components/LazyImage.astro +++ b/src/components/LazyImage.astro @@ -52,7 +52,6 @@ const { .placeholder { position: absolute; inset: 0; - background: #f3f4f6; background-image: linear-gradient( 90deg, #f3f4f6 0%, diff --git a/src/components/Post/Item.astro b/src/components/Post/Item.astro deleted file mode 100644 index aad7de3..0000000 --- a/src/components/Post/Item.astro +++ /dev/null @@ -1,185 +0,0 @@ ---- -import type {Post} from '@/types'; -import Eye from "@/assets/icon/eye.png?url"; -import Folder from "@/assets/icon/folder.png?url"; -import Calendar from "@/assets/icon/calendar.png?url"; -import LazyImage from "@/components/LazyImage.astro"; -import {formatDatetime} from "@/lib/format.ts"; - -interface Props { - post: Post -} - -const {post} = Astro.props; -const published_at = formatDatetime(post.published_at, 'YYYY-MM-DD') ---- - - -
-
- -
-
- -

- {post.title} -

-
- {post.summary} -
-
- {post.tags.map(tag => -
{tag.name}
)} -
-
-
-
- - diff --git a/src/components/Post/List.astro b/src/components/Post/List.astro index 80b39c3..771a3c9 100644 --- a/src/components/Post/List.astro +++ b/src/components/Post/List.astro @@ -1,37 +1,70 @@ --- -import type {Post} from "@/types/post"; -import PostItem from '@/components/Post/Item.astro' +import type {Post} from "@/types"; +import {formatDatetime} from '@/lib/format.ts' interface Props { posts: Post[] } - const {posts} = Astro.props --- -
-
- {posts.map(post => ( - - ))} -
-
+
+ {posts.map(item => ( +
+ {formatDatetime(item.published_at, 'YY.MM.DD')} +

{item.title}

+ +
+ ))} +
diff --git a/src/components/Post/Pagination.astro b/src/components/Post/Pagination.astro index bd46efc..16a3059 100644 --- a/src/components/Post/Pagination.astro +++ b/src/components/Post/Pagination.astro @@ -10,58 +10,40 @@ const {page, totalPage} = Astro.props {totalPage > 1 && } diff --git a/src/components/ThemeIcon.astro b/src/components/ThemeIcon.astro deleted file mode 100644 index 4f84c41..0000000 --- a/src/components/ThemeIcon.astro +++ /dev/null @@ -1,75 +0,0 @@ ---- ---- -
- -
- - - - diff --git a/src/consts.ts b/src/consts.ts index ab3587a..85cf289 100644 --- a/src/consts.ts +++ b/src/consts.ts @@ -1,5 +1,6 @@ -export const metadata = { +export const siteConfig = { site: "https://ua42.com", siteTitle: "Uroboros", - postPageSize: 6 + postPageSize: 7, + description: "Uroboros is a blog about programming, technology, and life." } diff --git a/src/layouts/Layout.astro b/src/layouts/Layout.astro index 9f9820d..cb4e2e9 100644 --- a/src/layouts/Layout.astro +++ b/src/layouts/Layout.astro @@ -1,42 +1,48 @@ --- import "@/styles/global.css"; - -import BaseHeader from "@/components/BaseHeader.astro"; -import BaseFooter from "@/components/BaseFooter.astro"; -import BackTop from "@/components/BackTop.astro"; - -import { metadata } from "@/consts.ts"; -const { siteTitle } = metadata; +import {siteConfig} from "@/consts.ts"; +const {siteTitle} = siteConfig; interface Props { title?: string; description?: string; } -const { title, description } = Astro.props; +const {title, description} = Astro.props; const pageTitle = title ? `${title} - ${siteTitle}` : siteTitle; --- - - - - - - - - {pageTitle} - - - -
- -
- - - + + + + + + + + {pageTitle} + + +
+ +
+ + + diff --git a/src/lib/highlight.js b/src/lib/highlight.js deleted file mode 100644 index b3c5501..0000000 --- a/src/lib/highlight.js +++ /dev/null @@ -1,85 +0,0 @@ -import {JSDOM} from 'jsdom' -import {createHighlighter} from 'shiki' - -const highlighter = await createHighlighter({ - themes: ['github-dark-dimmed'], - langs: [ - "bash", - "json", - "yaml", - "xml", - "html", - "css", - "scss", - "javascript", - "typescript", - "jsx", - "tsx", - "vue", - "markdown", - "sql", - "graphql", - "python", - "java", - "go", - "rust", - "c", - "cpp", - "csharp", - "php", - "ruby", - "kotlin", - "swift", - "dockerfile", - "nginx" - ] -}) - -const resolveLang = (lang) => { - const supported = highlighter.getLoadedLanguages?.() || [] - if (!lang) return 'text' - if (supported.includes(lang)) return lang - return 'text' -} - -export const highlightHtmlWithJsdom = async (rawHtml) => { - const dom = new JSDOM(rawHtml) - const document = dom.window.document - - const codeElements = document.querySelectorAll('pre code') - - for (const codeEl of codeElements) { - const preEl = codeEl.parentElement - if (!preEl || preEl.tagName !== 'PRE') continue - - const codeContent = codeEl.textContent?.trim() || '' - - // 提取语言 - let lang = 'text' - for (const cls of codeEl.classList) { - if (cls.startsWith('language-')) { - lang = cls.slice('language-'.length) - break - } - } - - lang = resolveLang(lang) - - // 生成高亮 HTML - const highlighted = highlighter.codeToHtml(codeContent, { - lang, - theme: 'github-dark-dimmed', - }) - - // 替换 DOM - const wrapper = document.createElement('div') - wrapper.innerHTML = highlighted.trim() - - const newNode = wrapper.firstChild - if (newNode && preEl.parentNode) { - preEl.parentNode.replaceChild(newNode, preEl) - } - } - - return dom.serialize() -} \ No newline at end of file diff --git a/src/lib/processHtml.ts b/src/lib/processHtml.ts new file mode 100644 index 0000000..8cbe07f --- /dev/null +++ b/src/lib/processHtml.ts @@ -0,0 +1,120 @@ +import {JSDOM} from 'jsdom' +import {createHighlighter} from 'shiki' + +const DEFAULT_THEME = 'github-dark-dimmed' + +const highlighter = await createHighlighter({ + themes: [DEFAULT_THEME], + langs: [ + "bash", + "json", + "yaml", + "xml", + "html", + "css", + "scss", + "javascript", + "typescript", + "jsx", + "tsx", + "vue", + "markdown", + "sql", + "python", + "go", + "dockerfile", + "nginx" + ] +}) + +const resolveLang = (lang: string) => { + const supported = highlighter.getLoadedLanguages?.() || [] + if (!lang) return 'text' + if (supported.includes(lang)) return lang + return 'text' +} + +export const processHtmlWithJsdom = async (rawHtml: string) => { + + const dom = new JSDOM(rawHtml) + const document = dom.window.document + + // 高亮处理代码块 + const codeBlocks = document.querySelectorAll('pre code') + + for (const codeBlock of codeBlocks) { + const preEl = codeBlock.parentElement + if (!preEl || preEl.tagName !== 'PRE') continue + + const codeContent = codeBlock.textContent || '' + let lang = 'text' + for (const cls of codeBlock.classList) { + if (cls.startsWith('language-')) { + lang = cls.slice('language-'.length) + break + } + } + + lang = resolveLang(lang) + + // 高亮 + const highlighted = highlighter.codeToHtml(codeContent, { + lang, + theme: DEFAULT_THEME, + }) + + // 替换 DOM + const wrapper = document.createElement('div') + wrapper.innerHTML = highlighted.trim() + + const newNode = wrapper.firstElementChild + if (newNode && preEl.parentNode) { + const container = document.createElement('div') + container.className = 'code-block' + + const header = document.createElement('div') + header.className = 'code-header' + + const headerLeft = document.createElement('div') + headerLeft.className = 'code-header-left' + + const headerRight = document.createElement('div') + headerRight.className = 'code-header-right' + + // 装饰点 + const actions = document.createElement('div') + actions.className = 'code-actions' + + const close = document.createElement('span') + close.className = 'dot close' + + const minimize = document.createElement('span') + minimize.className = 'dot minimize' + + const maximize = document.createElement('span') + maximize.className = 'dot maximize' + + actions.append(close, minimize, maximize) + + // 显示语言 + const langDisplay = document.createElement('div') + langDisplay.className = 'code-language' + langDisplay.textContent = lang ? lang : 'text' + + const copyBtn = document.createElement('div') + copyBtn.className = 'code-copy-btn' + copyBtn.textContent = '复制代码' + + headerLeft.append(actions, langDisplay) + headerRight.append(copyBtn) + + header.append(headerLeft, headerRight) + + container.append(header, newNode) + + preEl.parentNode.replaceChild(container, preEl) + } + } + + return dom.serialize() +} diff --git a/src/pages/404.astro b/src/pages/404.astro index f91ecdd..f94a2f7 100644 --- a/src/pages/404.astro +++ b/src/pages/404.astro @@ -2,28 +2,6 @@ import Layout from "@/layouts/Layout.astro"; --- - -
-

404 Not Found

- 返回首页 -
+ + - - diff --git a/src/pages/archive.astro b/src/pages/archive.astro index dd1465a..e69de29 100644 --- a/src/pages/archive.astro +++ b/src/pages/archive.astro @@ -1,198 +0,0 @@ ---- -import Layout from "@/layouts/Layout.astro"; -import type {Archive} from "@/types/archive"; -import {createHttp} from "@/lib/request"; - -const http = createHttp(Astro.request.headers); - -const {data} = await http.get>("posts/archives"); ---- - - -
-
- { - data.map((year) => ( -
-

- {year.year} - 共 {year.total} 篇 -

-
- {year.list.map((month) => ( -
-

{month.month} 月

-
    - {month.list.map((post) => ( -
  • - - {post.title} - {post.category_name} -
  • - ))} -
-
- ))} -
-
- )) - } -
-
-
- - diff --git a/src/pages/embed/gitea.astro b/src/pages/embed/gitea.astro index ecf3b48..3b582d4 100644 --- a/src/pages/embed/gitea.astro +++ b/src/pages/embed/gitea.astro @@ -1,6 +1,5 @@ --- const noindex = true; -import {metadata} from '@/consts.ts' --- diff --git a/src/pages/index.astro b/src/pages/index.astro index e2c08f3..2a093e5 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -1,25 +1,23 @@ --- import Layout from "@/layouts/Layout.astro"; -import IndexView from "@/components/IndexView.astro"; -import type { Post } from "@/types/post"; -import { createHttp } from "@/lib/request"; -import { metadata } from "@/consts.ts"; +import {siteConfig} from "@/consts.ts"; +import {createHttp} from "@/lib/request.ts"; +import type {Post} from "@/types"; +import IndexPage from "@/components/IndexPage.astro"; + +const {postPageSize, description} = siteConfig; const http = createHttp(Astro.request.headers); -const { list, total } = await http.get>("posts", { +const {list, total} = await http.get>("posts", { searchParams: { page: 1, - page_size: metadata.postPageSize, + page_size: postPageSize, }, }); -const totalPage = Math.ceil(total / metadata.postPageSize); +const totalPage = Math.ceil(total / postPageSize); --- - - + + diff --git a/src/pages/page/[...page].astro b/src/pages/page/[...page].astro index a1b2f9b..4fa2c03 100644 --- a/src/pages/page/[...page].astro +++ b/src/pages/page/[...page].astro @@ -1,36 +1,34 @@ --- import Layout from "@/layouts/Layout.astro"; -import type { Post } from "@/types/post"; -import { createHttp } from "@/lib/request"; -import { metadata } from "@/consts.ts"; -import IndexView from "@/components/IndexView.astro"; +import {siteConfig} from "@/consts.ts"; +import {createHttp} from "@/lib/request.ts"; +import type {Post} from "@/types"; +import IndexPage from "@/components/IndexPage.astro"; +const {page} = Astro.params; -const { page } = Astro.params; +const pageInt = Number(page); -if (isNaN(Number(page))) { +if (isNaN(pageInt) || pageInt <= 1) { return Astro.redirect("/"); } +const {postPageSize,description} = siteConfig; + const http = createHttp(Astro.request.headers); -const { list, total } = await http.get>("posts", { +const {list, total} = await http.get>("posts", { searchParams: { - page, - page_size: metadata.postPageSize, + page: pageInt, + page_size: postPageSize, }, }); +const totalPage = Math.ceil(total / postPageSize); -const totalPage = Math.ceil(total / metadata.postPageSize); - -if (totalPage < Number(page)) { +if (totalPage < pageInt) { return Astro.redirect("/404"); } --- - - + + diff --git a/src/pages/post/[...slug].astro b/src/pages/post/[...slug].astro index 416ab79..efff5d3 100644 --- a/src/pages/post/[...slug].astro +++ b/src/pages/post/[...slug].astro @@ -3,159 +3,186 @@ import "@/styles/github-markdown.css"; import "@/styles/github-markdown-reset.css"; import "photoswipe/dist/photoswipe.css"; -import Layout from "@/layouts/Layout.astro"; -import { createHttp } from "@/lib/request.ts"; -import { highlightHtmlWithJsdom } from "@/lib/highlight.js"; -import { formatDatetime } from "@/lib/format.ts"; -import type { Post } from "@/types"; +import Layout from '@/layouts/Layout.astro'; +import {createHttp} from "@/lib/request.ts"; +import type {Post} from "@/types"; +import {processHtmlWithJsdom} from "@/lib/processHtml.ts"; +import {siteConfig} from '@/consts' +import {formatDatetime} from "@/lib/format.ts"; -const { slug } = Astro.params; +const {siteTitle} = siteConfig -let data: Post; +const {slug} = Astro.params; const http = createHttp(Astro.request.headers); +let data: Post; + try { const res = await http.get>(`posts/${slug}`); data = res.data; } catch (error) { return Astro.redirect("/404"); } -const content = await highlightHtmlWithJsdom(data.content); -const published_at = formatDatetime(data.published_at, "YYYY 年 M 月 D 日"); + +const content = await processHtmlWithJsdom(data.content); +const published_at = formatDatetime(data.published_at, "YYYY.MM.DD"); --- - -
-
-

{data.title}

+ +
+ -