Compare commits
1 Commits
main
...
xl-window-
| Author | SHA1 | Date | |
|---|---|---|---|
| 8d5d1b3d82 |
1
.gitignore
vendored
@@ -1,6 +1,5 @@
|
||||
# build output
|
||||
dist/
|
||||
dist.zip
|
||||
# generated types
|
||||
.astro/
|
||||
|
||||
|
||||
58
README.md
@@ -5,22 +5,16 @@
|
||||
## dev.2ha.me
|
||||
### frok by [enscribe.dev](https://github.com/jktrn/enscribe.dev.git)
|
||||
|
||||
[![Code License]](LICENSE.md)
|
||||
|
||||
[![Code License]](LICENSE)
|
||||
|
||||
English | [简体中文](README_zh-Hans.md) | [繁體中文](README_zh-Hant.md)
|
||||
|
||||
[dev.2ha.me](https://dev.2ha.me) is a project I worked on while learning [Next.js](https://nextjs.org/). I discovered [enscribe.dev](https://github.com/jktrn/enscribe.dev.git) from the Examples in the [tailwind-nextjs-starter-blog](https://github.com/timlrx/tailwind-nextjs-starter-blog) project. Both [dev.2ha.me](https://dev.2ha.me) and [enscribe.dev](https://github.com/jktrn/enscribe.dev.git) use the same architecture, such as [Astro](https://astro.build/), [Tailwind](https://tailwindcss.com/), and [shadcn/ui](https://ui.shadcn.com/). On this basis, I modified some APIs to display data from domestic Chinese platforms. The main framework is consistent with the 3.0 version of [enscribe.dev](https://github.com/jktrn/enscribe.dev.git).
|
||||
[dev.2ha.me](https://dev.2ha.me) 是我在学习[Next.js](https://nextjs.org/)时练手的项目,我从[tailwind-nextjs-starter-blog](https://github.com/timlrx/tailwind-nextjs-starter-blog)项目的Examples发现了[enscribe.dev](https://github.com/jktrn/enscribe.dev.git),[dev.2ha.me](https://dev.2ha.me)和[enscribe.dev](https://github.com/jktrn/enscribe.dev.git)使用了相同的架构,如[Astro](https://astro.build/), [Tailwind](https://tailwindcss.com/), 和 [shadcn/ui](https://ui.shadcn.com/)。在此基础修改了一些API展示为中国国内的平台数据, 主体框架与[enscribe.dev](https://github.com/jktrn/enscribe.dev.git)的3.0版本一致。
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
> [!WARNING]
|
||||
This project does not use i18n.
|
||||
## 技术栈
|
||||
|
||||
## Technology Stack
|
||||
|
||||
These are the technology stacks originally used in [enscribe.dev](https://github.com/jktrn/enscribe.dev.git)
|
||||
这些是原[enscribe.dev](https://github.com/jktrn/enscribe.dev.git)使用到的技术栈
|
||||
|
||||
| Category | Technology Name |
|
||||
| ------------------- | -------------------------------------------------------------------------------------------------- |
|
||||
@@ -33,10 +27,9 @@ These are the technology stacks originally used in [enscribe.dev](https://github
|
||||
| Deployment | [Vercel](https://vercel.com) |
|
||||
|
||||
|
||||
|
||||
|
||||
> [!NOTE]
|
||||
[dev.2ha.me](https://dev.2ha.me) is based on the [enscribe.dev](https://github.com/jktrn/enscribe.dev.git) project. If you want to refer to other sample code repositories or use the components within them, you can check out the Examples in [enscribe.dev](https://github.com/jktrn/enscribe.dev.git) or the [tailwind-nextjs-starter-blog](https://github.com/timlrx/tailwind-nextjs-starter-blog) project. I have added support for NetEase Cloud Music and GiteaCalendar on top of [enscribe.dev](https://github.com/jktrn/enscribe.dev.git), replaced the original background image of [enscribe.dev](https://github.com/jktrn/enscribe.dev.git), and modified it to my own preferred style.
|
||||
> [dev.2ha.me](https://dev.2ha.me) 是基于 [enscribe.dev](https://github.com/jktrn/enscribe.dev.git) 这个项目的。如果您想参考其他样例的代码库或者使用其中的组件,可以查看[enscribe.dev](https://github.com/jktrn/enscribe.dev.git)或者[tailwind-nextjs-starter-blog](https://github.com/timlrx/tailwind-nextjs-starter-blog)项目的Examples;
|
||||
> 我在[enscribe.dev](https://github.com/jktrn/enscribe.dev.git)基础上添加了网易云音乐,GiteaCalendar的支持,替换了原[enscribe.dev](https://github.com/jktrn/enscribe.dev.git)背景图片,修改为我自己喜欢的风格。
|
||||
|
||||
| Category | Technology Name |
|
||||
| ------------------- | -------------------------------------------------------------------------------------------------- |
|
||||
@@ -44,34 +37,31 @@ These are the technology stacks originally used in [enscribe.dev](https://github
|
||||
| API | [githubCalendar](https://github.com/luckykeeper/giteaCalendar) |
|
||||
---
|
||||
|
||||
## 许可
|
||||
|
||||
## License
|
||||
### 原始模板
|
||||
|
||||
Original Template
|
||||
摘自许可证中的 “原始模板许可证 ”部分:
|
||||
|
||||
Extract from the "Original Template License" section of the license:
|
||||
> 本网站基于 [enscribe.dev](https://github.com/jktrn/enscribe.dev.git),而 [enscribe.dev](https://github.com/jktrn/enscribe.dev.git) 是基于 [jktrn/astro-erudite](https://github.com/jktrn/astro-erudite)的, [jktrn/astro-erudite](https://github.com/jktrn/astro-erudite)则衍生自松散的 MIT 许可项目 [trevortylerlee/astro-micro](https://github.com/trevortylerlee/astro-micro)
|
||||
|
||||
This website is based on [enscribe.dev](https://github.com/jktrn/enscribe.dev.git), which is based on [jktrn/astro-erudite](https://github.com/jktrn/astro-erudite), and [jktrn/astro-erudite](https://github.com/jktrn/astro-erudite) is derived from the loosely MIT-licensed project [trevortylerlee/astro-micro](https://github.com/trevortylerlee/astro-micro).
|
||||
[enscribe.dev](https://github.com/jktrn/enscribe.dev.git)的许可可以查看enscribe.dev的[Licence](https://github.com/jktrn/enscribe.dev/blob/main/LICENSE.md)文件
|
||||
### 站点代码
|
||||
|
||||
The license for [enscribe.dev](https://github.com/jktrn/enscribe.dev.git) can be viewed in the [License](https://github.com/jktrn/enscribe.dev/blob/main/LICENSE) file of enscribe.dev.
|
||||
[![Code License]](LICENSE.md)
|
||||
> 本许可证特别适用于对 [enscribe.dev](https://github.com/jktrn/enscribe.dev.git)(基于[jktrn/astro-erudite](https://github.com/jktrn/astro-erudite)) 模板所做的自定义修改。它并不延伸至原始模板代码,原始模板代码仍使用其原始 MIT 许可。
|
||||
|
||||
### Site Code
|
||||
就本许可而言,“代码”是指网站的软件组件、配置、布局、样式、功能、脚本和其他功能元素,但不包括此存储库中包含的内容呈现脚本(例如 MDX、Markdown、SVG、等媒体资源文件)。
|
||||
此存储库中的所有此类代码均根据 Apache License 2.0 获得许可:
|
||||
[dev.2ha.me](https://dev.2ha.me) © 2024 Jimleerx
|
||||
本文件遵循 Apache 许可证 2.0 版(简称“许可证”);您不得在未遵守该许可证的情况下使用本文件。您可以访问以下网址获取许可证副本:
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
除非适用法律另有规定或双方以书面形式达成一致,否则根据本许可证分发的软件均按“原样”分发,不附带任何明示或暗示的保证或条件。请参阅许可证,了解本许可证下特定语言的权限和限制规定。
|
||||
|
||||
[![Code License]](LICENSE)
|
||||
|
||||
> This license specifically applies to custom modifications made to the [enscribe.dev](https://github.com/jktrn/enscribe.dev.git) (based on [jktrn/astro-erudite](https://github.com/jktrn/astro-erudite)) template. It does not extend to the original template code, which remains under its original MIT license.
|
||||
|
||||
For the purposes of this license, "Code" refers to the software components, configuration, layout, style, functionality, scripts, and other functional elements of the website, but does not include content presentation scripts contained in this repository (such as MDX, Markdown, SVG, and other media resource files).
|
||||
All such Code in this repository is licensed under the Apache License 2.0: [dev.2ha.me](https://dev.2ha.me) © 2024 iluobei
|
||||
This document is subject to the Apache License, Version 2.0 (the "License"); you may not use this document except in compliance with the License. You can obtain a copy of the License at the following URL: http://www.apache.org/licenses/LICENSE-2.0
|
||||
Unless otherwise required by applicable law or agreed in writing by both parties, the software distributed under this license is distributed "as is", without any express or implied warranties or conditions. Please refer to the license for specific permissions and limitations under this license in the applicable language.
|
||||
|
||||
### Non-code content
|
||||
All external resources used are listed on the [dev.2ha.me](https://dev.2ha.me) About page. These resources are sourced from the internet. If used for commercial purposes, permission from the original author is required.
|
||||
|
||||
Any content not defined in the above code, including but not limited to MDX blog posts, SVG graphics, personal materials, other images, any written content in any format, and any accompanying documents, are licensed under the Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International (CC BY-NC-ND 4.0). This means you can use and share this content, but you must provide appropriate attribution, may not use it for commercial purposes, and may not distribute modified versions. For more details, please refer to CC BY-NC-ND 4.0.
|
||||
|
||||
Extracted from the [Website Code License](LICENSE#website-code-license) section.
|
||||
### 非代码内容
|
||||
使用的外部资源已在[dev.2ha.me](https://dev.2ha.me) 关于页面列出,资源均来自网络,如果是商业使用需得到原作者的授权。
|
||||
任何未在上述代码中定义的内容,包括但不限于 MDX 博客文章、SVG 图形、个人素材、其他图片、任何格式的书面内容以及任何随附文档,均受知识共享署名-非商业性使用-禁止演绎 4.0 国际 (CC BY-NC-ND 4.0) 许可。这意味着您可以使用和分享这些内容,但必须提供适当的署名,不得将其用于商业用途,也不得分发其修改版本。更多详情,请参阅 CC BY-NC-ND 4.0 。
|
||||
摘自 [Website Code License](LICENSE.md#website-code-license) 部分:
|
||||
|
||||
|
||||
[Code License]: https://img.shields.io/badge/License-Apache%202.0-blue.svg?style=for-the-badge&logo=github&logoColor=fff
|
||||
@@ -1,70 +0,0 @@
|
||||

|
||||
|
||||
<div align="center">
|
||||
|
||||
## dev.2ha.me
|
||||
### frok by [enscribe.dev](https://github.com/jktrn/enscribe.dev.git)
|
||||
|
||||
[![Code License]](LICENSE)
|
||||
|
||||
[English](README.md) | 简体中文 | [繁體中文](README_zh-Hant.md)
|
||||
|
||||
|
||||
[dev.2ha.me](https://dev.2ha.me) 是我在学习[Next.js](https://nextjs.org/)时练手的项目,我从[tailwind-nextjs-starter-blog](https://github.com/timlrx/tailwind-nextjs-starter-blog)项目的Examples发现了[enscribe.dev](https://github.com/jktrn/enscribe.dev.git),[dev.2ha.me](https://dev.2ha.me)和[enscribe.dev](https://github.com/jktrn/enscribe.dev.git)使用了相同的架构,如[Astro](https://astro.build/), [Tailwind](https://tailwindcss.com/), 和 [shadcn/ui](https://ui.shadcn.com/)。在此基础修改了一些API展示为中国国内的平台数据, 主体框架与[enscribe.dev](https://github.com/jktrn/enscribe.dev.git)的3.0版本一致。
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
## 技术栈
|
||||
|
||||
这些是原[enscribe.dev](https://github.com/jktrn/enscribe.dev.git)使用到的技术栈
|
||||
|
||||
| Category | Technology Name |
|
||||
| ------------------- | -------------------------------------------------------------------------------------------------- |
|
||||
| Framework | [Astro](https://astro.build/) |
|
||||
| Styling | [Tailwind](https://tailwindcss.com) |
|
||||
| Components | [shadcn/ui](https://ui.shadcn.com/) |
|
||||
| Content | [MDX](https://mdxjs.com/) |
|
||||
| Syntax Highlighting | [Shiki](https://github.com/shikijs/shiki) + [rehype-pretty-code](https://rehype-pretty.pages.dev/) |
|
||||
| Graphics | [Figma](https://www.figma.com/) |
|
||||
| Deployment | [Vercel](https://vercel.com) |
|
||||
|
||||
|
||||
> [!NOTE]
|
||||
> [dev.2ha.me](https://dev.2ha.me) 是基于 [enscribe.dev](https://github.com/jktrn/enscribe.dev.git) 这个项目的。如果您想参考其他样例的代码库或者使用其中的组件,可以查看[enscribe.dev](https://github.com/jktrn/enscribe.dev.git)或者[tailwind-nextjs-starter-blog](https://github.com/timlrx/tailwind-nextjs-starter-blog)项目的Examples;
|
||||
> 我在[enscribe.dev](https://github.com/jktrn/enscribe.dev.git)基础上添加了网易云音乐,GiteaCalendar的支持,替换了原[enscribe.dev](https://github.com/jktrn/enscribe.dev.git)背景图片,修改为我自己喜欢的风格。
|
||||
|
||||
| Category | Technology Name |
|
||||
| ------------------- | -------------------------------------------------------------------------------------------------- |
|
||||
| Components | [MagicUI](magicui.design), [reactbits](https://www.reactbits.dev/), [startlight](https://starlight.astro.build/), [CultUi](https://www.cult-ui.com/) |
|
||||
| API | [githubCalendar](https://github.com/luckykeeper/giteaCalendar) |
|
||||
---
|
||||
|
||||
## 许可
|
||||
|
||||
### 原始模板
|
||||
|
||||
摘自许可证中的 “原始模板许可证 ”部分:
|
||||
|
||||
> 本网站基于 [enscribe.dev](https://github.com/jktrn/enscribe.dev.git),而 [enscribe.dev](https://github.com/jktrn/enscribe.dev.git) 是基于 [jktrn/astro-erudite](https://github.com/jktrn/astro-erudite)的, [jktrn/astro-erudite](https://github.com/jktrn/astro-erudite)则衍生自松散的 MIT 许可项目 [trevortylerlee/astro-micro](https://github.com/trevortylerlee/astro-micro)
|
||||
|
||||
[enscribe.dev](https://github.com/jktrn/enscribe.dev.git)的许可可以查看enscribe.dev的[Licence](https://github.com/jktrn/enscribe.dev/blob/main/LICENSE)文件
|
||||
### 站点代码
|
||||
|
||||
[![Code License]](LICENSE)
|
||||
> 本许可证特别适用于对 [enscribe.dev](https://github.com/jktrn/enscribe.dev.git)(基于[jktrn/astro-erudite](https://github.com/jktrn/astro-erudite)) 模板所做的自定义修改。它并不延伸至原始模板代码,原始模板代码仍使用其原始 MIT 许可。
|
||||
|
||||
就本许可而言,“代码”是指网站的软件组件、配置、布局、样式、功能、脚本和其他功能元素,但不包括此存储库中包含的内容呈现脚本(例如 MDX、Markdown、SVG、等媒体资源文件)。
|
||||
此存储库中的所有此类代码均根据 Apache License 2.0 获得许可:
|
||||
[dev.2ha.me](https://dev.2ha.me) © 2024 iluobei
|
||||
本文件遵循 Apache 许可证 2.0 版(简称“许可证”);您不得在未遵守该许可证的情况下使用本文件。您可以访问以下网址获取许可证副本:
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
除非适用法律另有规定或双方以书面形式达成一致,否则根据本许可证分发的软件均按“原样”分发,不附带任何明示或暗示的保证或条件。请参阅许可证,了解本许可证下特定语言的权限和限制规定。
|
||||
|
||||
### 非代码内容
|
||||
使用的外部资源已在[dev.2ha.me](https://dev.2ha.me) 关于页面列出,资源均来自网络,如果是商业使用需得到原作者的授权。
|
||||
任何未在上述代码中定义的内容,包括但不限于 MDX 博客文章、SVG 图形、个人素材、其他图片、任何格式的书面内容以及任何随附文档,均受知识共享署名-非商业性使用-禁止演绎 4.0 国际 (CC BY-NC-ND 4.0) 许可。这意味着您可以使用和分享这些内容,但必须提供适当的署名,不得将其用于商业用途,也不得分发其修改版本。更多详情,请参阅 CC BY-NC-ND 4.0 。
|
||||
摘自 [Website Code License](LICENSE#website-code-license) 部分
|
||||
|
||||
|
||||
[Code License]: https://img.shields.io/badge/License-Apache%202.0-blue.svg?style=for-the-badge&logo=github&logoColor=fff
|
||||
@@ -1,119 +0,0 @@
|
||||

|
||||
|
||||
|
||||
|
||||
<div align="center">
|
||||
|
||||
|
||||
|
||||
## dev.2ha.me
|
||||
|
||||
### frok by [enscribe.dev](https://github.com/jktrn/enscribe.dev.git)
|
||||
|
||||
|
||||
|
||||
[![Code License]](LICENSE)
|
||||
|
||||
|
||||
|
||||
[English](README.md) | [簡體中文](README_zh-Hans.md) | 繁體中文
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
[dev.2ha.me](https://dev.2ha.me) 是我在學習[Next.js](https://nextjs.org/)時練手的項目,我從[tailwind-nextjs-starter-blog](https://github.com/timlrx/tailwind-nextjs-starter-blog)項目的Examples發現了[enscribe.dev](https://github.com/jktrn/enscribe.dev.git),[dev.2ha.me](https://dev.2ha.me)和[enscribe.dev](https://github.com/jktrn/enscribe.dev.git)使用了相同的架構,如[Astro](https://astro.build/), [Tailwind](https://tailwindcss.com/), 和 [shadcn/ui](https://ui.shadcn.com/)。在此基礎修改了一些API展示為中國國內的平臺數據, 主體框架與[enscribe.dev](https://github.com/jktrn/enscribe.dev.git)的3.0版本一致。
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
## 技術棧
|
||||
這些是原[enscribe.dev](https://github.com/jktrn/enscribe.dev.git)使用到的技術棧
|
||||
| Category | Technology Name |
|
||||
| ------------------- | -------------------------------------------------------------------------------------------------- |
|
||||
| Framework | [Astro](https://astro.build/) |
|
||||
| Styling | [Tailwind](https://tailwindcss.com) |
|
||||
| Components | [shadcn/ui](https://ui.shadcn.com/) |
|
||||
| Content | [MDX](https://mdxjs.com/) |
|
||||
| Syntax Highlighting | [Shiki](https://github.com/shikijs/shiki) + [rehype-pretty-code](https://rehype-pretty.pages.dev/) |
|
||||
| Graphics | [Figma](https://www.figma.com/) |
|
||||
| Deployment | [Vercel](https://vercel.com) |
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
> [!NOTE]
|
||||
> [dev.2ha.me](https://dev.2ha.me) 是基於 [enscribe.dev](https://github.com/jktrn/enscribe.dev.git) 這個項目的。如果您想參考其他樣例的代碼庫或者使用其中的組件,可以查看[enscribe.dev](https://github.com/jktrn/enscribe.dev.git)或者[tailwind-nextjs-starter-blog](https://github.com/timlrx/tailwind-nextjs-starter-blog)項目的Examples;
|
||||
|
||||
> 我在[enscribe.dev](https://github.com/jktrn/enscribe.dev.git)基礎上添加了網易雲音樂,GiteaCalendar的支持,替換了原[enscribe.dev](https://github.com/jktrn/enscribe.dev.git)背景圖片,修改為我自己喜歡的風格。
|
||||
|
||||
| Category | Technology Name |
|
||||
| ------------------- | -------------------------------------------------------------------------------------------------- |
|
||||
| Components | [MagicUI](magicui.design), [reactbits](https://www.reactbits.dev/), [startlight](https://starlight.astro.build/), [CultUi](https://www.cult-ui.com/) |
|
||||
| API | [githubCalendar](https://github.com/luckykeeper/giteaCalendar) |
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
## 許可
|
||||
|
||||
|
||||
|
||||
### 原始模板
|
||||
|
||||
|
||||
|
||||
摘自許可證中的 「原始模板許可證 」部分:
|
||||
|
||||
|
||||
|
||||
> 本網站基於 [enscribe.dev](https://github.com/jktrn/enscribe.dev.git),而 [enscribe.dev](https://github.com/jktrn/enscribe.dev.git) 是基於 [jktrn/astro-erudite](https://github.com/jktrn/astro-erudite)的, [jktrn/astro-erudite](https://github.com/jktrn/astro-erudite)則衍生自松散的 MIT 許可項目 [trevortylerlee/astro-micro](https://github.com/trevortylerlee/astro-micro)
|
||||
|
||||
|
||||
|
||||
[enscribe.dev](https://github.com/jktrn/enscribe.dev.git)的許可可以查看enscribe.dev的[Licence](https://github.com/jktrn/enscribe.dev/blob/main/LICENSE)文件
|
||||
|
||||
### 站點代碼
|
||||
|
||||
|
||||
|
||||
[![Code License]](LICENSE)
|
||||
|
||||
> 本許可證特別適用於對 [enscribe.dev](https://github.com/jktrn/enscribe.dev.git)(基於[jktrn/astro-erudite](https://github.com/jktrn/astro-erudite)) 模板所做的自定義修改。它並不延伸至原始模板代碼,原始模板代碼仍使用其原始 MIT 許可。
|
||||
|
||||
|
||||
|
||||
就本許可而言,「代碼」是指網站的軟件組件、配置、布局、樣式、功能、腳本和其他功能元素,但不包括此存儲庫中包含的內容呈現腳本(例如 MDX、Markdown、SVG、等媒體資源文件)。
|
||||
|
||||
此存儲庫中的所有此類代碼均根據 Apache License 2.0 獲得許可:
|
||||
|
||||
[dev.2ha.me](https://dev.2ha.me) © 2024 iluobei
|
||||
|
||||
本文件遵循 Apache 許可證 2.0 版(簡稱「許可證」);您不得在未遵守該許可證的情況下使用本文件。您可以訪問以下網址獲取許可證副本:
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
除非適用法律另有規定或雙方以書面形式達成一致,否則根據本許可證分發的軟件均按「原樣」分發,不附帶任何明示或暗示的保證或條件。請參閱許可證,了解本許可證下特定語言的權限和限製規定。
|
||||
|
||||
|
||||
|
||||
### 非代碼內容
|
||||
|
||||
使用的外部資源已在[dev.2ha.me](https://dev.2ha.me) 關於頁面列出,資源均來自網絡,如果是商業使用需得到原作者的授權。
|
||||
|
||||
任何未在上述代碼中定義的內容,包括但不限於 MDX 博客文章、SVG 圖形、個人素材、其他圖片、任何格式的書面內容以及任何隨附文檔,均受知識共享署名-非商業性使用-禁止演繹 4.0 國際 (CC BY-NC-ND 4.0) 許可。這意味著您可以使用和分享這些內容,但必須提供適當的署名,不得將其用於商業用途,也不得分發其修改版本。更多詳情,請參閱 CC BY-NC-ND 4.0 。
|
||||
|
||||
摘自 [Website Code License](LICENSE#website-code-license) 部分
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
[Code License]: https://img.shields.io/badge/License-Apache%202.0-blue.svg?style=for-the-badge&logo=github&logoColor=fff
|
||||
1008
package-lock.json
generated
@@ -9,8 +9,7 @@
|
||||
"build": "astro check && astro build",
|
||||
"preview": "astro preview",
|
||||
"astro": "astro",
|
||||
"prettier": "prettier --write ./src/**/*.{astro,ts,tsx,css}",
|
||||
"postinstall": "patch-package"
|
||||
"prettier": "prettier --write ./src/**/*.{astro,ts,tsx,css}"
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/check": "^0.9.4",
|
||||
@@ -61,7 +60,6 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/typography": "^0.5.15",
|
||||
"patch-package": "^8.0.1",
|
||||
"prettier": "^3.4.2",
|
||||
"prettier-plugin-astro": "^0.13.0",
|
||||
"prettier-plugin-astro-organize-imports": "^0.4.11",
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
diff --git a/node_modules/@shikijs/transformers/dist/index.mjs b/node_modules/@shikijs/transformers/dist/index.mjs
|
||||
index db4db63..c7f3ea2 100644
|
||||
--- a/node_modules/@shikijs/transformers/dist/index.mjs
|
||||
+++ b/node_modules/@shikijs/transformers/dist/index.mjs
|
||||
@@ -410,6 +410,9 @@ function transformerRenderWhitespace(options = {}) {
|
||||
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;
|
||||
|
Before Width: | Height: | Size: 87 KiB |
BIN
public/static/images/contributions-silhouette.png
Normal file
|
After Width: | Height: | Size: 8.3 KiB |
BIN
public/static/images/github-silhouette.png
Normal file
|
After Width: | Height: | Size: 5.9 KiB |
BIN
public/static/images/github-silhouette_old.png
Normal file
|
After Width: | Height: | Size: 9.3 KiB |
BIN
public/static/images/lastblogbg-silhouette.png
Normal file
|
After Width: | Height: | Size: 8.1 KiB |
BIN
public/static/images/lastblogbg-silhouette.webp
Normal file
|
After Width: | Height: | Size: 46 KiB |
BIN
public/static/images/music163-silhouette.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 41 KiB |
BIN
public/static/images/shortcuts-bg-silhouette.png
Normal file
|
After Width: | Height: | Size: 6.4 KiB |
BIN
public/static/images/waketime-silhouette.png
Normal file
|
After Width: | Height: | Size: 7.8 KiB |
|
Before Width: | Height: | Size: 70 KiB |
|
Before Width: | Height: | Size: 65 KiB |
@@ -69,7 +69,7 @@ const socialLinks: SocialLink[] = [
|
||||
<p class="text-sm text-muted-foreground">{bio}</p>
|
||||
<p class="text-sm text-muted-foreground flex items-center">
|
||||
这是一个开源的站点,你可以点击
|
||||
<Link href="https://github.com/iluobei/dev.2ha.me" target="_blank">
|
||||
<Link href="https://code.2ha.me/dev.2ha.me/dev.2ha.me" target="_blank">
|
||||
<svg
|
||||
class="size-4 ml-1 mr-1"
|
||||
style="filter: invert(100%);"
|
||||
|
||||
@@ -67,9 +67,9 @@ const SHORTS_CUTS_CLASS_NAMES: string[] = [
|
||||
'z-10 max-w max-h ml-[-0.2em] mt-[3.0em] inline-block sm:ml-[0em] sm:mt-[2.5em]',
|
||||
'z-10 max-w max-h mt-[-5.2em] inline-block sm:mt-[-3em]',
|
||||
'z-10 max-w max-h mt-[0.1em] inline-block sm:mt-[0.6em]',
|
||||
'z-10 max-w max-h ml-[0.15em] mt-[-7.5em] inline-block sm:ml-[0em] sm:mt-[-4.8em]',
|
||||
'z-10 max-w max-h ml-[-0.15em] mt-[0.2em] inline-block sm:mt-[0.9em] sm:ml-[0em]',
|
||||
'z-10 max-w max-h mt-[-7.5em] inline-block sm:mt-[-4.8em]',
|
||||
'z-10 max-w max-h ml-[0.15em] mt-[-7.5em] inline-block sm:ml-[0em] sm:mt-[-5.3em]',
|
||||
'z-10 max-w max-h ml-[-0.15em] mt-[0.2em] inline-block sm:mt-[0.6em]',
|
||||
'z-10 max-w max-h mt-[-7.5em] inline-block sm:mt-[-5.3em]',
|
||||
'z-10 max-w max-h mt-[1.88em] inline-block sm:mt-[1em]',
|
||||
'z-10 max-w max-h ml-[0.15em] mt-[-5.8em] inline-block sm:ml-[0em] sm:mt-[-4em]'
|
||||
]
|
||||
|
||||
@@ -1,406 +0,0 @@
|
||||
---
|
||||
import { DEV_LINKS } from '@/consts'
|
||||
import { Icon } from 'astro-icon/components'
|
||||
|
||||
// ------------- Hex math helpers (ported from react-hexgrid) -------------
|
||||
type Point = { x: number; y: number }
|
||||
type HexCoord = { q: number; r: number; s: number }
|
||||
type Orientation = {
|
||||
f0: number
|
||||
f1: number
|
||||
f2: number
|
||||
f3: number
|
||||
b0: number
|
||||
b1: number
|
||||
b2: number
|
||||
b3: number
|
||||
startAngle: number
|
||||
}
|
||||
type LayoutDimension = {
|
||||
size: Point
|
||||
spacing: number
|
||||
origin: Point
|
||||
orientation: Orientation
|
||||
}
|
||||
|
||||
const SQRT3 = Math.sqrt(3)
|
||||
const ORIENTATION_FLAT: Orientation = {
|
||||
f0: 3 / 2,
|
||||
f1: 0,
|
||||
f2: SQRT3 / 2,
|
||||
f3: SQRT3,
|
||||
b0: 2 / 3,
|
||||
b1: 0,
|
||||
b2: -1 / 3,
|
||||
b3: SQRT3 / 3,
|
||||
startAngle: 0,
|
||||
}
|
||||
|
||||
const BASE_HEX_SIZE = 104
|
||||
const HEX_SIZE = 168
|
||||
const BORDER_WIDTH = 24
|
||||
|
||||
const layout: LayoutDimension = {
|
||||
size: { x: HEX_SIZE, y: HEX_SIZE },
|
||||
spacing: 1,
|
||||
origin: { x: 0, y: 0 },
|
||||
orientation: ORIENTATION_FLAT,
|
||||
}
|
||||
|
||||
const GRID_WIDTH = 5
|
||||
const GRID_HEIGHT = 5
|
||||
|
||||
function calculatePolygonPoints(size: number, flat = false) {
|
||||
const angleOffset = flat ? 0 : Math.PI / 6
|
||||
const corners: Point[] = []
|
||||
for (let i = 0; i < 6; i++) {
|
||||
const angle = (2 * Math.PI * i) / 6 + angleOffset
|
||||
corners.push({
|
||||
x: size * Math.cos(angle),
|
||||
y: size * Math.sin(angle),
|
||||
})
|
||||
}
|
||||
|
||||
return corners.map((corner) => `${corner.x.toFixed(2)},${corner.y.toFixed(2)}`).join(' ')
|
||||
}
|
||||
|
||||
function coordKey(hex: HexCoord) {
|
||||
return `${hex.q},${hex.r},${hex.s}`
|
||||
}
|
||||
|
||||
function hexToPixel(hex: HexCoord, layout: LayoutDimension): Point {
|
||||
const { orientation: M, size, spacing, origin } = layout
|
||||
let x = (M.f0 * hex.q + M.f1 * hex.r) * size.x
|
||||
let y = (M.f2 * hex.q + M.f3 * hex.r) * size.y
|
||||
x *= spacing
|
||||
y *= spacing
|
||||
return { x: x + origin.x, y: y + origin.y }
|
||||
}
|
||||
|
||||
function rectangle(mapWidth: number, mapHeight: number): HexCoord[] {
|
||||
const cells: HexCoord[] = []
|
||||
for (let r = 0; r < mapHeight; r++) {
|
||||
const offset = Math.floor(r / 2)
|
||||
for (let q = -offset; q < mapWidth - offset; q++) {
|
||||
cells.push({ q, r, s: -q - r })
|
||||
}
|
||||
}
|
||||
return cells
|
||||
}
|
||||
|
||||
function hexagon(mapRadius: number): HexCoord[] {
|
||||
const cells: HexCoord[] = []
|
||||
for (let q = -mapRadius; q <= mapRadius; q++) {
|
||||
const r1 = Math.max(-mapRadius, -q - mapRadius)
|
||||
const r2 = Math.min(mapRadius, -q + mapRadius)
|
||||
for (let r = r1; r <= r2; r++) {
|
||||
cells.push({ q, r, s: -q - r })
|
||||
}
|
||||
}
|
||||
return cells
|
||||
}
|
||||
|
||||
// 根据cells的下标与对应值控制显示在哪些格子里
|
||||
const ACTIVE_COORDS: HexCoord[] = [
|
||||
// { q: -1, r: 0, s: 1 },
|
||||
// { q: 0, r: 0, s: 0 },
|
||||
// { q: 1, r: 0, s: -1 },
|
||||
{ q: 2, r: 0, s: -2 },
|
||||
{ q: 3, r: 0, s: -3 },
|
||||
{ q: 0, r: 1, s: -1 },
|
||||
{ q: 1, r: 1, s: -2 },
|
||||
{ q: 2, r: 1, s: -3 },
|
||||
{ q: 0, r: 2, s: -2 },
|
||||
{ q: 1, r: 2, s: -3 },
|
||||
{ q: 2, r: 2, s: -4 },
|
||||
{ q: 0, r: 3, s: -3 },
|
||||
{ q: 1, r: 3, s: -4 },
|
||||
{ q: 2, r: 3, s: -5 },
|
||||
]
|
||||
|
||||
const gridCoords = rectangle(GRID_WIDTH, GRID_HEIGHT)
|
||||
const gridKeyMap = new Map(gridCoords.map((hex) => [coordKey(hex), hex]))
|
||||
|
||||
const preferredCoords = ACTIVE_COORDS.map((hex) => gridKeyMap.get(coordKey(hex))).filter(
|
||||
Boolean,
|
||||
) as HexCoord[]
|
||||
|
||||
const preferredKeySet = new Set(preferredCoords.map((hex) => coordKey(hex)))
|
||||
const remainingGridCoords = gridCoords.filter((hex) => !preferredKeySet.has(coordKey(hex)))
|
||||
|
||||
let orderedActiveSlots = [...preferredCoords, ...remainingGridCoords]
|
||||
|
||||
if (DEV_LINKS.length > orderedActiveSlots.length) {
|
||||
const usedKeys = new Set(orderedActiveSlots.map((hex) => coordKey(hex)))
|
||||
let radius = Math.max(GRID_WIDTH, GRID_HEIGHT)
|
||||
|
||||
while (orderedActiveSlots.length < DEV_LINKS.length) {
|
||||
radius += 1
|
||||
for (const hex of hexagon(radius)) {
|
||||
const key = coordKey(hex)
|
||||
if (usedKeys.has(key)) continue
|
||||
orderedActiveSlots.push(hex)
|
||||
usedKeys.add(key)
|
||||
if (orderedActiveSlots.length === DEV_LINKS.length) break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const assignedCoords = orderedActiveSlots.slice(0, DEV_LINKS.length)
|
||||
const coordToLink = new Map<string, (typeof DEV_LINKS)[number]>()
|
||||
assignedCoords.forEach((hex, index) => {
|
||||
const link = DEV_LINKS[index]
|
||||
if (!hex || !link) return
|
||||
coordToLink.set(coordKey(hex), link)
|
||||
})
|
||||
|
||||
const renderCoordMap = new Map<string, HexCoord>()
|
||||
gridCoords.forEach((hex) => renderCoordMap.set(coordKey(hex), hex))
|
||||
assignedCoords.forEach((hex) => {
|
||||
if (!hex) return
|
||||
renderCoordMap.set(coordKey(hex), hex)
|
||||
})
|
||||
|
||||
const renderCoords = Array.from(renderCoordMap.values())
|
||||
|
||||
const hexagonPoints = calculatePolygonPoints(layout.size.x, true)
|
||||
// const circleRadius = layout.size.x * 0.84
|
||||
const circleRadius = 80
|
||||
|
||||
const baseCells = renderCoords.map((hex) => ({
|
||||
hex,
|
||||
link: coordToLink.get(coordKey(hex)) ?? null,
|
||||
center: hexToPixel(hex, layout),
|
||||
}))
|
||||
|
||||
const xs = baseCells.map((cell) => cell.center.x)
|
||||
const ys = baseCells.map((cell) => cell.center.y)
|
||||
const padding = layout.size.x * 0.45
|
||||
const minX = Math.min(...xs) - padding
|
||||
const maxX = Math.max(...xs) + padding
|
||||
const minY = Math.min(...ys) - padding
|
||||
const maxY = Math.max(...ys) + padding
|
||||
const sizeScale = HEX_SIZE / BASE_HEX_SIZE
|
||||
const rawWidth = maxX - minX
|
||||
const rawHeight = maxY - minY
|
||||
const centerX = (minX + maxX) / 2
|
||||
const centerY = (minY + maxY) / 2
|
||||
const viewWidth = rawWidth / sizeScale
|
||||
const viewHeight = rawHeight / sizeScale
|
||||
const viewMinX = centerX - viewWidth / 2
|
||||
const viewMinY = centerY - viewHeight / 2
|
||||
const viewBox = `${viewMinX} ${viewMinY} ${viewWidth} ${viewHeight}`
|
||||
const iconClipId = 'hexIconClip'
|
||||
|
||||
const cells = baseCells.map((cell) => ({
|
||||
...cell,
|
||||
showContent: Boolean(cell.link),
|
||||
}))
|
||||
---
|
||||
|
||||
<svg
|
||||
class="hex-grid hex-grid--polygons"
|
||||
width="100%"
|
||||
height="100%"
|
||||
viewBox={viewBox}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
preserveAspectRatio="xMidYMid slice"
|
||||
>
|
||||
<g class="hex-grid__group">
|
||||
{cells.map(({ link, center }, index) => {
|
||||
const isGhost = !link
|
||||
return (
|
||||
<polygon
|
||||
class={`hexagon-bg ${isGhost ? 'hexagon-bg--ghost' : ''}`}
|
||||
points={hexagonPoints}
|
||||
fill="transparent"
|
||||
stroke="#252525"
|
||||
stroke-width={BORDER_WIDTH}
|
||||
transform={`translate(${center.x}, ${center.y})`}
|
||||
data-index={index}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
<svg
|
||||
class="hex-grid hex-grid--icons"
|
||||
width="100%"
|
||||
height="100%"
|
||||
viewBox={viewBox}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
preserveAspectRatio="xMidYMid slice"
|
||||
>
|
||||
<defs>
|
||||
<clipPath id={iconClipId} clipPathUnits="userSpaceOnUse">
|
||||
<circle cx="0" cy="0" r={circleRadius} />
|
||||
</clipPath>
|
||||
</defs>
|
||||
|
||||
<g class="hex-grid__group">
|
||||
{cells.map(({ link, center, showContent }, index) => {
|
||||
if (!link || !showContent) return null
|
||||
return (
|
||||
<g
|
||||
class="hexagon-wrapper"
|
||||
transform={`translate(${center.x}, ${center.y})`}
|
||||
data-index={index}
|
||||
>
|
||||
<a
|
||||
href={link.href}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
aria-label={link.label}
|
||||
>
|
||||
<title>{link.title}</title>
|
||||
<g class="icon-group">
|
||||
<circle
|
||||
r={circleRadius}
|
||||
fill="hsl(var(--secondary))"
|
||||
fill-opacity="0.45"
|
||||
stroke="hsl(var(--border))"
|
||||
stroke-width={Math.max(4, BORDER_WIDTH * 0.32)}
|
||||
class="icon-circle z-10"
|
||||
/>
|
||||
|
||||
{link.icon.startsWith('mdi:') ? (
|
||||
<foreignObject
|
||||
x={-(layout.size.x * 0.7)}
|
||||
y={-(layout.size.y * 0.7)}
|
||||
width={layout.size.x * 1.4}
|
||||
height={layout.size.y * 1.4}
|
||||
clip-path={`url(#${iconClipId})`}
|
||||
class="icon-foreign"
|
||||
>
|
||||
<div class="icon-container">
|
||||
<Icon
|
||||
name={link.icon}
|
||||
class="hexagon-icon"
|
||||
style="color: rgb(233, 211, 182);"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</div>
|
||||
</foreignObject>
|
||||
) : (
|
||||
<image
|
||||
href={link.icon}
|
||||
x={-layout.size.x * 0.3}
|
||||
y={-layout.size.y * 0.3}
|
||||
width={layout.size.x * 0.6}
|
||||
height={layout.size.y * 0.6}
|
||||
clip-path={`url(#${iconClipId})`}
|
||||
class="hexagon-image"
|
||||
/>
|
||||
)}
|
||||
</g>
|
||||
</a>
|
||||
</g>
|
||||
)
|
||||
})}
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
<style>
|
||||
.hex-grid {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
}
|
||||
.hex-grid--polygons {
|
||||
z-index: 1;
|
||||
}
|
||||
.hex-grid--icons {
|
||||
z-index: 20;
|
||||
}
|
||||
|
||||
.hex-grid__group {
|
||||
transform-origin: center;
|
||||
transform: translateX(-114px) translateY(60px) scale(1.08);
|
||||
}
|
||||
|
||||
.hexagon-wrapper {
|
||||
cursor: pointer;
|
||||
pointer-events: auto;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
|
||||
.hexagon-wrapper a {
|
||||
outline: none;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
pointer-events: auto;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.hexagon-wrapper a title {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.hexagon-bg,
|
||||
.icon-circle,
|
||||
.icon-group {
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
.icon-circle {
|
||||
stroke-width: 4px;
|
||||
transform-origin: center;
|
||||
transform-box: fill-box;
|
||||
}
|
||||
.icon-group {
|
||||
transform-origin: center;
|
||||
transform-box: fill-box;
|
||||
filter: drop-shadow(0 4px 12px rgba(0, 0, 0, 0.45));
|
||||
}
|
||||
|
||||
.hexagon-wrapper:hover .icon-circle {
|
||||
fill-opacity: 0.85;
|
||||
stroke: hsl(var(--primary));
|
||||
stroke-width: 4px;
|
||||
transform: scale(1.08);
|
||||
}
|
||||
|
||||
.hexagon-wrapper:hover .icon-group {
|
||||
transform: rotate(12deg);
|
||||
transform-origin: center;
|
||||
}
|
||||
|
||||
.hexagon-bg--ghost {
|
||||
stroke-dasharray: none;
|
||||
}
|
||||
|
||||
.icon-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 9999px;
|
||||
}
|
||||
|
||||
.hexagon-icon {
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
max-width: 88px;
|
||||
max-height: 88px;
|
||||
}
|
||||
|
||||
.hexagon-image {
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.icon-foreign {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
:global(.hexagon-icon) {
|
||||
color: rgb(233, 211, 182) !important;
|
||||
}
|
||||
</style>
|
||||
@@ -10,13 +10,13 @@ import logo from '../../public/static/logo.webp'
|
||||
---
|
||||
|
||||
<header
|
||||
class="sticky top-0 z-[99] bg-background/50 backdrop-blur-md"
|
||||
class="sticky top-0 z-20 bg-background/50 backdrop-blur-md"
|
||||
transition:persist
|
||||
>
|
||||
<Container>
|
||||
<div class="flex flex-wrap items-center justify-between gap-4 py-4 ">
|
||||
<Link href="/" class="logo-link">
|
||||
<Image src={logo} alt="Logo" class="size-8 -scale-x-100 border-2 border-[color:rgba(241,140,110,0.4)] shadow-[4px_4px_0_rgba(0,0,0,0.2)] transition-all duration-300" />
|
||||
<Link href="/">
|
||||
<Image src={logo} alt="Logo" class="size-8 -scale-x-100 border-2 border-[color:rgba(241,140,110,0.4)] shadow-[4px_4px_0_rgba(0,0,0,0.2)]" />
|
||||
</Link>
|
||||
<div class="flex items-center gap-2 sm:gap-4">
|
||||
<nav class="hidden items-center gap-2 text-base sm:flex sm:gap-3">
|
||||
@@ -38,31 +38,3 @@ import logo from '../../public/static/logo.webp'
|
||||
</div>
|
||||
</Container>
|
||||
</header>
|
||||
|
||||
<style>
|
||||
.logo-link:hover img {
|
||||
border-color: hsl(var(--primary));
|
||||
box-shadow:
|
||||
0 0 12px rgba(241, 140, 110, 0.6),
|
||||
0 0 24px rgba(241, 140, 110, 0.4),
|
||||
0 0 36px rgba(241, 140, 110, 0.2),
|
||||
4px 4px 0 rgba(0, 0, 0, 0.2);
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
nav :global(.pixel-button:hover) {
|
||||
box-shadow:
|
||||
0 0 12px rgba(241, 140, 110, 0.5),
|
||||
0 0 24px rgba(241, 140, 110, 0.3),
|
||||
0 0 36px rgba(241, 140, 110, 0.15),
|
||||
4px 4px 0 rgba(0, 0, 0, 0.2) !important;
|
||||
}
|
||||
|
||||
.dark nav :global(.pixel-button:hover) {
|
||||
box-shadow:
|
||||
0 0 12px rgba(241, 140, 110, 0.5),
|
||||
0 0 24px rgba(241, 140, 110, 0.3),
|
||||
0 0 36px rgba(241, 140, 110, 0.15),
|
||||
4px 4px 0 rgba(0, 0, 0, 0.4) !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,46 +1,47 @@
|
||||
import AvatarComponent from '@/components/ui/avatar'
|
||||
|
||||
const AuthorPresence = () => {
|
||||
|
||||
|
||||
return (
|
||||
<div className="relative overflow-hidden sm:aspect-square select-none" style={{ cursor: 'default' }}>
|
||||
<div className="relative overflow-hidden sm:aspect-square">
|
||||
<div className="grid size-full grid-rows-4">
|
||||
<div className="bg-secondary/50"></div>
|
||||
<div className="row-span-3 flex flex-col gap-3 p-3">
|
||||
<div className="flex justify-between">
|
||||
<div className="flex justify-between gap-x-1">
|
||||
<div className="relative">
|
||||
<AvatarComponent
|
||||
src="/static/avatar.png"
|
||||
src="/static/avatar.webp"
|
||||
alt="Avatar"
|
||||
fallback="e"
|
||||
className="-mt-[4.5rem] aspect-square size-24 rounded-full"
|
||||
/>
|
||||
</div>
|
||||
{/* <div className="flex items-center rounded-xl bg-secondary/50 px-2">
|
||||
<div className="flex items-center rounded-xl bg-secondary/50 px-2">
|
||||
<img
|
||||
src="https://contrib.rocks/image?repo=iluobei/dev.2ha.me"
|
||||
src="/static/images/badges.png"
|
||||
alt="Discord Badges"
|
||||
width={104}
|
||||
height={24}
|
||||
className="grayscale"
|
||||
/>
|
||||
</div> */}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col items-start gap-y-1 rounded-xl bg-secondary/50 p-3 select-none">
|
||||
<span className="text-base leading-none select-none cursor-default">胡萝北(🥕)</span>
|
||||
<span className="text-xs leading-none text-muted-foreground select-none cursor-default">
|
||||
<div className="flex flex-col gap-y-1 rounded-xl bg-secondary/50 p-3">
|
||||
<span className="text-base leading-none">jimlee</span>
|
||||
<span className="text-xs leading-none text-muted-foreground">
|
||||
li@2ha.me
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex grow rounded-xl bg-secondary/50 px-3 py-2 select-none">
|
||||
<div className="flex size-full flex-col items-center justify-center gap-1 select-none">
|
||||
<div className="flex grow rounded-xl bg-secondary/50 px-3 py-2">
|
||||
<div className="flex size-full flex-col items-center justify-center gap-1">
|
||||
<img
|
||||
src="/static/images/lieflat.svg"
|
||||
alt="No Status Image"
|
||||
width={64}
|
||||
height={64}
|
||||
className="h-full rounded-lg select-none"
|
||||
className="h-full rounded-lg"
|
||||
/>
|
||||
<div className="text-[10px] text-muted-foreground select-none cursor-default">
|
||||
<div className="text-[10px] text-muted-foreground">
|
||||
但行好事,莫问前程。
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -55,12 +55,12 @@ const WakatimeGraph = ({ }: Props) => {
|
||||
useEffect(() => {
|
||||
setLanguages([
|
||||
{ name: 'java', hours: 1009, fill: 'hsl(var(--chart-1))' },
|
||||
{ name: 'javascript', hours: 586, fill: 'hsl(var(--chart-2))' },
|
||||
{ name: 'typescript', hours: 519, fill: 'hsl(var(--chart-4))' },
|
||||
{ name: 'react', hours: 442, fill: 'hsl(var(--chart-5))' },
|
||||
{ name: 'javascript', hours: 476, fill: 'hsl(var(--chart-2))' },
|
||||
{ name: 'kotlin', hours: 405, fill: 'hsl(var(--chart-3))' },
|
||||
{ name: 'go', hours: 301, fill: 'hsl(var(--chart-6))' },
|
||||
{ name: 'python', hours: 177, fill: 'hsl(var(--chart-7))' },
|
||||
{ name: 'typescript', hours: 401, fill: 'hsl(var(--chart-4))' },
|
||||
{ name: 'react', hours: 257, fill: 'hsl(var(--chart-5))' },
|
||||
{ name: 'go', hours: 125, fill: 'hsl(var(--chart-6))' },
|
||||
{ name: 'python', hours: 120, fill: 'hsl(var(--chart-7))' },
|
||||
])
|
||||
setIsLoading(false)
|
||||
}, [])
|
||||
|
||||
@@ -16,7 +16,7 @@ async function fetchCalendarData(): Promise<ApiResponse> {
|
||||
const data: ApiResponse | ApiErrorResponse = await response.json()
|
||||
if (!response.ok) {
|
||||
throw Error(
|
||||
`Fetching Gitea(code.2ha.me) contribution data for luobei failed, see https://github.com/luckykeeper/giteaCalendar.`,
|
||||
`Fetching Gitea(code.2ha.me) contribution data for jimlee failed, see https://github.com/luckykeeper/giteaCalendar.`,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ export const NAV_LINKS: Link[] = [
|
||||
]
|
||||
|
||||
export const SOCIAL_LINKS: Link[] = [
|
||||
{ href: 'https://github.com/iluobei', label: 'GitHub' },
|
||||
{ href: 'https://github.com/jimleerx', label: 'GitHub' },
|
||||
{ href: 'https://1ms.cc', label: 'HubProxy' },
|
||||
{ href: 'https://code.2ha.me', label: 'Gitea' },
|
||||
{ href: 'li@2ha.me', label: 'Email' },
|
||||
@@ -54,10 +54,10 @@ export const DEV_LINKS: DevLink[] = [
|
||||
icon: 'mdi:git',
|
||||
},
|
||||
{
|
||||
href: 'https://img.2ha.me',
|
||||
href: 'https://maven.2ha.me',
|
||||
label: 'Nexus',
|
||||
title: '图床',
|
||||
icon: 'mdi:image-multiple',
|
||||
title: 'Maven仓库',
|
||||
icon: 'mdi:chart-doughnut-variant',
|
||||
},
|
||||
{
|
||||
href: 'https://dms.2ha.me',
|
||||
@@ -69,13 +69,13 @@ export const DEV_LINKS: DevLink[] = [
|
||||
href: 'https://p.2ha.me',
|
||||
label: 'Zfile',
|
||||
title: '网盘',
|
||||
icon: 'mdi:harddisk',
|
||||
icon: 'mdi:cloud-arrow-up',
|
||||
},
|
||||
{
|
||||
href: 'https://tz.2ha.me',
|
||||
label: 'VPS Monitor',
|
||||
title: '探针',
|
||||
icon: 'mdi:chart-areaspline',
|
||||
href: 'https://photo.2ha.me',
|
||||
label: 'immich',
|
||||
title: '相册',
|
||||
icon: 'mdi:camera',
|
||||
},
|
||||
{
|
||||
href: 'https://f.2ha.me',
|
||||
@@ -83,12 +83,7 @@ export const DEV_LINKS: DevLink[] = [
|
||||
title: '文件服务器',
|
||||
icon: 'mdi:file-arrow-up-down-outline',
|
||||
},
|
||||
{
|
||||
href: 'https://status.2ha.me',
|
||||
label: 'Domain Status',
|
||||
title: '站点检测',
|
||||
icon: 'mdi:cloud-check'
|
||||
},
|
||||
{ href: 'https://v.2ha.me', label: 'Emby', title: 'Emby', icon: 'mdi:emby' },
|
||||
{
|
||||
href: 'https://in.2ha.me',
|
||||
label: '2ha.me statistics',
|
||||
@@ -96,16 +91,15 @@ export const DEV_LINKS: DevLink[] = [
|
||||
icon: 'mdi:sun-azimuth',
|
||||
},
|
||||
{
|
||||
href: 'https://miaomiaowu.net',
|
||||
href: 'https://mmwdemo.2ha.me/docs',
|
||||
label: '妙妙屋',
|
||||
title: '个人Clash订阅管理工具',
|
||||
icon: '/static/mmw.svg',
|
||||
// icon: 'mdi:cat',
|
||||
},
|
||||
{
|
||||
href: 'https://1ms.cc',
|
||||
label: 'hubproxy',
|
||||
title: 'GitHub&DockerHub加速',
|
||||
title: 'GitHub&DockerHub代理',
|
||||
icon: 'mdi:rocket-launch-outline',
|
||||
},
|
||||
]
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
---
|
||||
name: '胡萝北(🥕)'
|
||||
name: 'jimlee'
|
||||
pronouns: 'he/him'
|
||||
avatar: 'https://dev.2ha.me/static/avatar_256.webp'
|
||||
bio: 'd(-_-)b'
|
||||
website: 'https://dev.2ha.me'
|
||||
devintro: 'https://code.2ha.me/luobei'
|
||||
devintro: 'https://code.2ha.me/jimlee'
|
||||
gitea: 'https://code.2ha.me/dev.2ha.me'
|
||||
# twitter: 'https://twitter.com/enscry'
|
||||
github: 'https://github.com/iluobei'
|
||||
github: 'https://github.com/jimleerx'
|
||||
mail: 'li@2ha.me'
|
||||
---
|
||||
|
||||
@@ -4,7 +4,7 @@ description: '在debian上根据需要添加nginx模块,编译自定义的ngin
|
||||
date: 2025-04-11
|
||||
tags: ['nginx', 'debian', 'build']
|
||||
image: 'assets/nginx.svg'
|
||||
authors: ['胡萝北(🥕)']
|
||||
authors: ['jimlee']
|
||||
---
|
||||
|
||||
## 在debian上安装nginx
|
||||
|
||||
@@ -4,7 +4,7 @@ description: '常用的kotlin, python, shell, regex等代码块合集'
|
||||
date: 2025-04-10
|
||||
tags: ['kotlin', 'python', 'regex', 'linux', 'shell', 'javascript', 'nginx']
|
||||
image: 'assets/commoncodebanner.webp'
|
||||
authors: ['胡萝北(🥕)']
|
||||
authors: ['jimlee']
|
||||
---
|
||||
|
||||
## kotlin
|
||||
|
||||
@@ -4,7 +4,7 @@ description: '在dsm7.2上破解emby套件, 输入任意激活码即可获取小
|
||||
date: 2024-12-31
|
||||
tags: ['emby', 'synology', 'crack']
|
||||
image: 'assets/emby_banner.png'
|
||||
authors: ['胡萝北(🥕)']
|
||||
authors: ['jimlee']
|
||||
---
|
||||
import { Icon } from 'astro-icon/components'
|
||||
import Link from '@/components/Link.astro'
|
||||
|
||||
@@ -4,7 +4,7 @@ description: 'debian安装fail2ban保护nginx服务器,阻止暴力破解和
|
||||
date: 2025-10-06
|
||||
tags: ['debian', 'nginx', 'fail2ban', 'regex', 'shell']
|
||||
image: 'assets/nginxfail2ban.png'
|
||||
authors: ['胡萝北(🥕)']
|
||||
authors: ['jimlee']
|
||||
---
|
||||
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ description: '1. shikijs重复依赖打包失败; 2. 使用Rehype Pretty Code/Co
|
||||
date: 2025-05-12
|
||||
tags: ['typescript', 'astro', 'shiki']
|
||||
image: 'assets/shikicodecopybutton.png'
|
||||
authors: ['胡萝北(🥕)']
|
||||
authors: ['jimlee']
|
||||
---
|
||||
import FileTree from '@/components/starlight/FileTree.astro'
|
||||
|
||||
|
||||
@@ -3,6 +3,6 @@ name: '妙妙屋(个人clash订阅管理工具)'
|
||||
description: '妙妙屋是一个功能强大的Clash订阅管理平台,帮助您轻松管理订阅、节点和用户。'
|
||||
tags: ['open-source', 'personal', 'clash', 'substore']
|
||||
image: 'assets/mmw.png'
|
||||
link: 'https://miaomiaowu.net'
|
||||
link: 'https://mmwdemo.2ha.me'
|
||||
startDate: '2025-10-10'
|
||||
---
|
||||
|
||||
@@ -21,7 +21,7 @@ const { title, description, image } = Astro.props
|
||||
description={description}
|
||||
image={image}
|
||||
/>
|
||||
<script is:inline async defer src="https://in.2ha.me/script.js" data-website-id="34634aec-34a9-4ef4-9a8f-08ee96699a84"></script>
|
||||
<script is:inline defer src="https://in.2ha.me/script.js" data-website-id="34634aec-34a9-4ef4-9a8f-08ee96699a84"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div
|
||||
|
||||
@@ -30,6 +30,6 @@ export function transformerNotationSkip(
|
||||
return false
|
||||
},
|
||||
undefined, // remove empty lines
|
||||
) as ShikiTransformer
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ const sortedTags = tagKeys.sort((a, b) => tagCounts[b] - tagCounts[a])
|
||||
<Layout title="Blog" description="Blog">
|
||||
<!-- Fixed Sidebar -->
|
||||
<aside
|
||||
class="hidden xl:block fixed top-1/2 -translate-y-1/2 w-[240px] px-4 py-3 rounded-sm bg-background/80 backdrop-blur-sm border-2 border-[color:rgba(241,140,110,0.22)] shadow-[4px_4px_0_rgba(0,0,0,0.22)] dark:shadow-[4px_4px_0_rgba(0,0,0,0.65)] z-10"
|
||||
class="hidden xl:block fixed left-4 top-32 w-[240px] px-4 py-3 rounded-sm bg-background/80 backdrop-blur-sm border-2 border-[color:rgba(241,140,110,0.22)] shadow-[4px_4px_0_rgba(0,0,0,0.22)] dark:shadow-[4px_4px_0_rgba(0,0,0,0.65)] z-10"
|
||||
>
|
||||
<Link
|
||||
href={`/blog`}
|
||||
@@ -127,21 +127,3 @@ const sortedTags = tagKeys.sort((a, b) => tagCounts[b] - tagCounts[a])
|
||||
/>
|
||||
</Container>
|
||||
</Layout>
|
||||
|
||||
<style>
|
||||
ul li > :global(div:hover) {
|
||||
box-shadow:
|
||||
0 0 16px rgba(241, 140, 110, 0.5),
|
||||
0 0 32px rgba(241, 140, 110, 0.3),
|
||||
0 0 48px rgba(241, 140, 110, 0.15),
|
||||
6px 6px 0 rgba(0, 0, 0, 0.26) !important;
|
||||
}
|
||||
|
||||
.dark ul li > :global(div:hover) {
|
||||
box-shadow:
|
||||
0 0 16px rgba(241, 140, 110, 0.5),
|
||||
0 0 32px rgba(241, 140, 110, 0.3),
|
||||
0 0 48px rgba(241, 140, 110, 0.15),
|
||||
6px 6px 0 rgba(0, 0, 0, 0.75) !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -12,8 +12,7 @@ import { getCollection } from 'astro:content'
|
||||
import GiteaCalendar from '@/components/custom/GiteaCalendar'
|
||||
import Music163Player from '@/components/custom/Music163Player'
|
||||
import RandomAnimeBackground from '@/components/custom/RandomAnimeBackgrounds'
|
||||
// import DevShortCuts from '@/components/DevShortCuts.astro'
|
||||
import DevShortcutsHexagon from '@/components/DevShortcutsHexagon.astro'
|
||||
import DevShortCuts from '@/components/DevShortCuts.astro'
|
||||
|
||||
const latestPost = await getCollection('blog').then((posts: any[]) =>
|
||||
posts
|
||||
@@ -29,11 +28,11 @@ const latestPost = await getCollection('blog').then((posts: any[]) =>
|
||||
|
||||
<Layout title="主页" description={SITE.DESCRIPTION}>
|
||||
<section
|
||||
class="mx-auto grid w-full grid-cols-1 gap-4 px-4 [grid-template-areas:'a'_'d'_'b'_'e'_'g'_'f'_'j'_'i'_'k'] [grid-template-rows:repeat(9,auto)] *:border-2 *:border-[color-mix(in_srgb,hsl(var(--primary))_22%,hsl(var(--border)))] *:bg-secondary/25 *:bg-cover *:bg-center *:bg-no-repeat *:overflow-hidden *:w-full *:[box-shadow:4px_4px_0_rgba(0,0,0,0.22)] *:transition-all *:duration-200 sm:max-w-screen-sm sm:grid-cols-2 sm:[grid-template-areas:'a_a'_'b_d'_'e_e'_'j_g'_'f_f'_'i_k'] sm:[grid-template-rows:repeat(6,300px)] sm:*:max-w-[615px] xl:max-w-screen-xl xl:grid-cols-4 xl:[grid-template-areas:'a_a_b_g'_'d_e_e_i'_'k_j_f_f'] xl:[grid-template-rows:repeat(3,300px)] xl:*:max-w-none dark:*:[box-shadow:4px_4px_0_rgba(0,0,0,0.65)] xl:[&:hover:has(>.has-overlay:hover)>.first>.overlay]:opacity-100 xl:[&>*:not(.first):hover_.overlay]:opacity-100"
|
||||
class="mx-auto grid w-full grid-cols-1 gap-4 px-4 [grid-template-areas:'a'_'d'_'b'_'i'_'e'_'g'_'f'_'j'_'k'] [grid-template-rows:repeat(9,auto)] *:border-2 *:border-[color-mix(in_srgb,hsl(var(--primary))_22%,hsl(var(--border)))] *:bg-secondary/25 *:bg-cover *:bg-center *:bg-no-repeat *:overflow-hidden *:w-full *:[box-shadow:4px_4px_0_rgba(0,0,0,0.22)] *:transition-all *:duration-200 sm:max-w-screen-sm sm:grid-cols-2 sm:[grid-template-areas:'a_a'_'b_d'_'e_e'_'j_g'_'f_f'_'i_k'] sm:[grid-template-rows:repeat(6,300px)] sm:*:max-w-[615px] xl:max-w-screen-xl xl:[grid-template-columns:350px_350px_350px_350px] xl:[grid-template-areas:'a_a_b_g'_'d_e_e_i'_'k_j_f_f'] xl:[grid-template-rows:repeat(3,350px)] dark:*:[box-shadow:4px_4px_0_rgba(0,0,0,0.65)] xl:[&:hover:has(>.has-overlay:hover)>.first>.overlay]:opacity-100 xl:[&>*:not(.first):hover_.overlay]:opacity-100"
|
||||
aria-label="Personal information and activity grid"
|
||||
>
|
||||
<div
|
||||
class="first flex flex-row xl:max-h-[298px] xl:min-w-[615px] grow-[0] justify-center aspect-square bg-[url('/static/loading.gif')] border bg-cover bg-center bg-position-inherit bg-no-repeat [grid-area:a] sm:aspect-[2.1/1] xl:aspect-auto hover:[box-shadow:6px_6px_0_rgba(0,0,0,0.26)] hover:-translate-y-1 hover:border-[color-mix(in_srgb,hsl(var(--primary))_40%,hsl(var(--border)))] dark:hover:[box-shadow:6px_6px_0_rgba(0,0,0,0.75)]"
|
||||
class="first flex flex-row xl:max-h-[350px] xl:min-w-[715px] grow-[0] justify-center aspect-square bg-[url('/static/loading.gif')] border bg-cover bg-center bg-position-inherit bg-no-repeat [grid-area:a] sm:aspect-[2.1/1] xl:aspect-auto hover:[box-shadow:6px_6px_0_rgba(0,0,0,0.26)] hover:-translate-y-1 hover:border-[color-mix(in_srgb,hsl(var(--primary))_40%,hsl(var(--border)))] dark:hover:[box-shadow:6px_6px_0_rgba(0,0,0,0.75)]"
|
||||
role="img"
|
||||
|
||||
aria-label="Introduction"
|
||||
@@ -43,29 +42,28 @@ const latestPost = await getCollection('blog').then((posts: any[]) =>
|
||||
|
||||
<div
|
||||
class="has-overlay relative grid aspect-square grid-cols-4 grid-rows-3 items-center
|
||||
justify-center [grid-area:b] short-cuts-template hover:[box-shadow:6px_6px_0_rgba(0,0,0,0.26)] hover:-translate-y-1 hover:border-[color-mix(in_srgb,hsl(var(--primary))_40%,hsl(var(--border)))] dark:hover:[box-shadow:6px_6px_0_rgba(0,0,0,0.75)]"
|
||||
justify-center bg-[url('/static/honeycomb.webp')] [grid-area:b] short-cuts-template xl:aspect-square xl:w-[350px] xl:h-[350px] hover:[box-shadow:6px_6px_0_rgba(0,0,0,0.26)] hover:-translate-y-1 hover:border-[color-mix(in_srgb,hsl(var(--primary))_40%,hsl(var(--border)))] dark:hover:[box-shadow:6px_6px_0_rgba(0,0,0,0.75)]"
|
||||
style="grid-template-columns: 2.85fr 2.95fr 2.9fr 1.3fr;grid-template-rows: 4.7fr 3.4fr 2.8fr;"
|
||||
aria-label="Developer Stack Shortcuts"
|
||||
>
|
||||
<img
|
||||
class="overlay absolute bottom-0 right-0 no-repeat max-w-[28%] object-contain z-[88]"
|
||||
src="/static/images/shortcuts-bg-mini.png"
|
||||
class="overlay absolute no-repeat w-full max-w-fit justify-center object-cover z-9"
|
||||
src="/static/images/shortcuts-bg.png"
|
||||
/>
|
||||
<!-- <DevStackIconsCloud client:load/> -->
|
||||
<!-- <DevShortCuts /> -->
|
||||
<DevShortcutsHexagon />
|
||||
<DevShortCuts />
|
||||
</div>
|
||||
|
||||
<div class="relative overflow-hidden [grid-area:d] aspect-square hover:[box-shadow:6px_6px_0_rgba(0,0,0,0.26)] hover:-translate-y-1 hover:border-[color-mix(in_srgb,hsl(var(--primary))_40%,hsl(var(--border)))] dark:hover:[box-shadow:6px_6px_0_rgba(0,0,0,0.75)]">
|
||||
<div class="relative overflow-hidden [grid-area:d] aspect-square xl:aspect-square xl:w-[350px] xl:h-[350px] hover:[box-shadow:6px_6px_0_rgba(0,0,0,0.26)] hover:-translate-y-1 hover:border-[color-mix(in_srgb,hsl(var(--primary))_40%,hsl(var(--border)))] dark:hover:[box-shadow:6px_6px_0_rgba(0,0,0,0.75)]">
|
||||
<AuthorPresence client:load />
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="has-overlay min-h-[300px] relative flex grid aspect-[6/5] grid-rows-2 items-start overflow-hidden p-1 [grid-area:e] sm:aspect-[2.1/1] sm:items-center xl:aspect-auto hover:[box-shadow:6px_6px_0_rgba(0,0,0,0.26)] hover:-translate-y-1 hover:border-[color-mix(in_srgb,hsl(var(--primary))_40%,hsl(var(--border)))] dark:hover:[box-shadow:6px_6px_0_rgba(0,0,0,0.75)]"
|
||||
class="has-overlay min-h-[300px] xl:min-w-[715px] relative flex grid aspect-[6/5] grid-rows-2 items-start overflow-hidden p-1 [grid-area:e] sm:aspect-[2.1/1] sm:items-center xl:aspect-auto xl:min-h-0 hover:[box-shadow:6px_6px_0_rgba(0,0,0,0.26)] hover:-translate-y-1 hover:border-[color-mix(in_srgb,hsl(var(--primary))_40%,hsl(var(--border)))] dark:hover:[box-shadow:6px_6px_0_rgba(0,0,0,0.75)]"
|
||||
style="grid-template-rows: 9fr 1fr; "
|
||||
>
|
||||
<div
|
||||
class="overlay absolute inset-0 size-full rounded-3xl bg-[url('/static/images/lastblogbg-sm.webp')] bg-cover bg-no-repeat transition-opacity duration-200 sm:bg-[url('/static/images/lastblogbg.webp')] xl:opacity-100"
|
||||
class="overlay absolute inset-0 size-full rounded-3xl bg-[url('/static/images/lastblogbg-sm.webp')] bg-cover bg-center bg-no-repeat transition-opacity duration-200 sm:bg-[url('/static/images/lastblogbg.webp')] xl:opacity-100"
|
||||
>
|
||||
</div>
|
||||
{
|
||||
@@ -108,7 +106,7 @@ const latestPost = await getCollection('blog').then((posts: any[]) =>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="has-overlay xl:min-w-[615px] relative flex aspect-square items-center justify-center overflow-hidden [grid-area:f] sm:aspect-[2.1/1] xl:aspect-auto hover:[box-shadow:6px_6px_0_rgba(0,0,0,0.26)] hover:-translate-y-1 hover:border-[color-mix(in_srgb,hsl(var(--primary))_40%,hsl(var(--border)))] dark:hover:[box-shadow:6px_6px_0_rgba(0,0,0,0.75)]"
|
||||
class="has-overlay xl:min-w-[715px] relative flex aspect-square items-center justify-center overflow-hidden [grid-area:f] sm:aspect-[2.1/1] xl:aspect-auto hover:[box-shadow:6px_6px_0_rgba(0,0,0,0.26)] hover:-translate-y-1 hover:border-[color-mix(in_srgb,hsl(var(--primary))_40%,hsl(var(--border)))] dark:hover:[box-shadow:6px_6px_0_rgba(0,0,0,0.75)]"
|
||||
>
|
||||
<div
|
||||
class="overlay absolute inset-0 z-[1] size-full bg-[url('/static/images/contributions-square.png')] bg-cover bg-center bg-no-repeat transition-opacity duration-200 sm:bg-[url('/static/images/contributions.png')] xl:opacity-100"
|
||||
@@ -118,7 +116,7 @@ const latestPost = await getCollection('blog').then((posts: any[]) =>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="has-overlay relative aspect-square [grid-area:g] hover:bg-none hover:[box-shadow:6px_6px_0_rgba(0,0,0,0.26)] hover:-translate-y-1 hover:border-[color-mix(in_srgb,hsl(var(--primary))_40%,hsl(var(--border)))] dark:hover:[box-shadow:6px_6px_0_rgba(0,0,0,0.75)]"
|
||||
class="has-overlay relative aspect-square [grid-area:g] xl:aspect-square xl:w-[350px] xl:h-[350px] hover:bg-none hover:[box-shadow:6px_6px_0_rgba(0,0,0,0.26)] hover:-translate-y-1 hover:border-[color-mix(in_srgb,hsl(var(--primary))_40%,hsl(var(--border)))] dark:hover:[box-shadow:6px_6px_0_rgba(0,0,0,0.75)]"
|
||||
>
|
||||
<div
|
||||
class="overlay absolute inset-0 z-0 size-full bg-[url('/static/images/music163.png')] bg-cover bg-center bg-no-repeat transition-opacity duration-200 xl:opacity-100"
|
||||
@@ -128,7 +126,7 @@ const latestPost = await getCollection('blog').then((posts: any[]) =>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="has-overlay relative flex aspect-square items-center justify-center [grid-area:i] hover:[box-shadow:6px_6px_0_rgba(0,0,0,0.26)] hover:-translate-y-1 hover:border-[color-mix(in_srgb,hsl(var(--primary))_40%,hsl(var(--border)))] dark:hover:[box-shadow:6px_6px_0_rgba(0,0,0,0.75)]"
|
||||
class="has-overlay relative flex aspect-square items-center justify-center [grid-area:i] xl:aspect-square xl:w-[350px] xl:h-[350px] hover:[box-shadow:6px_6px_0_rgba(0,0,0,0.26)] hover:-translate-y-1 hover:border-[color-mix(in_srgb,hsl(var(--primary))_40%,hsl(var(--border)))] dark:hover:[box-shadow:6px_6px_0_rgba(0,0,0,0.75)]"
|
||||
>
|
||||
<div
|
||||
class="overlay absolute inset-0 size-full bg-[url('/static/images/github.png')] bg-cover bg-center bg-no-repeat transition-opacity duration-200 xl:opacity-100"
|
||||
@@ -153,12 +151,12 @@ const latestPost = await getCollection('blog').then((posts: any[]) =>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div class="has-overlay aspect-square [grid-area:j] bg-[url('/static/images/waketime.png')] bg-cover bg-center bg-no-repeat hover:[box-shadow:6px_6px_0_rgba(0,0,0,0.26)] hover:-translate-y-1 hover:border-[color-mix(in_srgb,hsl(var(--primary))_40%,hsl(var(--border)))] dark:hover:[box-shadow:6px_6px_0_rgba(0,0,0,0.75)]">
|
||||
<div class="has-overlay aspect-square [grid-area:j] xl:aspect-square xl:w-[350px] xl:h-[350px] bg-[url('/static/images/waketime.png')] bg-cover bg-center bg-no-repeat hover:[box-shadow:6px_6px_0_rgba(0,0,0,0.26)] hover:-translate-y-1 hover:border-[color-mix(in_srgb,hsl(var(--primary))_40%,hsl(var(--border)))] dark:hover:[box-shadow:6px_6px_0_rgba(0,0,0,0.75)]">
|
||||
<WakatimeGraph omitLanguages={['Markdown', 'JSON']} client:load />
|
||||
</div>
|
||||
|
||||
<!-- 字符滚动 -->
|
||||
<div class="aspect-square [grid-area:k] hover:[box-shadow:6px_6px_0_rgba(0,0,0,0.26)] hover:-translate-y-1 hover:border-[color-mix(in_srgb,hsl(var(--primary))_40%,hsl(var(--border)))] dark:hover:[box-shadow:6px_6px_0_rgba(0,0,0,0.75)]">
|
||||
<div class="aspect-square [grid-area:k] xl:aspect-square xl:w-[350px] xl:h-[350px] hover:[box-shadow:6px_6px_0_rgba(0,0,0,0.26)] hover:-translate-y-1 hover:border-[color-mix(in_srgb,hsl(var(--primary))_40%,hsl(var(--border)))] dark:hover:[box-shadow:6px_6px_0_rgba(0,0,0,0.75)]">
|
||||
<LetterGlitch
|
||||
client:load
|
||||
glitchColors={['#de17a5', '#5617de', '#e9d3b6']}
|
||||
@@ -171,21 +169,3 @@ const latestPost = await getCollection('blog').then((posts: any[]) =>
|
||||
</div>
|
||||
</section>
|
||||
</Layout>
|
||||
|
||||
<style>
|
||||
section > div:hover {
|
||||
box-shadow:
|
||||
0 0 16px rgba(241, 140, 110, 0.5),
|
||||
0 0 32px rgba(241, 140, 110, 0.3),
|
||||
0 0 48px rgba(241, 140, 110, 0.15),
|
||||
6px 6px 0 rgba(0, 0, 0, 0.26) !important;
|
||||
}
|
||||
|
||||
.dark section > div:hover {
|
||||
box-shadow:
|
||||
0 0 16px rgba(241, 140, 110, 0.5),
|
||||
0 0 32px rgba(241, 140, 110, 0.3),
|
||||
0 0 48px rgba(241, 140, 110, 0.15),
|
||||
6px 6px 0 rgba(0, 0, 0, 0.75) !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -37,21 +37,3 @@ const years = Object.keys(projectsByYear).sort(
|
||||
</div>
|
||||
</Container>
|
||||
</Layout>
|
||||
|
||||
<style>
|
||||
ul li > :global(div:hover) {
|
||||
box-shadow:
|
||||
0 0 16px rgba(241, 140, 110, 0.5),
|
||||
0 0 32px rgba(241, 140, 110, 0.3),
|
||||
0 0 48px rgba(241, 140, 110, 0.15),
|
||||
6px 6px 0 rgba(0, 0, 0, 0.26) !important;
|
||||
}
|
||||
|
||||
.dark ul li > :global(div:hover) {
|
||||
box-shadow:
|
||||
0 0 16px rgba(241, 140, 110, 0.5),
|
||||
0 0 32px rgba(241, 140, 110, 0.3),
|
||||
0 0 48px rgba(241, 140, 110, 0.15),
|
||||
6px 6px 0 rgba(0, 0, 0, 0.75) !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -4,6 +4,14 @@
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@font-face {
|
||||
font-family: 'haipaiqiangdiaosenxiyuan';
|
||||
src: url('/fonts/haipaiqiangdiaosenxiyuan.woff');
|
||||
font-weight: 100 800;
|
||||
font-style: normal;
|
||||
/* unicode-range: U+2E80-2EFF,U+3400-4DBF,U+4E00-9FFF; */
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'OPlusSans3-Medium';
|
||||
src: url('/fonts/OPlusSans3-Medium.woff2') format('woff2'),
|
||||
@@ -326,46 +334,161 @@
|
||||
0px 4px 4px hsla(0, 0%, 0%, 0.16), 0px 4px 2px hsla(0, 0%, 0%, 0.04);
|
||||
}
|
||||
|
||||
@layer components {
|
||||
article {
|
||||
@apply prose-headings:scroll-mt-20 prose-headings:break-words;
|
||||
@apply prose-p:break-words;
|
||||
@apply prose-a:!text-primary prose-a:!decoration-primary/50 prose-a:!underline-offset-[3px] prose-a:transition-colors hover:prose-a:!decoration-inherit;
|
||||
@apply prose-blockquote:!not-italic prose-blockquote:!text-muted-foreground;
|
||||
@apply prose-pre:!px-0;
|
||||
@apply prose-img:mx-auto prose-img:rounded-xl prose-img:border;
|
||||
@apply prose-table:mx-auto prose-table:block prose-table:max-w-fit prose-table:overflow-x-auto prose-td:break-words;
|
||||
@apply sm:prose-table:mx-0 sm:prose-table:table sm:prose-table:max-w-none;
|
||||
|
||||
/* Fixing Katex display */
|
||||
.katex-display {
|
||||
@apply overflow-x-auto overflow-y-hidden py-4;
|
||||
}
|
||||
|
||||
/* Fixing Katex fractions */
|
||||
.frac-line {
|
||||
@apply border-foreground;
|
||||
}
|
||||
|
||||
/* Removes background from <mark> elements */
|
||||
mark {
|
||||
@apply bg-transparent;
|
||||
}
|
||||
|
||||
/* Blanket syntax highlighting */
|
||||
code[data-theme*=' '] {
|
||||
span {
|
||||
color: var(--shiki-dark);
|
||||
}
|
||||
|
||||
.dark & span {
|
||||
color: var(--shiki-dark);
|
||||
}
|
||||
}
|
||||
|
||||
/* Inline code */
|
||||
:not(pre) > code {
|
||||
@apply relative rounded bg-muted/50 px-[0.3rem] py-[0.2rem] font-mono text-sm font-medium before:!content-none after:!content-none;
|
||||
}
|
||||
|
||||
/* Code blocks */
|
||||
figure[data-rehype-pretty-code-figure] {
|
||||
@apply relative;
|
||||
|
||||
/* Code block titles */
|
||||
[data-rehype-pretty-code-title] {
|
||||
@apply break-words rounded-t-xl border-x border-t px-4 py-2 text-sm font-medium text-foreground;
|
||||
|
||||
/* Remove top margin from code block if a title is present */
|
||||
& + pre {
|
||||
@apply mt-0 rounded-t-none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Shadcn-like scrollbar */
|
||||
pre::-webkit-scrollbar {
|
||||
@apply h-2.5 w-2.5;
|
||||
}
|
||||
|
||||
pre::-webkit-scrollbar-track {
|
||||
@apply bg-transparent;
|
||||
}
|
||||
|
||||
pre::-webkit-scrollbar-thumb {
|
||||
@apply rounded-full bg-border bg-clip-padding p-px;
|
||||
}
|
||||
|
||||
/* Code block styles */
|
||||
pre {
|
||||
@apply static max-h-[600px] overflow-auto rounded-xl border bg-secondary/20 py-4 text-sm leading-loose;
|
||||
|
||||
/* Code block content */
|
||||
> code {
|
||||
@apply whitespace-pre-wrap;
|
||||
counter-reset: line;
|
||||
|
||||
/* For code blocks with line numbers */
|
||||
&[data-line-numbers] {
|
||||
> [data-line]::before {
|
||||
counter-increment: line;
|
||||
content: counter(line);
|
||||
@apply mr-4 inline-block w-4 text-right text-muted-foreground;
|
||||
}
|
||||
}
|
||||
|
||||
/* For each line in the code block */
|
||||
> [data-line] {
|
||||
@apply px-4;
|
||||
}
|
||||
|
||||
/* Highlighted lines */
|
||||
[data-highlighted-line] {
|
||||
@apply bg-foreground/10;
|
||||
}
|
||||
|
||||
/* Highlighted characters */
|
||||
[data-highlighted-chars] > span {
|
||||
@apply bg-muted-foreground/40 py-[7px];
|
||||
}
|
||||
|
||||
.tab {
|
||||
@apply relative;
|
||||
}
|
||||
|
||||
.tab::before {
|
||||
@apply absolute opacity-30;
|
||||
content: '⇥';
|
||||
}
|
||||
|
||||
/* Skip lines */
|
||||
.skip {
|
||||
@apply my-2 bg-foreground/5 text-center text-foreground;
|
||||
|
||||
&::before {
|
||||
content: '' !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Diff lines */
|
||||
.diff {
|
||||
&.add {
|
||||
@apply bg-additive/15;
|
||||
}
|
||||
&.remove {
|
||||
@apply bg-destructive/15;
|
||||
&::before {
|
||||
content: '-';
|
||||
counter-increment: none;
|
||||
}
|
||||
}
|
||||
&.highlight {
|
||||
@apply bg-foreground/10;
|
||||
}
|
||||
}
|
||||
|
||||
/* Copy button */
|
||||
> button:has(> span) {
|
||||
@apply right-1 top-1 m-0 size-8 rounded-lg bg-secondary p-1 backdrop-blur-none transition-all duration-200;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border;
|
||||
}
|
||||
|
||||
/* Prevent text selection cursor on all elements by default */
|
||||
* {
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
/* Allow text selection in content areas */
|
||||
article,
|
||||
article *,
|
||||
.prose,
|
||||
.prose *,
|
||||
input,
|
||||
textarea,
|
||||
[contenteditable="true"] {
|
||||
user-select: text;
|
||||
-webkit-user-select: text;
|
||||
}
|
||||
|
||||
/* Only inputs and textareas should have text cursor */
|
||||
input,
|
||||
textarea,
|
||||
[contenteditable="true"] {
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
/* Ensure proper cursor on interactive elements */
|
||||
/* Prevent text selection cursor on interactive elements */
|
||||
button,
|
||||
a,
|
||||
div,
|
||||
img,
|
||||
svg,
|
||||
.pixel-button,
|
||||
[role="button"],
|
||||
[role="link"] {
|
||||
@@ -373,42 +496,6 @@
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Children of interactive elements should not have text cursor */
|
||||
button *,
|
||||
a *,
|
||||
.pixel-button *,
|
||||
[role="button"] *,
|
||||
[role="link"] * {
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
/* Images should never show text cursor */
|
||||
img,
|
||||
svg,
|
||||
video,
|
||||
canvas {
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
/* Allow text selection for content elements but keep default cursor */
|
||||
p,
|
||||
span,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6,
|
||||
li {
|
||||
user-select: text;
|
||||
-webkit-user-select: text;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
body {
|
||||
|
||||
@@ -2,6 +2,14 @@
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@font-face {
|
||||
font-family: 'haipaiqiangdiaosenxiyuan';
|
||||
src: url('/fonts/haipaiqiangdiaosenxiyuan.woff');
|
||||
font-weight: 100 800;
|
||||
font-style: normal;
|
||||
/* unicode-range: U+2E80-2EFF,U+3400-4DBF,U+4E00-9FFF; */
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'OPlusSans3-Medium';
|
||||
src: url('/fonts/OPlusSans3-Medium.woff');
|
||||
|
||||
@@ -9,6 +9,7 @@ const config: Config = {
|
||||
fontFamily: {
|
||||
sans: [
|
||||
'OPlusSans3-Medium',
|
||||
'haipaiqiangdiaosenxiyuan',
|
||||
...defaultTheme.fontFamily.sans
|
||||
],
|
||||
mono: [
|
||||
|
||||