switched to astro
|
@ -1,26 +0,0 @@
|
|||
on: push
|
||||
name: Build and deploy
|
||||
jobs:
|
||||
web-deploy:
|
||||
runs-on: docker
|
||||
container:
|
||||
image: catthehacker/ubuntu:act-latest
|
||||
options: --privileged
|
||||
steps:
|
||||
- name: Get latest code
|
||||
uses: actions/checkout@v2
|
||||
- name: Build hugo site
|
||||
uses: https://github.com/jakejarvis/hugo-build-action@master
|
||||
with:
|
||||
args: --minify
|
||||
- name: Clean permissions
|
||||
uses: https://github.com/main-quest/actions.clean-permissions@v1.0.4
|
||||
- name: Deploy to Uplink
|
||||
uses: https://github.com/shallwefootball/upload-s3-action@master
|
||||
with:
|
||||
aws_key_id: ${{ secrets.STORJ_ACCESS_KEY_ID }}
|
||||
aws_secret_access_key: ${{ secrets.STORJ_SECRET_ACCESS_KEY }}
|
||||
endpoint: ${{ secrets.STORJ_ENDPOINT }}
|
||||
aws_bucket: ${{ secrets.STORJ_BUCKET }}
|
||||
source_dir: "public"
|
||||
destination_dir: "hack13-blog"
|
77
.gitignore
vendored
|
@ -1,68 +1,23 @@
|
|||
# Build and Release Folders
|
||||
bin/
|
||||
bin-debug/
|
||||
bin-release/
|
||||
[Oo]bj/ # FlashDevelop obj
|
||||
[Bb]in/ # FlashDevelop bin
|
||||
# build output
|
||||
dist/
|
||||
|
||||
# Other files and folders
|
||||
.settings/
|
||||
.hugo_build.lock
|
||||
.nova/
|
||||
# generated types
|
||||
.astro/
|
||||
|
||||
# Executables
|
||||
*.swf
|
||||
*.air
|
||||
*.ipa
|
||||
*.apk
|
||||
# dependencies
|
||||
node_modules/
|
||||
|
||||
# Hugo
|
||||
public/
|
||||
# logs
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
# Backup Files
|
||||
*.markdown~
|
||||
*.md~
|
||||
# environment variables
|
||||
.env
|
||||
.env.production
|
||||
|
||||
# Apache
|
||||
.htpasswd
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (http://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Build directories
|
||||
public
|
||||
dist
|
||||
|
||||
# Dependency directory
|
||||
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
|
||||
node_modules
|
||||
package-lock.json
|
||||
bower_components
|
||||
|
||||
# Mac File System File...utterly useless to anyone but me.
|
||||
# macOS-specific files
|
||||
.DS_Store
|
||||
|
||||
# AWS stuff for publishing static files
|
||||
.aws-credentials.json
|
||||
.awspublish*
|
||||
.vercel
|
||||
|
|
3
.vscode/extensions.json
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"recommendations": ["biomejs.biome", "astro-build.astro-vscode"]
|
||||
}
|
22
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "biomejs.biome",
|
||||
"[javascript]": {
|
||||
"editor.defaultFormatter": "biomejs.biome"
|
||||
},
|
||||
"[javascriptreact]": {
|
||||
"editor.defaultFormatter": "biomejs.biome"
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "biomejs.biome"
|
||||
},
|
||||
"[typescriptreact]": {
|
||||
"editor.defaultFormatter": "biomejs.biome"
|
||||
},
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll": "explicit",
|
||||
"quickfix.biome": "always",
|
||||
"source.organizeImports.biome": "always"
|
||||
},
|
||||
"frontMatter.dashboard.openOnStart": false
|
||||
}
|
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2024 saicaca
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
55
README.ja-JP.md
Normal file
|
@ -0,0 +1,55 @@
|
|||
# 🍥Fuwari
|
||||
|
||||
[Astro](https://astro.build)で構築された静的ブログテンプレート
|
||||
|
||||
[**🖥️ライブデモ (Vercel)**](https://fuwari.vercel.app) / [**🌏中文**](https://github.com/saicaca/fuwari/blob/main/README.zh-CN.md) / [**🌏日本語**](https://github.com/saicaca/fuwari/blob/main/README.ja-JP.md) / [**📦旧Hexoバージョン**](https://github.com/saicaca/hexo-theme-vivia)
|
||||
|
||||

|
||||
|
||||
## ✨ 特徴
|
||||
|
||||
- [x] [Astro](https://astro.build)及び [Tailwind CSS](https://tailwindcss.com)で構築
|
||||
- [x] スムーズなアニメーションとページ遷移
|
||||
- [x] ライト/ダークテーマ対応
|
||||
- [x] カスタマイズ可能なテーマカラーとバナー
|
||||
- [x] レスポンシブデザイン
|
||||
- [ ] コメント機能
|
||||
- [x] 検索機能
|
||||
- [ ] 目次
|
||||
|
||||
## 🚀 使用方法
|
||||
|
||||
1. [テンプレート](https://github.com/saicaca/fuwari/generate)から新しいリポジトリを作成するかCloneをします。
|
||||
2. ブログをローカルで編集するには、リポジトリをクローンした後、`pnpm install` と `pnpm add sharp` を実行して依存関係をインストールします。
|
||||
- [pnpm](https://pnpm.io)がインストールされていない場合は `npm install -g pnpm` で導入可能です。
|
||||
3. `src/config.ts`ファイルを編集する事でブログを自分好みにカスタマイズ出来ます。
|
||||
4. `pnpm new-post <filename>`で新しい記事を作成し、`src/content/posts/`.フォルダ内で編集します。
|
||||
5. 作成したブログをVercel、Netlify、GitHub Pagesなどにデプロイするには[ガイド](https://docs.astro.build/ja/guides/deploy/)に従って下さい。加えて、別途デプロイを行う前に`astro.config.mjs`を編集してサイト構成を変更する必要があります。
|
||||
|
||||
## ⚙️ 記事のフロントマター
|
||||
|
||||
```yaml
|
||||
---
|
||||
title: My First Blog Post
|
||||
published: 2023-09-09
|
||||
description: This is the first post of my new Astro blog.
|
||||
image: /images/cover.jpg
|
||||
tags: [Foo, Bar]
|
||||
category: Front-end
|
||||
draft: false
|
||||
---
|
||||
```
|
||||
|
||||
## 🧞 コマンド
|
||||
|
||||
すべてのコマンドは、ターミナルでプロジェクトのルートから実行する必要があります:
|
||||
|
||||
| Command | Action |
|
||||
|:------------------------------------|:-------------------------------------------------|
|
||||
| `pnpm install` AND `pnpm add sharp` | 依存関係のインストール |
|
||||
| `pnpm dev` | `localhost:4321`で開発用ローカルサーバーを起動 |
|
||||
| `pnpm build` | `./dist/`にビルド内容を出力 |
|
||||
| `pnpm preview` | デプロイ前の内容をローカルでプレビュー |
|
||||
| `pnpm new-post <filename>` | 新しい投稿を作成 |
|
||||
| `pnpm astro ...` | `astro add`, `astro check`の様なコマンドを実行する際に使用 |
|
||||
| `pnpm astro --help` | Astro CLIのヘルプを表示 |
|
57
README.md
|
@ -1,4 +1,55 @@
|
|||
# hack13-blog
|
||||
Personal Blog Site
|
||||
# 🍥Fuwari
|
||||
|
||||
Moved to https://git.hack13.dev/hack13/hack13-blog
|
||||
A static blog template built with [Astro](https://astro.build).
|
||||
|
||||
[**🖥️Live Demo (Vercel)**](https://fuwari.vercel.app) / [**🌏中文 README**](https://github.com/saicaca/fuwari/blob/main/README.zh-CN.md) / [**🌏日本語 README**](https://github.com/saicaca/fuwari/blob/main/README.ja-JP.md) / [**📦Old Hexo Version**](https://github.com/saicaca/hexo-theme-vivia)
|
||||
|
||||

|
||||
|
||||
## ✨ Features
|
||||
|
||||
- [x] Built with [Astro](https://astro.build) and [Tailwind CSS](https://tailwindcss.com)
|
||||
- [x] Smooth animations and page transitions
|
||||
- [x] Light / dark mode
|
||||
- [x] Customizable theme colors & banner
|
||||
- [x] Responsive design
|
||||
- [ ] Comments
|
||||
- [x] Search
|
||||
- [ ] TOC
|
||||
|
||||
## 🚀 How to Use
|
||||
|
||||
1. [Generate a new repository](https://github.com/saicaca/fuwari/generate) from this template or fork this repository.
|
||||
2. To edit your blog locally, clone your repository, run `pnpm install` AND `pnpm add sharp` to install dependencies.
|
||||
- Install [pnpm](https://pnpm.io) `npm install -g pnpm` if you haven't.
|
||||
3. Edit the config file `src/config.ts` to customize your blog.
|
||||
4. Run `pnpm new-post <filename>` to create a new post and edit it in `src/content/posts/`.
|
||||
5. Deploy your blog to Vercel, Netlify, GitHub Pages, etc. following [the guides](https://docs.astro.build/en/guides/deploy/). You need to edit the site configuration in `astro.config.mjs` before deployment.
|
||||
|
||||
## ⚙️ Frontmatter of Posts
|
||||
|
||||
```yaml
|
||||
---
|
||||
title: My First Blog Post
|
||||
published: 2023-09-09
|
||||
description: This is the first post of my new Astro blog.
|
||||
image: /images/cover.jpg
|
||||
tags: [Foo, Bar]
|
||||
category: Front-end
|
||||
draft: false
|
||||
---
|
||||
```
|
||||
|
||||
## 🧞 Commands
|
||||
|
||||
All commands are run from the root of the project, from a terminal:
|
||||
|
||||
| Command | Action |
|
||||
|:------------------------------------|:-------------------------------------------------|
|
||||
| `pnpm install` AND `pnpm add sharp` | Installs dependencies |
|
||||
| `pnpm dev` | Starts local dev server at `localhost:4321` |
|
||||
| `pnpm build` | Build your production site to `./dist/` |
|
||||
| `pnpm preview` | Preview your build locally, before deploying |
|
||||
| `pnpm new-post <filename>` | Create a new post |
|
||||
| `pnpm astro ...` | Run CLI commands like `astro add`, `astro check` |
|
||||
| `pnpm astro --help` | Get help using the Astro CLI |
|
||||
|
|
55
README.zh-CN.md
Normal file
|
@ -0,0 +1,55 @@
|
|||
# 🍥Fuwari
|
||||
|
||||
基于 [Astro](https://astro.build) 开发的静态博客模板。
|
||||
|
||||
[**🖥️在线预览(Vercel)**](https://fuwari.vercel.app) / [**🌏English README**](https://github.com/saicaca/fuwari) / [**🌏日本語 README**](https://github.com/saicaca/fuwari/blob/main/README.ja-JP.md) / [**📦旧 Hexo 版本**](https://github.com/saicaca/hexo-theme-vivia)
|
||||
|
||||

|
||||
|
||||
## ✨ 功能特性
|
||||
|
||||
- [x] 基于 Astro 和 Tailwind CSS 开发
|
||||
- [x] 流畅的动画和页面过渡
|
||||
- [x] 亮色 / 暗色模式
|
||||
- [x] 自定义主题色和横幅图片
|
||||
- [x] 响应式设计
|
||||
- [ ] 评论
|
||||
- [x] 搜索
|
||||
- [ ] 文内目录
|
||||
|
||||
## 🚀 使用方法
|
||||
|
||||
1. 使用此模板[生成新仓库](https://github.com/saicaca/fuwari/generate)或 Fork 此仓库
|
||||
2. 进行本地开发,Clone 新的仓库,执行 `pnpm install` 和 `pnpm add sharp` 以安装依赖
|
||||
- 若未安装 [pnpm](https://pnpm.io),执行 `npm install -g pnpm`
|
||||
3. 通过配置文件 `src/config.ts` 自定义博客
|
||||
4. 执行 `pnpm new-post <filename>` 创建新文章,并在 `src/content/posts/` 目录中编辑
|
||||
5. 参考[官方指南](https://docs.astro.build/zh-cn/guides/deploy/)将博客部署至 Vercel, Netlify, GitHub Pages 等;部署前需编辑 `astro.config.mjs` 中的站点设置。
|
||||
|
||||
## ⚙️ 文章 Frontmatter
|
||||
|
||||
```yaml
|
||||
---
|
||||
title: My First Blog Post
|
||||
published: 2023-09-09
|
||||
description: This is the first post of my new Astro blog.
|
||||
image: /images/cover.jpg
|
||||
tags: [Foo, Bar]
|
||||
category: Front-end
|
||||
draft: false
|
||||
---
|
||||
```
|
||||
|
||||
## 🧞 指令
|
||||
|
||||
下列指令均需要在项目根目录执行:
|
||||
|
||||
| Command | Action |
|
||||
|:----------------------------------|:----------------------------------|
|
||||
| `pnpm install` 并 `pnpm add sharp` | 安装依赖 |
|
||||
| `pnpm dev` | 在 `localhost:4321` 启动本地开发服务器 |
|
||||
| `pnpm build` | 构建网站至 `./dist/` |
|
||||
| `pnpm preview` | 本地预览已构建的网站 |
|
||||
| `pnpm new-post <filename>` | 创建新文章 |
|
||||
| `pnpm astro ...` | 执行 `astro add`, `astro check` 等指令 |
|
||||
| `pnpm astro --help` | 显示 Astro CLI 帮助 |
|
|
@ -1,6 +0,0 @@
|
|||
---
|
||||
title: "{{ replace .Name "-" " " | title }}"
|
||||
date: {{ .Date }}
|
||||
draft: true
|
||||
---
|
||||
|
124
astro.config.mjs
Normal file
|
@ -0,0 +1,124 @@
|
|||
import tailwind from "@astrojs/tailwind"
|
||||
import Compress from "astro-compress"
|
||||
import icon from "astro-icon"
|
||||
import { defineConfig } from "astro/config"
|
||||
import Color from "colorjs.io"
|
||||
import rehypeAutolinkHeadings from "rehype-autolink-headings"
|
||||
import rehypeKatex from "rehype-katex"
|
||||
import rehypeSlug from "rehype-slug"
|
||||
import remarkMath from "remark-math"
|
||||
import { remarkReadingTime } from "./src/plugins/remark-reading-time.mjs"
|
||||
import { GithubCardComponent } from "./src/plugins/rehype-component-github-card.mjs"
|
||||
import { AdmonitionComponent } from "./src/plugins/rehype-component-admonition.mjs"
|
||||
import remarkDirective from "remark-directive" /* Handle directives */
|
||||
import remarkGithubAdmonitionsToDirectives from "remark-github-admonitions-to-directives";
|
||||
import rehypeComponents from "rehype-components"; /* Render the custom directive content */
|
||||
import svelte from "@astrojs/svelte"
|
||||
import swup from '@swup/astro';
|
||||
import sitemap from '@astrojs/sitemap';
|
||||
import {parseDirectiveNode} from "./src/plugins/remark-directive-rehype.js";
|
||||
|
||||
const oklchToHex = (str) => {
|
||||
const DEFAULT_HUE = 250
|
||||
const regex = /-?\d+(\.\d+)?/g
|
||||
const matches = str.string.match(regex)
|
||||
const lch = [matches[0], matches[1], DEFAULT_HUE]
|
||||
return new Color("oklch", lch).to("srgb").toString({
|
||||
format: "hex",
|
||||
})
|
||||
}
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
site: "https://fuwari.vercel.app/",
|
||||
base: "/",
|
||||
trailingSlash: "always",
|
||||
integrations: [
|
||||
tailwind(),
|
||||
swup({
|
||||
theme: false,
|
||||
animationClass: 'transition-',
|
||||
containers: ['main'],
|
||||
smoothScrolling: true,
|
||||
cache: true,
|
||||
preload: true,
|
||||
accessibility: true,
|
||||
globalInstance: true,
|
||||
}),
|
||||
icon({
|
||||
include: {
|
||||
"material-symbols": ["*"],
|
||||
"fa6-brands": ["*"],
|
||||
"fa6-regular": ["*"],
|
||||
"fa6-solid": ["*"],
|
||||
},
|
||||
}),
|
||||
Compress({
|
||||
Image: false,
|
||||
}),
|
||||
svelte(),
|
||||
sitemap(),
|
||||
],
|
||||
markdown: {
|
||||
remarkPlugins: [remarkMath, remarkReadingTime, remarkGithubAdmonitionsToDirectives, remarkDirective, parseDirectiveNode],
|
||||
rehypePlugins: [
|
||||
rehypeKatex,
|
||||
rehypeSlug,
|
||||
[rehypeComponents, {
|
||||
components: {
|
||||
github: GithubCardComponent,
|
||||
note: (x, y) => AdmonitionComponent(x, y, "note"),
|
||||
tip: (x, y) => AdmonitionComponent(x, y, "tip"),
|
||||
important: (x, y) => AdmonitionComponent(x, y, "important"),
|
||||
caution: (x, y) => AdmonitionComponent(x, y, "caution"),
|
||||
warning: (x, y) => AdmonitionComponent(x, y, "warning"),
|
||||
},
|
||||
}],
|
||||
[
|
||||
rehypeAutolinkHeadings,
|
||||
{
|
||||
behavior: "append",
|
||||
properties: {
|
||||
className: ["anchor"],
|
||||
},
|
||||
content: {
|
||||
type: "element",
|
||||
tagName: "span",
|
||||
properties: {
|
||||
className: ["anchor-icon"],
|
||||
'data-pagefind-ignore': true,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
type: "text",
|
||||
value: "#",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
vite: {
|
||||
build: {
|
||||
rollupOptions: {
|
||||
onwarn(warning, warn) {
|
||||
// temporarily suppress this warning
|
||||
if (warning.message.includes("is dynamically imported by") && warning.message.includes("but also statically imported by")) {
|
||||
return;
|
||||
}
|
||||
warn(warning);
|
||||
}
|
||||
}
|
||||
},
|
||||
css: {
|
||||
preprocessorOptions: {
|
||||
stylus: {
|
||||
define: {
|
||||
oklchToHex: oklchToHex,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
66
biome.json
Normal file
|
@ -0,0 +1,66 @@
|
|||
{
|
||||
"$schema": "https://biomejs.dev/schemas/1.4.1/schema.json",
|
||||
"extends": [],
|
||||
"files": { "ignoreUnknown": true },
|
||||
"organizeImports": {
|
||||
"enabled": true
|
||||
},
|
||||
"formatter": {
|
||||
"enabled": true,
|
||||
"formatWithErrors": false,
|
||||
"ignore": [],
|
||||
"indentStyle": "space",
|
||||
"indentWidth": 2,
|
||||
"lineWidth": 80
|
||||
},
|
||||
"javascript": {
|
||||
"parser": {
|
||||
"unsafeParameterDecoratorsEnabled": true
|
||||
},
|
||||
"formatter": {
|
||||
"quoteStyle": "single",
|
||||
"jsxQuoteStyle": "single",
|
||||
"trailingComma": "all",
|
||||
"semicolons": "asNeeded",
|
||||
"arrowParentheses": "asNeeded"
|
||||
}
|
||||
},
|
||||
"json": {
|
||||
"parser": { "allowComments": true },
|
||||
"formatter": {
|
||||
"enabled": true,
|
||||
"indentStyle": "space",
|
||||
"indentWidth": 2,
|
||||
"lineWidth": 80
|
||||
}
|
||||
},
|
||||
"linter": {
|
||||
"ignore": [],
|
||||
"rules": {
|
||||
"a11y": {
|
||||
"recommended": true
|
||||
},
|
||||
"complexity": {
|
||||
"recommended": true
|
||||
},
|
||||
"correctness": {
|
||||
"recommended": true
|
||||
},
|
||||
"performance": {
|
||||
"recommended": true
|
||||
},
|
||||
"security": {
|
||||
"recommended": true
|
||||
},
|
||||
"style": {
|
||||
"recommended": true
|
||||
},
|
||||
"suspicious": {
|
||||
"recommended": true
|
||||
},
|
||||
"nursery": {
|
||||
"recommended": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
20
config.toml
|
@ -1,20 +0,0 @@
|
|||
baseURL = "https://www.hack13.blog"
|
||||
languageCode = "en-us"
|
||||
title = "Hack13 Blog"
|
||||
theme = "hacksite"
|
||||
sectionPagesMenu = "main"
|
||||
copyright = 'This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.'
|
||||
|
||||
[menu]
|
||||
[[menu.main]]
|
||||
identifier = "home"
|
||||
name = "Home"
|
||||
title = "home"
|
||||
weight = "10"
|
||||
url = "/"
|
||||
[[menu.main]]
|
||||
identifier = "posts"
|
||||
name = "Posts"
|
||||
title = "posts"
|
||||
weight = "15"
|
||||
url = "/posts/"
|
|
@ -1,83 +0,0 @@
|
|||
---
|
||||
title: Coming Changes
|
||||
author: Timothy Rogers
|
||||
type: post
|
||||
date: 2012-05-05T11:17:00+00:00
|
||||
url: /2012/05/coming-changes/
|
||||
categories:
|
||||
- Archived
|
||||
|
||||
---
|
||||
{{< rawhtml >}}
|
||||
<div dir="ltr">
|
||||
</p>
|
||||
|
||||
<table cellpadding="0" cellspacing="0">
|
||||
</p>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<a href="http://3.bp.blogspot.com/-vkIcs3lVHyQ/T6S5NvtVEfI/AAAAAAAAAQE/o-Dk6As1Ly4/s1600/Snapshot_001.png"><img loading="lazy" border="0" height="167" src="https://3.bp.blogspot.com/-vkIcs3lVHyQ/T6S5NvtVEfI/AAAAAAAAAQE/o-Dk6As1Ly4/s320/Snapshot_001.png" width="320" /></a>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<p>
|
||||
</p>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
Me reading in our new office location in OSgrid
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<p>
|
||||
</tbody> </table>
|
||||
|
||||
<p>
|
||||
As you might have heard, I have recently moved my entire grid to OSgrid. This was a decision made my grid residents, and I was inclined to agree as I want to go back to focusing on community related efforts for the HyperGrid. So moving everything to OSgrid, allows me to not worry about any of the issues, just worry about all the work I need to get done for the community.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
I am not just working on the furry community for the hypergrid, but I want to bring together everyone, all walks of life. I am still working on creating a site, that will help residents of all hypergrid enabled grids come together and interact on a social network level. I feel the reason we are all having troubles of the hypergrid, is we are all spread to thin. We need to increase ourselves in community, to do this we just need to pull all our numbers together.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
I know that I cannot please everyone all the time, so I am still going to be offering some really nice deals on OSgrid region hosting. I will be introducing a few new features and places to the sites I have been wanting to work on for bringing the hypergrid community all together.
|
||||
</p>
|
||||
|
||||
<div>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
</p>
|
||||
|
||||
<div>
|
||||
</p>
|
||||
|
||||
<div>
|
||||
</div>
|
||||
|
||||
<table align="center" cellpadding="0" cellspacing="0">
|
||||
</p>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<a href="http://4.bp.blogspot.com/-9hL4j-axGZM/T6S5PbIZhcI/AAAAAAAAAQU/7jwUHX9stNo/s1600/Snapshot_003.png"><img loading="lazy" border="0" height="167" src="https://4.bp.blogspot.com/-9hL4j-axGZM/T6S5PbIZhcI/AAAAAAAAAQU/7jwUHX9stNo/s320/Snapshot_003.png" width="320" /></a>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<p>
|
||||
</p>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
Enjoying the view of the new regions from up above
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<p>
|
||||
</tbody> </table> </div>
|
||||
|
||||
<p>
|
||||
</div>
|
||||
{{< /rawhtml >}}
|
62
frontmatter.json
Normal file
|
@ -0,0 +1,62 @@
|
|||
{
|
||||
"$schema": "https://frontmatter.codes/frontmatter.schema.json",
|
||||
"frontMatter.framework.id": "astro",
|
||||
"frontMatter.preview.host": "http://localhost:4321",
|
||||
"frontMatter.content.publicFolder": "public",
|
||||
"frontMatter.content.pageFolders": [
|
||||
{
|
||||
"title": "posts",
|
||||
"path": "[[workspace]]/src/content/posts"
|
||||
}
|
||||
],
|
||||
"frontMatter.taxonomy.contentTypes": [
|
||||
{
|
||||
"name": "default",
|
||||
"pageBundle": true,
|
||||
"previewPath": "'blog'",
|
||||
"filePrefix": null,
|
||||
"clearEmpty": true,
|
||||
"fields": [
|
||||
{
|
||||
"title": "title",
|
||||
"name": "title",
|
||||
"type": "string",
|
||||
"single": true
|
||||
},
|
||||
{
|
||||
"title": "description",
|
||||
"name": "description",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"title": "published",
|
||||
"name": "published",
|
||||
"type": "datetime",
|
||||
"default": "{{now}}",
|
||||
"isPublishDate": true
|
||||
},
|
||||
{
|
||||
"title": "preview",
|
||||
"name": "image",
|
||||
"type": "image",
|
||||
"isPreviewImage": true
|
||||
},
|
||||
{
|
||||
"title": "tags",
|
||||
"name": "tags",
|
||||
"type": "list"
|
||||
},
|
||||
{
|
||||
"title": "category",
|
||||
"name": "category",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"title": "draft",
|
||||
"name": "draft",
|
||||
"type": "boolean"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
{{- partial "head.html" . -}}
|
||||
<body>
|
||||
{{- partial "header.html" . -}}
|
||||
<br>
|
||||
<main role="main">
|
||||
<div class="container">
|
||||
{{- block "main" . }}{{- end }}
|
||||
<h1>{{ .Params.title }}</h1>
|
||||
<strong>Author:</strong> {{ .Params.author }} | <strong>Published:</strong> {{ .Date.Format "Jan 2, 2006" }} <hr/>
|
||||
{{ .Content }}
|
||||
<button class="btn btn-secondary" onclick="window.history.go(-1)">⇽ Go Back</button>
|
||||
<br/>
|
||||
</div>
|
||||
</main>
|
||||
{{- partial "footer.html" . -}}
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
<!-- raw html -->
|
||||
{{.Inner}}
|
71
package.json
Normal file
|
@ -0,0 +1,71 @@
|
|||
{
|
||||
"name": "fuwari",
|
||||
"type": "module",
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"start": "astro dev",
|
||||
"build": "astro build && pagefind --site dist",
|
||||
"preview": "astro preview",
|
||||
"astro": "astro",
|
||||
"new-post": "node scripts/new-post.js",
|
||||
"format": "biome format --write ./src",
|
||||
"lint": "biome check --apply ./src"
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/check": "^0.7.0",
|
||||
"@astrojs/rss": "^4.0.6",
|
||||
"@astrojs/sitemap": "^3.1.6",
|
||||
"@astrojs/svelte": "^5.6.0",
|
||||
"@astrojs/tailwind": "^5.1.0",
|
||||
"@fontsource-variable/jetbrains-mono": "^5.0.21",
|
||||
"@fontsource/roboto": "^5.0.13",
|
||||
"@swup/astro": "^1.4.1",
|
||||
"astro": "^4.11.0",
|
||||
"astro-compress": "^2.2.28",
|
||||
"astro-icon": "1.1.0",
|
||||
"colorjs.io": "^0.5.0",
|
||||
"hastscript": "^9.0.0",
|
||||
"markdown-it": "^14.1.0",
|
||||
"mdast-util-to-string": "^4.0.0",
|
||||
"overlayscrollbars": "^2.8.3",
|
||||
"pagefind": "^1.1.0",
|
||||
"reading-time": "^1.5.0",
|
||||
"rehype-autolink-headings": "^7.1.0",
|
||||
"rehype-components": "^0.3.0",
|
||||
"rehype-katex": "^7.0.0",
|
||||
"rehype-slug": "^6.0.0",
|
||||
"remark-directive": "^3.0.0",
|
||||
"remark-directive-rehype": "^0.4.2",
|
||||
"remark-math": "^6.0.0",
|
||||
"sanitize-html": "^2.13.0",
|
||||
"sharp": "^0.33.4",
|
||||
"svelte": "^4.2.18",
|
||||
"tailwindcss": "^3.4.4",
|
||||
"typescript": "^5.5.2",
|
||||
"unist-util-visit": "^5.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@astrojs/ts-plugin": "^1.8.0",
|
||||
"@biomejs/biome": "1.8.2",
|
||||
"@iconify-json/fa6-brands": "^1.1.19",
|
||||
"@iconify-json/fa6-regular": "^1.1.19",
|
||||
"@iconify-json/fa6-solid": "^1.1.21",
|
||||
"@iconify-json/material-symbols": "^1.1.82",
|
||||
"@iconify/svelte": "^4.0.2",
|
||||
"@rollup/plugin-yaml": "^4.1.2",
|
||||
"@tailwindcss/typography": "^0.5.13",
|
||||
"@types/markdown-it": "^14.1.1",
|
||||
"@types/mdast": "^4.0.4",
|
||||
"@types/sanitize-html": "^2.11.0",
|
||||
"remark-github-admonitions-to-directives": "^1.0.5",
|
||||
"sass": "^1.77.6",
|
||||
"stylus": "^0.63.0"
|
||||
},
|
||||
"pnpm": {
|
||||
"overrides": {
|
||||
"vite-imagetools": "^6.2.7",
|
||||
"sharp": "^0.33.0"
|
||||
}
|
||||
}
|
||||
}
|
10250
pnpm-lock.yaml
generated
Normal file
BIN
public/favicon/favicon-dark-128.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
public/favicon/favicon-dark-180.png
Normal file
After Width: | Height: | Size: 2.3 KiB |
BIN
public/favicon/favicon-dark-192.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
BIN
public/favicon/favicon-dark-32.png
Normal file
After Width: | Height: | Size: 426 B |
BIN
public/favicon/favicon-light-128.png
Normal file
After Width: | Height: | Size: 2 KiB |
BIN
public/favicon/favicon-light-180.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
public/favicon/favicon-light-192.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
BIN
public/favicon/favicon-light-32.png
Normal file
After Width: | Height: | Size: 554 B |
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 53 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 5.8 KiB |
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 5.8 KiB |
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.8 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 270 KiB After Width: | Height: | Size: 270 KiB |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 565 KiB After Width: | Height: | Size: 565 KiB |
Before Width: | Height: | Size: 192 KiB After Width: | Height: | Size: 192 KiB |
Before Width: | Height: | Size: 588 KiB After Width: | Height: | Size: 588 KiB |
Before Width: | Height: | Size: 525 KiB After Width: | Height: | Size: 525 KiB |
Before Width: | Height: | Size: 673 KiB After Width: | Height: | Size: 673 KiB |
Before Width: | Height: | Size: 686 KiB After Width: | Height: | Size: 686 KiB |
Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 71 KiB |
52
scripts/new-post.js
Normal file
|
@ -0,0 +1,52 @@
|
|||
/* This is a script to create a new post markdown file with front-matter */
|
||||
|
||||
import fs from "fs"
|
||||
import path from "path"
|
||||
|
||||
function getDate() {
|
||||
const today = new Date()
|
||||
const year = today.getFullYear()
|
||||
const month = String(today.getMonth() + 1).padStart(2, "0")
|
||||
const day = String(today.getDate()).padStart(2, "0")
|
||||
|
||||
return `${year}-${month}-${day}`
|
||||
}
|
||||
|
||||
const args = process.argv.slice(2)
|
||||
|
||||
if (args.length === 0) {
|
||||
console.error(`Error: No filename argument provided
|
||||
Usage: npm run new-post -- <filename>`)
|
||||
process.exit(1) // Terminate the script and return error code 1
|
||||
}
|
||||
|
||||
let fileName = args[0]
|
||||
|
||||
// Add .md extension if not present
|
||||
const fileExtensionRegex = /\.(md|mdx)$/i
|
||||
if (!fileExtensionRegex.test(fileName)) {
|
||||
fileName += ".md"
|
||||
}
|
||||
|
||||
const targetDir = "./src/content/posts/"
|
||||
const fullPath = path.join(targetDir, fileName)
|
||||
|
||||
if (fs.existsSync(fullPath)) {
|
||||
console.error(`Error:File ${fullPath} already exists `)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const content = `---
|
||||
title: ${args[0]}
|
||||
published: ${getDate()}
|
||||
description: ''
|
||||
image: ''
|
||||
tags: []
|
||||
category: ''
|
||||
draft: false
|
||||
---
|
||||
`
|
||||
|
||||
fs.writeFileSync(path.join(targetDir, fileName), content)
|
||||
|
||||
console.log(`Post ${fullPath} created`)
|
BIN
src/assets/images/demo-banner.png
Normal file
After Width: | Height: | Size: 877 KiB |
BIN
src/assets/images/hack-avatar.png
Executable file
After Width: | Height: | Size: 558 KiB |
128
src/components/ArchivePanel.astro
Normal file
|
@ -0,0 +1,128 @@
|
|||
---
|
||||
import {getSortedPosts} from "../utils/content-utils";
|
||||
import {getPostUrlBySlug} from "../utils/url-utils";
|
||||
import {i18n} from "../i18n/translation";
|
||||
import I18nKey from "../i18n/i18nKey";
|
||||
import {UNCATEGORIZED} from "@constants/constants";
|
||||
|
||||
interface Props {
|
||||
keyword: string;
|
||||
tags: string[];
|
||||
categories: string[];
|
||||
}
|
||||
const { keyword, tags, categories} = Astro.props;
|
||||
|
||||
let posts = await getSortedPosts()
|
||||
|
||||
if (Array.isArray(tags) && tags.length > 0) {
|
||||
posts = posts.filter(post =>
|
||||
Array.isArray(post.data.tags) && post.data.tags.some(tag => tags.includes(tag))
|
||||
);
|
||||
}
|
||||
|
||||
if (Array.isArray(categories) && categories.length > 0) {
|
||||
posts = posts.filter(post =>
|
||||
(post.data.category && categories.includes(post.data.category)) ||
|
||||
(!post.data.category && categories.includes(UNCATEGORIZED))
|
||||
);
|
||||
}
|
||||
|
||||
const groups = function () {
|
||||
const groupedPosts = posts.reduce((grouped, post) => {
|
||||
const year = post.data.published.getFullYear()
|
||||
if (!grouped[year]) {
|
||||
grouped[year] = []
|
||||
}
|
||||
grouped[year].push(post)
|
||||
return grouped
|
||||
}, {})
|
||||
|
||||
// convert the object to an array
|
||||
const groupedPostsArray = Object.keys(groupedPosts).map(key => ({
|
||||
year: key,
|
||||
posts: groupedPosts[key]
|
||||
}))
|
||||
|
||||
// sort years by latest first
|
||||
groupedPostsArray.sort((a, b) => b.year - a.year)
|
||||
return groupedPostsArray;
|
||||
}();
|
||||
|
||||
function formatDate(date: Date) {
|
||||
const month = (date.getMonth() + 1).toString().padStart(2, '0');
|
||||
const day = date.getDate().toString().padStart(2, '0');
|
||||
return `${month}-${day}`;
|
||||
}
|
||||
|
||||
function formatTag(tag: string[]) {
|
||||
return tag.map(t => `#${t}`).join(' ');
|
||||
}
|
||||
|
||||
---
|
||||
|
||||
<div class="card-base px-8 py-6">
|
||||
{
|
||||
groups.map(group => (
|
||||
<div>
|
||||
<div class="flex flex-row w-full items-center h-[3.75rem]">
|
||||
<div class="w-[15%] md:w-[10%] transition text-2xl font-bold text-right text-75">{group.year}</div>
|
||||
<div class="w-[15%] md:w-[10%]">
|
||||
<div class="h-3 w-3 bg-none rounded-full outline outline-[var(--primary)] mx-auto -outline-offset-[2px] z-50 outline-3"></div>
|
||||
</div>
|
||||
<div class="w-[70%] md:w-[80%] transition text-left text-50">{group.posts.length} {i18n(I18nKey.postsCount)}</div>
|
||||
</div>
|
||||
{group.posts.map(post => (
|
||||
<a href={getPostUrlBySlug(post.slug)}
|
||||
aria-label={post.data.title}
|
||||
class="group btn-plain block h-10 w-full rounded-lg hover:text-[initial]"
|
||||
>
|
||||
<div class="flex flex-row justify-start items-center h-full">
|
||||
<!-- date -->
|
||||
<div class="w-[15%] md:w-[10%] transition text-sm text-right text-50">
|
||||
{formatDate(post.data.published)}
|
||||
</div>
|
||||
<!-- dot and line -->
|
||||
<div class="w-[15%] md:w-[10%] relative dash-line h-full flex items-center">
|
||||
<div class="transition-all mx-auto w-1 h-1 rounded group-hover:h-5
|
||||
bg-[oklch(0.5_0.05_var(--hue))] group-hover:bg-[var(--primary)]
|
||||
outline outline-4 z-50
|
||||
outline-[var(--card-bg)]
|
||||
group-hover:outline-[var(--btn-plain-bg-hover)]
|
||||
group-active:outline-[var(--btn-plain-bg-active)]
|
||||
"
|
||||
></div>
|
||||
</div>
|
||||
<!-- post title -->
|
||||
<div class="w-[70%] md:max-w-[65%] md:w-[65%] text-left font-bold
|
||||
group-hover:translate-x-1 transition-all group-hover:text-[var(--primary)]
|
||||
text-75 pr-8 whitespace-nowrap overflow-ellipsis overflow-hidden"
|
||||
>
|
||||
{post.data.title}
|
||||
</div>
|
||||
<!-- tag list -->
|
||||
<div class="hidden md:block md:w-[15%] text-left text-sm transition
|
||||
whitespace-nowrap overflow-ellipsis overflow-hidden
|
||||
text-30"
|
||||
>{formatTag(post.data.tags)}</div>
|
||||
</div>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer components {
|
||||
.dash-line {
|
||||
}
|
||||
.dash-line::before {
|
||||
content: "";
|
||||
@apply w-[10%] h-full absolute -top-1/2 left-[calc(50%_-_1px)] -top-[50%] border-l-[2px]
|
||||
border-dashed pointer-events-none border-[var(--line-color)] transition
|
||||
}
|
||||
}
|
||||
</style>
|
8
src/components/ConfigCarrier.astro
Normal file
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
|
||||
import {siteConfig} from "../config";
|
||||
|
||||
---
|
||||
|
||||
<div id="config-carrier" data-hue={siteConfig.themeColor.hue}>
|
||||
</div>
|
13
src/components/Footer.astro
Normal file
|
@ -0,0 +1,13 @@
|
|||
---
|
||||
|
||||
import {profileConfig} from "../config";
|
||||
|
||||
---
|
||||
|
||||
<div class="card-base max-w-[var(--page-width)] min-h-[4.5rem] rounded-b-none mx-auto flex items-center px-6">
|
||||
<div class="transition text-50 text-sm">
|
||||
© 2024 {profileConfig.name}. All Rights Reserved.
|
||||
<br>
|
||||
Powered by <a class="link text-[var(--primary)] font-medium" target="_blank" href="https://github.com/saicaca/fuwari">Fuwari</a>
|
||||
</div>
|
||||
</div>
|
298
src/components/GlobalStyles.astro
Normal file
|
@ -0,0 +1,298 @@
|
|||
---
|
||||
|
||||
---
|
||||
|
||||
<div>
|
||||
<slot/>
|
||||
</div>
|
||||
|
||||
<style is:global lang="stylus">
|
||||
|
||||
/* utils */
|
||||
white(a)
|
||||
rgba(255, 255, 255, a)
|
||||
|
||||
black(a)
|
||||
rgba(0, 0, 0, a)
|
||||
|
||||
isOklch(c)
|
||||
return substr(c, 0, 5) == 'oklch'
|
||||
|
||||
oklch_fallback(c)
|
||||
str = '' + c // convert color value to string
|
||||
if isOklch(str)
|
||||
return convert(oklchToHex(str))
|
||||
return c
|
||||
|
||||
color_set(colors)
|
||||
@supports (color: oklch(0 0 0))
|
||||
:root
|
||||
for key, value in colors
|
||||
{key}: value[0]
|
||||
:root.dark
|
||||
for key, value in colors
|
||||
if length(value) > 1
|
||||
{key}: value[1]
|
||||
/* provide fallback color for oklch */
|
||||
@supports not (color: oklch(0 0 0))
|
||||
:root
|
||||
for key, value in colors
|
||||
{key}: oklch_fallback(value[0])
|
||||
:root.dark
|
||||
for key, value in colors
|
||||
if length(value) > 1
|
||||
{key}: oklch_fallback(value[1])
|
||||
|
||||
rainbow-light = linear-gradient(to right, oklch(0.80 0.10 0), oklch(0.80 0.10 30), oklch(0.80 0.10 60), oklch(0.80 0.10 90), oklch(0.80 0.10 120), oklch(0.80 0.10 150), oklch(0.80 0.10 180), oklch(0.80 0.10 210), oklch(0.80 0.10 240), oklch(0.80 0.10 270), oklch(0.80 0.10 300), oklch(0.80 0.10 330), oklch(0.80 0.10 360))
|
||||
rainbow-dark = linear-gradient(to right, oklch(0.70 0.10 0), oklch(0.70 0.10 30), oklch(0.70 0.10 60), oklch(0.70 0.10 90), oklch(0.70 0.10 120), oklch(0.70 0.10 150), oklch(0.70 0.10 180), oklch(0.70 0.10 210), oklch(0.70 0.10 240), oklch(0.70 0.10 270), oklch(0.70 0.10 300), oklch(0.70 0.10 330), oklch(0.70 0.10 360))
|
||||
|
||||
:root
|
||||
--radius-large 1rem
|
||||
|
||||
--banner-height-home 60vh
|
||||
--banner-height 40vh
|
||||
|
||||
--content-delay 150ms
|
||||
|
||||
color_set({
|
||||
--primary: oklch(0.70 0.14 var(--hue)) oklch(0.75 0.14 var(--hue))
|
||||
--page-bg: oklch(0.95 0.01 var(--hue)) oklch(0.16 0.014 var(--hue))
|
||||
--card-bg: white oklch(0.23 0.015 var(--hue))
|
||||
|
||||
--btn-content: oklch(0.55 0.12 var(--hue)) oklch(0.75 0.1 var(--hue))
|
||||
|
||||
--btn-regular-bg: oklch(0.95 0.025 var(--hue)) oklch(0.33 0.035 var(--hue))
|
||||
--btn-regular-bg-hover: oklch(0.9 0.05 var(--hue)) oklch(0.38 0.04 var(--hue))
|
||||
--btn-regular-bg-active: oklch(0.85 0.08 var(--hue)) oklch(0.43 0.045 var(--hue))
|
||||
|
||||
--btn-plain-bg-hover: oklch(0.95 0.025 var(--hue)) oklch(0.30 0.035 var(--hue))
|
||||
--btn-plain-bg-active: oklch(0.98 0.01 var(--hue)) oklch(0.27 0.025 var(--hue))
|
||||
|
||||
--btn-card-bg-hover: oklch(0.98 0.005 var(--hue)) oklch(0.3 0.03 var(--hue))
|
||||
--btn-card-bg-active: oklch(0.9 0.03 var(--hue)) oklch(0.35 0.035 var(--hue))
|
||||
|
||||
--enter-btn-bg: var(--btn-regular-bg)
|
||||
--enter-btn-bg-hover: var(--btn-regular-bg-hover)
|
||||
--enter-btn-bg-active: var(--btn-regular-bg-active)
|
||||
|
||||
--deep-text: oklch(0.25 0.02 var(--hue))
|
||||
|
||||
--title-active: oklch(0.6 0.1 var(--hue))
|
||||
|
||||
--line-divider: black(0.08) white(0.08)
|
||||
|
||||
--line-color: black(0.1) white(0.1)
|
||||
--meta-divider: black(0.2) white(0.2)
|
||||
|
||||
--inline-code-bg: var(--btn-regular-bg)
|
||||
--inline-code-color: var(--btn-content)
|
||||
--selection-bg: oklch(0.90 0.05 var(--hue)) oklch(0.40 0.08 var(--hue))
|
||||
--codeblock-selection: oklch(0.40 0.08 var(--hue))
|
||||
--codeblock-bg: oklch(0.2 0.015 var(--hue)) oklch(0.17 0.015 var(--hue))
|
||||
|
||||
--license-block-bg: black(0.03) var(--codeblock-bg)
|
||||
|
||||
--link-underline: oklch(0.93 0.04 var(--hue)) oklch(0.40 0.08 var(--hue))
|
||||
--link-hover: oklch(0.95 0.025 var(--hue)) oklch(0.40 0.08 var(--hue))
|
||||
--link-active: oklch(0.90 0.05 var(--hue)) oklch(0.35 0.07 var(--hue))
|
||||
|
||||
--float-panel-bg: white oklch(0.19 0.015 var(--hue))
|
||||
|
||||
--scrollbar-bg-light: black(0.4)
|
||||
--scrollbar-bg-hover-light: black(0.5)
|
||||
--scrollbar-bg-active-light: black(0.6)
|
||||
|
||||
--scrollbar-bg-dark: white(0.4)
|
||||
--scrollbar-bg-hover-dark: white(0.5)
|
||||
--scrollbar-bg-active-dark: white(0.6)
|
||||
|
||||
--scrollbar-bg: var(--scrollbar-bg-light) var(--scrollbar-bg-dark)
|
||||
--scrollbar-bg-hover: var(--scrollbar-bg-hover-light) var(--scrollbar-bg-hover-dark)
|
||||
--scrollbar-bg-active: var(--scrollbar-bg-active-light) var(--scrollbar-bg-active-dark)
|
||||
|
||||
--color-selection-bar: rainbow-light rainbow-dark
|
||||
|
||||
--display-light-icon: 1 0
|
||||
--display-dark-icon: 0 1
|
||||
|
||||
--admonitions-color-tip: oklch(0.7 0.14 180) oklch(0.75 0.14 180)
|
||||
--admonitions-color-note: oklch(0.7 0.14 250) oklch(0.75 0.14 250)
|
||||
--admonitions-color-important: oklch(0.7 0.14 310) oklch(0.75 0.14 310)
|
||||
--admonitions-color-warning: oklch(0.7 0.14 60) oklch(0.75 0.14 60)
|
||||
--admonitions-color-caution: oklch(0.6 0.2 25) oklch(0.65 0.2 25)
|
||||
})
|
||||
|
||||
|
||||
/* some global styles */
|
||||
::selection
|
||||
background-color: var(--selection-bg)
|
||||
|
||||
.scrollbar-base.os-scrollbar
|
||||
transition: width 0.15s ease-in-out, height 0.15s ease-in-out, opacity 0.15s, visibility 0.15s, top 0.15s, right 0.15s, bottom 0.15s, left 0.15s;
|
||||
pointer-events: unset;
|
||||
&.os-scrollbar-horizontal
|
||||
padding-top: 4px;
|
||||
padding-bottom: 4px;
|
||||
height: 16px;
|
||||
.os-scrollbar-track .os-scrollbar-handle
|
||||
height: 4px;
|
||||
border-radius: 4px;
|
||||
&:hover
|
||||
.os-scrollbar-track .os-scrollbar-handle
|
||||
height: 8px;
|
||||
&.px-2
|
||||
padding-left: 8px;
|
||||
padding-right: 8px;
|
||||
&.os-scrollbar-vertical
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
width: 16px;
|
||||
.os-scrollbar-track .os-scrollbar-handle
|
||||
width: 4px;
|
||||
border-radius: 4px;
|
||||
&:hover
|
||||
.os-scrollbar-track .os-scrollbar-handle
|
||||
width: 8px;
|
||||
&.py-1
|
||||
padding-top: 4px;
|
||||
padding-bottom: 4px;
|
||||
|
||||
.scrollbar-auto
|
||||
&.os-scrollbar
|
||||
--os-handle-bg: var(--scrollbar-bg);
|
||||
--os-handle-bg-hover: var(--scrollbar-bg-hover);
|
||||
--os-handle-bg-active: var(--scrollbar-bg-active);
|
||||
|
||||
.scrollbar-dark
|
||||
&.os-scrollbar
|
||||
--os-handle-bg: var(--scrollbar-bg-dark);
|
||||
--os-handle-bg-hover: var(--scrollbar-bg-hover-dark);
|
||||
--os-handle-bg-active: var(--scrollbar-bg-active-dark);
|
||||
|
||||
.scrollbar-light
|
||||
&.os-scrollbar
|
||||
--os-handle-bg: var(--scrollbar-bg-light);
|
||||
--os-handle-bg-hover: var(--scrollbar-bg-hover-light);
|
||||
--os-handle-bg-active: var(--scrollbar-bg-active-light);
|
||||
|
||||
|
||||
</style>
|
||||
<style is:global lang="scss">
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer components {
|
||||
.card-base {
|
||||
@apply rounded-[var(--radius-large)] overflow-hidden bg-[var(--card-bg)] transition;
|
||||
}
|
||||
h1, h2, h3, h4, h5, h6, p, a, span, li, ul, ol, blockquote, code, pre, table, th, td, strong {
|
||||
@apply transition;
|
||||
}
|
||||
.card-shadow {
|
||||
@apply drop-shadow-[0_2px_4px_rgba(0,0,0,0.005)]
|
||||
}
|
||||
.expand-animation {
|
||||
@apply relative before:ease-out before:transition active:bg-none hover:before:bg-[var(--btn-plain-bg-hover)] active:before:bg-[var(--btn-plain-bg-active)] z-0
|
||||
before:absolute before:rounded-[inherit] before:inset-0 before:scale-[0.85] hover:before:scale-100 before:-z-10
|
||||
}
|
||||
.link {
|
||||
@apply transition rounded-md p-1 -m-1 expand-animation;
|
||||
}
|
||||
.link-lg {
|
||||
@apply transition rounded-md p-1.5 -m-1.5 expand-animation;
|
||||
}
|
||||
.float-panel {
|
||||
@apply top-[5.25rem] rounded-[var(--radius-large)] overflow-hidden bg-[var(--float-panel-bg)] transition shadow-xl dark:shadow-none
|
||||
}
|
||||
.float-panel-closed {
|
||||
@apply -translate-y-1 opacity-0 pointer-events-none
|
||||
}
|
||||
.search-panel mark {
|
||||
@apply bg-transparent text-[var(--primary)]
|
||||
}
|
||||
|
||||
.btn-card {
|
||||
@apply transition flex items-center justify-center bg-[var(--card-bg)] hover:bg-[var(--btn-card-bg-hover)]
|
||||
active:bg-[var(--btn-card-bg-active)]
|
||||
}
|
||||
.btn-card.disabled {
|
||||
@apply pointer-events-none text-black/10 dark:text-white/10
|
||||
}
|
||||
.btn-plain {
|
||||
@apply transition relative flex items-center justify-center bg-none
|
||||
text-black/75 hover:text-[var(--primary)] dark:text-white/75 dark:hover:text-[var(--primary)];
|
||||
&:not(.scale-animation) {
|
||||
@apply hover:bg-[var(--btn-plain-bg-hover)] active:bg-[var(--btn-plain-bg-active)]
|
||||
}
|
||||
&.scale-animation {
|
||||
@apply expand-animation;
|
||||
&.current-theme-btn {
|
||||
@apply before:scale-100 before:opacity-100 before:bg-[var(--btn-plain-bg-hover)] text-[var(--primary)]
|
||||
}
|
||||
}
|
||||
}
|
||||
.btn-regular {
|
||||
@apply transition flex items-center justify-center bg-[var(--btn-regular-bg)] hover:bg-[var(--btn-regular-bg-hover)] active:bg-[var(--btn-regular-bg-active)]
|
||||
text-[var(--btn-content)] dark:text-white/75
|
||||
}
|
||||
|
||||
.link-underline {
|
||||
@apply transition underline decoration-2 decoration-dashed decoration-[var(--link-underline)]
|
||||
hover:decoration-[var(--link-hover)] active:decoration-[var(--link-active)] underline-offset-[0.25rem]
|
||||
}
|
||||
|
||||
.text-90 {
|
||||
@apply text-black/90 dark:text-white/90
|
||||
}
|
||||
.text-75 {
|
||||
@apply text-black/75 dark:text-white/75
|
||||
}
|
||||
.text-50 {
|
||||
@apply text-black/50 dark:text-white/50
|
||||
}
|
||||
.text-30 {
|
||||
@apply text-black/30 dark:text-white/30
|
||||
}
|
||||
.text-25 {
|
||||
@apply text-black/25 dark:text-white/25
|
||||
}
|
||||
|
||||
html.is-changing .transition-fade {
|
||||
@apply transition-all duration-200
|
||||
}
|
||||
html.is-animating .transition-fade {
|
||||
@apply opacity-0 translate-y-4
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fade-in-up {
|
||||
0% {
|
||||
transform: translateY(2rem);
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
.onload-animation {
|
||||
opacity: 0;
|
||||
animation: 300ms fade-in-up;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
#top-row {
|
||||
animation-delay: 0ms
|
||||
}
|
||||
#sidebar {
|
||||
animation-delay: 100ms
|
||||
}
|
||||
#content-wrapper {
|
||||
animation-delay: var(--content-delay);
|
||||
}
|
||||
#footer {
|
||||
animation-delay: 400ms;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
120
src/components/LightDarkSwitch.svelte
Normal file
|
@ -0,0 +1,120 @@
|
|||
<script lang="ts">
|
||||
import type { LIGHT_DARK_MODE } from '@/types/config.ts'
|
||||
import {
|
||||
AUTO_MODE,
|
||||
DARK_MODE,
|
||||
LIGHT_MODE,
|
||||
} from '@constants/constants.ts'
|
||||
import I18nKey from '@i18n/i18nKey'
|
||||
import { i18n } from '@i18n/translation'
|
||||
import Icon from '@iconify/svelte'
|
||||
import {
|
||||
applyThemeToDocument,
|
||||
getStoredTheme,
|
||||
setTheme,
|
||||
} from '@utils/setting-utils.ts'
|
||||
import { onMount } from 'svelte'
|
||||
|
||||
const seq: LIGHT_DARK_MODE[] = [
|
||||
LIGHT_MODE,
|
||||
DARK_MODE,
|
||||
AUTO_MODE,
|
||||
]
|
||||
let mode: LIGHT_DARK_MODE = AUTO_MODE
|
||||
|
||||
onMount(() => {
|
||||
mode = getStoredTheme()
|
||||
const darkModePreference = window.matchMedia(
|
||||
'(prefers-color-scheme: dark)',
|
||||
)
|
||||
const changeThemeWhenSchemeChanged: Parameters<
|
||||
typeof darkModePreference.addEventListener<'change'>
|
||||
>[1] = e => {
|
||||
applyThemeToDocument(mode)
|
||||
}
|
||||
darkModePreference.addEventListener(
|
||||
'change',
|
||||
changeThemeWhenSchemeChanged,
|
||||
)
|
||||
return () => {
|
||||
darkModePreference.removeEventListener(
|
||||
'change',
|
||||
changeThemeWhenSchemeChanged,
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
function switchScheme(newMode: LIGHT_DARK_MODE) {
|
||||
mode = newMode
|
||||
setTheme(newMode)
|
||||
}
|
||||
|
||||
function toggleScheme() {
|
||||
let i = 0
|
||||
for (; i < seq.length; i++) {
|
||||
if (seq[i] === mode) {
|
||||
break
|
||||
}
|
||||
}
|
||||
switchScheme(seq[(i + 1) % seq.length])
|
||||
}
|
||||
|
||||
function showPanel() {
|
||||
const panel = document.querySelector('#light-dark-panel')
|
||||
panel.classList.remove('float-panel-closed')
|
||||
}
|
||||
|
||||
function hidePanel() {
|
||||
const panel = document.querySelector('#light-dark-panel')
|
||||
panel.classList.add('float-panel-closed')
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<!-- z-50 make the panel higher than other float panels -->
|
||||
<div class="relative z-50" role="menu" tabindex="-1" on:mouseleave={hidePanel}>
|
||||
<button aria-label="Light/Dark Mode" role="menuitem" class="relative btn-plain scale-animation rounded-lg h-11 w-11 active:scale-90" id="scheme-switch" on:click={toggleScheme} on:mouseenter={showPanel}>
|
||||
<div class="absolute" class:opacity-0={mode !== LIGHT_MODE}>
|
||||
<Icon icon="material-symbols:wb-sunny-outline-rounded" class="text-[1.25rem]"></Icon>
|
||||
</div>
|
||||
<div class="absolute" class:opacity-0={mode !== DARK_MODE}>
|
||||
<Icon icon="material-symbols:dark-mode-outline-rounded" class="text-[1.25rem]"></Icon>
|
||||
</div>
|
||||
<div class="absolute" class:opacity-0={mode !== AUTO_MODE}>
|
||||
<Icon icon="material-symbols:radio-button-partial-outline" class="text-[1.25rem]"></Icon>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<div id="light-dark-panel" class="hidden lg:block absolute transition float-panel-closed top-11 -right-2 pt-5" >
|
||||
<div class="card-base float-panel p-2">
|
||||
<button class="flex transition whitespace-nowrap items-center justify-start w-full btn-plain scale-animation rounded-lg h-9 px-3 font-medium active:scale-95 mb-0.5"
|
||||
class:current-theme-btn={mode === LIGHT_MODE}
|
||||
on:click={() => switchScheme(LIGHT_MODE)}
|
||||
>
|
||||
<Icon icon="material-symbols:wb-sunny-outline-rounded" class="text-[1.25rem] mr-3"></Icon>
|
||||
{i18n(I18nKey.lightMode)}
|
||||
</button>
|
||||
<button class="flex transition whitespace-nowrap items-center justify-start w-full btn-plain scale-animation rounded-lg h-9 px-3 font-medium active:scale-95 mb-0.5"
|
||||
class:current-theme-btn={mode === DARK_MODE}
|
||||
on:click={() => switchScheme(DARK_MODE)}
|
||||
>
|
||||
<Icon icon="material-symbols:dark-mode-outline-rounded" class="text-[1.25rem] mr-3"></Icon>
|
||||
{i18n(I18nKey.darkMode)}
|
||||
</button>
|
||||
<button class="flex transition whitespace-nowrap items-center justify-start w-full btn-plain scale-animation rounded-lg h-9 px-3 font-medium active:scale-95"
|
||||
class:current-theme-btn={mode === AUTO_MODE}
|
||||
on:click={() => switchScheme(AUTO_MODE)}
|
||||
>
|
||||
<Icon icon="material-symbols:radio-button-partial-outline" class="text-[1.25rem] mr-3"></Icon>
|
||||
{i18n(I18nKey.systemMode)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="css">
|
||||
.current-setting {
|
||||
background: var(--btn-plain-bg-hover);
|
||||
color: var(--primary);
|
||||
}
|
||||
</style>
|
120
src/components/Navbar.astro
Normal file
|
@ -0,0 +1,120 @@
|
|||
---
|
||||
import { Icon } from 'astro-icon/components';
|
||||
import DisplaySettings from "./widget/DisplaySettings.svelte";
|
||||
import {LinkPreset, NavBarLink} from "../types/config";
|
||||
import {navBarConfig, siteConfig} from "../config";
|
||||
import NavMenuPanel from "./widget/NavMenuPanel.astro";
|
||||
import Search from "./Search.svelte";
|
||||
import {LinkPresets} from "../constants/link-presets";
|
||||
import LightDarkSwitch from "./LightDarkSwitch.svelte";
|
||||
import {url} from "../utils/url-utils";
|
||||
const className = Astro.props.class;
|
||||
|
||||
let links: NavBarLink[] = navBarConfig.links.map((item: NavBarLink | LinkPreset): NavBarLink => {
|
||||
if (typeof item === "number") {
|
||||
return LinkPresets[item]
|
||||
}
|
||||
return item;
|
||||
});
|
||||
|
||||
---
|
||||
<div class:list={[
|
||||
className,
|
||||
"card-base sticky top-0 overflow-visible max-w-[var(--page-width)] h-[4.5rem] rounded-t-none mx-auto flex items-center justify-between px-4"]}>
|
||||
<a href={url('/')} class="btn-plain scale-animation rounded-lg h-[3.25rem] px-5 font-bold active:scale-95">
|
||||
<div class="flex flex-row text-[var(--primary)] items-center text-md">
|
||||
<Icon name="material-symbols:home-outline-rounded" size={"1.75rem"} class="mb-1 mr-2" />
|
||||
{siteConfig.title}
|
||||
</div>
|
||||
</a>
|
||||
<div class="hidden md:flex">
|
||||
{links.map((l) => {
|
||||
return <a aria-label={l.name} href={l.external ? l.url : url(l.url)} target={l.external ? "_blank" : null}
|
||||
class="btn-plain scale-animation rounded-lg h-11 font-bold px-5 active:scale-95"
|
||||
>
|
||||
<div class="flex items-center">
|
||||
{l.name}
|
||||
{l.external && <Icon size="14" name="fa6-solid:arrow-up-right-from-square" class="transition -translate-y-[1px] ml-1 text-black/[0.2] dark:text-white/[0.2]"></Icon>}
|
||||
</div>
|
||||
</a>;
|
||||
})}
|
||||
</div>
|
||||
<div class="flex">
|
||||
<!--<SearchPanel client:load>-->
|
||||
<Search client:load>
|
||||
<Icon slot="search-icon" name="material-symbols:search" size={"1.25rem"} class="absolute pointer-events-none ml-3 transition my-auto text-black/30 dark:text-white/30"></Icon>
|
||||
<!--<Icon slot="arrow-icon" name="material-symbols:chevron-right-rounded" size={"1.25rem"} class="transition my-auto text-[var(--primary)]"></Icon>-->
|
||||
<Icon slot="arrow-icon" name="fa6-solid:chevron-right" size={"0.75rem"} class="transition translate-x-0.5 my-auto text-[var(--primary)]"></Icon>
|
||||
<Icon slot="search-switch" name="material-symbols:search" size={"1.25rem"}></Icon>
|
||||
</Search>
|
||||
{!siteConfig.themeColor.fixed && (
|
||||
<button aria-label="Display Settings" class="btn-plain scale-animation rounded-lg h-11 w-11 active:scale-90" id="display-settings-switch">
|
||||
<Icon name="material-symbols:palette-outline" size={"1.25rem"}></Icon>
|
||||
</button>
|
||||
)}
|
||||
<LightDarkSwitch client:load></LightDarkSwitch>
|
||||
<button aria-label="Menu" name="Nav Menu" class="btn-plain scale-animation rounded-lg w-11 h-11 active:scale-90 md:hidden" id="nav-menu-switch">
|
||||
<Icon name="material-symbols:menu-rounded" size={"1.25rem"}></Icon>
|
||||
</button>
|
||||
</div>
|
||||
<NavMenuPanel links={links}></NavMenuPanel>
|
||||
<DisplaySettings client:only="svelte">
|
||||
<Icon slot="restore-icon" name="fa6-solid:arrow-rotate-left" size={"0.875rem"} class=""></Icon>
|
||||
</DisplaySettings>
|
||||
</div>
|
||||
|
||||
<style lang="stylus">
|
||||
</style>
|
||||
|
||||
<script>
|
||||
|
||||
function switchTheme() {
|
||||
if (localStorage.theme === 'dark') {
|
||||
document.documentElement.classList.remove('dark');
|
||||
localStorage.theme = 'light';
|
||||
} else {
|
||||
document.documentElement.classList.add('dark');
|
||||
localStorage.theme = 'dark';
|
||||
}
|
||||
}
|
||||
|
||||
function loadButtonScript() {
|
||||
let switchBtn = document.getElementById("scheme-switch");
|
||||
switchBtn.addEventListener("click", function () {
|
||||
switchTheme()
|
||||
});
|
||||
|
||||
let settingBtn = document.getElementById("display-settings-switch");
|
||||
if (settingBtn) {
|
||||
settingBtn.addEventListener("click", function () {
|
||||
let settingPanel = document.getElementById("display-setting");
|
||||
settingPanel.classList.toggle("float-panel-closed");
|
||||
});
|
||||
}
|
||||
|
||||
let menuBtn = document.getElementById("nav-menu-switch");
|
||||
menuBtn.addEventListener("click", function () {
|
||||
let menuPanel = document.getElementById("nav-menu-panel");
|
||||
menuPanel.classList.toggle("float-panel-closed");
|
||||
});
|
||||
}
|
||||
|
||||
loadButtonScript();
|
||||
|
||||
document.addEventListener('astro:after-swap', () => {
|
||||
loadButtonScript();
|
||||
}, { once: false });
|
||||
</script>
|
||||
|
||||
{import.meta.env.PROD && <script is:inline define:vars={{scriptUrl: url('/pagefind/pagefind.js')}}>
|
||||
async function loadPagefind() {
|
||||
const pagefind = await import(scriptUrl)
|
||||
await pagefind.options({
|
||||
'excerptLength': 20
|
||||
})
|
||||
pagefind.init()
|
||||
window.pagefind = pagefind
|
||||
pagefind.search('') // speed up the first search
|
||||
}
|
||||
loadPagefind()
|
||||
</script>}
|
95
src/components/PostCard.astro
Normal file
|
@ -0,0 +1,95 @@
|
|||
---
|
||||
import path from "path";
|
||||
import PostMetadata from "./PostMeta.astro";
|
||||
import ImageWrapper from "./misc/ImageWrapper.astro";
|
||||
import { Icon } from 'astro-icon/components';
|
||||
import {i18n} from "../i18n/translation";
|
||||
import I18nKey from "../i18n/i18nKey";
|
||||
import {getDir} from "../utils/url-utils";
|
||||
|
||||
interface Props {
|
||||
class: string;
|
||||
entry: any;
|
||||
title: string;
|
||||
url: string;
|
||||
published: Date;
|
||||
tags: string[];
|
||||
category: string;
|
||||
image: string;
|
||||
description: string;
|
||||
words: number;
|
||||
draft: boolean;
|
||||
style: string;
|
||||
}
|
||||
const { entry, title, url, published, tags, category, image, description, words, style } = Astro.props;
|
||||
const className = Astro.props.class;
|
||||
|
||||
const hasCover = image !== undefined && image !== null && image !== '';
|
||||
|
||||
const coverWidth = "28%";
|
||||
|
||||
const { remarkPluginFrontmatter } = await entry.render();
|
||||
|
||||
---
|
||||
<div class:list={["card-base flex flex-col-reverse md:flex-col w-full rounded-[var(--radius-large)] overflow-hidden relative", className]} style={style}>
|
||||
<div class:list={["pl-6 md:pl-9 pr-6 md:pr-2 pt-6 md:pt-7 pb-6 relative", {"w-full md:w-[calc(100%_-_52px_-_12px)]": !hasCover, "w-full md:w-[calc(100%_-_var(--coverWidth)_-_12px)]": hasCover}]}>
|
||||
<a href={url}
|
||||
class="transition group w-full block font-bold mb-3 text-3xl text-90
|
||||
hover:text-[var(--primary)] dark:hover:text-[var(--primary)]
|
||||
active:text-[var(--title-active)] dark:active:text-[var(--title-active)]
|
||||
before:w-1 before:h-5 before:rounded-md before:bg-[var(--primary)]
|
||||
before:absolute before:top-[35px] before:left-[18px] before:hidden md:before:block
|
||||
">
|
||||
{title}
|
||||
<Icon class="inline text-[var(--primary)] md:hidden translate-y-0.5 absolute" name="material-symbols:chevron-right-rounded" size="2rem" ></Icon>
|
||||
<Icon class="text-[var(--primary)] transition hidden md:inline absolute translate-y-0.5 opacity-0 group-hover:opacity-100 -translate-x-1 group-hover:translate-x-0" name="material-symbols:chevron-right-rounded" size="2rem" ></Icon>
|
||||
</a>
|
||||
|
||||
<!-- metadata -->
|
||||
<PostMetadata published={published} tags={tags} category={category} hideTagsForMobile={true} class="mb-4"></PostMetadata>
|
||||
|
||||
<!-- description -->
|
||||
<div class="transition text-75 mb-3.5 pr-4">
|
||||
{ description }
|
||||
</div>
|
||||
|
||||
<!-- word count and read time -->
|
||||
<div class="text-sm text-black/30 dark:text-white/30 flex gap-4 transition">
|
||||
<div>{remarkPluginFrontmatter.words} {" " + i18n(I18nKey.wordsCount)}</div>
|
||||
<div>|</div>
|
||||
<div>{remarkPluginFrontmatter.minutes} {" " + i18n(I18nKey.minutesCount)}</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{hasCover && <a href={url} aria-label={title}
|
||||
class:list={["group",
|
||||
"max-h-[20vh] md:max-h-none mx-4 mt-4 -mb-2 md:mb-0 md:mx-0 md:mt-0",
|
||||
"md:w-[var(--coverWidth)] relative md:absolute md:top-3 md:bottom-3 md:right-3 rounded-xl overflow-hidden active:scale-95"
|
||||
]} >
|
||||
<div class="absolute pointer-events-none z-10 w-full h-full group-hover:bg-black/30 group-active:bg-black/50 transition"></div>
|
||||
<div class="absolute pointer-events-none z-20 w-full h-full flex items-center justify-center ">
|
||||
<Icon name="material-symbols:chevron-right-rounded"
|
||||
class="transition opacity-0 group-hover:opacity-100 scale-50 group-hover:scale-100 text-white text-5xl">
|
||||
</Icon>
|
||||
</div>
|
||||
<ImageWrapper src={image} basePath={path.join("content/posts/", getDir(entry.id))} alt="Cover Image of the Post"
|
||||
class="w-full h-full">
|
||||
</ImageWrapper>
|
||||
</a>}
|
||||
|
||||
{!hasCover &&
|
||||
<a href={url} aria-label={title} class="hidden md:flex btn-regular w-[3.25rem]
|
||||
absolute right-3 top-3 bottom-3 rounded-xl bg-[var(--enter-btn-bg)]
|
||||
hover:bg-[var(--enter-btn-bg-hover)] active:bg-[var(--enter-btn-bg-active)] active:scale-95
|
||||
">
|
||||
<Icon name="material-symbols:chevron-right-rounded"
|
||||
class="transition text-[var(--primary)] text-4xl mx-auto">
|
||||
</Icon>
|
||||
</a>
|
||||
}
|
||||
</div>
|
||||
<div class="transition border-t-[1px] border-dashed mx-6 border-black/10 dark:border-white/[0.15] last:border-t-0 md:hidden"></div>
|
||||
|
||||
<style lang="stylus" define:vars={{coverWidth}}>
|
||||
</style>
|
77
src/components/PostMeta.astro
Normal file
|
@ -0,0 +1,77 @@
|
|||
---
|
||||
import {formatDateToYYYYMMDD} from "../utils/date-utils";
|
||||
import { Icon } from 'astro-icon/components';
|
||||
import {i18n} from "../i18n/translation";
|
||||
import I18nKey from "../i18n/i18nKey";
|
||||
import {url} from "../utils/url-utils";
|
||||
|
||||
interface Props {
|
||||
class: string;
|
||||
published: Date;
|
||||
tags: string[];
|
||||
category: string;
|
||||
hideTagsForMobile: boolean;
|
||||
}
|
||||
const {published, tags, category, hideTagsForMobile} = Astro.props;
|
||||
const className = Astro.props.class;
|
||||
---
|
||||
|
||||
<div class:list={["flex flex-wrap text-neutral-500 dark:text-neutral-400 items-center gap-4 gap-x-4 gap-y-2", className]}>
|
||||
<!-- publish date -->
|
||||
<div class="flex items-center">
|
||||
<div class="meta-icon"
|
||||
>
|
||||
<Icon name="material-symbols:calendar-today-outline-rounded" class="text-xl"></Icon>
|
||||
</div>
|
||||
<span class="text-50 text-sm font-medium">{formatDateToYYYYMMDD(published)}</span>
|
||||
</div>
|
||||
|
||||
<!-- categories -->
|
||||
<div class="flex items-center">
|
||||
<div class="meta-icon"
|
||||
>
|
||||
<Icon name="material-symbols:menu-rounded" class="text-xl"></Icon>
|
||||
</div>
|
||||
<div class="flex flex-row flex-nowrap items-center">
|
||||
<a href={url(`/archive/category/${category || 'uncategorized'}/`)} aria-label=`View all posts in the ${category} category`
|
||||
class="link-lg transition text-50 text-sm font-medium
|
||||
hover:text-[var(--primary)] dark:hover:text-[var(--primary)] whitespace-nowrap">
|
||||
{category || i18n(I18nKey.uncategorized)}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- tags -->
|
||||
<div class:list={["items-center", {"flex": !hideTagsForMobile, "hidden md:flex": hideTagsForMobile}]}>
|
||||
<div class="meta-icon"
|
||||
>
|
||||
<Icon name="material-symbols:tag-rounded" class="text-xl"></Icon>
|
||||
</div>
|
||||
<div class="flex flex-row flex-nowrap items-center">
|
||||
{(tags && tags.length > 0) && tags.map((tag, i) => (
|
||||
<div class:list={[{"hidden": i == 0}, "mx-1.5 text-[var(--meta-divider)] text-sm"]}>/</div>
|
||||
<a href={url(`/archive/tag/${tag}/`)} aria-label=`View all posts with the ${tag} tag`
|
||||
class="link-lg transition text-50 text-sm font-medium
|
||||
hover:text-[var(--primary)] dark:hover:text-[var(--primary)] whitespace-nowrap">
|
||||
{tag}
|
||||
</a>
|
||||
))}
|
||||
{!(tags && tags.length > 0) && <div class="transition text-50 text-sm font-medium">{i18n(I18nKey.noTags)}</div>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
@tailwind components;
|
||||
|
||||
@layer components {
|
||||
.meta-icon {
|
||||
@apply w-8 h-8 transition rounded-md flex items-center justify-center bg-[var(--btn-regular-bg)]
|
||||
text-[var(--btn-content)] mr-2
|
||||
}
|
||||
.with-divider {
|
||||
@apply before:content-['/'] before:ml-1.5 before:mr-1.5 before:text-[var(--meta-divider)] before:text-sm
|
||||
before:font-medium before:first-of-type:hidden before:transition
|
||||
}
|
||||
}
|
||||
</style>
|
28
src/components/PostPage.astro
Normal file
|
@ -0,0 +1,28 @@
|
|||
---
|
||||
import {getPostUrlBySlug} from "@utils/url-utils";
|
||||
import PostCard from "./PostCard.astro";
|
||||
|
||||
const {page} = Astro.props;
|
||||
|
||||
let delay = 0
|
||||
const interval = 50
|
||||
---
|
||||
<div class="transition flex flex-col rounded-[var(--radius-large)] bg-[var(--card-bg)] py-1 md:py-0 md:bg-transparent md:gap-4 mb-4">
|
||||
{page.data.map((entry: { data: { draft: boolean; title: string; tags: string[]; category: string; published: Date; image: string; description: string; }; slug: string; }) => {
|
||||
return (
|
||||
<PostCard
|
||||
entry={entry}
|
||||
title={entry.data.title}
|
||||
tags={entry.data.tags}
|
||||
category={entry.data.category}
|
||||
published={entry.data.published}
|
||||
url={getPostUrlBySlug(entry.slug)}
|
||||
image={entry.data.image}
|
||||
description={entry.data.description}
|
||||
draft={entry.data.draft}
|
||||
class:list="onload-animation"
|
||||
style={`animation-delay: calc(var(--content-delay) + ${delay++ * interval}ms);`}
|
||||
></PostCard>
|
||||
);
|
||||
})}
|
||||
</div>
|
116
src/components/Search.svelte
Normal file
|
@ -0,0 +1,116 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from 'svelte'
|
||||
import {url} from "@utils/url-utils.ts"
|
||||
import { i18n } from '@i18n/translation';
|
||||
import I18nKey from '@i18n/i18nKey';
|
||||
let keywordDesktop = ''
|
||||
let keywordMobile = ''
|
||||
let result = []
|
||||
const fakeResult = [{
|
||||
url: url('/'),
|
||||
meta: {
|
||||
title: 'This Is a Fake Search Result'
|
||||
},
|
||||
excerpt: 'Because the search cannot work in the <mark>dev</mark> environment.'
|
||||
}, {
|
||||
url: url('/'),
|
||||
meta: {
|
||||
title: 'If You Want to Test the Search'
|
||||
},
|
||||
excerpt: 'Try running <mark>npm build && npm preview</mark> instead.'
|
||||
}]
|
||||
|
||||
let search = (keyword: string, isDesktop: boolean) => {}
|
||||
|
||||
onMount(() => {
|
||||
search = async (keyword: string, isDesktop: boolean) => {
|
||||
let panel = document.getElementById('search-panel')
|
||||
if (!panel)
|
||||
return
|
||||
|
||||
if (!keyword && isDesktop) {
|
||||
panel.classList.add("float-panel-closed")
|
||||
return
|
||||
}
|
||||
|
||||
let arr = [];
|
||||
if (import.meta.env.PROD) {
|
||||
const ret = await pagefind.search(keyword)
|
||||
for (const item of ret.results) {
|
||||
arr.push(await item.data())
|
||||
}
|
||||
} else {
|
||||
// Mock data for non-production environment
|
||||
// arr = JSON.parse('[{"url":"/","content":"Simple Guides for Fuwari. Cover image source: Source. This blog template is built with Astro. For the things that are not mentioned in this guide, you may find the answers in the Astro Docs. Front-matter of Posts. --- title: My First Blog Post published: 2023-09-09 description: This is the first post of my new Astro blog. image: ./cover.jpg tags: [Foo, Bar] category: Front-end draft: false ---AttributeDescription title. The title of the post. published. The date the post was published. description. A short description of the post. Displayed on index page. image. The cover image path of the post. 1. Start with http:// or https://: Use web image 2. Start with /: For image in public dir 3. With none of the prefixes: Relative to the markdown file. tags. The tags of the post. category. The category of the post. draft. If this post is still a draft, which won’t be displayed. Where to Place the Post Files. Your post files should be placed in src/content/posts/ directory. You can also create sub-directories to better organize your posts and assets. src/content/posts/ ├── post-1.md └── post-2/ ├── cover.png └── index.md.","word_count":187,"filters":{},"meta":{"title":"This Is a Fake Search Result"},"anchors":[{"element":"h2","id":"front-matter-of-posts","text":"Front-matter of Posts","location":34},{"element":"h2","id":"where-to-place-the-post-files","text":"Where to Place the Post Files","location":151}],"weighted_locations":[{"weight":10,"balanced_score":57600,"location":3}],"locations":[3],"raw_content":"Simple Guides for Fuwari. Cover image source: Source. This blog template is built with Astro. For the things that are not mentioned in this guide, you may find the answers in the Astro Docs. Front-matter of Posts. --- title: My First Blog Post published: 2023-09-09 description: This is the first post of my new Astro blog. image: ./cover.jpg tags: [Foo, Bar] category: Front-end draft: false ---AttributeDescription title. The title of the post. published. The date the post was published. description. A short description of the post. Displayed on index page. image. The cover image path of the post. 1. Start with http:// or https://: Use web image 2. Start with /: For image in public dir 3. With none of the prefixes: Relative to the markdown file. tags. The tags of the post. category. The category of the post. draft. If this post is still a draft, which won’t be displayed. Where to Place the Post Files. Your post files should be placed in src/content/posts/ directory. You can also create sub-directories to better organize your posts and assets. src/content/posts/ ├── post-1.md └── post-2/ ├── cover.png └── index.md.","raw_url":"/posts/guide/","excerpt":"Because the search cannot work in the <mark>dev</mark> environment.","sub_results":[{"title":"Simple Guides for Fuwari - Fuwari","url":"/posts/guide/","weighted_locations":[{"weight":10,"balanced_score":57600,"location":3}],"locations":[3],"excerpt":"Simple Guides for <mark>Fuwari.</mark> Cover image source: Source. This blog template is built with Astro. For the things that are not mentioned in this guide, you may find the answers"}]},{"url":"/","content":"About. This is the demo site for Fuwari. Sources of images used in this site. Unsplash. 星と少女 by Stella. Rabbit - v1.4 Showcase by Rabbit_YourMajesty.","word_count":25,"filters":{},"meta":{"title":"If You Want to Test the Search"},"anchors":[{"element":"h1","id":"about","text":"About","location":0},{"element":"h3","id":"sources-of-images-used-in-this-site","text":"Sources of images used in this site","location":8}],"weighted_locations":[{"weight":1,"balanced_score":576,"location":7}],"locations":[7],"raw_content":"About. This is the demo site for Fuwari. Sources of images used in this site. Unsplash. 星と少女 by Stella. Rabbit - v1.4 Showcase by Rabbit_YourMajesty.","raw_url":"/about/","excerpt":"Try running <mark>npm build && npm preview</mark> instead.","sub_results":[{"title":"About","url":"/about/#about","anchor":{"element":"h1","id":"about","text":"About","location":0},"weighted_locations":[{"weight":1,"balanced_score":576,"location":7}],"locations":[7],"excerpt":"About. This is the demo site for <mark>Fuwari.</mark>"}]}]')
|
||||
arr = fakeResult
|
||||
}
|
||||
|
||||
if (!arr.length && isDesktop) {
|
||||
panel.classList.add("float-panel-closed")
|
||||
return
|
||||
}
|
||||
|
||||
if (isDesktop) {
|
||||
panel.classList.remove("float-panel-closed")
|
||||
}
|
||||
result = arr
|
||||
}
|
||||
})
|
||||
|
||||
const togglePanel = () => {
|
||||
let panel = document.getElementById('search-panel')
|
||||
panel?.classList.toggle("float-panel-closed")
|
||||
}
|
||||
|
||||
$: search(keywordDesktop, true)
|
||||
$: search(keywordMobile, false)
|
||||
</script>
|
||||
|
||||
<!-- search bar for desktop view -->
|
||||
<div id="search-bar" class="hidden lg:flex transition-all items-center h-11 mr-2 rounded-lg
|
||||
bg-black/[0.04] hover:bg-black/[0.06] focus-within:bg-black/[0.06]
|
||||
dark:bg-white/5 dark:hover:bg-white/10 dark:focus-within:bg-white/10
|
||||
">
|
||||
<slot name="search-icon"></slot>
|
||||
<input placeholder="{i18n(I18nKey.search)}" bind:value={keywordDesktop} on:focus={() => search(keywordDesktop, true)}
|
||||
class="transition-all pl-10 text-sm bg-transparent outline-0
|
||||
h-full w-40 active:w-60 focus:w-60 text-black/50 dark:text-white/50"
|
||||
>
|
||||
</div>
|
||||
|
||||
<!-- toggle btn for phone/tablet view -->
|
||||
<button on:click={togglePanel} aria-label="Search Panel" id="search-switch"
|
||||
class="btn-plain scale-animation lg:hidden rounded-lg w-11 h-11 active:scale-90">
|
||||
<slot name="search-switch"></slot>
|
||||
</button>
|
||||
|
||||
<!-- search panel -->
|
||||
<div id="search-panel" class="float-panel float-panel-closed search-panel absolute md:w-[30rem]
|
||||
top-20 left-4 md:left-[unset] right-4 shadow-2xl rounded-2xl p-2">
|
||||
|
||||
<!-- search bar inside panel for phone/tablet -->
|
||||
<div id="search-bar-inside" class="flex relative lg:hidden transition-all items-center h-11 rounded-xl
|
||||
bg-black/[0.04] hover:bg-black/[0.06] focus-within:bg-black/[0.06]
|
||||
dark:bg-white/5 dark:hover:bg-white/10 dark:focus-within:bg-white/10
|
||||
">
|
||||
<slot name="search-icon"></slot>
|
||||
<input placeholder="Search" bind:value={keywordMobile}
|
||||
class="pl-10 absolute inset-0 text-sm bg-transparent outline-0
|
||||
focus:w-60 text-black/50 dark:text-white/50"
|
||||
>
|
||||
</div>
|
||||
|
||||
<!-- search results -->
|
||||
{#each result as item}
|
||||
<a href={item.url}
|
||||
class="transition first-of-type:mt-2 lg:first-of-type:mt-0 group block
|
||||
rounded-xl text-lg px-3 py-2 hover:bg-[var(--btn-plain-bg-hover)] active:bg-[var(--btn-plain-bg-active)]">
|
||||
<div class="transition text-90 inline-flex font-bold group-hover:text-[var(--primary)]">
|
||||
{item.meta.title}<slot name="arrow-icon"></slot>
|
||||
</div>
|
||||
<div class="transition text-sm text-50">
|
||||
{@html item.excerpt}
|
||||
</div>
|
||||
</a>
|
||||
{/each}
|
||||
</div>
|
57
src/components/control/BackToTop.astro
Normal file
|
@ -0,0 +1,57 @@
|
|||
---
|
||||
import { Icon } from 'astro-icon/components';
|
||||
---
|
||||
|
||||
<!-- There can't be a filter on parent element, or it will break `fixed` -->
|
||||
<div class="back-to-top-wrapper hidden lg:block">
|
||||
<div id="back-to-top-btn" class="back-to-top-btn hide flex items-center rounded-2xl overflow-hidden transition" onclick="backToTop()">
|
||||
<button aria-label="Back to Top" class="btn-card h-[3.75rem] w-[3.75rem]">
|
||||
<Icon name="material-symbols:keyboard-arrow-up-rounded" class="mx-auto"></Icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="stylus">
|
||||
.back-to-top-wrapper
|
||||
width: 3.75rem
|
||||
height: 3.75rem
|
||||
position: absolute
|
||||
right: 0
|
||||
top: 0
|
||||
|
||||
.back-to-top-btn
|
||||
color: var(--primary)
|
||||
font-size: 2.25rem
|
||||
font-weight: bold
|
||||
border: none
|
||||
position: fixed
|
||||
bottom: 15rem
|
||||
opacity: 1
|
||||
cursor: pointer
|
||||
transform: translateX(5rem)
|
||||
i
|
||||
font-size: 1.75rem
|
||||
&.hide
|
||||
transform: translateX(5rem) scale(0.9)
|
||||
opacity: 0
|
||||
pointer-events: none
|
||||
&:active
|
||||
transform: translateX(5rem) scale(0.9)
|
||||
|
||||
</style>
|
||||
|
||||
<script is:raw>
|
||||
function backToTop() {
|
||||
window.scroll({ top: 0, behavior: 'smooth' });
|
||||
}
|
||||
|
||||
function scrollFunction() {
|
||||
let btn = document.getElementById('back-to-top-btn');
|
||||
if (document.body.scrollTop > 600 || document.documentElement.scrollTop > 600) {
|
||||
btn.classList.remove('hide')
|
||||
} else {
|
||||
btn.classList.add('hide')
|
||||
}
|
||||
}
|
||||
window.onscroll = scrollFunction
|
||||
</script>
|
43
src/components/control/ButtonLink.astro
Normal file
|
@ -0,0 +1,43 @@
|
|||
---
|
||||
interface Props {
|
||||
badge?: string
|
||||
url?: string
|
||||
label?: string
|
||||
}
|
||||
const { badge, url, name } = Astro.props
|
||||
---
|
||||
<a href={url} aria-label={name}>
|
||||
<button
|
||||
class:list={`
|
||||
w-full
|
||||
h-10
|
||||
rounded-lg
|
||||
bg-none
|
||||
hover:bg-[var(--btn-plain-bg-hover)]
|
||||
active:bg-[var(--btn-plain-bg-active)]
|
||||
transition-all
|
||||
pl-2
|
||||
hover:pl-3
|
||||
|
||||
text-neutral-700
|
||||
hover:text-[var(--primary)]
|
||||
dark:text-neutral-300
|
||||
dark:hover:text-[var(--primary)]
|
||||
`
|
||||
}
|
||||
>
|
||||
<div class="flex items-center justify-between relative mr-2">
|
||||
<div class="overflow-hidden text-left whitespace-nowrap overflow-ellipsis ">
|
||||
<slot></slot>
|
||||
</div>
|
||||
{ badge !== undefined && badge !== null && badge !== '' &&
|
||||
<div class="transition h-7 ml-4 min-w-[2rem] rounded-lg text-sm font-bold
|
||||
text-[var(--btn-content)] dark:text-[var(--deep-text)]
|
||||
bg-[oklch(0.95_0.025_var(--hue))] dark:bg-[var(--primary)]
|
||||
flex items-center justify-center">
|
||||
{ badge }
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</button>
|
||||
</a>
|
13
src/components/control/ButtonTag.astro
Normal file
|
@ -0,0 +1,13 @@
|
|||
---
|
||||
interface Props {
|
||||
size?: string;
|
||||
dot?: boolean;
|
||||
href?: string;
|
||||
label?: string;
|
||||
}
|
||||
const { size, dot, href, label }: Props = Astro.props;
|
||||
---
|
||||
<a href={href} aria-label={label} class="btn-regular h-8 text-sm px-3 rounded-lg">
|
||||
{dot && <div class="h-1 w-1 bg-[var(--btn-content)] dark:bg-[var(--card-bg)] transition rounded-md mr-2"></div>}
|
||||
<slot></slot>
|
||||
</a>
|
91
src/components/control/Pagination.astro
Normal file
|
@ -0,0 +1,91 @@
|
|||
---
|
||||
import type { Page } from "astro";
|
||||
import { Icon } from 'astro-icon/components';
|
||||
import {url} from "../../utils/url-utils";
|
||||
interface Props {
|
||||
page: Page;
|
||||
class?: string;
|
||||
style?: string;
|
||||
}
|
||||
|
||||
const {page, style} = Astro.props;
|
||||
|
||||
const HIDDEN = -1;
|
||||
|
||||
const className = Astro.props.class;
|
||||
|
||||
const ADJ_DIST = 2;
|
||||
const VISIBLE = ADJ_DIST * 2 + 1;
|
||||
|
||||
// for test
|
||||
let count = 1;
|
||||
let l = page.currentPage, r = page.currentPage;
|
||||
while (0 < l - 1 && r + 1 <= page.lastPage && count + 2 <= VISIBLE) {
|
||||
count += 2;
|
||||
l--;
|
||||
r++;
|
||||
}
|
||||
while (0 < l - 1 && count < VISIBLE) {
|
||||
count++;
|
||||
l--;
|
||||
}
|
||||
while (r + 1 <= page.lastPage && count < VISIBLE) {
|
||||
count++;
|
||||
r++;
|
||||
}
|
||||
|
||||
let pages: number[] = [];
|
||||
if (l > 1)
|
||||
pages.push(1);
|
||||
if (l == 3)
|
||||
pages.push(2);
|
||||
if (l > 3)
|
||||
pages.push(HIDDEN);
|
||||
for (let i = l; i <= r; i++)
|
||||
pages.push(i);
|
||||
if (r < page.lastPage - 2)
|
||||
pages.push(HIDDEN);
|
||||
if (r == page.lastPage - 2)
|
||||
pages.push(page.lastPage - 1);
|
||||
if (r < page.lastPage)
|
||||
pages.push(page.lastPage);
|
||||
|
||||
const getPageUrl = (p: number) => {
|
||||
if (p == 1)
|
||||
return '/';
|
||||
return `/${p}/`;
|
||||
}
|
||||
|
||||
---
|
||||
|
||||
<div class:list={[className, "flex flex-row gap-3 justify-center"]} style={style}>
|
||||
<a href={url(page.url.prev)} aria-label={page.url.prev ? "Previous Page" : null}
|
||||
class:list={["btn-card overflow-hidden rounded-lg text-[var(--primary)] w-11 h-11",
|
||||
{"disabled": page.url.prev == undefined}
|
||||
]}
|
||||
>
|
||||
<Icon name="material-symbols:chevron-left-rounded" size="1.75rem"></Icon>
|
||||
</a>
|
||||
<div class="bg-[var(--card-bg)] flex flex-row rounded-lg items-center text-neutral-700 dark:text-neutral-300 font-bold">
|
||||
{pages.map((p) => {
|
||||
if (p == HIDDEN)
|
||||
return <Icon name="material-symbols:more-horiz" class="mx-1"/>;
|
||||
if (p == page.currentPage)
|
||||
return <div class="h-11 w-11 rounded-lg bg-[var(--primary)] flex items-center justify-center
|
||||
font-bold text-white dark:text-black/70"
|
||||
>
|
||||
{p}
|
||||
</div>
|
||||
return <a href={url(getPageUrl(p))} aria-label=`Page ${p}`
|
||||
class="btn-card w-11 h-11 rounded-lg overflow-hidden active:scale-[0.85]"
|
||||
>{p}</a>
|
||||
})}
|
||||
</div>
|
||||
<a href={url(page.url.next)} aria-label={page.url.next ? "Next Page" : null}
|
||||
class:list={["btn-card overflow-hidden rounded-lg text-[var(--primary)] w-11 h-11",
|
||||
{"disabled": page.url.next == undefined}
|
||||
]}
|
||||
>
|
||||
<Icon name="material-symbols:chevron-right-rounded" size="1.75rem"></Icon>
|
||||
</a>
|
||||
</div>
|
37
src/components/misc/ImageWrapper.astro
Normal file
|
@ -0,0 +1,37 @@
|
|||
---
|
||||
import path from "path";
|
||||
interface Props {
|
||||
id?: string
|
||||
src: string;
|
||||
class?: string;
|
||||
alt?: string
|
||||
position?: string;
|
||||
basePath?: string
|
||||
}
|
||||
import { Image } from 'astro:assets';
|
||||
import { url } from "../../utils/url-utils";
|
||||
|
||||
const {id, src, alt, position = 'center', basePath = '/'} = Astro.props;
|
||||
const className = Astro.props.class;
|
||||
|
||||
const isLocal = !(src.startsWith('/') || src.startsWith('http') || src.startsWith('https') || src.startsWith('data:'));
|
||||
const isPublic = src.startsWith('/');
|
||||
|
||||
// TODO temporary workaround for images dynamic import
|
||||
// https://github.com/withastro/astro/issues/3373
|
||||
let img;
|
||||
if (isLocal) {
|
||||
const files = import.meta.glob<ImageMetadata>("../../**", { import: 'default' });
|
||||
let normalizedPath = path.normalize(path.join("../../", basePath, src)).replace(/\\/g, "/");
|
||||
img = await (files[normalizedPath])();
|
||||
}
|
||||
|
||||
const imageClass = 'w-full h-full object-cover';
|
||||
const imageStyle = `object-position: ${position}`
|
||||
---
|
||||
<div class:list={[className, 'overflow-hidden relative']}>
|
||||
<div class="transition absolute inset-0 dark:bg-black/10 bg-opacity-50 pointer-events-none"></div>
|
||||
{isLocal && <Image src={img} alt={alt || ""} class={imageClass} style={imageStyle}/>}
|
||||
{!isLocal && <img src={isPublic ? url(src) : src} alt={alt || ""} class={imageClass} style={imageStyle}/>}
|
||||
</div>
|
||||
|
44
src/components/misc/License.astro
Normal file
|
@ -0,0 +1,44 @@
|
|||
---
|
||||
import {formatDateToYYYYMMDD} from "../../utils/date-utils";
|
||||
import { Icon } from 'astro-icon/components';
|
||||
import {licenseConfig, profileConfig} from "../../config";
|
||||
import {i18n} from "../../i18n/translation";
|
||||
import I18nKey from "../../i18n/i18nKey";
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
slug: string;
|
||||
pubDate: Date;
|
||||
class: string;
|
||||
}
|
||||
|
||||
const { title, slug, pubDate } = Astro.props;
|
||||
const className = Astro.props.class;
|
||||
const profileConf = profileConfig;
|
||||
const licenseConf = licenseConfig;
|
||||
const postUrl = decodeURIComponent(Astro.url.toString());
|
||||
|
||||
---
|
||||
<div class=`relative transition overflow-hidden bg-[var(--license-block-bg)] py-5 px-6 ${className}`>
|
||||
<div class="transition font-bold text-black/75 dark:text-white/75">
|
||||
{title}
|
||||
</div>
|
||||
<a href={postUrl} class="link text-[var(--primary)]">
|
||||
{postUrl}
|
||||
</a>
|
||||
<div class="flex gap-6 mt-2">
|
||||
<div>
|
||||
<div class="transition text-black/30 dark:text-white/30 text-sm">{i18n(I18nKey.author)}</div>
|
||||
<div class="transition text-black/75 dark:text-white/75 whitespace-nowrap">{profileConf.name}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="transition text-black/30 dark:text-white/30 text-sm">{i18n(I18nKey.publishedAt)}</div>
|
||||
<div class="transition text-black/75 dark:text-white/75 whitespace-nowrap">{formatDateToYYYYMMDD(pubDate)}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="transition text-black/30 dark:text-white/30 text-sm">{i18n(I18nKey.license)}</div>
|
||||
<a href={licenseConf.url} target="_blank" class="link text-[var(--primary)] whitespace-nowrap">{licenseConf.name}</a>
|
||||
</div>
|
||||
</div>
|
||||
<Icon name="fa6-brands:creative-commons" class="transition absolute pointer-events-none right-6 top-1/2 -translate-y-1/2 text-black/5 dark:text-white/5" size="240"></Icon>
|
||||
</div>
|
491
src/components/misc/Markdown.astro
Normal file
|
@ -0,0 +1,491 @@
|
|||
---
|
||||
import '@fontsource-variable/jetbrains-mono';
|
||||
import '@fontsource-variable/jetbrains-mono/wght-italic.css';
|
||||
|
||||
interface Props {
|
||||
class: string;
|
||||
}
|
||||
const className = Astro.props.class;
|
||||
---
|
||||
<div data-pagefind-body class=`prose dark:prose-invert prose-base max-w-none custom-md ${className}`>
|
||||
<!--<div class="prose dark:prose-invert max-w-none custom-md">-->
|
||||
<!--<div class="max-w-none custom-md">-->
|
||||
<slot/>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const observer = new MutationObserver(addPreCopyButton);
|
||||
observer.observe(document.body, { childList: true, subtree: true });
|
||||
|
||||
document.addEventListener("DOMContentLoaded", addPreCopyButton);
|
||||
|
||||
function addPreCopyButton() {
|
||||
observer.disconnect();
|
||||
|
||||
let codeBlocks = Array.from(document.querySelectorAll("pre"));
|
||||
|
||||
for (let codeBlock of codeBlocks) {
|
||||
if (codeBlock.parentElement?.nodeName === "DIV" && codeBlock.parentElement?.classList.contains("code-block")) continue
|
||||
|
||||
let wrapper = document.createElement("div");
|
||||
wrapper.className = "relative code-block";
|
||||
|
||||
let copyButton = document.createElement("button");
|
||||
copyButton.className = "copy-btn btn-regular-dark absolute active:scale-90 h-8 w-8 top-2 right-2 opacity-75 text-sm p-1.5 rounded-lg transition-all ease-in-out";
|
||||
|
||||
codeBlock.setAttribute("tabindex", "0");
|
||||
if (codeBlock.parentNode) {
|
||||
codeBlock.parentNode.insertBefore(wrapper, codeBlock);
|
||||
}
|
||||
|
||||
let copyIcon = `<svg class="copy-btn-icon copy-icon" xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 -960 960 960" width="20px"><path d="M368.37-237.37q-34.48 0-58.74-24.26-24.26-24.26-24.26-58.74v-474.26q0-34.48 24.26-58.74 24.26-24.26 58.74-24.26h378.26q34.48 0 58.74 24.26 24.26 24.26 24.26 58.74v474.26q0 34.48-24.26 58.74-24.26 24.26-58.74 24.26H368.37Zm0-83h378.26v-474.26H368.37v474.26Zm-155 238q-34.48 0-58.74-24.26-24.26-24.26-24.26-58.74v-515.76q0-17.45 11.96-29.48 11.97-12.02 29.33-12.02t29.54 12.02q12.17 12.03 12.17 29.48v515.76h419.76q17.45 0 29.48 11.96 12.02 11.97 12.02 29.33t-12.02 29.54q-12.03 12.17-29.48 12.17H213.37Zm155-238v-474.26 474.26Z"/></svg>`
|
||||
let successIcon = `<svg class="copy-btn-icon success-icon" xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 -960 960 960" width="20px"><path d="m389-377.13 294.7-294.7q12.58-12.67 29.52-12.67 16.93 0 29.61 12.67 12.67 12.68 12.67 29.53 0 16.86-12.28 29.14L419.07-288.41q-12.59 12.67-29.52 12.67-16.94 0-29.62-12.67L217.41-430.93q-12.67-12.68-12.79-29.45-.12-16.77 12.55-29.45 12.68-12.67 29.62-12.67 16.93 0 29.28 12.67L389-377.13Z"/></svg>`
|
||||
copyButton.innerHTML = `<div>${copyIcon} ${successIcon}</div>
|
||||
`
|
||||
|
||||
wrapper.appendChild(codeBlock);
|
||||
wrapper.appendChild(copyButton);
|
||||
|
||||
let timeout;
|
||||
copyButton.addEventListener("click", async () => {
|
||||
if (timeout) {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
let text = codeBlock?.querySelector("code")?.innerText;
|
||||
await navigator.clipboard.writeText(text);
|
||||
copyButton.classList.add("success");
|
||||
timeout = setTimeout(() => {
|
||||
copyButton.classList.remove("success");
|
||||
}, 1000);
|
||||
});
|
||||
}
|
||||
|
||||
observer.observe(document.body, { childList: true, subtree: true });
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Styles for copy-code-button -->
|
||||
<style lang="css" is:global>
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer components {
|
||||
.btn-regular-dark {
|
||||
@apply flex items-center justify-center
|
||||
bg-[oklch(0.45_0.01_var(--hue))] hover:bg-[oklch(0.50_0.01_var(--hue))] active:bg-[oklch(0.55_0.01_var(--hue))]
|
||||
dark:bg-[oklch(0.30_0.02_var(--hue))] dark:hover:bg-[oklch(0.35_0.03_var(--hue))] dark:active:bg-[oklch(0.40_0.03_var(--hue))]
|
||||
}
|
||||
.btn-regular-dark.success {
|
||||
@apply bg-[oklch(0.75_0.14_var(--hue))] dark:bg-[oklch(0.75_0.14_var(--hue))]
|
||||
}
|
||||
|
||||
.copy-btn-icon {
|
||||
@apply absolute top-1/2 left-1/2 transition -translate-x-1/2 -translate-y-1/2
|
||||
}
|
||||
.copy-btn .copy-icon {
|
||||
@apply opacity-100 fill-white dark:fill-white/75
|
||||
}
|
||||
.copy-btn.success .copy-icon {
|
||||
@apply opacity-0 fill-[var(--deep-text)]
|
||||
}
|
||||
.copy-btn .success-icon {
|
||||
@apply opacity-0
|
||||
}
|
||||
.copy-btn.success .success-icon {
|
||||
@apply opacity-100
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="stylus" is:global>
|
||||
.custom-md
|
||||
h1, h2, h3, h4, h5, h6
|
||||
.anchor
|
||||
margin: -0.125rem !important
|
||||
margin-left: 0.2ch !important
|
||||
padding: 0.125rem !important
|
||||
user-select: none !important
|
||||
opacity: 0 !important
|
||||
text-decoration: none !important
|
||||
transition: opacity 0.15s ease-in-out, background 0.15s ease-in-out !important
|
||||
|
||||
.anchor-icon
|
||||
margin-left: 0.45ch !important
|
||||
margin-right: 0.45ch !important
|
||||
|
||||
&:hover
|
||||
.anchor
|
||||
opacity: 1 !important
|
||||
|
||||
a:not(.no-styling)
|
||||
position: relative
|
||||
background: none
|
||||
margin: -0.25rem
|
||||
padding: 0.25rem
|
||||
border-radius: 0.375rem
|
||||
font-weight: 500
|
||||
color: var(--primary)
|
||||
text-decoration-line: underline
|
||||
text-decoration-color: var(--link-underline)
|
||||
text-decoration-thickness: 0.125rem
|
||||
text-decoration-style: dashed
|
||||
text-underline-offset: 0.25rem
|
||||
/*&:after*/
|
||||
/* content: ''*/
|
||||
/* position: absolute*/
|
||||
/* left: 2px*/
|
||||
/* right: 2px*/
|
||||
/* bottom: 4px*/
|
||||
/* height: 6px*/
|
||||
/* border-radius: 3px*/
|
||||
/* background: var(--link-hover)*/
|
||||
/* transition: background 0.15s ease-in-out;*/
|
||||
/* z-index: -1;*/
|
||||
|
||||
&:hover
|
||||
background: var(--link-hover)
|
||||
text-decoration-color: var(--link-hover)
|
||||
|
||||
&:active
|
||||
background: var(--link-active)
|
||||
text-decoration-color: var(--link-active)
|
||||
|
||||
code
|
||||
font-family: 'JetBrains Mono Variable', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace
|
||||
background: var(--inline-code-bg)
|
||||
color: var(--inline-code-color)
|
||||
padding: 0.125rem 0.25rem
|
||||
border-radius: 0.25rem
|
||||
overflow: hidden
|
||||
counter-reset: line
|
||||
|
||||
&:before
|
||||
content: none
|
||||
|
||||
&:after
|
||||
content: none
|
||||
|
||||
span.line
|
||||
&:before
|
||||
content: counter(line)
|
||||
counter-increment: line
|
||||
direction: rtl
|
||||
display: inline-block
|
||||
margin-right: 1rem
|
||||
width: 1rem
|
||||
color: rgba(255, 255, 255, 0.25)
|
||||
&:last-child:empty, &:last-child:has(> span:empty:only-child)
|
||||
display: none
|
||||
|
||||
pre
|
||||
background: var(--codeblock-bg) !important
|
||||
border-radius: 0.75rem
|
||||
padding-left: 1.25rem
|
||||
padding-right: 1.25rem
|
||||
|
||||
code
|
||||
color: unset
|
||||
font-size: 0.875rem
|
||||
padding: 0
|
||||
background: none
|
||||
|
||||
::selection
|
||||
background: var(--codeblock-selection)
|
||||
|
||||
span.br::selection
|
||||
background: var(--codeblock-selection)
|
||||
|
||||
ul
|
||||
li
|
||||
&::marker
|
||||
color: var(--primary)
|
||||
|
||||
ol
|
||||
li
|
||||
&::marker
|
||||
color: var(--primary)
|
||||
|
||||
blockquote
|
||||
font-style: normal
|
||||
font-weight: inherit
|
||||
border-left-color: rgba(0, 0, 0, 0)
|
||||
position: relative;
|
||||
|
||||
&:before
|
||||
content: ''
|
||||
position: absolute
|
||||
left: -0.25rem
|
||||
display: block
|
||||
transition: background 0.15s ease-in-out;
|
||||
background: var(--btn-regular-bg)
|
||||
height: 100%
|
||||
width: 0.25rem
|
||||
border-radius: 1rem
|
||||
|
||||
p
|
||||
&:before
|
||||
content: none
|
||||
|
||||
&:after
|
||||
content: none
|
||||
|
||||
blockquote.admonition
|
||||
.bdm-title
|
||||
display: flex
|
||||
align-items: center
|
||||
margin-bottom: -.9rem
|
||||
font-weight: bold
|
||||
|
||||
&:before
|
||||
content: ' '
|
||||
display: inline-block
|
||||
font-size: inherit
|
||||
overflow: visible
|
||||
margin-right: .6rem
|
||||
height: 1em
|
||||
width: 1em
|
||||
vertical-align: -.126em
|
||||
mask-size: contain
|
||||
mask-position: center
|
||||
mask-repeat: no-repeat
|
||||
transform: translateY(-0.0625rem)
|
||||
&.bdm-tip
|
||||
.bdm-title
|
||||
color: var(--admonitions-color-tip)
|
||||
|
||||
&:before
|
||||
background: var(--admonitions-color-tip)
|
||||
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' version='1.1' width='16' height='16' aria-hidden='true'%3E%3Cpath d='M8 1.5c-2.363 0-4 1.69-4 3.75 0 .984.424 1.625.984 2.304l.214.253c.223.264.47.556.673.848.284.411.537.896.621 1.49a.75.75 0 0 1-1.484.211c-.04-.282-.163-.547-.37-.847a8.456 8.456 0 0 0-.542-.68c-.084-.1-.173-.205-.268-.32C3.201 7.75 2.5 6.766 2.5 5.25 2.5 2.31 4.863 0 8 0s5.5 2.31 5.5 5.25c0 1.516-.701 2.5-1.328 3.259-.095.115-.184.22-.268.319-.207.245-.383.453-.541.681-.208.3-.33.565-.37.847a.751.751 0 0 1-1.485-.212c.084-.593.337-1.078.621-1.489.203-.292.45-.584.673-.848.075-.088.147-.173.213-.253.561-.679.985-1.32.985-2.304 0-2.06-1.637-3.75-4-3.75ZM5.75 12h4.5a.75.75 0 0 1 0 1.5h-4.5a.75.75 0 0 1 0-1.5ZM6 15.25a.75.75 0 0 1 .75-.75h2.5a.75.75 0 0 1 0 1.5h-2.5a.75.75 0 0 1-.75-.75Z'%3E%3C/path%3E%3C/svg%3E")
|
||||
|
||||
&:before
|
||||
background: var(--admonitions-color-tip)
|
||||
&.bdm-note
|
||||
.bdm-title
|
||||
color: var(--admonitions-color-note)
|
||||
|
||||
&:before
|
||||
background: var(--admonitions-color-note)
|
||||
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' version='1.1' width='16' height='16' aria-hidden='true'%3E%3Cpath fill='var(--admonitions-color-tip)' d='M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z'%3E%3C/path%3E%3C/svg%3E")
|
||||
|
||||
&:before
|
||||
background: var(--admonitions-color-note)
|
||||
&.bdm-important
|
||||
.bdm-title
|
||||
color: var(--admonitions-color-important)
|
||||
|
||||
&:before
|
||||
background: var(--admonitions-color-important)
|
||||
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' version='1.1' width='16' height='16' aria-hidden='true'%3E%3Cpath d='M0 1.75C0 .784.784 0 1.75 0h12.5C15.216 0 16 .784 16 1.75v9.5A1.75 1.75 0 0 1 14.25 13H8.06l-2.573 2.573A1.458 1.458 0 0 1 3 14.543V13H1.75A1.75 1.75 0 0 1 0 11.25Zm1.75-.25a.25.25 0 0 0-.25.25v9.5c0 .138.112.25.25.25h2a.75.75 0 0 1 .75.75v2.19l2.72-2.72a.749.749 0 0 1 .53-.22h6.5a.25.25 0 0 0 .25-.25v-9.5a.25.25 0 0 0-.25-.25Zm7 2.25v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 9a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z'%3E%3C/path%3E%3C/svg%3E")
|
||||
|
||||
&:before
|
||||
background: var(--admonitions-color-important)
|
||||
&.bdm-warning
|
||||
.bdm-title
|
||||
color: var(--admonitions-color-warning)
|
||||
|
||||
&:before
|
||||
background: var(--admonitions-color-warning)
|
||||
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' version='1.1' width='16' height='16' aria-hidden='true'%3E%3Cpath d='M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z'%3E%3C/path%3E%3C/svg%3E")
|
||||
|
||||
&:before
|
||||
background: var(--admonitions-color-warning)
|
||||
&.bdm-caution
|
||||
.bdm-title
|
||||
color: var(--admonitions-color-caution)
|
||||
|
||||
&:before
|
||||
background: var(--admonitions-color-caution)
|
||||
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' version='1.1' width='16' height='16' aria-hidden='true'%3E%3Cpath d='M4.47.22A.749.749 0 0 1 5 0h6c.199 0 .389.079.53.22l4.25 4.25c.141.14.22.331.22.53v6a.749.749 0 0 1-.22.53l-4.25 4.25A.749.749 0 0 1 11 16H5a.749.749 0 0 1-.53-.22L.22 11.53A.749.749 0 0 1 0 11V5c0-.199.079-.389.22-.53Zm.84 1.28L1.5 5.31v5.38l3.81 3.81h5.38l3.81-3.81V5.31L10.69 1.5ZM8 4a.75.75 0 0 1 .75.75v3.5a.75.75 0 0 1-1.5 0v-3.5A.75.75 0 0 1 8 4Zm0 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z'%3E%3C/path%3E%3C/svg%3E")
|
||||
|
||||
&:before
|
||||
background: var(--admonitions-color-caution)
|
||||
|
||||
img
|
||||
border-radius: 0.75rem
|
||||
|
||||
hr
|
||||
border-color: var(--line-divider)
|
||||
border-style: dashed
|
||||
|
||||
iframe
|
||||
border-radius: 0.75rem
|
||||
margin-left: auto
|
||||
margin-right: auto
|
||||
max-width: 100%
|
||||
|
||||
a.card-github
|
||||
display: block
|
||||
background: var(--license-block-bg)
|
||||
position: relative
|
||||
margin: 0.5rem 0
|
||||
padding: 1.1rem 1.5rem 1.1rem 1.5rem
|
||||
color: var(--tw-prose-body)
|
||||
border-radius: var(--radius-large)
|
||||
text-decoration-thickness: 0px
|
||||
text-decoration-line: none
|
||||
|
||||
&:hover
|
||||
background-color: var(--btn-regular-bg-hover)
|
||||
|
||||
.gc-titlebar
|
||||
color: var(--btn-content)
|
||||
|
||||
.gc-stars, .gc-forks, .gc-license, .gc-description
|
||||
color: var(--tw-prose-headings)
|
||||
|
||||
&:before
|
||||
background-color: var(--tw-prose-headings)
|
||||
|
||||
&:active
|
||||
scale: .98
|
||||
background-color: var(--btn-regular-bg-active);
|
||||
|
||||
.gc-titlebar
|
||||
display: flex
|
||||
align-items: center
|
||||
justify-content: space-between
|
||||
margin-bottom: 0.5rem
|
||||
color: var(--tw-prose-headings)
|
||||
font-size: 1.25rem
|
||||
font-weight: 500
|
||||
|
||||
.gc-titlebar-left
|
||||
display: flex
|
||||
flex-flow: row nowrap
|
||||
gap: 0.5rem
|
||||
|
||||
.gc-repo
|
||||
font-weight: bold
|
||||
|
||||
.gc-owner
|
||||
font-weight: 300
|
||||
position: relative
|
||||
display: flex
|
||||
flex-flow: row nowrap
|
||||
gap: 0.5rem
|
||||
align-items: center
|
||||
|
||||
.gc-avatar
|
||||
display: block
|
||||
overflow: hidden
|
||||
width: 1.5rem
|
||||
height: 1.5rem
|
||||
margin-top: -0.1rem
|
||||
background-color: var(--primary)
|
||||
background-size: cover
|
||||
border-radius: 50%
|
||||
|
||||
.gc-description
|
||||
margin-bottom: 0.7rem
|
||||
font-size: 1rem
|
||||
font-weight: 300
|
||||
line-height: 1.5rem
|
||||
color: var(--tw-prose-body)
|
||||
|
||||
.gc-infobar
|
||||
display: flex
|
||||
flex-flow: row nowrap
|
||||
gap: 1.5rem
|
||||
color: var(--tw-prose-body)
|
||||
width: fit-content
|
||||
|
||||
.gc-language
|
||||
display: none
|
||||
|
||||
.gc-stars, .gc-forks, .gc-license, .github-logo
|
||||
font-weight: 500
|
||||
font-size: 0.875rem
|
||||
opacity: 0.9;
|
||||
|
||||
&:before
|
||||
content: ' '
|
||||
display: inline-block
|
||||
height: 1.3em
|
||||
width: 1.3em
|
||||
margin-right: .4rem
|
||||
vertical-align: -.24em
|
||||
font-size: inherit
|
||||
background-color: var(--tw-prose-body)
|
||||
overflow: visible
|
||||
mask-size: contain
|
||||
mask-position: center
|
||||
mask-repeat: no-repeat
|
||||
transition-property: background-color, background;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1)
|
||||
transition-duration: 0.15s
|
||||
|
||||
.gc-stars
|
||||
&:before
|
||||
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' aria-hidden='true' height='16' viewBox='0 0 16 16' version='1.1' width='16'%3E%3Cpath d='M8 .25a.75.75 0 0 1 .673.418l1.882 3.815 4.21.612a.75.75 0 0 1 .416 1.279l-3.046 2.97.719 4.192a.751.751 0 0 1-1.088.791L8 12.347l-3.766 1.98a.75.75 0 0 1-1.088-.79l.72-4.194L.818 6.374a.75.75 0 0 1 .416-1.28l4.21-.611L7.327.668A.75.75 0 0 1 8 .25Zm0 2.445L6.615 5.5a.75.75 0 0 1-.564.41l-3.097.45 2.24 2.184a.75.75 0 0 1 .216.664l-.528 3.084 2.769-1.456a.75.75 0 0 1 .698 0l2.77 1.456-.53-3.084a.75.75 0 0 1 .216-.664l2.24-2.183-3.096-.45a.75.75 0 0 1-.564-.41L8 2.694Z'%3E%3C/path%3E%3C/svg%3E")
|
||||
|
||||
.gc-license
|
||||
&:before
|
||||
margin-right: .5rem
|
||||
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' aria-hidden='true' height='16' viewBox='0 0 16 16' version='1.1' width='16'%3E%3Cpath d='M8.75.75V2h.985c.304 0 .603.08.867.231l1.29.736c.038.022.08.033.124.033h2.234a.75.75 0 0 1 0 1.5h-.427l2.111 4.692a.75.75 0 0 1-.154.838l-.53-.53.529.531-.001.002-.002.002-.006.006-.006.005-.01.01-.045.04c-.21.176-.441.327-.686.45C14.556 10.78 13.88 11 13 11a4.498 4.498 0 0 1-2.023-.454 3.544 3.544 0 0 1-.686-.45l-.045-.04-.016-.015-.006-.006-.004-.004v-.001a.75.75 0 0 1-.154-.838L12.178 4.5h-.162c-.305 0-.604-.079-.868-.231l-1.29-.736a.245.245 0 0 0-.124-.033H8.75V13h2.5a.75.75 0 0 1 0 1.5h-6.5a.75.75 0 0 1 0-1.5h2.5V3.5h-.984a.245.245 0 0 0-.124.033l-1.289.737c-.265.15-.564.23-.869.23h-.162l2.112 4.692a.75.75 0 0 1-.154.838l-.53-.53.529.531-.001.002-.002.002-.006.006-.016.015-.045.04c-.21.176-.441.327-.686.45C4.556 10.78 3.88 11 3 11a4.498 4.498 0 0 1-2.023-.454 3.544 3.544 0 0 1-.686-.45l-.045-.04-.016-.015-.006-.006-.004-.004v-.001a.75.75 0 0 1-.154-.838L2.178 4.5H1.75a.75.75 0 0 1 0-1.5h2.234a.249.249 0 0 0 .125-.033l1.288-.737c.265-.15.564-.23.869-.23h.984V.75a.75.75 0 0 1 1.5 0Zm2.945 8.477c.285.135.718.273 1.305.273s1.02-.138 1.305-.273L13 6.327Zm-10 0c.285.135.718.273 1.305.273s1.02-.138 1.305-.273L3 6.327Z'%3E%3C/path%3E%3C/svg%3E")
|
||||
|
||||
.gc-forks
|
||||
&:before
|
||||
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' aria-hidden='true' height='16' viewBox='0 0 16 16' version='1.1' width='16'%3E%3Cpath d='M5 5.372v.878c0 .414.336.75.75.75h4.5a.75.75 0 0 0 .75-.75v-.878a2.25 2.25 0 1 1 1.5 0v.878a2.25 2.25 0 0 1-2.25 2.25h-1.5v2.128a2.251 2.251 0 1 1-1.5 0V8.5h-1.5A2.25 2.25 0 0 1 3.5 6.25v-.878a2.25 2.25 0 1 1 1.5 0ZM5 3.25a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0Zm6.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Zm-3 8.75a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0Z'%3E%3C/path%3E%3C/svg%3E")
|
||||
|
||||
.github-logo
|
||||
font-size: 1.25rem
|
||||
|
||||
&:before
|
||||
background-color: var(--tw-prose-headings)
|
||||
margin-right: 0
|
||||
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='31' height='32' viewBox='0 0 496 512'%3E%3Cpath fill='%23a1f7cb' d='M165.9 397.4c0 2-2.3 3.6-5.2 3.6c-3.3.3-5.6-1.3-5.6-3.6c0-2 2.3-3.6 5.2-3.6c3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9c2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9c.3 2 2.9 3.3 5.9 2.6c2.9-.7 4.9-2.6 4.6-4.6c-.3-1.9-3-3.2-5.9-2.9M244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2c12.8 2.3 17.3-5.6 17.3-12.1c0-6.2-.3-40.4-.3-61.4c0 0-70 15-84.7-29.8c0 0-11.4-29.1-27.8-36.6c0 0-22.9-15.7 1.6-15.4c0 0 24.9 2 38.6 25.8c21.9 38.6 58.6 27.5 72.9 20.9c2.3-16 8.8-27.1 16-33.7c-55.9-6.2-112.3-14.3-112.3-110.5c0-27.5 7.6-41.3 23.6-58.9c-2.6-6.5-11.1-33.3 2.6-67.9c20.9-6.5 69 27 69 27c20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27c13.7 34.7 5.2 61.4 2.6 67.9c16 17.7 25.8 31.5 25.8 58.9c0 96.5-58.9 104.2-114.8 110.5c9.2 7.9 17 22.9 17 46.4c0 33.7-.3 75.4-.3 83.6c0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252C496 113.3 383.5 8 244.8 8M97.2 352.9c-1.3 1-1 3.3.7 5.2c1.6 1.6 3.9 2.3 5.2 1c1.3-1 1-3.3-.7-5.2c-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9c1.6 1 3.6.7 4.3-.7c.7-1.3-.3-2.9-2.3-3.9c-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2c2.3 2.3 5.2 2.6 6.5 1c1.3-1.3.7-4.3-1.3-6.2c-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9c1.6 2.3 4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2c-1.4-2.3-4-3.3-5.6-2'/%3E%3C/svg%3E")
|
||||
|
||||
a.card-github.fetch-waiting
|
||||
pointer-events: none
|
||||
opacity: 0.7
|
||||
transition: opacity 0.15s ease-in-out
|
||||
|
||||
.gc-description, .gc-infobar
|
||||
background-color: var(--tw-prose-body)
|
||||
color: transparent
|
||||
opacity: 0.5;
|
||||
border-radius: 0.5rem
|
||||
animation: pulsate 2s infinite linear
|
||||
user-select: none
|
||||
|
||||
&:before
|
||||
background-color: transparent
|
||||
|
||||
.gc-avatar
|
||||
display: none
|
||||
|
||||
.gc-repo
|
||||
margin-left: -0.1rem
|
||||
|
||||
a.card-github.fetch-error
|
||||
pointer-events: all
|
||||
opacity: 1
|
||||
|
||||
@keyframes pulsate
|
||||
0%
|
||||
opacity: 0.15
|
||||
50%
|
||||
opacity: 0.25
|
||||
100%
|
||||
opacity: 0.15
|
||||
|
||||
.card-github, .gc-description, .gc-titlebar, .gc-stars, .gc-forks, .gc-license, .gc-avatar, github-logo
|
||||
transition-property: all
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1)
|
||||
transition-duration: 0.15s
|
||||
|
||||
</style>
|
||||
|
||||
<style lang="css" is:global>
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer components {
|
||||
.custom-md h1 {
|
||||
@apply text-3xl
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
38
src/components/widget/Categories.astro
Normal file
|
@ -0,0 +1,38 @@
|
|||
---
|
||||
import WidgetLayout from "./WidgetLayout.astro";
|
||||
|
||||
import {i18n} from "../../i18n/translation";
|
||||
import I18nKey from "../../i18n/i18nKey";
|
||||
import {Category, getCategoryList} from "../../utils/content-utils";
|
||||
import {getCategoryUrl} from "../../utils/url-utils";
|
||||
import ButtonLink from "../control/ButtonLink.astro";
|
||||
|
||||
const categories = await getCategoryList();
|
||||
|
||||
const COLLAPSED_HEIGHT = "7.5rem";
|
||||
const COLLAPSE_THRESHOLD = 5;
|
||||
|
||||
const isCollapsed = categories.length >= COLLAPSE_THRESHOLD;
|
||||
|
||||
interface Props {
|
||||
class?: string;
|
||||
style?: string;
|
||||
}
|
||||
const className = Astro.props.class
|
||||
const style = Astro.props.style
|
||||
|
||||
---
|
||||
|
||||
<WidgetLayout name={i18n(I18nKey.categories)} id="categories" isCollapsed={isCollapsed} collapsedHeight={COLLAPSED_HEIGHT}
|
||||
class={className} style={style}
|
||||
>
|
||||
{categories.map((c) =>
|
||||
<ButtonLink
|
||||
url={getCategoryUrl(c.name)}
|
||||
badge={c.count}
|
||||
label=`View all posts in the ${c.name} category`
|
||||
>
|
||||
{c.name}
|
||||
</ButtonLink>
|
||||
)}
|
||||
</WidgetLayout>
|
91
src/components/widget/DisplaySettings.svelte
Normal file
|
@ -0,0 +1,91 @@
|
|||
<script lang="ts">
|
||||
import {i18n} from '@i18n/translation';
|
||||
import I18nKey from '@i18n/i18nKey';
|
||||
import {getDefaultHue, getHue, setHue} from '@utils/setting-utils';
|
||||
|
||||
let hue = getHue()
|
||||
const defaultHue = getDefaultHue()
|
||||
|
||||
function resetHue() {
|
||||
hue = getDefaultHue()
|
||||
}
|
||||
|
||||
$: if (hue || hue === 0) {
|
||||
setHue(hue)
|
||||
}
|
||||
</script>
|
||||
|
||||
<div id="display-setting" class="float-panel float-panel-closed absolute transition-all w-80 right-4 px-4 py-4">
|
||||
<div class="flex flex-row gap-2 mb-3 items-center justify-between">
|
||||
<div class="flex gap-2 font-bold text-lg text-neutral-900 dark:text-neutral-100 transition relative ml-3
|
||||
before:w-1 before:h-4 before:rounded-md before:bg-[var(--primary)]
|
||||
before:absolute before:-left-3 before:top-[0.33rem]"
|
||||
>
|
||||
{i18n(I18nKey.themeColor)}
|
||||
<button aria-label="Reset to Default" class="btn-regular w-7 h-7 rounded-md active:scale-90"
|
||||
class:opacity-0={hue === defaultHue} class:pointer-events-none={hue === defaultHue} on:click={resetHue}>
|
||||
<div class="text-[var(--btn-content)]">
|
||||
<slot name="restore-icon"></slot>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex gap-1">
|
||||
<div id="hueValue" class="transition bg-[var(--btn-regular-bg)] w-10 h-7 rounded-md flex justify-center
|
||||
font-bold text-sm items-center text-[var(--btn-content)]">
|
||||
{hue}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full h-6 px-1 bg-[oklch(0.80_0.10_0)] dark:bg-[oklch(0.70_0.10_0)] rounded select-none">
|
||||
<input aria-label={i18n(I18nKey.themeColor)} type="range" min="0" max="360" bind:value={hue}
|
||||
class="slider" id="colorSlider" step="5" style="width: 100%;">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<style lang="stylus">
|
||||
#display-setting
|
||||
input[type="range"]
|
||||
-webkit-appearance: none;
|
||||
height: 1.5rem;
|
||||
background-image: var(--color-selection-bar)
|
||||
transition: background-image 0.15s ease-in-out
|
||||
|
||||
/* Input Thumb */
|
||||
::-webkit-slider-thumb
|
||||
-webkit-appearance: none;
|
||||
height: 1rem;
|
||||
width: 0.5rem;
|
||||
border-radius: 0.125rem;
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
box-shadow: none;
|
||||
&:hover
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
&:active
|
||||
background: rgba(255, 255, 255, 0.6);
|
||||
|
||||
::-moz-range-thumb
|
||||
-webkit-appearance: none;
|
||||
height: 1rem;
|
||||
width: 0.5rem;
|
||||
border-radius: 0.125rem;
|
||||
border-width: 0
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
box-shadow: none;
|
||||
&:hover
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
&:active
|
||||
background: rgba(255, 255, 255, 0.6);
|
||||
|
||||
&::-ms-thumb
|
||||
-webkit-appearance: none;
|
||||
height: 1rem;
|
||||
width: 0.5rem;
|
||||
border-radius: 0.125rem;
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
box-shadow: none;
|
||||
&:hover
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
&:active
|
||||
background: rgba(255, 255, 255, 0.6);
|
||||
</style>
|
32
src/components/widget/NavMenuPanel.astro
Normal file
|
@ -0,0 +1,32 @@
|
|||
---
|
||||
import {NavBarLink} from "../../types/config";
|
||||
import {Icon} from "astro-icon/components";
|
||||
import {url} from "../../utils/url-utils";
|
||||
|
||||
interface Props {
|
||||
links: NavBarLink[],
|
||||
}
|
||||
|
||||
const links = Astro.props.links;
|
||||
---
|
||||
<div id="nav-menu-panel" class:list={["float-panel float-panel-closed absolute transition-all fixed right-4 px-2 py-2"]}>
|
||||
{links.map((link) => (
|
||||
<a href={link.external ? link.url : url(link.url)} class="group flex justify-between items-center py-2 pl-3 pr-1 rounded-lg gap-8
|
||||
hover:bg-[var(--btn-plain-bg-hover)] active:bg-[var(--btn-plain-bg-active)] transition
|
||||
"
|
||||
target={link.external ? "_blank" : null}
|
||||
>
|
||||
<div class="transition text-black/75 dark:text-white/75 font-bold group-hover:text-[var(--primary)] group-active:text-[var(--primary)]">
|
||||
{link.name}
|
||||
</div>
|
||||
{!link.external && <Icon name="material-symbols:chevron-right-rounded"
|
||||
class="transition text-[var(--primary)]" size="20"
|
||||
>
|
||||
</Icon>}
|
||||
{link.external && <Icon name="fa6-solid:arrow-up-right-from-square"
|
||||
class="transition text-black/25 dark:text-white/25 -translate-x-1" size="12"
|
||||
>
|
||||
</Icon>}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
39
src/components/widget/Profile.astro
Normal file
|
@ -0,0 +1,39 @@
|
|||
---
|
||||
import ImageWrapper from "../misc/ImageWrapper.astro";
|
||||
import {Icon} from "astro-icon/components";
|
||||
import {profileConfig} from "../../config";
|
||||
import {url} from "../../utils/url-utils";
|
||||
|
||||
const config = profileConfig;
|
||||
---
|
||||
<div class="card-base p-3">
|
||||
<a aria-label="Go to About Page" href={url('/about/')}
|
||||
class="group block relative mx-auto mt-1 lg:mx-0 lg:mt-0 mb-3
|
||||
max-w-[240px] lg:max-w-none overflow-hidden rounded-xl active:scale-95">
|
||||
<div class="absolute transition pointer-events-none group-hover:bg-black/30 group-active:bg-black/50
|
||||
w-full h-full z-50 flex items-center justify-center">
|
||||
<Icon name="fa6-regular:address-card"
|
||||
class="transition opacity-0 scale-90 group-hover:scale-100 group-hover:opacity-100 text-white text-5xl">
|
||||
</Icon>
|
||||
</div>
|
||||
<ImageWrapper src={config.avatar} alt="Profile Image of the Author" class="mx-auto lg:w-full h-full lg:mt-0 "></ImageWrapper>
|
||||
</a>
|
||||
<div class="px-2">
|
||||
<div class="font-bold text-xl text-center mb-1 dark:text-neutral-50 transition">{config.name}</div>
|
||||
<div class="h-1 w-5 bg-[var(--primary)] mx-auto rounded-full mb-2 transition"></div>
|
||||
<div class="text-center text-neutral-400 mb-2.5 transition">{config.bio}</div>
|
||||
<div class="flex gap-2 justify-center mb-1">
|
||||
{config.links.length > 1 && config.links.map(item =>
|
||||
<a rel="me" aria-label={item.name} href={item.url} target="_blank" class="btn-regular rounded-lg h-10 w-10 active:scale-90">
|
||||
<Icon name={item.icon} size="1.5rem"></Icon>
|
||||
</a>
|
||||
)}
|
||||
{config.links.length == 1 && <a rel="me" aria-label={config.links[0].name} href={config.links[0].url} target="_blank"
|
||||
class="btn-regular rounded-lg h-10 gap-2 px-3 font-bold active:scale-95">
|
||||
<Icon name={config.links[0].icon} size="1.5rem"></Icon>
|
||||
{config.links[0].name}
|
||||
</a>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
16
src/components/widget/SideBar.astro
Normal file
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
import Profile from "./Profile.astro";
|
||||
import Tag from "./Tags.astro";
|
||||
import Categories from "./Categories.astro";
|
||||
|
||||
const className = Astro.props.class;
|
||||
---
|
||||
<div id="sidebar" class:list={[className, "w-full"]}>
|
||||
<div class="flex flex-col w-full gap-4 mb-4">
|
||||
<Profile></Profile>
|
||||
</div>
|
||||
<div class="flex flex-col w-full gap-4 top-4 sticky top-4">
|
||||
<Categories class="onload-animation" style="animation-delay: 150ms"></Categories>
|
||||
<Tag class="onload-animation" style="animation-delay: 200ms"></Tag>
|
||||
</div>
|
||||
</div>
|
32
src/components/widget/Tags.astro
Normal file
|
@ -0,0 +1,32 @@
|
|||
---
|
||||
|
||||
import WidgetLayout from "./WidgetLayout.astro";
|
||||
import ButtonTag from "../control/ButtonTag.astro";
|
||||
import {getTagList} from "../../utils/content-utils";
|
||||
import {i18n} from "../../i18n/translation";
|
||||
import I18nKey from "../../i18n/i18nKey";
|
||||
import {url} from "../../utils/url-utils";
|
||||
|
||||
const tags = await getTagList();
|
||||
|
||||
const COLLAPSED_HEIGHT = "7.5rem";
|
||||
|
||||
const isCollapsed = tags.length >= 20;
|
||||
|
||||
interface Props {
|
||||
class?: string;
|
||||
style?: string;
|
||||
}
|
||||
const className = Astro.props.class
|
||||
const style = Astro.props.style
|
||||
|
||||
---
|
||||
<WidgetLayout name={i18n(I18nKey.tags)} id="tags" isCollapsed={isCollapsed} collapsedHeight={COLLAPSED_HEIGHT} class={className} style={style}>
|
||||
<div class="flex gap-2 flex-wrap">
|
||||
{tags.map(t => (
|
||||
<ButtonTag href={url(`/archive/tag/${t.name}/`)} label={`View all posts with the ${t.name} tag`}>
|
||||
{t.name}
|
||||
</ButtonTag>
|
||||
))}
|
||||
</div>
|
||||
</WidgetLayout>
|
65
src/components/widget/WidgetLayout.astro
Normal file
|
@ -0,0 +1,65 @@
|
|||
---
|
||||
import { Icon } from 'astro-icon/components';
|
||||
import {i18n} from "../../i18n/translation";
|
||||
import I18nKey from "../../i18n/i18nKey";
|
||||
interface Props {
|
||||
id: string;
|
||||
name?: string;
|
||||
isCollapsed?: boolean;
|
||||
collapsedHeight?: string;
|
||||
class?: string;
|
||||
style?: string;
|
||||
}
|
||||
const props = Astro.props;
|
||||
const {
|
||||
id,
|
||||
name,
|
||||
isCollapsed,
|
||||
collapsedHeight,
|
||||
style,
|
||||
} = Astro.props
|
||||
const className = Astro.props.class
|
||||
|
||||
---
|
||||
<widget-layout data-id={id} data-is-collapsed={isCollapsed} class={"pb-4 card-base " + className} style={style}>
|
||||
<div class="font-bold transition text-lg text-neutral-900 dark:text-neutral-100 relative ml-8 mt-4 mb-2
|
||||
before:w-1 before:h-4 before:rounded-md before:bg-[var(--primary)]
|
||||
before:absolute before:left-[-16px] before:top-[5.5px]">{name}</div>
|
||||
<div id={id} class:list={["collapse-wrapper px-4 overflow-hidden", {"collapsed": isCollapsed}]}>
|
||||
<slot></slot>
|
||||
</div>
|
||||
{isCollapsed && <div class="expand-btn px-4 -mb-2">
|
||||
<button class="btn-plain rounded-lg w-full h-9">
|
||||
<div class="text-[var(--primary)] flex items-center justify-center gap-2 -translate-x-2">
|
||||
<Icon name="material-symbols:more-horiz" size={28}></Icon> {i18n(I18nKey.more)}
|
||||
</div>
|
||||
</button>
|
||||
</div>}
|
||||
</widget-layout>
|
||||
|
||||
<style define:vars={{ collapsedHeight }}>
|
||||
.collapsed {
|
||||
height: var(--collapsedHeight);
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
class WidgetLayout extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
if (this.dataset.isCollapsed === undefined || this.dataset.isCollapsed === false)
|
||||
return;
|
||||
|
||||
const id = this.dataset.id;
|
||||
const btn = this.querySelector('.expand-btn');
|
||||
const wrapper = this.querySelector(`#${id}`)
|
||||
btn.addEventListener('click', () => {
|
||||
wrapper.classList.remove('collapsed');
|
||||
btn.classList.add('hidden');
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('widget-layout', WidgetLayout);
|
||||
</script>
|
74
src/config.ts
Normal file
|
@ -0,0 +1,74 @@
|
|||
import type {
|
||||
LicenseConfig,
|
||||
NavBarConfig,
|
||||
ProfileConfig,
|
||||
SiteConfig,
|
||||
} from './types/config'
|
||||
import { LinkPreset } from './types/config'
|
||||
|
||||
export const siteConfig: SiteConfig = {
|
||||
title: 'Hack13 Blog',
|
||||
subtitle: 'The blog of a enby foxo',
|
||||
lang: 'en', // 'en', 'zh_CN', 'zh_TW', 'ja'
|
||||
themeColor: {
|
||||
hue: 340, // Default hue for the theme color, from 0 to 360. e.g. red: 0, teal: 200, cyan: 250, pink: 345
|
||||
fixed: true, // Hide the theme color picker for visitors
|
||||
},
|
||||
banner: {
|
||||
enable: false,
|
||||
src: 'assets/images/demo-banner.png', // Relative to the /src directory. Relative to the /public directory if it starts with '/'
|
||||
position: 'center', // Equivalent to object-position, defaults center
|
||||
},
|
||||
favicon: [
|
||||
// Leave this array empty to use the default favicon
|
||||
// {
|
||||
// src: '/favicon/icon.png', // Path of the favicon, relative to the /public directory
|
||||
// theme: 'light', // (Optional) Either 'light' or 'dark', set only if you have different favicons for light and dark mode
|
||||
// sizes: '32x32', // (Optional) Size of the favicon, set only if you have favicons of different sizes
|
||||
// }
|
||||
],
|
||||
}
|
||||
|
||||
export const navBarConfig: NavBarConfig = {
|
||||
links: [
|
||||
LinkPreset.Home,
|
||||
LinkPreset.Archive,
|
||||
LinkPreset.About,
|
||||
{
|
||||
name: 'GitHub',
|
||||
url: 'https://github.com/hack13', // Internal links should not include the base path, as it is automatically added
|
||||
external: true, // Show an external link icon and will open in a new tab
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
export const profileConfig: ProfileConfig = {
|
||||
avatar: 'assets/images/hack-avatar.png', // Relative to the /src directory. Relative to the /public directory if it starts with '/'
|
||||
name: 'Hack13',
|
||||
bio: 'A nerdy enby foxo on the internet.',
|
||||
links: [
|
||||
{
|
||||
name: 'Twitter',
|
||||
icon: 'fa6-brands:mastodon', // Visit https://icones.js.org/ for icon codes
|
||||
// You will need to install the corresponding icon set if it's not already included
|
||||
// `pnpm add @iconify-json/<icon-set-name>`
|
||||
url: 'https://cyberfurz.social/@hack13',
|
||||
},
|
||||
{
|
||||
name: 'Telegram',
|
||||
icon: 'fa6-brands:telegram',
|
||||
url: 'https://t.me/kite5521',
|
||||
},
|
||||
{
|
||||
name: 'Discord',
|
||||
icon: 'fa6-brands:git',
|
||||
url: 'https://git.hack13.dev/hack13',
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
export const licenseConfig: LicenseConfig = {
|
||||
enable: true,
|
||||
name: 'CC BY-NC-SA 4.0',
|
||||
url: 'https://creativecommons.org/licenses/by-nc-sa/4.0/',
|
||||
}
|
6
src/constants/constants.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
export const UNCATEGORIZED = '__uncategorized__'
|
||||
|
||||
export const PAGE_SIZE = 8
|
||||
|
||||
export const LIGHT_MODE = 'light', DARK_MODE = 'dark', AUTO_MODE = 'auto'
|
||||
export const DEFAULT_THEME = AUTO_MODE
|
37
src/constants/icon.ts
Normal file
|
@ -0,0 +1,37 @@
|
|||
import type {Favicon} from "@/types/config.ts";
|
||||
|
||||
export const defaultFavicons: Favicon[] = [
|
||||
{
|
||||
src: '/favicon/favicon-light-32.png',
|
||||
theme: 'light',
|
||||
sizes: '32x32',
|
||||
}, {
|
||||
src: '/favicon/favicon-light-128.png',
|
||||
theme: 'light',
|
||||
sizes: '128x128',
|
||||
}, {
|
||||
src: '/favicon/favicon-light-180.png',
|
||||
theme: 'light',
|
||||
sizes: '180x180',
|
||||
}, {
|
||||
src: '/favicon/favicon-light-192.png',
|
||||
theme: 'light',
|
||||
sizes: '192x192',
|
||||
}, {
|
||||
src: '/favicon/favicon-dark-32.png',
|
||||
theme: 'dark',
|
||||
sizes: '32x32',
|
||||
}, {
|
||||
src: '/favicon/favicon-dark-128.png',
|
||||
theme: 'dark',
|
||||
sizes: '128x128',
|
||||
}, {
|
||||
src: '/favicon/favicon-dark-180.png',
|
||||
theme: 'dark',
|
||||
sizes: '180x180',
|
||||
}, {
|
||||
src: '/favicon/favicon-dark-192.png',
|
||||
theme: 'dark',
|
||||
sizes: '192x192',
|
||||
}
|
||||
]
|
18
src/constants/link-presets.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
import { LinkPreset, type NavBarLink } from '@/types/config'
|
||||
import I18nKey from '@i18n/i18nKey'
|
||||
import { i18n } from '@i18n/translation'
|
||||
|
||||
export const LinkPresets: { [key in LinkPreset]: NavBarLink } = {
|
||||
[LinkPreset.Home]: {
|
||||
name: i18n(I18nKey.home),
|
||||
url: '/',
|
||||
},
|
||||
[LinkPreset.About]: {
|
||||
name: i18n(I18nKey.about),
|
||||
url: '/about/',
|
||||
},
|
||||
[LinkPreset.Archive]: {
|
||||
name: i18n(I18nKey.archive),
|
||||
url: '/archive/',
|
||||
},
|
||||
}
|
16
src/content/config.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
import { defineCollection, z } from 'astro:content'
|
||||
|
||||
const postsCollection = defineCollection({
|
||||
schema: z.object({
|
||||
title: z.string(),
|
||||
published: z.date(),
|
||||
draft: z.boolean().optional(),
|
||||
description: z.string().optional(),
|
||||
image: z.string().optional(),
|
||||
tags: z.array(z.string()).optional(),
|
||||
category: z.string().optional(),
|
||||
}),
|
||||
})
|
||||
export const collections = {
|
||||
posts: postsCollection,
|
||||
}
|
|
@ -1,11 +1,12 @@
|
|||
---
|
||||
title: New Blog
|
||||
author: Timothy Rogers
|
||||
description: This is the very first post ever on this blog
|
||||
type: post
|
||||
date: 2010-06-01T19:28:00+00:00
|
||||
url: /2010/06/new-blog/
|
||||
categories:
|
||||
- Archived
|
||||
published: 2010-06-01
|
||||
draft: false
|
||||
#url: /2010/06/new-blog/
|
||||
tags: [OpenSim]
|
||||
category: Archive
|
||||
|
||||
---
|
||||
Hello this is your friendly OSgrid Folf (fox/wolf) who loves to explore the grid and help out those who I can. I want to start off by saying this blog has no official connection to OSgrid nor its operations. I am just here righting about my experiences in OSgrid and how much I enjoy it and helping people out in it.
|
|
@ -1,11 +1,12 @@
|
|||
---
|
||||
title: Been A While
|
||||
author: Timothy Rogers
|
||||
description: It has been quite a while since I have posted some updates to this old blog of mine...
|
||||
type: post
|
||||
date: 2012-04-04T21:38:00+00:00
|
||||
published: 2012-04-04
|
||||
url: /2012/04/been-a-while/
|
||||
categories:
|
||||
- Archived
|
||||
tags: [Personal]
|
||||
category: Archive
|
||||
draft: false
|
||||
|
||||
---
|
||||
It has been quite a while since I have posted some updates to this old blog of mine. I will be more active on this personal blog, because well I think I need to have a personal blog. I know it seems kinda bad as running an opensim grid, and having my own personal thoughts xD but you know what, I don't care. I support the freedom of speech and free flow of thought. So I will be posting my little rants here, my thoughts, and ideas for opensim. Now they will not always directly reflect my views on Cyber Wrld, I will say this I do plan on keeping Cyber Wrld a free and Open Source grid. We just run stable releases of OpenSim instead of bleeding edge, and we have support for currency and voice grid wide. We are still supporting those who want to try hosting their own regions with us.
|
|
@ -1,11 +1,11 @@
|
|||
---
|
||||
title: My Take on OpenSim Grids
|
||||
author: Timothy Rogers
|
||||
type: post
|
||||
date: 2012-04-05T02:26:00+00:00
|
||||
description: I am tired of all the fighting over virtual worlds, I mean mainly the OpenSim grids...
|
||||
draft: false
|
||||
published: 2012-04-05
|
||||
url: /2012/04/my-take-on-opensim-grids/
|
||||
categories:
|
||||
- Archived
|
||||
category: Archive
|
||||
tags: [OpenSim, Opinion]
|
||||
|
||||
---
|
||||
I am tired of all the fighting over virtual worlds, I mean mainly the OpenSim grids. I hear people arguing over having the hypergrid turned off or that they allow anyone to make oar and iar backups of their creations. I do not mind this, I actually think this is a good thing. Variety helps economy, we shouldn't be a at throat competition with other grids, but rather friendly competition. I understand how some grids cannot be public, because they want the ultimate security. Yes this can be argued, but the best systems are systems with access restricted to public. Now in the since of contributing back, we do need to consider that some of these grids also have to stay closed source if they put in stuff that makes it work better, but the parts of code belong to another company and they are being leased for use only with a said grid. So I see it understandable, and I think it is good to see grids branch off and do their own thing, that's for them, and what their user base wants.</p>
|
|
@ -1,11 +1,12 @@
|
|||
---
|
||||
title: My Vision of the HyperGrid
|
||||
description: I have been thinking for a long time, and you know I think I have my idea of how I really want Cyber Wrld...
|
||||
author: Timothy Rogers
|
||||
type: post
|
||||
date: 2012-04-05T18:06:00+00:00
|
||||
tags: [Personal, OpenSim]
|
||||
published: 2012-04-05
|
||||
url: /2012/04/my-vision-of-the-hypergrid/
|
||||
categories:
|
||||
- General
|
||||
category: Archive
|
||||
draft: false
|
||||
|
||||
---
|
||||
I have been thinking for a long time, and you know I think I have my idea of how I really want Cyber Wrld to use the HyperGrid, and well I have been doing it already in a sense. But here it is, a way for people to come hang out on our grid. Not only that, have our grid as a shopping destination. We use OMC, letting people come to our grid and purchase items in world, even without an OMC account and pay through paypal. Take the stuff back home, or just come to our grid directly and sell stuff here and go out and explore. I mean I sound like I am plugging my grid here, but that is what I envision for Cyber Wrld. Being a hub for shopping, visitors, parties and more. I mean we are fully open for leaving our grid and coming back home, nothing too dangerous there.
|
|
@ -1,11 +1,12 @@
|
|||
---
|
||||
title: We Have HyperGrid USE IT!!!!
|
||||
description: It is starting to sadden me, I have slowly become aware of things, that I rather not have come into knowing...
|
||||
author: Timothy Rogers
|
||||
type: post
|
||||
date: 2012-04-11T21:23:00+00:00
|
||||
tags: [OpenSim, Opinion]
|
||||
published: 2012-04-11
|
||||
url: /2012/04/we-have-hypergrid-use-it/
|
||||
categories:
|
||||
- General
|
||||
category: Archive
|
||||
draft: false
|
||||
|
||||
---
|
||||
It is starting to sadden me, I have slowly become aware of things, that I rather not have come into knowing. Now I have learned that one of the most famous content creators of virtual worlds named Linda Kellie might be pulling out of virtual worlds all together. I am sorry to hear that, but I do respect and understand her reasoning. It is very difficult for grid owners such as myself to regulate the flow of content over the metaverse, reason why my grid doesn’t just allow anyone to connect their regions. I urge her to keep creating and just go standalone. Saying that makes me want to go into a conversation I had with my developer not to long ago.
|
21
src/content/posts/2012-05-05-coming-changes.md
Normal file
|
@ -0,0 +1,21 @@
|
|||
---
|
||||
title: Coming Changes
|
||||
description: Old post about spending time in OSGrid
|
||||
tags: [OpenSim]
|
||||
published: 2012-05-05
|
||||
url: /2012/05/coming-changes/
|
||||
category: Archive
|
||||
draft: false
|
||||
|
||||
---
|
||||

|
||||
>Me reading in our new office location in OSgrid
|
||||
|
||||
As you might have heard, I have recently moved my entire grid to OSgrid. This was a decision made my grid residents, and I was inclined to agree as I want to go back to focusing on community related efforts for the HyperGrid. So moving everything to OSgrid, allows me to not worry about any of the issues, just worry about all the work I need to get done for the community.
|
||||
|
||||
I am not just working on the furry community for the hypergrid, but I want to bring together everyone, all walks of life. I am still working on creating a site, that will help residents of all hypergrid enabled grids come together and interact on a social network level. I feel the reason we are all having troubles of the hypergrid, is we are all spread to thin. We need to increase ourselves in community, to do this we just need to pull all our numbers together.
|
||||
|
||||
I know that I cannot please everyone all the time, so I am still going to be offering some really nice deals on OSgrid region hosting. I will be introducing a few new features and places to the sites I have been wanting to work on for bringing the hypergrid community all together.
|
||||
|
||||

|
||||
>Enjoying the view of the new regions from up above
|
|
@ -1,11 +1,11 @@
|
|||
---
|
||||
title: Timothy’s Thoughts
|
||||
author: Timothy Rogers
|
||||
type: post
|
||||
date: 2012-06-11T19:29:00+00:00
|
||||
description: I have to admit lately, I have been quite depressed about a lot of things in RL. I have not let effect my work...
|
||||
tags: [OpenSim, AuroraSim]
|
||||
published: 2012-06-11
|
||||
url: /2012/06/timothys-thoughts/
|
||||
categories:
|
||||
- Archived
|
||||
category: Archive
|
||||
draft: false
|
||||
|
||||
---
|
||||
I have to admit lately, I have been quite depressed about a lot of things in RL. I have not let effect my work, but it has made it harder to stay focused on work. I am kinda glad Cyber Wrld has not yet worked its way back into any grid list anywhere, as well it hasn't been doing too well at all. Well hasn't been doing anything at all, I have added several features and got a ton of work done to it, and it is ready for production use, minus currency and a few other small things.
|
|
@ -1,11 +1,11 @@
|
|||
---
|
||||
title: Finding My Place
|
||||
author: Timothy Rogers
|
||||
type: post
|
||||
date: 2012-07-02T00:57:00+00:00
|
||||
description: SoftPaw Estates is starting to really kick off and do quite well for itself. Clients are already telling me that they love the service...
|
||||
tags: [OpenSim]
|
||||
published: 2012-07-02
|
||||
url: /2012/07/finding-my-place/
|
||||
categories:
|
||||
- Archived
|
||||
category: Archive
|
||||
draft: false
|
||||
|
||||
---
|
||||
SoftPaw Estates is starting to really kick off and do quite well for itself. Clients are already telling me that they love the service. I have been cranking out updates to the webui, that should be launching soon, something that I think is long deserved and greatly needed. The ability to control your region from a web page. I haven't determined how much I will be charging, but I am looking for current clients will be getting it at no additional cost, however if clients in the future wish to have the web panel, I will charge 5-10usd extra for it.
|
|
@ -1,11 +1,11 @@
|
|||
---
|
||||
title: 'Lil About Me & Some Rage'
|
||||
author: Timothy Rogers
|
||||
type: post
|
||||
date: 2012-07-14T23:17:00+00:00
|
||||
title: Lil About Me & Some Rage
|
||||
author: First off you might be asking why the hell am I putting a Nicki Minaj music video on my blog? Well although I am hypocritical...
|
||||
tags: [Vent, Opinion]
|
||||
published: 2012-07-14
|
||||
url: /2012/07/lil-about-me-some-rage/
|
||||
categories:
|
||||
- Archived
|
||||
category: Archive
|
||||
draft: false
|
||||
|
||||
---
|
||||
First off you might be asking why the hell am I putting a Nicki Minaj music video on my blog? Well although I am hypocritical, I have accepted the fact that the lyrics of this song is what I have done to move forward with my life. My good friend Linda Kellie, I have come to view her as life hero, someone who knows what it is to live with the same things I deal with. I have ADHD, and I am sick and tired of people saying it is not a real disorder, because you try being me. It is really like the joke people make, ADH-oooo look shiny thing! I get off track so much I have to use my computer to keep my mind focused, it is called Attention Deficit Hyperactivity Disorder. Because of this disorder, I am unable to complete many tasks that are set before me in timely fashions reasons I have a smart phone and computer with timers and locks to remind me what I am doing. It is much worse than people make it out to be, and because of this, I had developed depression, why because when I am not working on things, I am working my mind and it tends to wonder to extremes with questions that cannot be answered and that drives me crazy and gets me depressed.
|
|
@ -1,11 +1,11 @@
|
|||
---
|
||||
title: Battle of Business vs OpenSource
|
||||
author: Timothy Rogers
|
||||
type: post
|
||||
date: 2012-08-09T04:02:00+00:00
|
||||
description: Alright lately I have been getting overwhelmed with all the drama of the oldest argument in the world of internet technologies. So who should win? Big business or opensource projects? Well the answer is simple, THE ARE NOT AT WAR!!!!
|
||||
tags: [OpenSim, Opinion]
|
||||
published: 2012-08-09
|
||||
url: /2012/08/battle-of-business-vs-opensource/
|
||||
categories:
|
||||
- Archived
|
||||
category: Archive
|
||||
draft: false
|
||||
|
||||
---
|
||||
Alright lately I have been getting overwhelmed with all the drama of the oldest argument in the world of internet technologies. So who should win? Big business or opensource projects? Well the answer is simple, THE ARE NOT AT WAR!!!!
|
|
@ -1,11 +1,11 @@
|
|||
---
|
||||
title: New Years Resolutions
|
||||
author: Timothy Rogers
|
||||
type: post
|
||||
date: 2012-12-31T14:40:00+00:00
|
||||
description: It is that time of the year, when people start making New Year Resolutions. Well I have never really made any other than once for a class assignment. But this year I really think I need to make some real resolutions. So I thought I would post them here on my personal blog for everyone to read and be aware.
|
||||
tags: [Personal]
|
||||
published: 2012-12-31
|
||||
url: /2012/12/new-years-resolutions/
|
||||
categories:
|
||||
- Archived
|
||||
category: Archive
|
||||
draft: false
|
||||
|
||||
---
|
||||
It is that time of the year, when people start making New Year Resolutions. Well I have never really made any other than once for a class assignment. But this year I really think I need to make some real resolutions. So I thought I would post them here on my personal blog for everyone to read and be aware.
|
|
@ -1,12 +1,12 @@
|
|||
---
|
||||
title: Explaining OpenSim Memory Usage
|
||||
author: Timothy Rogers
|
||||
type: post
|
||||
date: 2013-02-11T20:15:00+00:00
|
||||
description: I have had a couple people ask me to show them some tips and show them how to conserve memory usage in OpenSim. So I am going to be using sim-on-a-stick for today's tips and tricks...
|
||||
tags: [Resources, OpenSim]
|
||||
published: 2013-02-11
|
||||
url: /2013/02/explaining-opensim-memory-usage/
|
||||
categories:
|
||||
- Resources
|
||||
- Archived
|
||||
category: Archive
|
||||
draft: false
|
||||
|
||||
---
|
||||
I have had a couple people ask me to show them some tips and show them how to conserve memory usage in OpenSim. So I am going to be using sim-on-a-stick for today's tips and tricks. Now the reason for this is because now with sim-on-a-stick, we have the database running in MySQL which saves a bit of memory, and the fact it is already setup and ready to go out of the box.</p>
|
||||
|
|
@ -1,12 +1,11 @@
|
|||
---
|
||||
title: Thank You
|
||||
author: Timothy Rogers
|
||||
type: post
|
||||
date: 2013-02-13T23:32:00+00:00
|
||||
description: Thank You AuroraScape Community
|
||||
tags: [AuroraSim]
|
||||
published: 2013-02-13
|
||||
url: /2013/02/thank-you/
|
||||
categories:
|
||||
- Archived
|
||||
- Aurora-Sim
|
||||
category: Archive
|
||||
draft: false
|
||||
|
||||
---
|
||||
Thank You AuroraScape Community,
|
|
@ -1,11 +1,11 @@
|
|||
---
|
||||
title: Needing Rest
|
||||
author: Timothy Rogers
|
||||
type: post
|
||||
date: 2013-02-20T05:43:00+00:00
|
||||
description: It sure has been a long first 20 days, I can't believe how far all of us have come. Yes after the upgrade happened, we lost a nice chunk of regions, but I am sure we will be getting them back soon. All I can say is I am taking a few steps back for the next few days, from grid upgrades.
|
||||
tags: [AuroraSim]
|
||||
published: 2013-02-20
|
||||
url: /2013/02/needing-rest/
|
||||
categories:
|
||||
- Archived
|
||||
category: Archive
|
||||
draft: false
|
||||
|
||||
---
|
||||
It sure has been a long first 20 days, I can't believe how far all of us have come. Yes after the upgrade happened, we lost a nice chunk of regions, but I am sure we will be getting them back soon. All I can say is I am taking a few steps back for the next few days, from grid upgrades. I will still be around, doing money cash outs, trouble shooting, and processing orders. I just need a break.
|
|
@ -1,15 +1,14 @@
|
|||
---
|
||||
title: Top 10 Reasons Not To Give Up On Aurora-Sim
|
||||
author: Timothy Rogers
|
||||
type: post
|
||||
date: 2013-02-21T05:48:00+00:00
|
||||
description: I am very sad to see so many people tell me that I should give up on Aurora-Sim, and that it is a waste. I think it is mostly been placed there because people remember its biggest tank, and the failure of Nova Grid.
|
||||
tags: [AuroraSim]
|
||||
published: 2013-02-21
|
||||
url: /2013/02/top-10-reasons-not-to-give-up-on-aurora-sim/
|
||||
categories:
|
||||
- Archived
|
||||
- Aurora-Sim
|
||||
category: Archive
|
||||
draft: false
|
||||
|
||||
---
|
||||
I am very sad to see so many people tell me that I should give up on Aurora-Sim, and that it is a waste. I think it is mostly been placed there because people remember its biggest tank, and the failure of Nova Grid. So I am going to show you the top 10 reasons why I think the metaverse should give it another shot, and get back to helping with its development.</p>
|
||||
I am very sad to see so many people tell me that I should give up on Aurora-Sim, and that it is a waste. I think it is mostly been placed there because people remember its biggest tank, and the failure of Nova Grid. So I am going to show you the top 10 reasons why I think the metaverse should give it another shot, and get back to helping with its development.
|
||||
|
||||
**Ready Out of the Box:** When you download the latest aurora-sim master code, and compile it. First time run, it has a built in fully functional WebUI, Fully Working Groups, Fully Working Profiles, Fully Working Search, and easy to use Region GUI. All of this is built into the code, all running for the core code. This all works even in standalone mode all out of the single Aurora.exe!
|
||||
|
|
@ -1,12 +1,11 @@
|
|||
---
|
||||
title: Who Is Timothy Vyper/Hoxley/Rogers
|
||||
author: Timothy Rogers
|
||||
type: post
|
||||
date: 2013-02-23T01:43:00+00:00
|
||||
description: I just keep reading all this stuff about all these other grids, and the people who have influences there. Now that I am a grid owner, been making myself know more and more over the past year with all the things I have started. Grid-Press, SoftPaw Estates, Zetamex, and my latest AuroraScape.
|
||||
tags: [Personal]
|
||||
published: 2013-02-23
|
||||
url: /2013/02/who-is-timothy-vyperhoxleyrogers/
|
||||
categories:
|
||||
- Archived
|
||||
- Personal
|
||||
category: Archive
|
||||
draft: false
|
||||
|
||||
---
|
||||
I just keep reading all this stuff about all these other grids, and the people who have influences there. Now that I am a grid owner, been making myself know more and more over the past year with all the things I have started. Grid-Press, SoftPaw Estates, Zetamex, and my latest AuroraScape. I will admit I am not perfect, hell I never even went to college! Also another zinger for all you people, did you all know that I am only 20 years old? This year I turn 21, on April 28th. Which surprises a lot of people when I tell them all that, but I did take some college classes in High School, because my school was merged with the local college (due to budget cuts) and got 3 certification, that don't mean crap now.
|
|
@ -1,12 +1,11 @@
|
|||
---
|
||||
title: Why I Closed AuroraScape
|
||||
author: Timothy Rogers
|
||||
type: post
|
||||
date: 2013-03-05T22:20:00+00:00
|
||||
description: I first want to thank everyone who was on AuroraScape, and I have to apologize to all of you. I know the life of AuroraScape was short but there are multiple reasons to this, and I mean no ill intent to the developers of Aurora by what I have to say in this blog.
|
||||
tags: [AuroraSim]
|
||||
published: 2013-03-05
|
||||
url: /2013/03/why-i-closed-aurorascape/
|
||||
categories:
|
||||
- Archived
|
||||
- Aurora-Sim
|
||||
category: Archive
|
||||
draft: false
|
||||
|
||||
---
|
||||
I first want to thank everyone who was on AuroraScape, and I have to apologize to all of you. I know the life of AuroraScape was short but there are multiple reasons to this, and I mean no ill intent to the developers of Aurora by what I have to say in this blog. Firstly, AuroraScape was doing pretty good. But that was how it looked on the outside, there were a lot of problems, things I just cannot work on as I am not a programmer. The software is very stable, very easy to use, and works out of the box. That I am not trying to fight with, but there was just too many people bitching about it.
|
|
@ -1,12 +1,11 @@
|
|||
---
|
||||
title: Closed vs Open? Really?
|
||||
author: Timothy Rogers
|
||||
type: post
|
||||
date: 2013-03-11T12:03:00+00:00
|
||||
description: I am dealing with a lot of people getting stressed over the debate of if grids should all be open or at least have hypergrid open. I am just going to put this out there, there are tons of people who want everything free in life, and never want to pay for anything.
|
||||
tags: [OpenSim]
|
||||
published: 2013-03-11T12:03:00+00:00
|
||||
url: /2013/03/closed-vs-open-really/
|
||||
categories:
|
||||
- Archived
|
||||
- OpenSimulator
|
||||
category: Archive
|
||||
draft: false
|
||||
|
||||
---
|
||||
I am dealing with a lot of people getting stressed over the debate of if grids should all be open or at least have hypergrid open. I am just going to put this out there, there are tons of people who want everything free in life, and never want to pay for anything. I am sorry but the world requires money for us to get things done.
|
|
@ -1,12 +1,11 @@
|
|||
---
|
||||
title: 'Moving Up & Around'
|
||||
author: Timothy Rogers
|
||||
type: post
|
||||
date: 2013-04-08T13:17:00+00:00
|
||||
title: Moving Up & Around
|
||||
description: It has been one hell of a past month, I have had to deal with a lot of drama in my real personal life. Sometimes I wish we could all just live in virtual worlds, but drama would just pile up their too...
|
||||
tags: [Personal]
|
||||
published: 2013-04-08
|
||||
url: /2013/04/moving-up-around/
|
||||
categories:
|
||||
- Archived
|
||||
- Personal
|
||||
category: Archive
|
||||
draft: false
|
||||
|
||||
---
|
||||
It has been one hell of a past month, I have had to deal with a lot of drama in my real personal life. Sometimes I wish we could all just live in virtual worlds, but drama would just pile up their too... so what else is new on that. But besides that point, I have been doing some plotting and planning with Zetamex these past few days so going to be talking a bit about that in this blog. I am also going to gloat about my new boy friend, because I can tell you right now, without him this past month would have just been me crying my heart out.
|
|
@ -1,12 +1,11 @@
|
|||
---
|
||||
title: My Clocks Are Ticking
|
||||
author: Timothy Rogers
|
||||
type: post
|
||||
date: 2013-04-26T01:41:00+00:00
|
||||
description: Well it has been a while since I have written on my personal blog again. However I have been feeling a little down lately and I think I need to write to pick up my spirits a bit again.
|
||||
tags: [Personal]
|
||||
published: 2013-04-26
|
||||
url: /2013/04/my-clocks-are-ticking/
|
||||
categories:
|
||||
- Archived
|
||||
- Personal
|
||||
category: Archive
|
||||
draft: false
|
||||
|
||||
---
|
||||
Well it has been a while since I have written on my personal blog again. However I have been feeling a little down lately and I think I need to write to pick up my spirits a bit again. I just been going through a lot of feelings lately. I guess I should start from the top, which is well I am just so immersed in my work I feel I am loosing more and more touch with my real life. I mean not that is a bad thing, but I am getting to the point that I blow off my boyfriend. I already lost one boyfriend, that I had been with for 2 years, it wasn't only because I was so immersed in my work there were other reasons but this was part of it.
|
|
@ -1,12 +1,11 @@
|
|||
---
|
||||
title: Upset With Stiffled Innovation
|
||||
author: Timothy Rogers
|
||||
type: post
|
||||
date: 2013-06-19T12:04:00+00:00
|
||||
description: I am constantly hearing about all these people stiffing innovation, well today I had the last straw I could take. Before I begin, I want to make sure say this boldly and clearly.
|
||||
tags: [OpenSim]
|
||||
published: 2013-06-19
|
||||
url: /2013/06/upset-with-stiffled-innovation/
|
||||
categories:
|
||||
- Archived
|
||||
- OpenSimulator
|
||||
category: Archive
|
||||
draft: false
|
||||
|
||||
---
|
||||
I am constantly hearing about all these people stiffing innovation, well today I had the last straw I could take. Before I begin, I want to make sure say this boldly and clearly. **I do not directly represent Zetamex's personal interests, Zetamex represents it's own identity at http://blog.zetamex.com/ and anything said on this blog is unrelated to Zetamex's personal interests.** Now that I got that out of the way, I want to tell you how I was rudely addressed for the last time.
|
|
@ -1,15 +1,14 @@
|
|||
---
|
||||
title: Depression
|
||||
author: Timothy Rogers
|
||||
type: post
|
||||
date: 2013-08-12T11:27:00+00:00
|
||||
description: Well I never thought the day would come when I would say that I feel depressed with my work, well I been this way quite a bit lately. Work is getting harder and harder to keep up with lately, not due to being over extended, just that Zetamex seems to be falling behind lately.
|
||||
tags: [Personal]
|
||||
published: 2013-08-12
|
||||
url: /2013/08/depression/
|
||||
categories:
|
||||
- Archived
|
||||
- Personal
|
||||
category: Archive
|
||||
draft: false
|
||||
|
||||
---
|
||||
Well I never thought the day would come when I would say that I feel depressed with my work, well I been this way quite a bit lately. Work is getting harder and harder to keep up with lately, not due to being over extended, just that Zetamex seems to be falling behind lately.</p>
|
||||
Well I never thought the day would come when I would say that I feel depressed with my work, well I been this way quite a bit lately. Work is getting harder and harder to keep up with lately, not due to being over extended, just that Zetamex seems to be falling behind lately.
|
||||
|
||||
I try my absolute hardest to provide the latest and greatest opensim features, code, and even give back as much as I can. But it just seems that I have started to hit a road block. Which has just made me keep pushing and pushing harder, but it doesn't seem to be budging at all. I mean Zetaworlds is delayed because I am just to finish the new ZetaPanel 2.0 which is very complex piece of work. I am the only developer of it, and it so hard to put together since I am not even a real programmer.
|
||||
|