fix: fix errors and reformat code
This commit is contained in:
parent
1f93499ece
commit
0ad144add3
|
@ -8,7 +8,7 @@
|
||||||
"formatter": {
|
"formatter": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"formatWithErrors": false,
|
"formatWithErrors": false,
|
||||||
"ignore": [],
|
"ignore": ["src/config.ts"],
|
||||||
"indentStyle": "space",
|
"indentStyle": "space",
|
||||||
"indentWidth": 2,
|
"indentWidth": 2,
|
||||||
"lineWidth": 80
|
"lineWidth": 80
|
||||||
|
|
|
@ -1,63 +1,68 @@
|
||||||
---
|
---
|
||||||
import {getSortedPosts} from "../utils/content-utils";
|
import { getSortedPosts } from '../utils/content-utils'
|
||||||
import {getPostUrlBySlug} from "../utils/url-utils";
|
import { getPostUrlBySlug } from '../utils/url-utils'
|
||||||
import {i18n} from "../i18n/translation";
|
import { i18n } from '../i18n/translation'
|
||||||
import I18nKey from "../i18n/i18nKey";
|
import I18nKey from '../i18n/i18nKey'
|
||||||
import {UNCATEGORIZED} from "@constants/constants";
|
import { UNCATEGORIZED } from '@constants/constants'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
keyword?: string;
|
keyword?: string
|
||||||
tags?: string[];
|
tags?: string[]
|
||||||
categories?: string[];
|
categories?: string[]
|
||||||
}
|
}
|
||||||
const { keyword, tags, categories} = Astro.props;
|
const { keyword, tags, categories } = Astro.props
|
||||||
|
|
||||||
let posts = await getSortedPosts()
|
let posts = await getSortedPosts()
|
||||||
|
|
||||||
if (Array.isArray(tags) && tags.length > 0) {
|
if (Array.isArray(tags) && tags.length > 0) {
|
||||||
posts = posts.filter(post =>
|
posts = posts.filter(
|
||||||
Array.isArray(post.data.tags) && post.data.tags.some(tag => tags.includes(tag))
|
post =>
|
||||||
);
|
Array.isArray(post.data.tags) &&
|
||||||
|
post.data.tags.some(tag => tags.includes(tag)),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Array.isArray(categories) && categories.length > 0) {
|
if (Array.isArray(categories) && categories.length > 0) {
|
||||||
posts = posts.filter(post =>
|
posts = posts.filter(
|
||||||
(post.data.category && categories.includes(post.data.category)) ||
|
post =>
|
||||||
(!post.data.category && categories.includes(UNCATEGORIZED))
|
(post.data.category && categories.includes(post.data.category)) ||
|
||||||
);
|
(!post.data.category && categories.includes(UNCATEGORIZED)),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const groups: {year: number, posts: typeof posts}[] = function () {
|
const groups: { year: number; posts: typeof posts }[] = (function () {
|
||||||
const groupedPosts = posts.reduce((grouped: {[year: number]: typeof posts}, post) => {
|
const groupedPosts = posts.reduce(
|
||||||
const year = post.data.published.getFullYear()
|
(grouped: { [year: number]: typeof posts }, post) => {
|
||||||
if (!grouped[year]) {
|
const year = post.data.published.getFullYear()
|
||||||
grouped[year] = []
|
if (!grouped[year]) {
|
||||||
}
|
grouped[year] = []
|
||||||
grouped[year].push(post)
|
}
|
||||||
return grouped
|
grouped[year].push(post)
|
||||||
}, {})
|
return grouped
|
||||||
|
},
|
||||||
|
{},
|
||||||
|
)
|
||||||
|
|
||||||
// convert the object to an array
|
// convert the object to an array
|
||||||
const groupedPostsArray = Object.keys(groupedPosts).map(key => ({
|
const groupedPostsArray = Object.keys(groupedPosts).map(key => ({
|
||||||
year: parseInt(key),
|
year: parseInt(key),
|
||||||
posts: groupedPosts[parseInt(key)]
|
posts: groupedPosts[parseInt(key)],
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// sort years by latest first
|
// sort years by latest first
|
||||||
groupedPostsArray.sort((a, b) => b.year - a.year)
|
groupedPostsArray.sort((a, b) => b.year - a.year)
|
||||||
return groupedPostsArray;
|
return groupedPostsArray
|
||||||
}();
|
})()
|
||||||
|
|
||||||
function formatDate(date: Date) {
|
function formatDate(date: Date) {
|
||||||
const month = (date.getMonth() + 1).toString().padStart(2, '0');
|
const month = (date.getMonth() + 1).toString().padStart(2, '0')
|
||||||
const day = date.getDate().toString().padStart(2, '0');
|
const day = date.getDate().toString().padStart(2, '0')
|
||||||
return `${month}-${day}`;
|
return `${month}-${day}`
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatTag(tag: string[]) {
|
function formatTag(tag: string[]) {
|
||||||
return tag.map(t => `#${t}`).join(' ');
|
return tag.map(t => `#${t}`).join(' ')
|
||||||
}
|
}
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<div class="card-base px-8 py-6">
|
<div class="card-base px-8 py-6">
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
---
|
---
|
||||||
|
|
||||||
import {siteConfig} from "../config";
|
import { siteConfig } from '../config'
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<div id="config-carrier" data-hue={siteConfig.themeColor.hue}>
|
<div id="config-carrier" data-hue={siteConfig.themeColor.hue}>
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
---
|
---
|
||||||
|
|
||||||
import {profileConfig} from "../config";
|
import { profileConfig } from '../config'
|
||||||
import {url} from "../utils/url-utils";
|
import { url } from '../utils/url-utils'
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<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="card-base max-w-[var(--page-width)] min-h-[4.5rem] rounded-b-none mx-auto flex items-center px-6">
|
||||||
|
|
|
@ -1,10 +1,6 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { LIGHT_DARK_MODE } from '@/types/config.ts'
|
import type { LIGHT_DARK_MODE } from '@/types/config.ts'
|
||||||
import {
|
import { AUTO_MODE, DARK_MODE, LIGHT_MODE } from '@constants/constants.ts'
|
||||||
AUTO_MODE,
|
|
||||||
DARK_MODE,
|
|
||||||
LIGHT_MODE,
|
|
||||||
} from '@constants/constants.ts'
|
|
||||||
import I18nKey from '@i18n/i18nKey'
|
import I18nKey from '@i18n/i18nKey'
|
||||||
import { i18n } from '@i18n/translation'
|
import { i18n } from '@i18n/translation'
|
||||||
import Icon from '@iconify/svelte'
|
import Icon from '@iconify/svelte'
|
||||||
|
@ -15,27 +11,18 @@ import {
|
||||||
} from '@utils/setting-utils.ts'
|
} from '@utils/setting-utils.ts'
|
||||||
import { onMount } from 'svelte'
|
import { onMount } from 'svelte'
|
||||||
|
|
||||||
const seq: LIGHT_DARK_MODE[] = [
|
const seq: LIGHT_DARK_MODE[] = [LIGHT_MODE, DARK_MODE, AUTO_MODE]
|
||||||
LIGHT_MODE,
|
|
||||||
DARK_MODE,
|
|
||||||
AUTO_MODE,
|
|
||||||
]
|
|
||||||
let mode: LIGHT_DARK_MODE = AUTO_MODE
|
let mode: LIGHT_DARK_MODE = AUTO_MODE
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
mode = getStoredTheme()
|
mode = getStoredTheme()
|
||||||
const darkModePreference = window.matchMedia(
|
const darkModePreference = window.matchMedia('(prefers-color-scheme: dark)')
|
||||||
'(prefers-color-scheme: dark)',
|
|
||||||
)
|
|
||||||
const changeThemeWhenSchemeChanged: Parameters<
|
const changeThemeWhenSchemeChanged: Parameters<
|
||||||
typeof darkModePreference.addEventListener<'change'>
|
typeof darkModePreference.addEventListener<'change'>
|
||||||
>[1] = e => {
|
>[1] = e => {
|
||||||
applyThemeToDocument(mode)
|
applyThemeToDocument(mode)
|
||||||
}
|
}
|
||||||
darkModePreference.addEventListener(
|
darkModePreference.addEventListener('change', changeThemeWhenSchemeChanged)
|
||||||
'change',
|
|
||||||
changeThemeWhenSchemeChanged,
|
|
||||||
)
|
|
||||||
return () => {
|
return () => {
|
||||||
darkModePreference.removeEventListener(
|
darkModePreference.removeEventListener(
|
||||||
'change',
|
'change',
|
||||||
|
@ -50,25 +37,24 @@ function switchScheme(newMode: LIGHT_DARK_MODE) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleScheme() {
|
function toggleScheme() {
|
||||||
let i = 0
|
let i = 0
|
||||||
for (; i < seq.length; i++) {
|
for (; i < seq.length; i++) {
|
||||||
if (seq[i] === mode) {
|
if (seq[i] === mode) {
|
||||||
break
|
break
|
||||||
}
|
|
||||||
}
|
}
|
||||||
switchScheme(seq[(i + 1) % seq.length])
|
}
|
||||||
|
switchScheme(seq[(i + 1) % seq.length])
|
||||||
}
|
}
|
||||||
|
|
||||||
function showPanel() {
|
function showPanel() {
|
||||||
const panel = document.querySelector('#light-dark-panel')
|
const panel = document.querySelector('#light-dark-panel')
|
||||||
panel.classList.remove('float-panel-closed')
|
panel.classList.remove('float-panel-closed')
|
||||||
}
|
}
|
||||||
|
|
||||||
function hidePanel() {
|
function hidePanel() {
|
||||||
const panel = document.querySelector('#light-dark-panel')
|
const panel = document.querySelector('#light-dark-panel')
|
||||||
panel.classList.add('float-panel-closed')
|
panel.classList.add('float-panel-closed')
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- z-50 make the panel higher than other float panels -->
|
<!-- z-50 make the panel higher than other float panels -->
|
||||||
|
|
|
@ -1,22 +1,23 @@
|
||||||
---
|
---
|
||||||
import { Icon } from 'astro-icon/components';
|
import { Icon } from 'astro-icon/components'
|
||||||
import DisplaySettings from "./widget/DisplaySettings.svelte";
|
import DisplaySettings from './widget/DisplaySettings.svelte'
|
||||||
import {LinkPreset, type NavBarLink} from "../types/config";
|
import { LinkPreset, type NavBarLink } from '../types/config'
|
||||||
import {navBarConfig, siteConfig} from "../config";
|
import { navBarConfig, siteConfig } from '../config'
|
||||||
import NavMenuPanel from "./widget/NavMenuPanel.astro";
|
import NavMenuPanel from './widget/NavMenuPanel.astro'
|
||||||
import Search from "./Search.svelte";
|
import Search from './Search.svelte'
|
||||||
import {LinkPresets} from "../constants/link-presets";
|
import { LinkPresets } from '../constants/link-presets'
|
||||||
import LightDarkSwitch from "./LightDarkSwitch.svelte";
|
import LightDarkSwitch from './LightDarkSwitch.svelte'
|
||||||
import {url} from "../utils/url-utils";
|
import { url } from '../utils/url-utils'
|
||||||
const className = Astro.props.class;
|
const className = Astro.props.class
|
||||||
|
|
||||||
let links: NavBarLink[] = navBarConfig.links.map((item: NavBarLink | LinkPreset): NavBarLink => {
|
let links: NavBarLink[] = navBarConfig.links.map(
|
||||||
if (typeof item === "number") {
|
(item: NavBarLink | LinkPreset): NavBarLink => {
|
||||||
return LinkPresets[item]
|
if (typeof item === 'number') {
|
||||||
|
return LinkPresets[item]
|
||||||
}
|
}
|
||||||
return item;
|
return item
|
||||||
});
|
},
|
||||||
|
)
|
||||||
---
|
---
|
||||||
<div id="navbar" class="sticky top-0 z-50 onload-animation">
|
<div id="navbar" class="sticky top-0 z-50 onload-animation">
|
||||||
<div class="absolute h-8 left-0 right-0 -top-8 bg-[var(--card-bg)] transition"></div> <!-- used for onload animation -->
|
<div class="absolute h-8 left-0 right-0 -top-8 bg-[var(--card-bg)] transition"></div> <!-- used for onload animation -->
|
||||||
|
|
|
@ -1,34 +1,43 @@
|
||||||
---
|
---
|
||||||
import path from "path";
|
import path from 'path'
|
||||||
import PostMetadata from "./PostMeta.astro";
|
import PostMetadata from './PostMeta.astro'
|
||||||
import ImageWrapper from "./misc/ImageWrapper.astro";
|
import ImageWrapper from './misc/ImageWrapper.astro'
|
||||||
import { Icon } from 'astro-icon/components';
|
import { Icon } from 'astro-icon/components'
|
||||||
import {i18n} from "../i18n/translation";
|
import { i18n } from '../i18n/translation'
|
||||||
import I18nKey from "../i18n/i18nKey";
|
import I18nKey from '../i18n/i18nKey'
|
||||||
import {getDir} from "../utils/url-utils";
|
import { getDir } from '../utils/url-utils'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
class?: string;
|
class?: string
|
||||||
entry: any;
|
entry: any
|
||||||
title: string;
|
title: string
|
||||||
url: string;
|
url: string
|
||||||
published: Date;
|
published: Date
|
||||||
tags: string[];
|
tags: string[]
|
||||||
category: string;
|
category: string
|
||||||
image: string;
|
image: string
|
||||||
description: string;
|
description: string
|
||||||
draft: boolean;
|
draft: boolean
|
||||||
style: string;
|
style: string
|
||||||
}
|
}
|
||||||
const { entry, title, url, published, tags, category, image, description, style } = Astro.props;
|
const {
|
||||||
const className = Astro.props.class;
|
entry,
|
||||||
|
title,
|
||||||
|
url,
|
||||||
|
published,
|
||||||
|
tags,
|
||||||
|
category,
|
||||||
|
image,
|
||||||
|
description,
|
||||||
|
style,
|
||||||
|
} = Astro.props
|
||||||
|
const className = Astro.props.class
|
||||||
|
|
||||||
const hasCover = image !== undefined && image !== null && image !== '';
|
const hasCover = image !== undefined && image !== null && image !== ''
|
||||||
|
|
||||||
const coverWidth = "28%";
|
const coverWidth = '28%'
|
||||||
|
|
||||||
const { remarkPluginFrontmatter } = await entry.render();
|
|
||||||
|
|
||||||
|
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={["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}]}>
|
<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}]}>
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
---
|
---
|
||||||
import {formatDateToYYYYMMDD} from "../utils/date-utils";
|
import { formatDateToYYYYMMDD } from '../utils/date-utils'
|
||||||
import { Icon } from 'astro-icon/components';
|
import { Icon } from 'astro-icon/components'
|
||||||
import {i18n} from "../i18n/translation";
|
import { i18n } from '../i18n/translation'
|
||||||
import I18nKey from "../i18n/i18nKey";
|
import I18nKey from '../i18n/i18nKey'
|
||||||
import {url} from "../utils/url-utils";
|
import { url } from '../utils/url-utils'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
class: string;
|
class: string
|
||||||
published: Date;
|
published: Date
|
||||||
tags: string[];
|
tags: string[]
|
||||||
category: string;
|
category: string
|
||||||
hideTagsForMobile?: boolean;
|
hideTagsForMobile?: boolean
|
||||||
}
|
}
|
||||||
const {published, tags, category, hideTagsForMobile = false} = Astro.props;
|
const { published, tags, category, hideTagsForMobile = false } = Astro.props
|
||||||
const className = Astro.props.class;
|
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]}>
|
<div class:list={["flex flex-wrap text-neutral-500 dark:text-neutral-400 items-center gap-4 gap-x-4 gap-y-2", className]}>
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
---
|
---
|
||||||
import {getPostUrlBySlug} from "@utils/url-utils";
|
import { getPostUrlBySlug } from '@utils/url-utils'
|
||||||
import PostCard from "./PostCard.astro";
|
import PostCard from './PostCard.astro'
|
||||||
|
|
||||||
const {page} = Astro.props;
|
const { page } = Astro.props
|
||||||
|
|
||||||
let delay = 0
|
let delay = 0
|
||||||
const interval = 50
|
const interval = 50
|
||||||
|
|
|
@ -1,65 +1,68 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onMount } from 'svelte'
|
import { onMount } from 'svelte'
|
||||||
import {url} from "@utils/url-utils.ts"
|
import { url } from '@utils/url-utils.ts'
|
||||||
import { i18n } from '@i18n/translation';
|
import { i18n } from '@i18n/translation'
|
||||||
import I18nKey from '@i18n/i18nKey';
|
import I18nKey from '@i18n/i18nKey'
|
||||||
let keywordDesktop = ''
|
let keywordDesktop = ''
|
||||||
let keywordMobile = ''
|
let keywordMobile = ''
|
||||||
let result = []
|
let result = []
|
||||||
const fakeResult = [{
|
const fakeResult = [
|
||||||
|
{
|
||||||
url: url('/'),
|
url: url('/'),
|
||||||
meta: {
|
meta: {
|
||||||
title: 'This Is a Fake Search Result'
|
title: 'This Is a Fake Search Result',
|
||||||
},
|
},
|
||||||
excerpt: 'Because the search cannot work in the <mark>dev</mark> environment.'
|
excerpt:
|
||||||
}, {
|
'Because the search cannot work in the <mark>dev</mark> environment.',
|
||||||
|
},
|
||||||
|
{
|
||||||
url: url('/'),
|
url: url('/'),
|
||||||
meta: {
|
meta: {
|
||||||
title: 'If You Want to Test the Search'
|
title: 'If You Want to Test the Search',
|
||||||
},
|
},
|
||||||
excerpt: 'Try running <mark>npm build && npm preview</mark> instead.'
|
excerpt: 'Try running <mark>npm build && npm preview</mark> instead.',
|
||||||
}]
|
},
|
||||||
|
]
|
||||||
|
|
||||||
let search = (keyword: string, isDesktop: boolean) => {}
|
let search = (keyword: string, isDesktop: boolean) => {}
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
search = async (keyword: string, isDesktop: boolean) => {
|
search = async (keyword: string, isDesktop: boolean) => {
|
||||||
let panel = document.getElementById('search-panel')
|
let panel = document.getElementById('search-panel')
|
||||||
if (!panel)
|
if (!panel) return
|
||||||
return
|
|
||||||
|
|
||||||
if (!keyword && isDesktop) {
|
if (!keyword && isDesktop) {
|
||||||
panel.classList.add("float-panel-closed")
|
panel.classList.add('float-panel-closed')
|
||||||
return
|
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 = () => {
|
const togglePanel = () => {
|
||||||
let panel = document.getElementById('search-panel')
|
let panel = document.getElementById('search-panel')
|
||||||
panel?.classList.toggle("float-panel-closed")
|
panel?.classList.toggle('float-panel-closed')
|
||||||
}
|
}
|
||||||
|
|
||||||
$: search(keywordDesktop, true)
|
$: search(keywordDesktop, true)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
---
|
---
|
||||||
import { Icon } from 'astro-icon/components';
|
import { Icon } from 'astro-icon/components'
|
||||||
---
|
---
|
||||||
|
|
||||||
<!-- There can't be a filter on parent element, or it will break `fixed` -->
|
<!-- There can't be a filter on parent element, or it will break `fixed` -->
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
---
|
---
|
||||||
interface Props {
|
interface Props {
|
||||||
badge?: string
|
badge?: string
|
||||||
url?: string
|
url?: string
|
||||||
label?: string
|
label?: string
|
||||||
}
|
}
|
||||||
const { badge, url, label } = Astro.props
|
const { badge, url, label } = Astro.props
|
||||||
---
|
---
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
---
|
---
|
||||||
interface Props {
|
interface Props {
|
||||||
size?: string;
|
size?: string
|
||||||
dot?: boolean;
|
dot?: boolean
|
||||||
href?: string;
|
href?: string
|
||||||
label?: string;
|
label?: string
|
||||||
}
|
}
|
||||||
const { size, dot, href, label }: Props = Astro.props;
|
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">
|
<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>}
|
{dot && <div class="h-1 w-1 bg-[var(--btn-content)] dark:bg-[var(--card-bg)] transition rounded-md mr-2"></div>}
|
||||||
|
|
|
@ -1,61 +1,53 @@
|
||||||
---
|
---
|
||||||
import type { Page } from "astro";
|
import type { Page } from 'astro'
|
||||||
import { Icon } from 'astro-icon/components';
|
import { Icon } from 'astro-icon/components'
|
||||||
import {url} from "../../utils/url-utils";
|
import { url } from '../../utils/url-utils'
|
||||||
interface Props {
|
interface Props {
|
||||||
page: Page;
|
page: Page
|
||||||
class?: string;
|
class?: string
|
||||||
style?: string;
|
style?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const {page, style} = Astro.props;
|
const { page, style } = Astro.props
|
||||||
|
|
||||||
const HIDDEN = -1;
|
const HIDDEN = -1
|
||||||
|
|
||||||
const className = Astro.props.class;
|
const className = Astro.props.class
|
||||||
|
|
||||||
const ADJ_DIST = 2;
|
const ADJ_DIST = 2
|
||||||
const VISIBLE = ADJ_DIST * 2 + 1;
|
const VISIBLE = ADJ_DIST * 2 + 1
|
||||||
|
|
||||||
// for test
|
// for test
|
||||||
let count = 1;
|
let count = 1
|
||||||
let l = page.currentPage, r = page.currentPage;
|
let l = page.currentPage,
|
||||||
|
r = page.currentPage
|
||||||
while (0 < l - 1 && r + 1 <= page.lastPage && count + 2 <= VISIBLE) {
|
while (0 < l - 1 && r + 1 <= page.lastPage && count + 2 <= VISIBLE) {
|
||||||
count += 2;
|
count += 2
|
||||||
l--;
|
l--
|
||||||
r++;
|
r++
|
||||||
}
|
}
|
||||||
while (0 < l - 1 && count < VISIBLE) {
|
while (0 < l - 1 && count < VISIBLE) {
|
||||||
count++;
|
count++
|
||||||
l--;
|
l--
|
||||||
}
|
}
|
||||||
while (r + 1 <= page.lastPage && count < VISIBLE) {
|
while (r + 1 <= page.lastPage && count < VISIBLE) {
|
||||||
count++;
|
count++
|
||||||
r++;
|
r++
|
||||||
}
|
}
|
||||||
|
|
||||||
let pages: number[] = [];
|
let pages: number[] = []
|
||||||
if (l > 1)
|
if (l > 1) pages.push(1)
|
||||||
pages.push(1);
|
if (l == 3) pages.push(2)
|
||||||
if (l == 3)
|
if (l > 3) pages.push(HIDDEN)
|
||||||
pages.push(2);
|
for (let i = l; i <= r; i++) pages.push(i)
|
||||||
if (l > 3)
|
if (r < page.lastPage - 2) pages.push(HIDDEN)
|
||||||
pages.push(HIDDEN);
|
if (r == page.lastPage - 2) pages.push(page.lastPage - 1)
|
||||||
for (let i = l; i <= r; i++)
|
if (r < page.lastPage) pages.push(page.lastPage)
|
||||||
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) => {
|
const getPageUrl = (p: number) => {
|
||||||
if (p == 1)
|
if (p == 1) return '/'
|
||||||
return '/';
|
return `/${p}/`
|
||||||
return `/${p}/`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<div class:list={[className, "flex flex-row gap-3 justify-center"]} style={style}>
|
<div class:list={[className, "flex flex-row gap-3 justify-center"]} style={style}>
|
||||||
|
|
|
@ -1,35 +1,44 @@
|
||||||
---
|
---
|
||||||
import path from "path";
|
import path from 'path'
|
||||||
interface Props {
|
interface Props {
|
||||||
id?: string
|
id?: string
|
||||||
src: string;
|
src: string
|
||||||
class?: string;
|
class?: string
|
||||||
alt?: string
|
alt?: string
|
||||||
position?: string;
|
position?: string
|
||||||
basePath?: string
|
basePath?: string
|
||||||
}
|
}
|
||||||
import { Image } from 'astro:assets';
|
import { Image } from 'astro:assets'
|
||||||
import { url } from "../../utils/url-utils";
|
import { url } from '../../utils/url-utils'
|
||||||
|
|
||||||
const {id, src, alt, position = 'center', basePath = '/'} = Astro.props;
|
const { id, src, alt, position = 'center', basePath = '/' } = Astro.props
|
||||||
const className = Astro.props.class;
|
const className = Astro.props.class
|
||||||
|
|
||||||
const isLocal = !(src.startsWith('/') || src.startsWith('http') || src.startsWith('https') || src.startsWith('data:'));
|
const isLocal = !(
|
||||||
const isPublic = src.startsWith('/');
|
src.startsWith('/') ||
|
||||||
|
src.startsWith('http') ||
|
||||||
|
src.startsWith('https') ||
|
||||||
|
src.startsWith('data:')
|
||||||
|
)
|
||||||
|
const isPublic = src.startsWith('/')
|
||||||
|
|
||||||
// TODO temporary workaround for images dynamic import
|
// TODO temporary workaround for images dynamic import
|
||||||
// https://github.com/withastro/astro/issues/3373
|
// https://github.com/withastro/astro/issues/3373
|
||||||
let img;
|
let img
|
||||||
if (isLocal) {
|
if (isLocal) {
|
||||||
const files = import.meta.glob<ImageMetadata>("../../**", { import: 'default' });
|
const files = import.meta.glob<ImageMetadata>('../../**', {
|
||||||
let normalizedPath = path.normalize(path.join("../../", basePath, src)).replace(/\\/g, "/");
|
import: 'default',
|
||||||
img = await (files[normalizedPath])();
|
})
|
||||||
if (!img) {
|
let normalizedPath = path
|
||||||
console.error(`No image found for path ${normalizedPath}`);
|
.normalize(path.join('../../', basePath, src))
|
||||||
}
|
.replace(/\\/g, '/')
|
||||||
|
img = await files[normalizedPath]()
|
||||||
|
if (!img) {
|
||||||
|
console.error(`No image found for path ${normalizedPath}`)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const imageClass = 'w-full h-full object-cover';
|
const imageClass = 'w-full h-full object-cover'
|
||||||
const imageStyle = `object-position: ${position}`
|
const imageStyle = `object-position: ${position}`
|
||||||
---
|
---
|
||||||
<div id={id} class:list={[className, 'overflow-hidden relative']}>
|
<div id={id} class:list={[className, 'overflow-hidden relative']}>
|
||||||
|
|
|
@ -1,23 +1,22 @@
|
||||||
---
|
---
|
||||||
import {formatDateToYYYYMMDD} from "../../utils/date-utils";
|
import { formatDateToYYYYMMDD } from '../../utils/date-utils'
|
||||||
import { Icon } from 'astro-icon/components';
|
import { Icon } from 'astro-icon/components'
|
||||||
import {licenseConfig, profileConfig} from "../../config";
|
import { licenseConfig, profileConfig } from '../../config'
|
||||||
import {i18n} from "../../i18n/translation";
|
import { i18n } from '../../i18n/translation'
|
||||||
import I18nKey from "../../i18n/i18nKey";
|
import I18nKey from '../../i18n/i18nKey'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
title: string;
|
title: string
|
||||||
slug: string;
|
slug: string
|
||||||
pubDate: Date;
|
pubDate: Date
|
||||||
class: string;
|
class: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const { title, slug, pubDate } = Astro.props;
|
const { title, slug, pubDate } = Astro.props
|
||||||
const className = Astro.props.class;
|
const className = Astro.props.class
|
||||||
const profileConf = profileConfig;
|
const profileConf = profileConfig
|
||||||
const licenseConf = licenseConfig;
|
const licenseConf = licenseConfig
|
||||||
const postUrl = decodeURIComponent(Astro.url.toString());
|
const postUrl = decodeURIComponent(Astro.url.toString())
|
||||||
|
|
||||||
---
|
---
|
||||||
<div class=`relative transition overflow-hidden bg-[var(--license-block-bg)] py-5 px-6 ${className}`>
|
<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">
|
<div class="transition font-bold text-black/75 dark:text-white/75">
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
---
|
---
|
||||||
import '@fontsource-variable/jetbrains-mono';
|
import '@fontsource-variable/jetbrains-mono'
|
||||||
import '@fontsource-variable/jetbrains-mono/wght-italic.css';
|
import '@fontsource-variable/jetbrains-mono/wght-italic.css'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
class: string;
|
class: string
|
||||||
}
|
}
|
||||||
const className = Astro.props.class;
|
const className = Astro.props.class
|
||||||
---
|
---
|
||||||
<div data-pagefind-body class=`prose dark:prose-invert prose-base max-w-none custom-md ${className}`>
|
<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="prose dark:prose-invert max-w-none custom-md">-->
|
||||||
|
@ -46,12 +46,13 @@ const className = Astro.props.class;
|
||||||
wrapper.appendChild(codeBlock);
|
wrapper.appendChild(codeBlock);
|
||||||
wrapper.appendChild(copyButton);
|
wrapper.appendChild(copyButton);
|
||||||
|
|
||||||
let timeout;
|
let timeout: ReturnType<typeof setTimeout>;
|
||||||
copyButton.addEventListener("click", async () => {
|
copyButton.addEventListener("click", async () => {
|
||||||
if (timeout) {
|
if (timeout) {
|
||||||
clearTimeout(timeout);
|
clearTimeout(timeout);
|
||||||
}
|
}
|
||||||
let text = codeBlock?.querySelector("code")?.innerText;
|
let text = codeBlock?.querySelector("code")?.innerText;
|
||||||
|
if (text === undefined) return;
|
||||||
await navigator.clipboard.writeText(text);
|
await navigator.clipboard.writeText(text);
|
||||||
copyButton.classList.add("success");
|
copyButton.classList.add("success");
|
||||||
timeout = setTimeout(() => {
|
timeout = setTimeout(() => {
|
||||||
|
|
|
@ -1,26 +1,25 @@
|
||||||
---
|
---
|
||||||
import WidgetLayout from "./WidgetLayout.astro";
|
import WidgetLayout from './WidgetLayout.astro'
|
||||||
|
|
||||||
import {i18n} from "../../i18n/translation";
|
import { i18n } from '../../i18n/translation'
|
||||||
import I18nKey from "../../i18n/i18nKey";
|
import I18nKey from '../../i18n/i18nKey'
|
||||||
import {getCategoryList} from "../../utils/content-utils";
|
import { getCategoryList } from '../../utils/content-utils'
|
||||||
import {getCategoryUrl} from "../../utils/url-utils";
|
import { getCategoryUrl } from '../../utils/url-utils'
|
||||||
import ButtonLink from "../control/ButtonLink.astro";
|
import ButtonLink from '../control/ButtonLink.astro'
|
||||||
|
|
||||||
const categories = await getCategoryList();
|
const categories = await getCategoryList()
|
||||||
|
|
||||||
const COLLAPSED_HEIGHT = "7.5rem";
|
const COLLAPSED_HEIGHT = '7.5rem'
|
||||||
const COLLAPSE_THRESHOLD = 5;
|
const COLLAPSE_THRESHOLD = 5
|
||||||
|
|
||||||
const isCollapsed = categories.length >= COLLAPSE_THRESHOLD;
|
const isCollapsed = categories.length >= COLLAPSE_THRESHOLD
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
class?: string;
|
class?: string
|
||||||
style?: string;
|
style?: string
|
||||||
}
|
}
|
||||||
const className = Astro.props.class
|
const className = Astro.props.class
|
||||||
const style = Astro.props.style
|
const style = Astro.props.style
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<WidgetLayout name={i18n(I18nKey.categories)} id="categories" isCollapsed={isCollapsed} collapsedHeight={COLLAPSED_HEIGHT}
|
<WidgetLayout name={i18n(I18nKey.categories)} id="categories" isCollapsed={isCollapsed} collapsedHeight={COLLAPSED_HEIGHT}
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {i18n} from '@i18n/translation';
|
import { i18n } from '@i18n/translation'
|
||||||
import I18nKey from '@i18n/i18nKey';
|
import I18nKey from '@i18n/i18nKey'
|
||||||
import {getDefaultHue, getHue, setHue} from '@utils/setting-utils';
|
import { getDefaultHue, getHue, setHue } from '@utils/setting-utils'
|
||||||
|
|
||||||
let hue = getHue()
|
let hue = getHue()
|
||||||
const defaultHue = getDefaultHue()
|
const defaultHue = getDefaultHue()
|
||||||
|
|
||||||
function resetHue() {
|
function resetHue() {
|
||||||
hue = getDefaultHue()
|
hue = getDefaultHue()
|
||||||
}
|
}
|
||||||
|
|
||||||
$: if (hue || hue === 0) {
|
$: if (hue || hue === 0) {
|
||||||
setHue(hue)
|
setHue(hue)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
---
|
---
|
||||||
import {type NavBarLink} from "../../types/config";
|
import { type NavBarLink } from '../../types/config'
|
||||||
import {Icon} from "astro-icon/components";
|
import { Icon } from 'astro-icon/components'
|
||||||
import {url} from "../../utils/url-utils";
|
import { url } from '../../utils/url-utils'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
links: NavBarLink[],
|
links: NavBarLink[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const links = Astro.props.links;
|
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"]}>
|
<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) => (
|
{links.map((link) => (
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
---
|
---
|
||||||
import ImageWrapper from "../misc/ImageWrapper.astro";
|
import ImageWrapper from '../misc/ImageWrapper.astro'
|
||||||
import {Icon} from "astro-icon/components";
|
import { Icon } from 'astro-icon/components'
|
||||||
import {profileConfig} from "../../config";
|
import { profileConfig } from '../../config'
|
||||||
import {url} from "../../utils/url-utils";
|
import { url } from '../../utils/url-utils'
|
||||||
|
|
||||||
const config = profileConfig;
|
const config = profileConfig
|
||||||
---
|
---
|
||||||
<div class="card-base p-3">
|
<div class="card-base p-3">
|
||||||
<a aria-label="Go to About Page" href={url('/about/')}
|
<a aria-label="Go to About Page" href={url('/about/')}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
---
|
---
|
||||||
import Profile from "./Profile.astro";
|
import Profile from './Profile.astro'
|
||||||
import Tag from "./Tags.astro";
|
import Tag from './Tags.astro'
|
||||||
import Categories from "./Categories.astro";
|
import Categories from './Categories.astro'
|
||||||
|
|
||||||
const className = Astro.props.class;
|
const className = Astro.props.class
|
||||||
---
|
---
|
||||||
<div id="sidebar" class:list={[className, "w-full"]}>
|
<div id="sidebar" class:list={[className, "w-full"]}>
|
||||||
<div class="flex flex-col w-full gap-4 mb-4">
|
<div class="flex flex-col w-full gap-4 mb-4">
|
||||||
|
|
|
@ -1,25 +1,24 @@
|
||||||
---
|
---
|
||||||
|
|
||||||
import WidgetLayout from "./WidgetLayout.astro";
|
import WidgetLayout from './WidgetLayout.astro'
|
||||||
import ButtonTag from "../control/ButtonTag.astro";
|
import ButtonTag from '../control/ButtonTag.astro'
|
||||||
import {getTagList} from "../../utils/content-utils";
|
import { getTagList } from '../../utils/content-utils'
|
||||||
import {i18n} from "../../i18n/translation";
|
import { i18n } from '../../i18n/translation'
|
||||||
import I18nKey from "../../i18n/i18nKey";
|
import I18nKey from '../../i18n/i18nKey'
|
||||||
import {url} from "../../utils/url-utils";
|
import { url } from '../../utils/url-utils'
|
||||||
|
|
||||||
const tags = await getTagList();
|
const tags = await getTagList()
|
||||||
|
|
||||||
const COLLAPSED_HEIGHT = "7.5rem";
|
const COLLAPSED_HEIGHT = '7.5rem'
|
||||||
|
|
||||||
const isCollapsed = tags.length >= 20;
|
const isCollapsed = tags.length >= 20
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
class?: string;
|
class?: string
|
||||||
style?: string;
|
style?: string
|
||||||
}
|
}
|
||||||
const className = Astro.props.class
|
const className = Astro.props.class
|
||||||
const style = Astro.props.style
|
const style = Astro.props.style
|
||||||
|
|
||||||
---
|
---
|
||||||
<WidgetLayout name={i18n(I18nKey.tags)} id="tags" isCollapsed={isCollapsed} collapsedHeight={COLLAPSED_HEIGHT} class={className} style={style}>
|
<WidgetLayout name={i18n(I18nKey.tags)} id="tags" isCollapsed={isCollapsed} collapsedHeight={COLLAPSED_HEIGHT} class={className} style={style}>
|
||||||
<div class="flex gap-2 flex-wrap">
|
<div class="flex gap-2 flex-wrap">
|
||||||
|
|
|
@ -1,25 +1,18 @@
|
||||||
---
|
---
|
||||||
import { Icon } from 'astro-icon/components';
|
import { Icon } from 'astro-icon/components'
|
||||||
import {i18n} from "../../i18n/translation";
|
import { i18n } from '../../i18n/translation'
|
||||||
import I18nKey from "../../i18n/i18nKey";
|
import I18nKey from '../../i18n/i18nKey'
|
||||||
interface Props {
|
interface Props {
|
||||||
id: string;
|
id: string
|
||||||
name?: string;
|
name?: string
|
||||||
isCollapsed?: boolean;
|
isCollapsed?: boolean
|
||||||
collapsedHeight?: string;
|
collapsedHeight?: string
|
||||||
class?: string;
|
class?: string
|
||||||
style?: string;
|
style?: string
|
||||||
}
|
}
|
||||||
const props = Astro.props;
|
const props = Astro.props
|
||||||
const {
|
const { id, name, isCollapsed, collapsedHeight, style } = Astro.props
|
||||||
id,
|
|
||||||
name,
|
|
||||||
isCollapsed,
|
|
||||||
collapsedHeight,
|
|
||||||
style,
|
|
||||||
} = Astro.props
|
|
||||||
const className = Astro.props.class
|
const className = Astro.props.class
|
||||||
|
|
||||||
---
|
---
|
||||||
<widget-layout data-id={id} data-is-collapsed={String(isCollapsed)} class={"pb-4 card-base " + className} style={style}>
|
<widget-layout data-id={id} data-is-collapsed={String(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
|
<div class="font-bold transition text-lg text-neutral-900 dark:text-neutral-100 relative ml-8 mt-4 mb-2
|
||||||
|
|
|
@ -2,5 +2,7 @@ export const UNCATEGORIZED = '__uncategorized__'
|
||||||
|
|
||||||
export const PAGE_SIZE = 8
|
export const PAGE_SIZE = 8
|
||||||
|
|
||||||
export const LIGHT_MODE = 'light', DARK_MODE = 'dark', AUTO_MODE = 'auto'
|
export const LIGHT_MODE = 'light',
|
||||||
|
DARK_MODE = 'dark',
|
||||||
|
AUTO_MODE = 'auto'
|
||||||
export const DEFAULT_THEME = AUTO_MODE
|
export const DEFAULT_THEME = AUTO_MODE
|
|
@ -1,37 +1,44 @@
|
||||||
import type {Favicon} from "@/types/config.ts";
|
import type { Favicon } from '@/types/config.ts'
|
||||||
|
|
||||||
export const defaultFavicons: Favicon[] = [
|
export const defaultFavicons: Favicon[] = [
|
||||||
{
|
{
|
||||||
src: '/favicon/favicon-light-32.png',
|
src: '/favicon/favicon-light-32.png',
|
||||||
theme: 'light',
|
theme: 'light',
|
||||||
sizes: '32x32',
|
sizes: '32x32',
|
||||||
}, {
|
},
|
||||||
src: '/favicon/favicon-light-128.png',
|
{
|
||||||
theme: 'light',
|
src: '/favicon/favicon-light-128.png',
|
||||||
sizes: '128x128',
|
theme: 'light',
|
||||||
}, {
|
sizes: '128x128',
|
||||||
src: '/favicon/favicon-light-180.png',
|
},
|
||||||
theme: 'light',
|
{
|
||||||
sizes: '180x180',
|
src: '/favicon/favicon-light-180.png',
|
||||||
}, {
|
theme: 'light',
|
||||||
src: '/favicon/favicon-light-192.png',
|
sizes: '180x180',
|
||||||
theme: 'light',
|
},
|
||||||
sizes: '192x192',
|
{
|
||||||
}, {
|
src: '/favicon/favicon-light-192.png',
|
||||||
src: '/favicon/favicon-dark-32.png',
|
theme: 'light',
|
||||||
theme: 'dark',
|
sizes: '192x192',
|
||||||
sizes: '32x32',
|
},
|
||||||
}, {
|
{
|
||||||
src: '/favicon/favicon-dark-128.png',
|
src: '/favicon/favicon-dark-32.png',
|
||||||
theme: 'dark',
|
theme: 'dark',
|
||||||
sizes: '128x128',
|
sizes: '32x32',
|
||||||
}, {
|
},
|
||||||
src: '/favicon/favicon-dark-180.png',
|
{
|
||||||
theme: 'dark',
|
src: '/favicon/favicon-dark-128.png',
|
||||||
sizes: '180x180',
|
theme: 'dark',
|
||||||
}, {
|
sizes: '128x128',
|
||||||
src: '/favicon/favicon-dark-192.png',
|
},
|
||||||
theme: 'dark',
|
{
|
||||||
sizes: '192x192',
|
src: '/favicon/favicon-dark-180.png',
|
||||||
}
|
theme: 'dark',
|
||||||
|
sizes: '180x180',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: '/favicon/favicon-dark-192.png',
|
||||||
|
theme: 'dark',
|
||||||
|
sizes: '192x192',
|
||||||
|
},
|
||||||
]
|
]
|
|
@ -5,16 +5,16 @@ const postsCollection = defineCollection({
|
||||||
title: z.string(),
|
title: z.string(),
|
||||||
published: z.date(),
|
published: z.date(),
|
||||||
draft: z.boolean().optional().default(false),
|
draft: z.boolean().optional().default(false),
|
||||||
description: z.string().optional().default(""),
|
description: z.string().optional().default(''),
|
||||||
image: z.string().optional().default(""),
|
image: z.string().optional().default(''),
|
||||||
tags: z.array(z.string()).optional().default([]),
|
tags: z.array(z.string()).optional().default([]),
|
||||||
category: z.string().optional().default(""),
|
category: z.string().optional().default(''),
|
||||||
|
|
||||||
/* For internal use */
|
/* For internal use */
|
||||||
prevTitle: z.string().default(""),
|
prevTitle: z.string().default(''),
|
||||||
prevSlug: z.string().default(""),
|
prevSlug: z.string().default(''),
|
||||||
nextTitle: z.string().default(""),
|
nextTitle: z.string().default(''),
|
||||||
nextSlug: z.string().default(""),
|
nextSlug: z.string().default(''),
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
export const collections = {
|
export const collections = {
|
||||||
|
|
|
@ -22,7 +22,7 @@ const map: { [key: string]: Translation } = {
|
||||||
ja: ja,
|
ja: ja,
|
||||||
ja_jp: ja,
|
ja_jp: ja,
|
||||||
ko: ko,
|
ko: ko,
|
||||||
ko_kr: ko
|
ko_kr: ko,
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getTranslation(lang: string): Translation {
|
export function getTranslation(lang: string): Translation {
|
||||||
|
|
|
@ -297,7 +297,7 @@ const setup = () => {
|
||||||
document.documentElement.style.setProperty('--content-delay', '0ms')
|
document.documentElement.style.setProperty('--content-delay', '0ms')
|
||||||
})
|
})
|
||||||
window.swup.hooks.on('content:replace', initCustomScrollbar)
|
window.swup.hooks.on('content:replace', initCustomScrollbar)
|
||||||
window.swup.hooks.on('visit:start', (visit) => {
|
window.swup.hooks.on('visit:start', (visit: {to: {url: string}}) => {
|
||||||
// change banner height immediately when a link is clicked
|
// change banner height immediately when a link is clicked
|
||||||
const bodyElement = document.querySelector('body')
|
const bodyElement = document.querySelector('body')
|
||||||
if (pathsEqual(visit.to.url, url('/'))) {
|
if (pathsEqual(visit.to.url, url('/'))) {
|
||||||
|
@ -364,7 +364,6 @@ const setup = () => {
|
||||||
window.swup.hooks.on(
|
window.swup.hooks.on(
|
||||||
"content:replace",
|
"content:replace",
|
||||||
() => {
|
() => {
|
||||||
console.log("content:replace")
|
|
||||||
lightbox?.destroy?.()
|
lightbox?.destroy?.()
|
||||||
},
|
},
|
||||||
{ before: true },
|
{ before: true },
|
||||||
|
|
|
@ -4,8 +4,8 @@ import Navbar from '@components/Navbar.astro'
|
||||||
import BackToTop from '@components/control/BackToTop.astro'
|
import BackToTop from '@components/control/BackToTop.astro'
|
||||||
import SideBar from '@components/widget/SideBar.astro'
|
import SideBar from '@components/widget/SideBar.astro'
|
||||||
import Layout from './Layout.astro'
|
import Layout from './Layout.astro'
|
||||||
import { Icon } from 'astro-icon/components';
|
import { Icon } from 'astro-icon/components'
|
||||||
import { siteConfig } from '../config';
|
import { siteConfig } from '../config'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
title?: string
|
title?: string
|
||||||
|
@ -14,8 +14,9 @@ interface Props {
|
||||||
}
|
}
|
||||||
|
|
||||||
const { title, banner, description } = Astro.props
|
const { title, banner, description } = Astro.props
|
||||||
const hasBannerCredit = siteConfig.banner.enable && siteConfig.banner.credit.enable
|
const hasBannerCredit =
|
||||||
const hasBannerLink = !!(siteConfig.banner.credit.url)
|
siteConfig.banner.enable && siteConfig.banner.credit.enable
|
||||||
|
const hasBannerLink = !!siteConfig.banner.credit.url
|
||||||
---
|
---
|
||||||
|
|
||||||
<Layout title={title} banner={banner} description={description}>
|
<Layout title={title} banner={banner} description={description}>
|
||||||
|
|
|
@ -1,16 +1,15 @@
|
||||||
---
|
---
|
||||||
|
|
||||||
import MainGridLayout from "../layouts/MainGridLayout.astro";
|
import MainGridLayout from '../layouts/MainGridLayout.astro'
|
||||||
|
|
||||||
import { getEntry } from 'astro:content'
|
import { getEntry } from 'astro:content'
|
||||||
import {i18n} from "../i18n/translation";
|
import { i18n } from '../i18n/translation'
|
||||||
import I18nKey from "../i18n/i18nKey";
|
import I18nKey from '../i18n/i18nKey'
|
||||||
import Markdown from "@components/misc/Markdown.astro";
|
import Markdown from '@components/misc/Markdown.astro'
|
||||||
|
|
||||||
const aboutPost = await getEntry('spec', 'about')
|
const aboutPost = await getEntry('spec', 'about')
|
||||||
|
|
||||||
const { Content } = await aboutPost.render()
|
const { Content } = await aboutPost.render()
|
||||||
|
|
||||||
---
|
---
|
||||||
<MainGridLayout title={i18n(I18nKey.about)} description={i18n(I18nKey.about)}>
|
<MainGridLayout title={i18n(I18nKey.about)} description={i18n(I18nKey.about)}>
|
||||||
<div class="flex w-full rounded-[var(--radius-large)] overflow-hidden relative min-h-32">
|
<div class="flex w-full rounded-[var(--radius-large)] overflow-hidden relative min-h-32">
|
||||||
|
|
|
@ -1,23 +1,22 @@
|
||||||
---
|
---
|
||||||
import {getCategoryList} from "@utils/content-utils";
|
import { getCategoryList } from '@utils/content-utils'
|
||||||
import MainGridLayout from "@layouts/MainGridLayout.astro";
|
import MainGridLayout from '@layouts/MainGridLayout.astro'
|
||||||
import ArchivePanel from "@components/ArchivePanel.astro";
|
import ArchivePanel from '@components/ArchivePanel.astro'
|
||||||
import {i18n} from "@i18n/translation";
|
import { i18n } from '@i18n/translation'
|
||||||
import I18nKey from "@i18n/i18nKey";
|
import I18nKey from '@i18n/i18nKey'
|
||||||
|
|
||||||
export async function getStaticPaths() {
|
export async function getStaticPaths() {
|
||||||
const categories = await getCategoryList();
|
const categories = await getCategoryList()
|
||||||
return categories.map(category => {
|
return categories.map(category => {
|
||||||
return {
|
return {
|
||||||
params: {
|
params: {
|
||||||
category: category.name
|
category: category.name,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const category = Astro.params.category as string;
|
const category = Astro.params.category as string
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<MainGridLayout title={i18n(I18nKey.archive)} description={i18n(I18nKey.archive)}>
|
<MainGridLayout title={i18n(I18nKey.archive)} description={i18n(I18nKey.archive)}>
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
---
|
---
|
||||||
import MainGridLayout from "@layouts/MainGridLayout.astro";
|
import MainGridLayout from '@layouts/MainGridLayout.astro'
|
||||||
import ArchivePanel from "@components/ArchivePanel.astro";
|
import ArchivePanel from '@components/ArchivePanel.astro'
|
||||||
import {i18n} from "@i18n/translation";
|
import { i18n } from '@i18n/translation'
|
||||||
import I18nKey from "@i18n/i18nKey";
|
import I18nKey from '@i18n/i18nKey'
|
||||||
import {UNCATEGORIZED} from "@constants/constants";
|
import { UNCATEGORIZED } from '@constants/constants'
|
||||||
---
|
---
|
||||||
|
|
||||||
<MainGridLayout title={i18n(I18nKey.archive)}>
|
<MainGridLayout title={i18n(I18nKey.archive)}>
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
---
|
---
|
||||||
import MainGridLayout from "@layouts/MainGridLayout.astro";
|
import MainGridLayout from '@layouts/MainGridLayout.astro'
|
||||||
import ArchivePanel from "@components/ArchivePanel.astro";
|
import ArchivePanel from '@components/ArchivePanel.astro'
|
||||||
import {i18n} from "@i18n/translation";
|
import { i18n } from '@i18n/translation'
|
||||||
import I18nKey from "@i18n/i18nKey";
|
import I18nKey from '@i18n/i18nKey'
|
||||||
---
|
---
|
||||||
|
|
||||||
<MainGridLayout title={i18n(I18nKey.archive)}>
|
<MainGridLayout title={i18n(I18nKey.archive)}>
|
||||||
|
|
|
@ -1,31 +1,30 @@
|
||||||
---
|
---
|
||||||
import {getSortedPosts} from "@utils/content-utils";
|
import { getSortedPosts } from '@utils/content-utils'
|
||||||
import MainGridLayout from "@layouts/MainGridLayout.astro";
|
import MainGridLayout from '@layouts/MainGridLayout.astro'
|
||||||
import ArchivePanel from "@components/ArchivePanel.astro";
|
import ArchivePanel from '@components/ArchivePanel.astro'
|
||||||
import {i18n} from "@i18n/translation";
|
import { i18n } from '@i18n/translation'
|
||||||
import I18nKey from "@i18n/i18nKey";
|
import I18nKey from '@i18n/i18nKey'
|
||||||
|
|
||||||
export async function getStaticPaths() {
|
export async function getStaticPaths() {
|
||||||
let posts = await getSortedPosts()
|
let posts = await getSortedPosts()
|
||||||
|
|
||||||
const allTags = posts.reduce((acc, post) => {
|
const allTags = posts.reduce((acc, post) => {
|
||||||
post.data.tags.forEach(tag => acc.add(tag));
|
post.data.tags.forEach(tag => acc.add(tag))
|
||||||
return acc;
|
return acc
|
||||||
}, new Set());
|
}, new Set())
|
||||||
|
|
||||||
const allTagsArray = Array.from(allTags);
|
const allTagsArray = Array.from(allTags)
|
||||||
|
|
||||||
return allTagsArray.map(tag => {
|
return allTagsArray.map(tag => {
|
||||||
return {
|
return {
|
||||||
params: {
|
params: {
|
||||||
tag: tag
|
tag: tag,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const tag= Astro.params.tag as string;
|
const tag = Astro.params.tag as string
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<MainGridLayout title={i18n(I18nKey.archive)} description={i18n(I18nKey.archive)}>
|
<MainGridLayout title={i18n(I18nKey.archive)} description={i18n(I18nKey.archive)}>
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
import type { APIRoute } from 'astro';
|
import type { APIRoute } from 'astro'
|
||||||
|
|
||||||
const robotsTxt = `
|
const robotsTxt = `
|
||||||
User-agent: *
|
User-agent: *
|
||||||
Allow: /
|
Allow: /
|
||||||
|
|
||||||
Sitemap: ${new URL('sitemap-index.xml', import.meta.env.SITE).href}
|
Sitemap: ${new URL('sitemap-index.xml', import.meta.env.SITE).href}
|
||||||
`.trim();
|
`.trim()
|
||||||
|
|
||||||
export const GET: APIRoute = () => {
|
export const GET: APIRoute = () => {
|
||||||
return new Response(robotsTxt, {
|
return new Response(robotsTxt, {
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'text/plain; charset=utf-8',
|
'Content-Type': 'text/plain; charset=utf-8',
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
|
|
|
@ -1,25 +1,25 @@
|
||||||
import rss from '@astrojs/rss';
|
import rss from '@astrojs/rss'
|
||||||
import {siteConfig} from '@/config';
|
import { siteConfig } from '@/config'
|
||||||
import sanitizeHtml from 'sanitize-html';
|
import sanitizeHtml from 'sanitize-html'
|
||||||
import MarkdownIt from 'markdown-it';
|
import MarkdownIt from 'markdown-it'
|
||||||
import {getSortedPosts} from "@utils/content-utils.ts";
|
import { getSortedPosts } from '@utils/content-utils.ts'
|
||||||
const parser = new MarkdownIt();
|
const parser = new MarkdownIt()
|
||||||
|
|
||||||
export async function GET(context: any) {
|
export async function GET(context: any) {
|
||||||
const blog = await getSortedPosts();
|
const blog = await getSortedPosts()
|
||||||
return rss({
|
return rss({
|
||||||
title: siteConfig.title,
|
title: siteConfig.title,
|
||||||
description: siteConfig.subtitle || 'No description',
|
description: siteConfig.subtitle || 'No description',
|
||||||
site: context.site,
|
site: context.site,
|
||||||
items: blog.map((post) => ({
|
items: blog.map(post => ({
|
||||||
title: post.data.title,
|
title: post.data.title,
|
||||||
pubDate: post.data.published,
|
pubDate: post.data.published,
|
||||||
description: post.data.description,
|
description: post.data.description,
|
||||||
link: `/posts/${post.slug}/`,
|
link: `/posts/${post.slug}/`,
|
||||||
content: sanitizeHtml(parser.render(post.body), {
|
content: sanitizeHtml(parser.render(post.body), {
|
||||||
allowedTags: sanitizeHtml.defaults.allowedTags.concat(['img'])
|
allowedTags: sanitizeHtml.defaults.allowedTags.concat(['img']),
|
||||||
}),
|
}),
|
||||||
})),
|
})),
|
||||||
customData: `<language>${siteConfig.lang}</language>`,
|
customData: `<language>${siteConfig.lang}</language>`,
|
||||||
});
|
})
|
||||||
}
|
}
|
|
@ -11,21 +11,22 @@ import { h } from 'hastscript'
|
||||||
* @returns {import('mdast').Parent} The created admonition component.
|
* @returns {import('mdast').Parent} The created admonition component.
|
||||||
*/
|
*/
|
||||||
export function AdmonitionComponent(properties, children, type) {
|
export function AdmonitionComponent(properties, children, type) {
|
||||||
if (!Array.isArray(children) || children.length === 0)
|
if (!Array.isArray(children) || children.length === 0)
|
||||||
return h("div",
|
return h(
|
||||||
{ class: 'hidden' },
|
'div',
|
||||||
'Invalid admonition directive. (Admonition directives must be of block type ":::note{name="name"} <content> :::")'
|
{ class: 'hidden' },
|
||||||
);
|
'Invalid admonition directive. (Admonition directives must be of block type ":::note{name="name"} <content> :::")',
|
||||||
|
)
|
||||||
|
|
||||||
let label = null
|
let label = null
|
||||||
if (properties && properties['has-directive-label']) {
|
if (properties && properties['has-directive-label']) {
|
||||||
label = children[0]; // The first child is the label
|
label = children[0] // The first child is the label
|
||||||
children = children.slice(1);
|
children = children.slice(1)
|
||||||
label.tagName = "div"; // Change the tag <p> to <div>
|
label.tagName = 'div' // Change the tag <p> to <div>
|
||||||
}
|
}
|
||||||
|
|
||||||
return h(`blockquote`,
|
return h(`blockquote`, { class: `admonition bdm-${type}` }, [
|
||||||
{ class: `admonition bdm-${type}` },
|
h('span', { class: `bdm-title` }, label ? label : type.toUpperCase()),
|
||||||
[ h("span", { class: `bdm-title` }, label ? label : type.toUpperCase()), ...children]
|
...children,
|
||||||
);
|
])
|
||||||
}
|
}
|
|
@ -10,72 +10,53 @@ import { h } from 'hastscript'
|
||||||
* @returns {import('mdast').Parent} The created GitHub Card component.
|
* @returns {import('mdast').Parent} The created GitHub Card component.
|
||||||
*/
|
*/
|
||||||
export function GithubCardComponent(properties, children) {
|
export function GithubCardComponent(properties, children) {
|
||||||
if (Array.isArray(children) && children.length !== 0)
|
if (Array.isArray(children) && children.length !== 0)
|
||||||
return h("div",
|
return h('div', { class: 'hidden' }, [
|
||||||
{ class: 'hidden' },
|
'Invalid directive. ("github" directive must be leaf type "::github{repo="owner/repo"}")',
|
||||||
['Invalid directive. ("github" directive must be leaf type "::github{repo="owner/repo"}")']
|
])
|
||||||
);
|
|
||||||
|
|
||||||
if (!properties.repo || !properties.repo.includes("/"))
|
if (!properties.repo || !properties.repo.includes('/'))
|
||||||
return h("div",
|
return h(
|
||||||
{ class: 'hidden' },
|
'div',
|
||||||
'Invalid repository. ("repo" attributte must be in the format "owner/repo")'
|
{ class: 'hidden' },
|
||||||
);
|
'Invalid repository. ("repo" attributte must be in the format "owner/repo")',
|
||||||
|
)
|
||||||
|
|
||||||
const repo = properties.repo;
|
const repo = properties.repo
|
||||||
const cardUuid = `GC${Math.random().toString(36).slice(-6)}` // Collisions are not important
|
const cardUuid = `GC${Math.random().toString(36).slice(-6)}` // Collisions are not important
|
||||||
|
|
||||||
const nAvatar = h(
|
const nAvatar = h(`div#${cardUuid}-avatar`, { class: 'gc-avatar' })
|
||||||
`div#${cardUuid}-avatar`,
|
|
||||||
{ class: "gc-avatar"},
|
|
||||||
)
|
|
||||||
const nLanguage = h(
|
const nLanguage = h(
|
||||||
`span#${cardUuid}-language`,
|
`span#${cardUuid}-language`,
|
||||||
{ class: "gc-language" },
|
{ class: 'gc-language' },
|
||||||
"Waiting..."
|
'Waiting...',
|
||||||
)
|
)
|
||||||
|
|
||||||
const nTitle = h(
|
const nTitle = h(`div`, { class: 'gc-titlebar' }, [
|
||||||
`div`,
|
h('div', { class: 'gc-titlebar-left' }, [
|
||||||
{ class: "gc-titlebar" },
|
h('div', { class: 'gc-owner' }, [
|
||||||
[
|
nAvatar,
|
||||||
h("div", { class: "gc-titlebar-left"}, [
|
h('div', { class: 'gc-user' }, repo.split('/')[0]),
|
||||||
h("div", { class: "gc-owner" }, [
|
|
||||||
nAvatar,
|
|
||||||
h("div", { class: "gc-user" }, repo.split("/")[0] ),
|
|
||||||
]),
|
|
||||||
h("div", { class: "gc-divider" }, "/" ),
|
|
||||||
h("div", { class: "gc-repo" }, repo.split("/")[1] )
|
|
||||||
]),
|
]),
|
||||||
h("div", { class: "github-logo"})
|
h('div', { class: 'gc-divider' }, '/'),
|
||||||
]
|
h('div', { class: 'gc-repo' }, repo.split('/')[1]),
|
||||||
)
|
]),
|
||||||
|
h('div', { class: 'github-logo' }),
|
||||||
|
])
|
||||||
|
|
||||||
const nDescription = h(
|
const nDescription = h(
|
||||||
`div#${cardUuid}-description`,
|
`div#${cardUuid}-description`,
|
||||||
{ class: "gc-description" },
|
{ class: 'gc-description' },
|
||||||
"Waiting for api.github.com..."
|
'Waiting for api.github.com...',
|
||||||
)
|
)
|
||||||
|
|
||||||
const nStars = h(
|
const nStars = h(`div#${cardUuid}-stars`, { class: 'gc-stars' }, '00K')
|
||||||
`div#${cardUuid}-stars`,
|
const nForks = h(`div#${cardUuid}-forks`, { class: 'gc-forks' }, '0K')
|
||||||
{ class: "gc-stars" },
|
const nLicense = h(`div#${cardUuid}-license`, { class: 'gc-license' }, '0K')
|
||||||
"00K"
|
|
||||||
)
|
|
||||||
const nForks = h(
|
|
||||||
`div#${cardUuid}-forks`,
|
|
||||||
{ class: "gc-forks" },
|
|
||||||
"0K"
|
|
||||||
)
|
|
||||||
const nLicense = h(
|
|
||||||
`div#${cardUuid}-license`,
|
|
||||||
{ class: "gc-license" },
|
|
||||||
"0K"
|
|
||||||
)
|
|
||||||
|
|
||||||
const nScript = h(
|
const nScript = h(
|
||||||
`script#${cardUuid}-script`,
|
`script#${cardUuid}-script`,
|
||||||
{ type: "text/javascript", defer: true },
|
{ type: 'text/javascript', defer: true },
|
||||||
`
|
`
|
||||||
fetch('https://api.github.com/repos/${repo}', { referrerPolicy: "no-referrer" }).then(response => response.json()).then(data => {
|
fetch('https://api.github.com/repos/${repo}', { referrerPolicy: "no-referrer" }).then(response => response.json()).then(data => {
|
||||||
document.getElementById('${cardUuid}-description').innerText = data.description.replace(/:[a-zA-Z0-9_]+:/g, '');
|
document.getElementById('${cardUuid}-description').innerText = data.description.replace(/:[a-zA-Z0-9_]+:/g, '');
|
||||||
|
@ -97,22 +78,22 @@ export function GithubCardComponent(properties, children) {
|
||||||
c.classList.add("fetch-error");
|
c.classList.add("fetch-error");
|
||||||
console.warn("[GITHUB-CARD] (Error) Loading card for ${repo} | ${cardUuid}.")
|
console.warn("[GITHUB-CARD] (Error) Loading card for ${repo} | ${cardUuid}.")
|
||||||
})
|
})
|
||||||
`
|
`,
|
||||||
)
|
)
|
||||||
|
|
||||||
return h(`a#${cardUuid}-card`,
|
return h(
|
||||||
{ class: "card-github fetch-waiting no-styling",
|
`a#${cardUuid}-card`,
|
||||||
|
{
|
||||||
|
class: 'card-github fetch-waiting no-styling',
|
||||||
href: `https://github.com/${repo}`,
|
href: `https://github.com/${repo}`,
|
||||||
target: '_blank',
|
target: '_blank',
|
||||||
repo },
|
repo,
|
||||||
|
},
|
||||||
[
|
[
|
||||||
nTitle,
|
nTitle,
|
||||||
nDescription,
|
nDescription,
|
||||||
h("div",
|
h('div', { class: 'gc-infobar' }, [nStars, nForks, nLicense, nLanguage]),
|
||||||
{ class: "gc-infobar" },
|
nScript,
|
||||||
[nStars, nForks, nLicense, nLanguage]
|
],
|
||||||
),
|
)
|
||||||
nScript
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
}
|
|
@ -1,27 +1,30 @@
|
||||||
// biome-ignore lint/suspicious/noShadowRestrictedNames: <explanation>
|
// biome-ignore lint/suspicious/noShadowRestrictedNames: <explanation>
|
||||||
import { h } from 'hastscript';
|
import { h } from 'hastscript'
|
||||||
import {visit} from 'unist-util-visit'
|
import { visit } from 'unist-util-visit'
|
||||||
|
|
||||||
export function parseDirectiveNode() {
|
export function parseDirectiveNode() {
|
||||||
return (tree, { data }) => {
|
return (tree, { data }) => {
|
||||||
visit(tree, function (node) {
|
visit(tree, function (node) {
|
||||||
if (
|
if (
|
||||||
node.type === 'containerDirective' ||
|
node.type === 'containerDirective' ||
|
||||||
node.type === 'leafDirective' ||
|
node.type === 'leafDirective' ||
|
||||||
node.type === 'textDirective'
|
node.type === 'textDirective'
|
||||||
) {
|
) {
|
||||||
const data = node.data || (node.data = {})
|
const data = node.data || (node.data = {})
|
||||||
node.attributes = node.attributes || {}
|
node.attributes = node.attributes || {}
|
||||||
if (node.children.length > 0 && node.children[0].data && node.children[0].data.directiveLabel) {
|
if (
|
||||||
// Add a flag to the node to indicate that it has a directive label
|
node.children.length > 0 &&
|
||||||
node.attributes['has-directive-label'] = true
|
node.children[0].data &&
|
||||||
}
|
node.children[0].data.directiveLabel
|
||||||
const hast = h(node.name, node.attributes)
|
) {
|
||||||
|
// Add a flag to the node to indicate that it has a directive label
|
||||||
|
node.attributes['has-directive-label'] = true
|
||||||
|
}
|
||||||
|
const hast = h(node.name, node.attributes)
|
||||||
|
|
||||||
data.hName = hast.tagName
|
data.hName = hast.tagName
|
||||||
data.hProperties = hast.properties
|
data.hProperties = hast.properties
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,15 +2,15 @@ import { toString } from 'mdast-util-to-string'
|
||||||
|
|
||||||
/* Use the post's first paragraph as the excerpt */
|
/* Use the post's first paragraph as the excerpt */
|
||||||
export function remarkExcerpt() {
|
export function remarkExcerpt() {
|
||||||
return (tree, { data }) => {
|
return (tree, { data }) => {
|
||||||
let excerpt = '';
|
let excerpt = ''
|
||||||
for (let node of tree.children) {
|
for (let node of tree.children) {
|
||||||
if (node.type !== 'paragraph') {
|
if (node.type !== 'paragraph') {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
excerpt = toString(node)
|
excerpt = toString(node)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
data.astro.frontmatter.excerpt = excerpt
|
data.astro.frontmatter.excerpt = excerpt
|
||||||
};
|
}
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
import type { LIGHT_MODE, DARK_MODE, AUTO_MODE } from "@constants/constants"
|
import type { LIGHT_MODE, DARK_MODE, AUTO_MODE } from '@constants/constants'
|
||||||
|
|
||||||
export type SiteConfig = {
|
export type SiteConfig = {
|
||||||
title: string
|
title: string
|
||||||
|
@ -63,4 +63,7 @@ export type LicenseConfig = {
|
||||||
url: string
|
url: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type LIGHT_DARK_MODE = typeof LIGHT_MODE | typeof DARK_MODE | typeof AUTO_MODE
|
export type LIGHT_DARK_MODE =
|
||||||
|
| typeof LIGHT_MODE
|
||||||
|
| typeof DARK_MODE
|
||||||
|
| typeof AUTO_MODE
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
import {AUTO_MODE, DARK_MODE, DEFAULT_THEME, LIGHT_MODE} from "@constants/constants.ts";
|
import {
|
||||||
|
AUTO_MODE,
|
||||||
|
DARK_MODE,
|
||||||
|
DEFAULT_THEME,
|
||||||
|
LIGHT_MODE,
|
||||||
|
} from '@constants/constants.ts'
|
||||||
import type { LIGHT_DARK_MODE } from '@/types/config'
|
import type { LIGHT_DARK_MODE } from '@/types/config'
|
||||||
|
|
||||||
export function getDefaultHue(): number {
|
export function getDefaultHue(): number {
|
||||||
|
@ -21,7 +26,6 @@ export function setHue(hue: number): void {
|
||||||
r.style.setProperty('--hue', String(hue))
|
r.style.setProperty('--hue', String(hue))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function applyThemeToDocument(theme: LIGHT_DARK_MODE) {
|
export function applyThemeToDocument(theme: LIGHT_DARK_MODE) {
|
||||||
switch (theme) {
|
switch (theme) {
|
||||||
case LIGHT_MODE:
|
case LIGHT_MODE:
|
||||||
|
@ -46,5 +50,5 @@ export function setTheme(theme: LIGHT_DARK_MODE): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getStoredTheme(): LIGHT_DARK_MODE {
|
export function getStoredTheme(): LIGHT_DARK_MODE {
|
||||||
return localStorage.getItem('theme') as LIGHT_DARK_MODE || DEFAULT_THEME
|
return (localStorage.getItem('theme') as LIGHT_DARK_MODE) || DEFAULT_THEME
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ export function pathsEqual(path1: string, path2: string) {
|
||||||
|
|
||||||
function joinUrl(...parts: string[]): string {
|
function joinUrl(...parts: string[]): string {
|
||||||
const joined = parts.join('/')
|
const joined = parts.join('/')
|
||||||
return joined.replace(/\/+/g, '/');
|
return joined.replace(/\/+/g, '/')
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getPostUrlBySlug(slug: string): string {
|
export function getPostUrlBySlug(slug: string): string {
|
||||||
|
|
Loading…
Reference in New Issue