Compare commits
20 Commits
1.1.1
...
build_1.1.
| Author | SHA1 | Date | |
|---|---|---|---|
| 6156b512e9 | |||
| 9e5d98e2e2 | |||
| 101db5aa83 | |||
| 9ef5139d93 | |||
| f999f0912e | |||
| 75cd6ce315 | |||
| fe3978ddbf | |||
| f76e5719a2 | |||
| 79db601591 | |||
| 3b12d07b51 | |||
| 6e13092027 | |||
| ee31225f15 | |||
| cbe2957f80 | |||
| c3e50ccff2 | |||
| e0a0576ee1 | |||
| b60cdafc0a | |||
| fcf44c1d15 | |||
| 246eb86d8a | |||
| db2e8936f3 | |||
| 5e9389f5de |
@@ -18,6 +18,7 @@ import remarkToc from 'remark-toc'
|
|||||||
import sectionize from '@hbsnow/rehype-sectionize'
|
import sectionize from '@hbsnow/rehype-sectionize'
|
||||||
import { transformerNotationSkip } from './src/lib/transformerNotationSkip'
|
import { transformerNotationSkip } from './src/lib/transformerNotationSkip'
|
||||||
import { transformerDiffHighlight } from './src/lib/transformerDiffHighlight'
|
import { transformerDiffHighlight } from './src/lib/transformerDiffHighlight'
|
||||||
|
import { transformerCopyButton } from './src/lib/transformerCopyButton'
|
||||||
|
|
||||||
import icon from 'astro-icon'
|
import icon from 'astro-icon'
|
||||||
|
|
||||||
@@ -64,6 +65,11 @@ export default defineConfig({
|
|||||||
transformerRenderWhitespace(),
|
transformerRenderWhitespace(),
|
||||||
transformerNotationSkip(),
|
transformerNotationSkip(),
|
||||||
transformerDiffHighlight(),
|
transformerDiffHighlight(),
|
||||||
|
transformerCopyButton({
|
||||||
|
duration: 1000,
|
||||||
|
successIcon: "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' stroke='rgba(5,223,114,1)' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' viewBox='0 0 24 24'%3E%3Crect width='8' height='4' x='8' y='2' rx='1' ry='1'/%3E%3Cpath d='M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2'/%3E%3Cpath d='m9 14 2 2 4-4'/%3E%3C/svg%3E",
|
||||||
|
copyIcon: "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' stroke='rgba(128,128,128,1)' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' viewBox='0 0 24 24'%3E%3Crect width='8' height='4' x='8' y='2' rx='1' ry='1'/%3E%3Cpath d='M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2'/%3E%3C/svg%3E",
|
||||||
|
})
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -86,5 +86,8 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@rollup/rollup-linux-x64-gnu": "*"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
public/static/anime-bg/225.mp4
Normal file
BIN
public/static/anime-bg/830.mp4
Normal file
BIN
public/static/anime-bg/guduyaogun.mp4
Normal file
BIN
public/static/anime-bg/guduyaogun1.mp4
Normal file
BIN
public/static/anime-bg/guduyaogun2.mp4
Normal file
BIN
public/static/anime-bg/kisaki.mp4
Normal file
BIN
public/static/anime-bg/lige.mp4
Normal file
BIN
public/static/anime-bg/loading.mp4
Normal file
BIN
public/static/anime-bg/lycoris2.mp4
Normal file
BIN
public/static/anime-bg/maoliang.mp4
Normal file
BIN
public/static/anime-bg/miku.mp4
Normal file
BIN
public/static/anime-bg/miku2.mp4
Normal file
BIN
public/static/anime-bg/sanlian.mp4
Normal file
|
Before Width: | Height: | Size: 2.7 MiB |
|
Before Width: | Height: | Size: 9.3 MiB |
|
Before Width: | Height: | Size: 4.9 MiB |
|
Before Width: | Height: | Size: 4.4 MiB |
|
Before Width: | Height: | Size: 4.7 MiB After Width: | Height: | Size: 4.7 MiB |
|
Before Width: | Height: | Size: 2.1 MiB |
|
Before Width: | Height: | Size: 4.4 MiB |
|
Before Width: | Height: | Size: 2.4 MiB |
@@ -10,10 +10,11 @@ interface Track {
|
|||||||
image: { '#text': string }[] //
|
image: { '#text': string }[] //
|
||||||
url: string
|
url: string
|
||||||
'@attr'?: { nowplaying: string },
|
'@attr'?: { nowplaying: string },
|
||||||
outerurl: string
|
outerurl: string,
|
||||||
|
backupurl: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const SpotifyPresence = () => {
|
const Music163Player = () => {
|
||||||
const [displayData, setDisplayData] = useState<Track | null>(null)
|
const [displayData, setDisplayData] = useState<Track | null>(null)
|
||||||
const [isLoading, setIsLoading] = useState(true)
|
const [isLoading, setIsLoading] = useState(true)
|
||||||
const [isPlaying, setIsPlaying] = useState(false);
|
const [isPlaying, setIsPlaying] = useState(false);
|
||||||
@@ -47,9 +48,10 @@ const SpotifyPresence = () => {
|
|||||||
album: {
|
album: {
|
||||||
'#text': lastweekFirstSong.al.name
|
'#text': lastweekFirstSong.al.name
|
||||||
},
|
},
|
||||||
image: [{'#text': lastweekFirstSong.al.picUrl}],
|
image: [{'#text': lastweekFirstSong.al.picUrl.replace('http:', 'https:')}],
|
||||||
url: 'https://music.163.com/song?id=' + lastweekFirstSong.id,
|
url: 'https://music.163.com/song?id=' + lastweekFirstSong.id,
|
||||||
outerurl: "https://music.163.com/song/media/outer/url?id=" + lastweekFirstSong.id + ".mp3"
|
outerurl: "https://music.163.com/song/media/outer/url?id=" + lastweekFirstSong.id + ".mp3",
|
||||||
|
backupurl: "https://f.2ha.me/music/" + lastweekFirstSong.id + ".mp3"
|
||||||
}
|
}
|
||||||
setDisplayData(track)
|
setDisplayData(track)
|
||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
@@ -82,7 +84,7 @@ const SpotifyPresence = () => {
|
|||||||
|
|
||||||
if (!displayData) return <p>Something absolutely horrible has gone wrong</p>
|
if (!displayData) return <p>Something absolutely horrible has gone wrong</p>
|
||||||
|
|
||||||
const { name: song, artist, album, image, url, outerurl } = displayData
|
const { name: song, artist, album, image, url, outerurl, backupurl } = displayData
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -139,7 +141,10 @@ const SpotifyPresence = () => {
|
|||||||
<span className="w-[85%] truncate text-xs text-muted-foreground">
|
<span className="w-[85%] truncate text-xs text-muted-foreground">
|
||||||
<span className="font-semibold text-secondary-foreground">
|
<span className="font-semibold text-secondary-foreground">
|
||||||
<div>
|
<div>
|
||||||
<audio ref={audioRef} src={outerurl} />
|
<audio ref={audioRef} >
|
||||||
|
<source src={outerurl} type="audio/mp3" />
|
||||||
|
<source src={backupurl} type="audio/mp3" />
|
||||||
|
</audio>
|
||||||
</div>
|
</div>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
@@ -160,4 +165,4 @@ const SpotifyPresence = () => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SpotifyPresence
|
export default Music163Player
|
||||||
81
src/components/custom/RandomAnimeBackgrounds.tsx
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
import { useEffect, useRef, useState } from "react";
|
||||||
|
|
||||||
|
export const videoBackgrounds: string[] = [
|
||||||
|
'225.mp4',
|
||||||
|
'830.mp4',
|
||||||
|
'guduyaogun.mp4',
|
||||||
|
'guduyaogun1.mp4',
|
||||||
|
'guduyaogun2.mp4',
|
||||||
|
'lige.mp4',
|
||||||
|
'maoliang.mp4',
|
||||||
|
// 'miku.mp4',
|
||||||
|
'miku2.mp4',
|
||||||
|
'sanlian.mp4',
|
||||||
|
'lycoris2.mp4',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
const RandomAnimeBackground = () => {
|
||||||
|
const [index, setIndex] = useState<number>(0)
|
||||||
|
const [isLoading, setIsLoading] = useState(true)
|
||||||
|
const videoRef = useRef<HTMLVideoElement | null>(null);
|
||||||
|
const [bindEvent, setBindEvent] = useState<boolean>(true);
|
||||||
|
|
||||||
|
const handleVideoEnded = () => {
|
||||||
|
setIsLoading(true)
|
||||||
|
setIndex(getRandomIndex())
|
||||||
|
if (bindEvent && videoRef.current) {
|
||||||
|
videoRef.current.addEventListener('canplay', handleCanPlayThrough);
|
||||||
|
setBindEvent(false)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCanPlayThrough = () => {
|
||||||
|
setTimeout(() => {
|
||||||
|
setIsLoading(false)
|
||||||
|
}, 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
const getRandomIndex = () => {
|
||||||
|
return Math.floor(Math.random() * videoBackgrounds.length);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setIndex(getRandomIndex())
|
||||||
|
// setTimeout(() => {
|
||||||
|
setIsLoading(false)
|
||||||
|
// }, 100);
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
// if (isLoading) {
|
||||||
|
// return (
|
||||||
|
// <video className="no-repeat relative w-full justify-center rounded-[1.4em] object-cover"
|
||||||
|
// src='/static/anime-bg/loading.mp4'
|
||||||
|
// autoPlay muted loop>
|
||||||
|
// Your browser does not support the video tag.
|
||||||
|
// </video>
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{
|
||||||
|
isLoading ?
|
||||||
|
<video className="no-repeat relative w-full justify-center rounded-[1.4em] object-cover"
|
||||||
|
src='/static/anime-bg/loading.mp4'
|
||||||
|
autoPlay muted loop>
|
||||||
|
Your browser does not support the video tag.
|
||||||
|
</video>
|
||||||
|
:
|
||||||
|
<video ref={videoRef} className="no-repeat relative w-full justify-center rounded-[1.4em] object-cover"
|
||||||
|
src={'/static/anime-bg/' + videoBackgrounds[index]}
|
||||||
|
onEnded={handleVideoEnded}
|
||||||
|
autoPlay muted>
|
||||||
|
Your browser does not support the video tag.
|
||||||
|
</video>
|
||||||
|
}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default RandomAnimeBackground
|
||||||
|
After Width: | Height: | Size: 243 KiB |
404
src/content/blog/shikicodecopybutton-2025/shikicodecopybutton.mdx
vendored
Normal file
@@ -0,0 +1,404 @@
|
|||||||
|
---
|
||||||
|
title: 'Astro集成Shikijs 和RehypePrettyCode踩坑'
|
||||||
|
description: '1. shikijs重复依赖打包失败; 2. 使用Rehype Pretty Code/Copy Button 给markdown代码块添加复制按钮时发生错误Cannot read properties of undefined (reading "type")'
|
||||||
|
date: 2025-05-12
|
||||||
|
tags: ['typescript', 'astro', 'shiki']
|
||||||
|
image: 'assets/shikicodecopybutton.png'
|
||||||
|
authors: ['jimlee']
|
||||||
|
---
|
||||||
|
import FileTree from '@/components/starlight/FileTree.astro'
|
||||||
|
|
||||||
|
|
||||||
|
# Shikijs重复依赖导致代码报错,打包失败
|
||||||
|
## 执行 npm run build 后发生以下错误
|
||||||
|
```log
|
||||||
|
src/lib/transformerNotationSkip.ts - error ts(2322): Type 'import("/var/jenkins_home/workspace/dev.2ha.me/node_modules/@shikijs/transformers/node_modules/@shikijs/types/dist/index").ShikiTransformer' is not assignable to type 'import("/var/jenkins_home/workspace/dev.2ha.me/node_modules/@shikijs/types/dist/index").ShikiTransformer'.
|
||||||
|
Types of property 'preprocess' are incompatible.
|
||||||
|
|
||||||
|
return createCommentNotationTransformer(
|
||||||
|
~~~~~~
|
||||||
|
|
||||||
|
Result (78 files):
|
||||||
|
- 1 error
|
||||||
|
- 0 warnings
|
||||||
|
- 0 hints
|
||||||
|
```
|
||||||
|
## 检查src/lib/transformerNotationSkip.ts代码
|
||||||
|
这里引用的ShikiTransformer是/node_modules/@shikijs/types 中的 ShikiTransformer, 与 node_modules/@shikijs/transformers/node_modules/@shikijs/types 中的 ShikiTransformer 代码相同,但是引用不同
|
||||||
|
```typescript
|
||||||
|
import { type ShikiTransformer } from '@shikijs/types';
|
||||||
|
import { createCommentNotationTransformer } from '@shikijs/transformers'
|
||||||
|
|
||||||
|
export interface TransformerNotationSkipOptions {
|
||||||
|
/**
|
||||||
|
* Class for skipped lines
|
||||||
|
*/
|
||||||
|
classActiveSkip?: string
|
||||||
|
/**
|
||||||
|
* Class added to the root element when the code has skipped lines
|
||||||
|
*/
|
||||||
|
classActivePre?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export function transformerNotationSkip(
|
||||||
|
options: TransformerNotationSkipOptions = {},
|
||||||
|
): ShikiTransformer {
|
||||||
|
const { classActiveSkip = 'skip', classActivePre = undefined } = options
|
||||||
|
|
||||||
|
return createCommentNotationTransformer(
|
||||||
|
'skip-lines',
|
||||||
|
// comment-start | marker | range | comment-end
|
||||||
|
/^\s*(?:\/\/|\/\*|<!--|#)\s+\[!code skip:(\d+):(\d+)\]\s*(?:\*\/|-->)?/,
|
||||||
|
function ([_, start, end], _line) {
|
||||||
|
_line.children = [{ type: 'text', value: `${start}-${end}` }]
|
||||||
|
_line.properties = { style: `counter-set:line ${end}` }
|
||||||
|
|
||||||
|
if (classActiveSkip) this.addClassToHast(_line, classActiveSkip)
|
||||||
|
if (classActivePre) this.addClassToHast(this.pre, classActivePre)
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
undefined, // remove empty lines
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
## 检查安装好依赖后的@shikijs目录
|
||||||
|
<FileTree>
|
||||||
|
- node_modules
|
||||||
|
- @shikijs
|
||||||
|
- engine-javascript
|
||||||
|
- dist
|
||||||
|
- README.md
|
||||||
|
- package.json
|
||||||
|
- LICENSE
|
||||||
|
- types
|
||||||
|
- dist
|
||||||
|
- README.md
|
||||||
|
- package.json
|
||||||
|
- LICENSE
|
||||||
|
- langs
|
||||||
|
- dist
|
||||||
|
- README.md
|
||||||
|
- package.json
|
||||||
|
- LICENSE
|
||||||
|
- engine-oniguruma
|
||||||
|
- dist
|
||||||
|
- README.md
|
||||||
|
- package.json
|
||||||
|
- LICENSE
|
||||||
|
- transformers
|
||||||
|
- dist
|
||||||
|
- README.md
|
||||||
|
- package.json
|
||||||
|
- node_modules // 重复依赖最外层 node_modules 下的 @shikijs shiki
|
||||||
|
- shiki // 与 node_modules/shiki 重复
|
||||||
|
- dist
|
||||||
|
- README.md
|
||||||
|
- package.json
|
||||||
|
- LICENSE
|
||||||
|
- oniguruma-to-es
|
||||||
|
- dist
|
||||||
|
- README.md
|
||||||
|
- package.json
|
||||||
|
- types
|
||||||
|
- LICENSE
|
||||||
|
- @shikijs // 与 node_modules/@shikijs 重复
|
||||||
|
- engine-javascript
|
||||||
|
- types
|
||||||
|
- engine-oniguruma
|
||||||
|
- vscode-textmate
|
||||||
|
- core
|
||||||
|
- LICENSE
|
||||||
|
- themes
|
||||||
|
- dist
|
||||||
|
- README.md
|
||||||
|
- package.json
|
||||||
|
- LICENSE
|
||||||
|
- vscode-textmate
|
||||||
|
- dist
|
||||||
|
- README.md
|
||||||
|
- package.json
|
||||||
|
- LICENSE.md
|
||||||
|
- core
|
||||||
|
- dist
|
||||||
|
- README.md
|
||||||
|
- package.json
|
||||||
|
- LICENSE
|
||||||
|
</FileTree>
|
||||||
|
## 删除node_modules中的重复依赖
|
||||||
|
```shellscript
|
||||||
|
rm -rf node_modules/@shikijs/transformers/node_modules/
|
||||||
|
```
|
||||||
|
|
||||||
|
# 给Astro博客Markdown代码块添加复制按钮
|
||||||
|
通过查找[Rehype Pretty Code文档](https://rehype-pretty.pages.dev/)找到 [Rehype Pretty Code/Copy Button](https://rehype-pretty.pages.dev/plugins/copy-button/)这个实验性功能
|
||||||
|
## 1. 安装依赖
|
||||||
|
```shellscript
|
||||||
|
npm install @rehype-pretty/transformers
|
||||||
|
```
|
||||||
|
## 2. 添加transformerCopyButton.ts
|
||||||
|
这个ts文件是基于node_modules\@rehype-pretty\transformers\dist\copy-button.js修改而来
|
||||||
|
```typescript
|
||||||
|
import type { ShikiTransformer } from "shiki";
|
||||||
|
import { h } from "hastscript";
|
||||||
|
|
||||||
|
export interface CopyButtonOptions {
|
||||||
|
duration?: number;
|
||||||
|
copyIcon?: string;
|
||||||
|
successIcon?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const transformerCopyButton = (
|
||||||
|
options: CopyButtonOptions = {
|
||||||
|
duration: 1000
|
||||||
|
}
|
||||||
|
): ShikiTransformer => {
|
||||||
|
return {
|
||||||
|
name: 'shiki-transformer-copy-button',
|
||||||
|
code(node) {
|
||||||
|
const button = h('button', {
|
||||||
|
class: 'shiki-transformer-button-copy',
|
||||||
|
'data-code': this.source,
|
||||||
|
onclick: `
|
||||||
|
navigator.clipboard.writeText(this.dataset.code);
|
||||||
|
this.classList.add('shiki-transformer-button-copied');
|
||||||
|
setTimeout(() => this.classList.remove('shiki-transformer-button-copied'), ${options.duration})
|
||||||
|
`
|
||||||
|
}, [
|
||||||
|
h('span', { class: 'ready' }),
|
||||||
|
h('span', { class: 'success' })
|
||||||
|
]);
|
||||||
|
node.children.push(button)
|
||||||
|
node.children.push({
|
||||||
|
type: 'element',
|
||||||
|
tagName: 'style',
|
||||||
|
properties: {},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
value: buttonStyles({
|
||||||
|
successIcon: options.successIcon,
|
||||||
|
copyIcon: options.copyIcon
|
||||||
|
})
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function buttonStyles({
|
||||||
|
successIcon = "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='1em' height='1em' viewBox='0 0 24 24'%3E%3Cpath fill='none' stroke='rgba(5,223,114,1)' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M16 3h2.6A2.4 2.4 0 0 1 21 5.4v15.2a2.4 2.4 0 0 1-2.4 2.4H5.4A2.4 2.4 0 0 1 3 20.6V5.4A2.4 2.4 0 0 1 5.4 3H8m0 11l3 3l5-7M8.8 1h6.4a.8.8 0 0 1 .8.8v2.4a.8.8 0 0 1-.8.8H8.8a.8.8 0 0 1-.8-.8V1.8a.8.8 0 0 1 .8-.8'/%3E%3C/svg%3E",
|
||||||
|
copyIcon = "data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20fill='none'%20stroke='rgba(128,128,128,1)'%20stroke-linecap='round'%20stroke-linejoin='round'%20stroke-width='2'%20viewBox='0%200%2024%2024'%3E%3Crect%20width='8'%20height='4'%20x='8'%20y='2'%20rx='1'%20ry='1'/%3E%3Cpath%20d='M16%204h2a2%202%200%200%201%202%202v14a2%202%200%200%201-2%202H6a2%202%200%200%201-2-2V6a2%202%200%200%201%202-2h2'/%3E%3C/svg%3E",
|
||||||
|
}: {
|
||||||
|
successIcon?: string,
|
||||||
|
copyIcon?: string
|
||||||
|
}) {
|
||||||
|
let buttonStyle =
|
||||||
|
`
|
||||||
|
:root {
|
||||||
|
--border-color: #e2e2e3;
|
||||||
|
--background-color: #f6f6f7;
|
||||||
|
--hover-background-color: #ffff
|
||||||
|
}
|
||||||
|
|
||||||
|
pre:has(code) {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre button.shiki-transformer-button-copy {
|
||||||
|
position: absolute;
|
||||||
|
top: 12px;
|
||||||
|
right: 12px;
|
||||||
|
z-index: 3;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 4px;
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
place-items: center;
|
||||||
|
background-color: var(--background-color);
|
||||||
|
cursor: pointer;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
transition: var(--border-color) .25s, var(--background-color) .25s, opacity .25s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--hover-background-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
& span {
|
||||||
|
width: 100%;
|
||||||
|
aspect-ratio: 1 / 1;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: center;
|
||||||
|
background-size: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
& .ready {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
background-image: url("${copyIcon}");
|
||||||
|
}
|
||||||
|
|
||||||
|
& .success {
|
||||||
|
display: none;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
background-image: url("${successIcon}");
|
||||||
|
}
|
||||||
|
|
||||||
|
&.shiki-transformer-button-copied {
|
||||||
|
& .success {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
& .ready {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
return buttonStyle
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. 添加插件
|
||||||
|
```typescript
|
||||||
|
+ import { transformerCopyButton } from './src/lib/transformerCopyButton'
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
site: 'https://dev.2ha.me',
|
||||||
|
integrations: [
|
||||||
|
tailwind({
|
||||||
|
applyBaseStyles: false,
|
||||||
|
}),
|
||||||
|
sitemap(),
|
||||||
|
mdx(),
|
||||||
|
react(),
|
||||||
|
icon(),
|
||||||
|
],
|
||||||
|
markdown: {
|
||||||
|
syntaxHighlight: false,
|
||||||
|
rehypePlugins: [
|
||||||
|
[
|
||||||
|
rehypeExternalLinks,
|
||||||
|
{
|
||||||
|
target: '_blank',
|
||||||
|
rel: ['nofollow', 'noreferrer', 'noopener'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
rehypeHeadingIds,
|
||||||
|
[
|
||||||
|
rehypeKatex,
|
||||||
|
{
|
||||||
|
strict: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
sectionize as any,
|
||||||
|
[
|
||||||
|
rehypePrettyCode,
|
||||||
|
{
|
||||||
|
theme: {
|
||||||
|
light: 'everforest-dark',
|
||||||
|
dark: 'everforest-dark',
|
||||||
|
},
|
||||||
|
transformers: [
|
||||||
|
transformerNotationDiff(),
|
||||||
|
transformerMetaHighlight(),
|
||||||
|
transformerRenderWhitespace(),
|
||||||
|
transformerNotationSkip(),
|
||||||
|
transformerDiffHighlight(),
|
||||||
|
+ transformerCopyButton({
|
||||||
|
+ duration: 1000,
|
||||||
|
+ successIcon: "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' stroke='rgba(5,223,114,1)' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' viewBox='0 0 24 24'%3E%3Crect width='8' height='4' x='8' y='2' rx='1' ry='1'/%3E%3Cpath d='M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2'/%3E%3Cpath d='m9 14 2 2 4-4'/%3E%3C/svg%3E",
|
||||||
|
+ copyIcon: "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' stroke='rgba(128,128,128,1)' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' viewBox='0 0 24 24'%3E%3Crect width='8' height='4' x='8' y='2' rx='1' ry='1'/%3E%3Cpath d='M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2'/%3E%3C/svg%3E",
|
||||||
|
+ })
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
],
|
||||||
|
remarkPlugins: [remarkToc, remarkMath, remarkEmoji],
|
||||||
|
},
|
||||||
|
server: {
|
||||||
|
port: 1234,
|
||||||
|
host: true,
|
||||||
|
},
|
||||||
|
devToolbar: {
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
```
|
||||||
|
## 3. 启动项目后,访问blog后报错
|
||||||
|
```log
|
||||||
|
TypeError: Cannot read properties of undefined (reading 'type')
|
||||||
|
at /vscodeProjects/dev.2ha.me/node_modules/@shikijs/transformers/dist/index.mjs:516:22
|
||||||
|
at Array.flatMap (<anonymous>)
|
||||||
|
at /vscodeProjects/dev.2ha.me/node_modules/@shikijs/transformers/dist/index.mjs:507:41
|
||||||
|
at Array.forEach (<anonymous>)
|
||||||
|
at Object.root (/vscodeProjects/dev.2ha.me/node_modules/@shikijs/transformers/dist/index.mjs:501:21)
|
||||||
|
at tokensToHast (/vscodeProjects/dev.2ha.me/node_modules/@shikijs/core/dist/index.mjs:1313:33)
|
||||||
|
at codeToHast (/vscodeProjects/dev.2ha.me/node_modules/@shikijs/core/dist/index.mjs:1188:10… …
|
||||||
|
```
|
||||||
|
|
||||||
|
## 5. 修改node_modules/@shikijs/transformers依赖中的transformerRenderWhitespace方法
|
||||||
|
```javascript
|
||||||
|
function transformerRenderWhitespace(options = {}) {
|
||||||
|
const classMap = {
|
||||||
|
" ": options.classSpace ?? "space",
|
||||||
|
" ": options.classTab ?? "tab"
|
||||||
|
};
|
||||||
|
const position = options.position ?? "all";
|
||||||
|
const keys = Object.keys(classMap);
|
||||||
|
return {
|
||||||
|
name: "@shikijs/transformers:render-whitespace",
|
||||||
|
// We use `root` hook here to ensure it runs after all other transformers
|
||||||
|
root(root) {
|
||||||
|
const pre = root.children[0];
|
||||||
|
const code = pre.children[0];
|
||||||
|
code.children.forEach(
|
||||||
|
(line) => {
|
||||||
|
if (line.type !== "element")
|
||||||
|
return;
|
||||||
|
const elements = line.children.filter((token) => token.type === "element");
|
||||||
|
const last = elements.length - 1;
|
||||||
|
line.children = line.children.flatMap((token) => {
|
||||||
|
if (token.type !== "element")
|
||||||
|
return token;
|
||||||
|
const index = elements.indexOf(token);
|
||||||
|
if (position === "boundary" && index !== 0 && index !== last)
|
||||||
|
return token;
|
||||||
|
if (position === "trailing" && index !== last)
|
||||||
|
return token;
|
||||||
|
+ if (token.children.length === 0) {
|
||||||
|
+ return token;
|
||||||
|
+ }
|
||||||
|
const node = token.children[0];
|
||||||
|
if (node.type !== "text" || !node.value)
|
||||||
|
return token;
|
||||||
|
const parts = splitSpaces(
|
||||||
|
node.value.split(/([ \t])/).filter((i) => i.length),
|
||||||
|
position === "boundary" && index === last && last !== 0 ? "trailing" : position,
|
||||||
|
position !== "trailing"
|
||||||
|
);
|
||||||
|
if (parts.length <= 1)
|
||||||
|
return token;
|
||||||
|
return parts.map((part) => {
|
||||||
|
const clone = {
|
||||||
|
...token,
|
||||||
|
properties: { ...token.properties }
|
||||||
|
};
|
||||||
|
clone.children = [{ type: "text", value: part }];
|
||||||
|
if (keys.includes(part)) {
|
||||||
|
this.addClassToHast(clone, classMap[part]);
|
||||||
|
delete clone.properties.style;
|
||||||
|
}
|
||||||
|
return clone;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
133
src/lib/transformerCopyButton.ts
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
import type { ShikiTransformer } from "shiki";
|
||||||
|
import { h } from "hastscript";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* shikijs transformerRenderWhitespace 会报错 Cannot read properties of undefined (reading 'type'), 解决办法是
|
||||||
|
* @shikijs/transformers/dist/index.mjs:516:22 加一个判断 提前返回
|
||||||
|
* 如下
|
||||||
|
* if (token.children.length === 0) {
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
在这行代码之前加上面这段代码
|
||||||
|
const node = token.children[0];
|
||||||
|
if (node.type !== "text" || !node.value)
|
||||||
|
return token;
|
||||||
|
*/
|
||||||
|
export interface CopyButtonOptions {
|
||||||
|
duration?: number;
|
||||||
|
copyIcon?: string;
|
||||||
|
successIcon?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const transformerCopyButton = (
|
||||||
|
options: CopyButtonOptions = {
|
||||||
|
duration: 1000
|
||||||
|
}
|
||||||
|
): ShikiTransformer => {
|
||||||
|
return {
|
||||||
|
name: 'shiki-transformer-copy-button',
|
||||||
|
code(node) {
|
||||||
|
const button = h('button', {
|
||||||
|
class: 'shiki-transformer-button-copy',
|
||||||
|
'data-code': this.source,
|
||||||
|
onclick: `
|
||||||
|
navigator.clipboard.writeText(this.dataset.code);
|
||||||
|
this.classList.add('shiki-transformer-button-copied');
|
||||||
|
setTimeout(() => this.classList.remove('shiki-transformer-button-copied'), ${options.duration})
|
||||||
|
`
|
||||||
|
}, [
|
||||||
|
h('span', { class: 'ready' }),
|
||||||
|
h('span', { class: 'success' })
|
||||||
|
]);
|
||||||
|
node.children.push(button)
|
||||||
|
node.children.push({
|
||||||
|
type: 'element',
|
||||||
|
tagName: 'style',
|
||||||
|
properties: {},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
value: buttonStyles({
|
||||||
|
successIcon: options.successIcon,
|
||||||
|
copyIcon: options.copyIcon
|
||||||
|
})
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function buttonStyles({
|
||||||
|
successIcon = "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='1em' height='1em' viewBox='0 0 24 24'%3E%3Cpath fill='none' stroke='rgba(5,223,114,1)' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M16 3h2.6A2.4 2.4 0 0 1 21 5.4v15.2a2.4 2.4 0 0 1-2.4 2.4H5.4A2.4 2.4 0 0 1 3 20.6V5.4A2.4 2.4 0 0 1 5.4 3H8m0 11l3 3l5-7M8.8 1h6.4a.8.8 0 0 1 .8.8v2.4a.8.8 0 0 1-.8.8H8.8a.8.8 0 0 1-.8-.8V1.8a.8.8 0 0 1 .8-.8'/%3E%3C/svg%3E",
|
||||||
|
copyIcon = "data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20fill='none'%20stroke='rgba(128,128,128,1)'%20stroke-linecap='round'%20stroke-linejoin='round'%20stroke-width='2'%20viewBox='0%200%2024%2024'%3E%3Crect%20width='8'%20height='4'%20x='8'%20y='2'%20rx='1'%20ry='1'/%3E%3Cpath%20d='M16%204h2a2%202%200%200%201%202%202v14a2%202%200%200%201-2%202H6a2%202%200%200%201-2-2V6a2%202%200%200%201%202-2h2'/%3E%3C/svg%3E",
|
||||||
|
}: {
|
||||||
|
successIcon?: string,
|
||||||
|
copyIcon?: string
|
||||||
|
}) {
|
||||||
|
let buttonStyle =
|
||||||
|
`
|
||||||
|
:root {
|
||||||
|
--border-color: #e2e2e3;
|
||||||
|
--background-color: #f6f6f7;
|
||||||
|
--hover-background-color: #ffff
|
||||||
|
}
|
||||||
|
|
||||||
|
pre:has(code) {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre button.shiki-transformer-button-copy {
|
||||||
|
position: absolute;
|
||||||
|
top: 12px;
|
||||||
|
right: 12px;
|
||||||
|
z-index: 3;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 4px;
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
place-items: center;
|
||||||
|
background-color: var(--background-color);
|
||||||
|
cursor: pointer;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
transition: var(--border-color) .25s, var(--background-color) .25s, opacity .25s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--hover-background-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
& span {
|
||||||
|
width: 100%;
|
||||||
|
aspect-ratio: 1 / 1;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: center;
|
||||||
|
background-size: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
& .ready {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
background-image: url("${copyIcon}");
|
||||||
|
}
|
||||||
|
|
||||||
|
& .success {
|
||||||
|
display: none;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
background-image: url("${successIcon}");
|
||||||
|
}
|
||||||
|
|
||||||
|
&.shiki-transformer-button-copied {
|
||||||
|
& .success {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
& .ready {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
return buttonStyle
|
||||||
|
}
|
||||||
@@ -29,7 +29,7 @@ export function transformerNotationSkip(
|
|||||||
if (classActivePre) this.addClassToHast(this.pre, classActivePre)
|
if (classActivePre) this.addClassToHast(this.pre, classActivePre)
|
||||||
return false
|
return false
|
||||||
},
|
},
|
||||||
false, // remove empty lines
|
undefined, // remove empty lines
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ const authors = await getCollection('authors')
|
|||||||
<TableHeader>
|
<TableHeader>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableHead className="max-w-[80px]">Path</TableHead>
|
<TableHead className="max-w-[80px]">Path</TableHead>
|
||||||
<TableHead className="max-w-[80px] flex items-center">Illustrator</TableHead>
|
<TableHead className="max-w-[80px] flex items-center">Author</TableHead>
|
||||||
<TableHead className="text-right">Resources</TableHead>
|
<TableHead className="text-right">Resources</TableHead>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHeader>
|
</TableHeader>
|
||||||
@@ -134,6 +134,19 @@ const authors = await getCollection('authors')
|
|||||||
</Link>
|
</Link>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell className="font-medium">Background</TableCell>
|
||||||
|
<TableCell className="flex items-center">
|
||||||
|
<Link class="contents" href="https://mall.bilibili.com/neul-next/index.html?page=mall-up_itemDetail&noTitleBar=1&itemsId=1107984035&from=items_share&msource=items_share" target="_blank">
|
||||||
|
@小猫女仆降临 <svg class="size-8" style="filter: invert(100%);" xmlns="http://www.w3.org/2000/svg" role="img" viewBox="0 0 24 24"><title>Bilibili</title><path d="M17.813 4.653h.854c1.51.054 2.769.578 3.773 1.574 1.004.995 1.524 2.249 1.56 3.76v7.36c-.036 1.51-.556 2.769-1.56 3.773s-2.262 1.524-3.773 1.56H5.333c-1.51-.036-2.769-.556-3.773-1.56S.036 18.858 0 17.347v-7.36c.036-1.511.556-2.765 1.56-3.76 1.004-.996 2.262-1.52 3.773-1.574h.774l-1.174-1.12a1.234 1.234 0 0 1-.373-.906c0-.356.124-.658.373-.907l.027-.027c.267-.249.573-.373.92-.373.347 0 .653.124.92.373L9.653 4.44c.071.071.134.142.187.213h4.267a.836.836 0 0 1 .16-.213l2.853-2.747c.267-.249.573-.373.92-.373.347 0 .662.151.929.4.267.249.391.551.391.907 0 .355-.124.657-.373.906zM5.333 7.24c-.746.018-1.373.276-1.88.773-.506.498-.769 1.13-.786 1.894v7.52c.017.764.28 1.395.786 1.893.507.498 1.134.756 1.88.773h13.334c.746-.017 1.373-.275 1.88-.773.506-.498.769-1.129.786-1.893v-7.52c-.017-.765-.28-1.396-.786-1.894-.507-.497-1.134-.755-1.88-.773zM8 11.107c.373 0 .684.124.933.373.25.249.383.569.4.96v1.173c-.017.391-.15.711-.4.96-.249.25-.56.374-.933.374s-.684-.125-.933-.374c-.25-.249-.383-.569-.4-.96V12.44c0-.373.129-.689.386-.947.258-.257.574-.386.947-.386zm8 0c.373 0 .684.124.933.373.25.249.383.569.4.96v1.173c-.017.391-.15.711-.4.96-.249.25-.56.374-.933.374s-.684-.125-.933-.374c-.25-.249-.383-.569-.4-.96V12.44c.017-.391.15-.711.4-.96.249-.249.56-.373.933-.373Z"/></svg>
|
||||||
|
</Link>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className="text-right">
|
||||||
|
<Link href="https://www.bilibili.com/video/BV1462uY8Eo4" target="_blank">
|
||||||
|
<img class="h-12 w-25" src="/static/images/maoliang.gif" />
|
||||||
|
</Link>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import AuthorPresence from '@/components/bento/AuthorPresence'
|
import AuthorPresence from '@/components/bento/AuthorPresence'
|
||||||
import WakatimeGraph from '@/components/bento/WakatimeGraph.tsx'
|
import WakatimeGraph from '@/components/bento/WakatimeGraph.tsx'
|
||||||
import Link from '@/components/Link.astro'
|
import Link from '@/components/Link.astro'
|
||||||
import FuzzyText from '@/components/magicui/fuzzy-text'
|
// import FuzzyText from '@/components/magicui/fuzzy-text'
|
||||||
import GradientText from '@/components/magicui/gradint-text'
|
import GradientText from '@/components/magicui/gradint-text'
|
||||||
import LetterGlitch from '@/components/magicui/letter-glitch'
|
import LetterGlitch from '@/components/magicui/letter-glitch'
|
||||||
import ShortCuts from '@/components/ShortCuts.astro'
|
import ShortCuts from '@/components/ShortCuts.astro'
|
||||||
@@ -10,8 +10,9 @@ import { SITE, SOCIAL_LINKS } from '@/consts'
|
|||||||
import Layout from '@/layouts/Layout.astro'
|
import Layout from '@/layouts/Layout.astro'
|
||||||
import { Icon } from 'astro-icon/components'
|
import { Icon } from 'astro-icon/components'
|
||||||
import { getCollection } from 'astro:content'
|
import { getCollection } from 'astro:content'
|
||||||
import GiteaCalendar from '@/components/bento/GiteaCalendar'
|
import GiteaCalendar from '@/components/custom/GiteaCalendar'
|
||||||
import Music163Player from '@/components/bento/Music163Player'
|
import Music163Player from '@/components/custom/Music163Player'
|
||||||
|
import RandomAnimeBackground from '@/components/custom/RandomAnimeBackgrounds'
|
||||||
|
|
||||||
const latestPost = await getCollection('blog').then((posts: any[]) =>
|
const latestPost = await getCollection('blog').then((posts: any[]) =>
|
||||||
posts
|
posts
|
||||||
@@ -22,47 +23,20 @@ const latestPost = await getCollection('blog').then((posts: any[]) =>
|
|||||||
.filter((post) => !post.data.hidden && !post.data.draft)
|
.filter((post) => !post.data.hidden && !post.data.draft)
|
||||||
.at(0),
|
.at(0),
|
||||||
)
|
)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<Layout title="主页" description={SITE.DESCRIPTION}>
|
<Layout title="主页" description={SITE.DESCRIPTION}>
|
||||||
<section
|
<section
|
||||||
class="mx-auto grid max-w-[375px] grid-cols-2 gap-4 px-4 [grid-template-areas:'a_a'_'a_a'_'b_b'_'b_b'_'e_e'_'h_i'_'h_c'_'k_c'_'d_d'_'d_d'_'g_g'_'g_g'_'f_f'_'j_j'_'j_j'] *:rounded-3xl *:border *:bg-secondary/25 *:bg-cover *:bg-center *:bg-no-repeat sm:max-w-screen-sm sm:[grid-template-areas:'a_a'_'b_d'_'e_e'_'j_g'_'h_i'_'h_c'_'k_c'_'f_f'] xl:max-w-screen-xl xl:grid-cols-4 xl:[grid-template-areas:'a_a_b_c'_'d_e_e_c'_'h_f_f_g'_'h_i_j_k'] xl:[&:hover:has(>.has-overlay:hover)>.first>.overlay]:opacity-100 xl:[&:hover>*:not(.first):hover_.overlay]:opacity-100"
|
class="mx-auto grid max-w-[375px] grid-cols-2 gap-4 px-4 [grid-template-areas:'a_a'_'a_a'_'b_b'_'b_b'_'e_e'_'h_i'_'h_c'_'k_c'_'d_d'_'d_d'_'g_g'_'g_g'_'f_f'_'j_j'_'j_j'] *:rounded-3xl *:border *:bg-secondary/25 *:bg-cover *:bg-center *:bg-no-repeat sm:max-w-screen-sm sm:[grid-template-areas:'a_a'_'b_d'_'e_e'_'j_g'_'h_i'_'h_c'_'k_c'_'f_f'] xl:max-w-screen-xl xl:grid-cols-4 xl:[grid-template-areas:'a_a_b_c'_'d_e_e_c'_'h_f_f_g'_'h_i_j_k'] xl:[&:hover:has(>.has-overlay:hover)>.first>.overlay]:opacity-100 xl:[&:hover>*:not(.first):hover_.overlay]:opacity-100"
|
||||||
aria-label="Personal information and activity grid"
|
aria-label="Personal information and activity grid"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="first flex flex-row justify-center aspect-square rounded-3xl border bg-[url('/static/images/tou.gif')] bg-cover bg-color bg-center bg-position-inherit bg-no-repeat [grid-area:a] sm:aspect-[2.1/1] sm:bg-[url('/static/images/tou.gif')] xl:aspect-auto"
|
class="first flex flex-row xl:max-h-[298px] grow-[0] justify-center aspect-square rounded-3xl border bg-cover bg-center bg-position-inherit bg-no-repeat [grid-area:a] sm:aspect-[2.1/1] xl:aspect-auto"
|
||||||
role="img"
|
role="img"
|
||||||
aria-label="Introduction"
|
aria-label="Introduction"
|
||||||
style="filter: contrast(2) saturate(0) invert(100) sepia(100); border-color: #afafaf;"
|
|
||||||
>
|
>
|
||||||
<!-- <div
|
<RandomAnimeBackground client:load/>
|
||||||
class="overlay size-full rounded-3xl bg-[url('/static/404.webp')] bg-cover bg-center bg-no-repeat opacity-0 transition-opacity duration-200"
|
|
||||||
>
|
|
||||||
</div> -->
|
|
||||||
<!-- class="first relative flex aspect-square justify-center rounded-3xl border [grid-area:a] sm:aspect-[2.1/1] xl:aspect-auto" -->
|
|
||||||
<!-- <img
|
|
||||||
class="no-repeat relative w-full max-w-fit justify-center rounded-3xl object-cover"
|
|
||||||
src="/static/404_white_mask.webp"
|
|
||||||
/> -->
|
|
||||||
<!-- <div class="absolute self-end p-2 justify-center">
|
|
||||||
<FuzzyText
|
|
||||||
client:load
|
|
||||||
baseIntensity={0.04}
|
|
||||||
hoverIntensity={0.24}
|
|
||||||
fontSize={96}
|
|
||||||
color="#fbb229"
|
|
||||||
,>4O4</FuzzyText
|
|
||||||
>
|
|
||||||
<div class="mb-3 mt-3"></div>
|
|
||||||
<FuzzyText
|
|
||||||
client:load
|
|
||||||
baseIntensity={0.04}
|
|
||||||
hoverIntensity={0.24}
|
|
||||||
fontSize={36}
|
|
||||||
color="#f95038">Not Found</FuzzyText
|
|
||||||
>
|
|
||||||
</div> -->
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
@@ -114,7 +88,7 @@ const latestPost = await getCollection('blog').then((posts: any[]) =>
|
|||||||
alt={`Featured image for the latest post: ${latestPost.data.title}`}
|
alt={`Featured image for the latest post: ${latestPost.data.title}`}
|
||||||
width={477}
|
width={477}
|
||||||
height={251}
|
height={251}
|
||||||
class="w-full rounded-2xl border border-border sm:ml-1 sm:w-[82%] "
|
class="w-full rounded-2xl border border-border sm:w-[82%] "
|
||||||
/>
|
/>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
|
|||||||
@@ -517,4 +517,9 @@
|
|||||||
|
|
||||||
.bg-color {
|
.bg-color {
|
||||||
background-color: #e9d3b6 !important;
|
background-color: #e9d3b6 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shiki-transformer-button-copy {
|
||||||
|
border: none !important;
|
||||||
|
background-color: hsl(0deg 0% 0% / 0%) !important;
|
||||||
}
|
}
|
||||||