Compare commits

...

4 commits

Author SHA1 Message Date
grassblock
e605e4ed42 feat: search engine config for search bar without javascript 2025-05-23 18:00:42 +08:00
grassblock
b695fe9fd5 feat: icon for post reply via email 2025-05-23 17:59:08 +08:00
grassblock
cbdc63a6eb feat: post reply via email (WIP) 2025-05-23 17:11:25 +08:00
grassblock
a74b7619d0 feat: create new css-only ThemeSwitcher 2025-05-23 17:09:00 +08:00
6 changed files with 95 additions and 7 deletions

View file

@ -0,0 +1,4 @@
---
const { title } = Astro.props;
---
<a href={`mailto:test@email.com?subject=RE:${title}&body=Hi,\n\nI would like to reply to your post "${title}".`}>&#x21A9; Reply via Email</a>

View file

@ -1,13 +1,26 @@
---
import {siteConfig} from "../config";
const noscript = siteConfig.noClientJavaScript
const searchEngine = siteConfig.searchEngine || 'google'
const domain = Astro.url.host
---
{noscript ?
<form class="search-container" action="https://www.google.com/search" method="GET" target="_blank">
<form class="search-container" action={searchEngine === "duckduckgo" ? "https://duckduckgo.com/" :
searchEngine === "bing" ? "https://www.bing.com/search" :
"https://www.google.com/search"} method="GET" target="_blank">
<div>
<label for="search-input"><span class="command">search</span></label>
<input name="q" type="text" id="search-input" class="search-input" autocomplete="off" placeholder="Type to search..." />
<input type="hidden" name="as_sitesearch" value={Astro.url.host} />
{searchEngine === "duckduckgo" &&
<input type="hidden" name="sites" value={domain} />
}
{searchEngine === "google" &&
<input type="hidden" name="as_sitesearch" value={domain} />
}
{/* broken until M1cr0$0ft get support for it */}
{searchEngine === "bing" &&
<input type="hidden" name="site" value={domain} />
}
</div>
<input type="submit" style="display: none" />
</form>

View file

@ -0,0 +1,63 @@
---
---
<div class="theme-switcher">
<input type="checkbox" id="theme-toggle" class="theme-toggle" />
<label for="theme-toggle" class="theme-label">
<span>Theme</span>
</label>
</div>
<style>
.theme-switcher {
display: flex;
align-items: center;
justify-content: center;
}
/* Hide the checkbox but keep it accessible */
.theme-toggle {
opacity: 0;
cursor: pointer;
height: 1px;
width: 1px;
margin: -1px;
}
.theme-label span {
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
line-height: normal;
font-size: 0.9rem;
}
/* Theme Switch */
/* light theme (when nojs checkbox is checked) */
/* TODO: more compatible way */
body:has(.theme-toggle:checked) {
--bg-color: #eceff4;
--text-color: #2e3440;
--secondary-text-color: #4c566a;
--accent-color: #486090;
--border-color: #d1d5db;
--header-color: #2e3440;
--terminal-green: #4b644b;
--terminal-yellow: #ebcb8b;
--terminal-red: #bf616a;
}
@media (prefers-color-scheme: light) {
/* Dark theme (when nojs checkbox is checked) */
body:has(.theme-toggle:checked) {
--bg-color: #2e3440;
--text-color: #d8dee9;
--secondary-text-color: #c8c8c8;
--accent-color: #90a8c0;
--border-color: #3b4351;
--header-color: #eceff4;
--terminal-green: #a3be8c;
--terminal-yellow: #ebcb8b;
--terminal-red: #bf616a;
}
}
</style>

View file

@ -7,9 +7,10 @@ export const siteConfig = {
noClientJavaScript: false, // disable client-side javascript, this will:
// 1. disable all built-in client-side javascript from rendering
// 2. the full text search will be redirected to a search engine
// 3. the comments will be replaced with email reply
// 3. the comments will be globally disabled
// 4. the night mode & back to top will not use Javascript to function
// 5. the neko will be force-disabled
authorDefaultEmail: '',
// site components
navBarItems: [
// additional items in the navbar
@ -18,6 +19,9 @@ export const siteConfig = {
{ text: "RSS", link: "/rss.xml" },
{ text: "GitHub", link: "https://github.com/GrassBlock1/mercury" },
],
// search
// This only works when noClientJavaScript is enabled
searchEngine: 'bing', // 'google', 'duckduckgo', 'bing'(broken until M1cr0$0ft get support for it), defaults to 'google'
// footer
// yes you can write html safely here
customFooter: '<i>I have no mouth, and I must SCREAM</i>',

View file

@ -2,6 +2,7 @@
import '../styles/global.css';
import Search from '../components/Search.astro';
import ThemeSwitcher from '../components/ThemeSwitcher.astro';
import ThemeSwitcher_CSSOnly from '../components/ThemeSwitcher@CSSOnly.astro';
import BackToTop from "../components/BackToTop.astro";
import Meta from "../components/helper/head/Meta.astro";
@ -66,7 +67,7 @@ const { title = pageTitle, description = siteConfig.description, ogImage = "" }
<footer class="footer">
<div class="floating">
<BackToTop/>
<ThemeSwitcher/>
{noscript ? <ThemeSwitcher_CSSOnly/> : <ThemeSwitcher/>}
</div>
<div class="container">
<Fragment set:html={customFooter} />

View file

@ -7,6 +7,8 @@ import { unified } from "unified";
import { select } from "unist-util-select";
import remarkMdx from "remark-mdx";
import remarkParse from "remark-parse";
import {siteConfig} from "../../config";
import ReplyViaEmail from "../../components/ReplyViaEmail.astro";
export async function getStaticPaths() {
const blogEntries = await getCollection('posts');
@ -17,7 +19,7 @@ export async function getStaticPaths() {
const { entry } = Astro.props;
const { Content } = await entry.render();
const noscript = siteConfig.noClientJavaScript
const slug = Astro.params.slug;
// get featured image and use it as og:image
@ -45,9 +47,10 @@ const cover = customFeaturedImage || matchedImage_src?.src || `/post/${slug}/fea
</div>
<div style="margin-top: 2rem; border-top: 1px solid var(--border-color); padding-top: 1rem;">
<h2>Comments</h2>
<Comments path={`post/${slug}`} />
<ReplyViaEmail title={entry.data.title} />
<br>
<a href="/blog">&larr; Back to posts</a>
{noscript && <h2>Comments</h2> <Comments path={`post/${slug}`} />}
</div>
</Layout>