first commit
This commit is contained in:
commit
216cd24b44
27 files changed
+18836
No files matched your search
@@ -0,0 +1,6 @@
|
|||||||
|
# BlogFlare Project Rules
|
||||||
|
|
||||||
|
## Package Manager
|
||||||
|
- **ALWAYS use `pnpm`** as the package manager for this project. Do not use `npm` or `yarn`.
|
||||||
|
- When running one-off scripts, use `pnpm dlx` instead of `npx`.
|
||||||
|
- When installing dependencies, use `pnpm install` or `pnpm add`.
|
||||||
+72
@@ -0,0 +1,72 @@
|
|||||||
|
# ========================
|
||||||
|
# 依赖包 (Dependencies)
|
||||||
|
# ========================
|
||||||
|
node_modules/
|
||||||
|
.pnpm-store/
|
||||||
|
|
||||||
|
# ========================
|
||||||
|
# 构建与输出产物 (Build outputs)
|
||||||
|
# ========================
|
||||||
|
# SvelteKit / Vite
|
||||||
|
/.svelte-kit
|
||||||
|
/build
|
||||||
|
/dist
|
||||||
|
.output
|
||||||
|
vite.config.js.timestamp-*
|
||||||
|
vite.config.ts.timestamp-*
|
||||||
|
|
||||||
|
# 部署产物 (Deployment)
|
||||||
|
.vercel
|
||||||
|
.netlify
|
||||||
|
.wrangler
|
||||||
|
|
||||||
|
# ========================
|
||||||
|
# 日志文件 (Logs)
|
||||||
|
# ========================
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
# ========================
|
||||||
|
# 环境变量 (Environment)
|
||||||
|
# ========================
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
!.env.example
|
||||||
|
!.env.test
|
||||||
|
|
||||||
|
# ========================
|
||||||
|
# 测试产物 (Testing)
|
||||||
|
# ========================
|
||||||
|
/coverage
|
||||||
|
/playwright-report
|
||||||
|
/test-results
|
||||||
|
/.nyc_output
|
||||||
|
|
||||||
|
# ========================
|
||||||
|
# 系统和编辑器文件 (OS & IDE)
|
||||||
|
# ========================
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
.idea/
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/settings.json
|
||||||
|
!.vscode/tasks.json
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.vscode/extensions.json
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
|
|
||||||
|
# ========================
|
||||||
|
# 其他缓存 (Misc Caches)
|
||||||
|
# ========================
|
||||||
|
*.tsbuildinfo
|
||||||
|
.eslintcache
|
||||||
|
.stylelintcache
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# Package Managers
|
||||||
|
package-lock.json
|
||||||
|
pnpm-lock.yaml
|
||||||
|
yarn.lock
|
||||||
|
bun.lock
|
||||||
|
bun.lockb
|
||||||
|
|
||||||
|
# Miscellaneous
|
||||||
|
/static/
|
||||||
Vendored
+7
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"recommendations": [
|
||||||
|
"svelte.svelte-vscode",
|
||||||
|
"esbenp.prettier-vscode",
|
||||||
|
"dbaeumer.vscode-eslint"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
# sv
|
||||||
|
|
||||||
|
Everything you need to build a Svelte project, powered by [`sv`](https://github.com/sveltejs/cli).
|
||||||
|
|
||||||
|
## Creating a project
|
||||||
|
|
||||||
|
If you're seeing this, you've probably already done this step. Congrats!
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# create a new project
|
||||||
|
npx sv create my-app
|
||||||
|
```
|
||||||
|
|
||||||
|
To recreate this project with the same configuration:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# recreate this project
|
||||||
|
npx sv@0.16.2 create --template minimal --types ts --add prettier eslint sveltekit-adapter="adapter:cloudflare+cfTarget:pages" mdsvex --no-install .
|
||||||
|
```
|
||||||
|
|
||||||
|
## Developing
|
||||||
|
|
||||||
|
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm run dev
|
||||||
|
|
||||||
|
# or start the server and open the app in a new browser tab
|
||||||
|
npm run dev -- --open
|
||||||
|
```
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
To create a production version of your app:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
You can preview the production build with `npm run preview`.
|
||||||
|
|
||||||
|
> To deploy your app, you may need to install an [adapter](https://svelte.dev/docs/kit/adapters) for your target environment.
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import prettier from 'eslint-config-prettier';
|
||||||
|
import path from 'node:path';
|
||||||
|
import js from '@eslint/js';
|
||||||
|
import svelte from 'eslint-plugin-svelte';
|
||||||
|
import { defineConfig, includeIgnoreFile } from 'eslint/config';
|
||||||
|
import globals from 'globals';
|
||||||
|
import ts from 'typescript-eslint';
|
||||||
|
|
||||||
|
const gitignorePath = path.resolve(import.meta.dirname, '.gitignore');
|
||||||
|
|
||||||
|
export default defineConfig(
|
||||||
|
includeIgnoreFile(gitignorePath),
|
||||||
|
js.configs.recommended,
|
||||||
|
ts.configs.recommended,
|
||||||
|
svelte.configs.recommended,
|
||||||
|
prettier,
|
||||||
|
svelte.configs.prettier,
|
||||||
|
{
|
||||||
|
languageOptions: { globals: { ...globals.browser, ...globals.node } },
|
||||||
|
rules: {
|
||||||
|
// typescript-eslint strongly recommend that you do not use the no-undef lint rule on TypeScript projects.
|
||||||
|
// see: https://typescript-eslint.io/troubleshooting/faqs/eslint/#i-get-errors-from-the-no-undef-rule-about-global-variables-not-being-defined-even-though-there-are-no-typescript-errors
|
||||||
|
"no-undef": 'off'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['**/*.svelte', '**/*.svelte.ts', '**/*.svelte.js'],
|
||||||
|
languageOptions: {
|
||||||
|
parserOptions: {
|
||||||
|
projectService: true,
|
||||||
|
extraFileExtensions: ['.svelte'],
|
||||||
|
parser: ts.parser
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Override or add rule settings here, such as:
|
||||||
|
// 'svelte/button-has-type': 'error'
|
||||||
|
rules: {}
|
||||||
|
}
|
||||||
|
);
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
{
|
||||||
|
"name": "blogflare",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.1",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite dev",
|
||||||
|
"build": "wrangler types && vite build",
|
||||||
|
"preview": "wrangler pages dev .svelte-kit/cloudflare --port 4173",
|
||||||
|
"prepare": "svelte-kit sync || echo ''",
|
||||||
|
"check": "wrangler types && svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||||
|
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||||
|
"lint": "prettier --check . && eslint .",
|
||||||
|
"format": "prettier --write .",
|
||||||
|
"gen": "wrangler types"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@eslint/js": "^10.0.1",
|
||||||
|
"@sveltejs/adapter-cloudflare": "^7.2.8",
|
||||||
|
"@sveltejs/kit": "^2.63.0",
|
||||||
|
"@sveltejs/vite-plugin-svelte": "^7.1.2",
|
||||||
|
"@types/dompurify": "^3.2.0",
|
||||||
|
"@types/node": "^24",
|
||||||
|
"eslint": "^10.4.1",
|
||||||
|
"eslint-config-prettier": "^10.1.8",
|
||||||
|
"eslint-plugin-svelte": "^3.19.0",
|
||||||
|
"globals": "^17.6.0",
|
||||||
|
"mdsvex": "^0.12.7",
|
||||||
|
"prettier": "^3.8.3",
|
||||||
|
"prettier-plugin-svelte": "^4.1.0",
|
||||||
|
"svelte": "^5.56.1",
|
||||||
|
"svelte-check": "^4.6.0",
|
||||||
|
"typescript": "^6.0.3",
|
||||||
|
"typescript-eslint": "^8.60.1",
|
||||||
|
"vite": "^8.0.16",
|
||||||
|
"wrangler": "^4.97.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"dompurify": "^3.4.11",
|
||||||
|
"highlight.js": "^11.11.1",
|
||||||
|
"marked": "^18.0.6",
|
||||||
|
"marked-highlight": "^2.2.4"
|
||||||
|
},
|
||||||
|
"pnpm": {
|
||||||
|
"onlyBuiltDependencies": [
|
||||||
|
"esbuild",
|
||||||
|
"workerd",
|
||||||
|
"sharp"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
Generated
+2793
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,4 @@
|
|||||||
|
allowBuilds:
|
||||||
|
workerd: true
|
||||||
|
sharp: true
|
||||||
|
esbuild: true
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
/** @type {import("prettier").Config} */
|
||||||
|
const config = {
|
||||||
|
useTabs: true,
|
||||||
|
singleQuote: true,
|
||||||
|
trailingComma: 'none',
|
||||||
|
printWidth: 100,
|
||||||
|
plugins: ['prettier-plugin-svelte'],
|
||||||
|
overrides: [{ files: '*.svelte', options: { parser: 'svelte' } }]
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
||||||
+52
@@ -0,0 +1,52 @@
|
|||||||
|
DROP TABLE IF EXISTS posts;
|
||||||
|
DROP TABLE IF EXISTS users;
|
||||||
|
DROP TABLE IF EXISTS tags;
|
||||||
|
DROP TABLE IF EXISTS post_tags;
|
||||||
|
|
||||||
|
CREATE TABLE users (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
username TEXT NOT NULL UNIQUE,
|
||||||
|
password_hash TEXT NOT NULL,
|
||||||
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE posts (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
slug TEXT NOT NULL UNIQUE,
|
||||||
|
title TEXT NOT NULL,
|
||||||
|
excerpt TEXT,
|
||||||
|
content TEXT NOT NULL,
|
||||||
|
author_id INTEGER NOT NULL,
|
||||||
|
published_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY (author_id) REFERENCES users(id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE tags (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
name TEXT NOT NULL UNIQUE
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE post_tags (
|
||||||
|
post_id INTEGER NOT NULL,
|
||||||
|
tag_id INTEGER NOT NULL,
|
||||||
|
PRIMARY KEY (post_id, tag_id),
|
||||||
|
FOREIGN KEY (post_id) REFERENCES posts(id) ON DELETE CASCADE,
|
||||||
|
FOREIGN KEY (tag_id) REFERENCES tags(id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Insert a default user (password should be hashed in a real scenario, this is a placeholder)
|
||||||
|
INSERT INTO users (username, password_hash) VALUES ('admin', 'admin');
|
||||||
|
|
||||||
|
-- Insert some dummy posts
|
||||||
|
INSERT INTO posts (slug, title, excerpt, content, author_id) VALUES
|
||||||
|
('hello-world', 'Hello, BlogFlare!', 'This is my first post on BlogFlare.', 'Welcome to my new blog powered by **SvelteKit** and **Cloudflare D1**.
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> This is a DoIt style admonition!
|
||||||
|
|
||||||
|
It is incredibly fast and cheap to host.', 1),
|
||||||
|
('void-theme', 'The Beauty of VOID', 'Typography is essential.', 'Typecho Theme VOID inspired this design. It uses a very clean layout.', 1);
|
||||||
|
|
||||||
|
INSERT INTO tags (name) VALUES ('Welcome'), ('Design');
|
||||||
|
INSERT INTO post_tags (post_id, tag_id) VALUES (1, 1), (2, 2);
|
||||||
+593
@@ -0,0 +1,593 @@
|
|||||||
|
@import url('https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@400;700&family=Open+Sans:wght@400;600;700&display=swap');
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--bg-color: #ffffff;
|
||||||
|
--text-color: rgba(0, 0, 0, 0.86);
|
||||||
|
--text-muted: rgba(0, 0, 0, 0.6);
|
||||||
|
--border-color: rgba(0, 0, 0, 0.05);
|
||||||
|
|
||||||
|
--header-bg: #000000;
|
||||||
|
--header-bg-blur: rgba(0, 0, 0, 0.85);
|
||||||
|
--header-text: #ffffff;
|
||||||
|
--header-text-hover: #ffffff;
|
||||||
|
|
||||||
|
--accent-color: #bd6262;
|
||||||
|
|
||||||
|
--font-sans: 'PingFang SC', 'Hiragino Sans GB', 'Microsoft Yahei', 'WenQuanYi Micro Hei', 'Segoe UI Emoji', 'Segoe UI Symbol', Helvetica, Arial, -apple-system, system-ui, sans-serif;
|
||||||
|
--font-serif: 'Noto Serif SC', var(--font-sans);
|
||||||
|
|
||||||
|
--layout-width: 860px;
|
||||||
|
|
||||||
|
--hero-height: 40vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
background-color: var(--bg-color);
|
||||||
|
color: var(--text-color);
|
||||||
|
line-height: 1.8;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, h2, h3, h4, h5, h6 {
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
color: var(--title-color);
|
||||||
|
font-weight: 600;
|
||||||
|
margin-top: 1.5em;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Base Layout */
|
||||||
|
.wrapper {
|
||||||
|
max-width: var(--layout-width);
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 0 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Header (VOID style - dark glass, centered links) */
|
||||||
|
.header {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 60px;
|
||||||
|
background-color: var(--header-bg);
|
||||||
|
color: var(--header-text);
|
||||||
|
z-index: 1000;
|
||||||
|
transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||||
|
}
|
||||||
|
|
||||||
|
@supports (backdrop-filter: blur(10px)) or (-webkit-backdrop-filter: blur(10px)) {
|
||||||
|
.header {
|
||||||
|
background-color: var(--header-bg-blur);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
-webkit-backdrop-filter: blur(10px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.header .container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center; /* Center the navigation */
|
||||||
|
align-items: center;
|
||||||
|
height: 100%;
|
||||||
|
max-width: var(--layout-width);
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 0 20px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header .brand {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 1.7rem;
|
||||||
|
letter-spacing: 0.075rem;
|
||||||
|
color: var(--header-text);
|
||||||
|
position: absolute;
|
||||||
|
left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header nav {
|
||||||
|
display: flex;
|
||||||
|
height: 100%;
|
||||||
|
justify-content: center;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header nav a {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: var(--header-text);
|
||||||
|
font-size: 0.95rem;
|
||||||
|
padding: 0 1rem;
|
||||||
|
height: 100%;
|
||||||
|
transition: 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header nav a:hover {
|
||||||
|
color: var(--header-text-hover);
|
||||||
|
background-color: rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* DoIt Style Home Profile */
|
||||||
|
.home-profile {
|
||||||
|
text-align: center;
|
||||||
|
padding: 8vh 0 2rem;
|
||||||
|
animation: fade-in 1s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-avatar {
|
||||||
|
padding: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-avatar img {
|
||||||
|
display: inline-block;
|
||||||
|
width: 8rem;
|
||||||
|
height: auto;
|
||||||
|
margin: 0 auto;
|
||||||
|
border-radius: 100%;
|
||||||
|
box-shadow: 0 0 0 0.3618em rgba(0, 0, 0, 0.05);
|
||||||
|
transition: all 0.4s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-avatar img:hover {
|
||||||
|
transform: translateY(-0.75rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-title {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: bold;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0.5rem;
|
||||||
|
color: var(--title-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-subtitle {
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: normal;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0.5rem;
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fade-in {
|
||||||
|
from { opacity: 0; transform: translateY(20px); }
|
||||||
|
to { opacity: 1; transform: translateY(0); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Post List & Grid Layout */
|
||||||
|
.post-list {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 30px;
|
||||||
|
margin: 2rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Single column toggle */
|
||||||
|
body.single-col .post-list {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 767px) {
|
||||||
|
.post-list {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
padding-bottom: 30px;
|
||||||
|
background-color: transparent;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 767px) {
|
||||||
|
.card {
|
||||||
|
border-bottom: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 767px) {
|
||||||
|
.card {
|
||||||
|
padding: 20px;
|
||||||
|
border: 2px solid var(--text-color);
|
||||||
|
border-bottom: 2px solid var(--text-color);
|
||||||
|
transition: 0.2s cubic-bezier(0.25, 0.46, 0.45, 0.94) border-color;
|
||||||
|
}
|
||||||
|
.card:hover {
|
||||||
|
border-color: var(--accent-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card .title {
|
||||||
|
font-size: 1.71rem;
|
||||||
|
font-weight: bold;
|
||||||
|
margin: 0 0 0.6rem 0;
|
||||||
|
color: var(--text-color);
|
||||||
|
line-height: 1.35;
|
||||||
|
transition: 0.2s color;
|
||||||
|
}
|
||||||
|
@media screen and (min-width: 767px) {
|
||||||
|
.card .title {
|
||||||
|
font-size: 1.85rem;
|
||||||
|
}
|
||||||
|
.card:hover .title {
|
||||||
|
color: var(--accent-color) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card .post-meta-index {
|
||||||
|
color: rgba(0, 0, 0, 0.125);
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: bold;
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
text-transform: uppercase;
|
||||||
|
margin-bottom: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card .headline {
|
||||||
|
color: #999999;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 300;
|
||||||
|
line-height: 1.3;
|
||||||
|
margin-top: 0.6rem;
|
||||||
|
margin-bottom: 0;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Article Detail */
|
||||||
|
.article-detail {
|
||||||
|
padding: 2rem 0 4rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-header {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-header h1 {
|
||||||
|
font-size: 2.2rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.headline-meta {
|
||||||
|
color: #999;
|
||||||
|
font-size: 1.42em;
|
||||||
|
font-weight: 300;
|
||||||
|
margin: 1.5em 0;
|
||||||
|
position: relative;
|
||||||
|
padding: 25px 0;
|
||||||
|
text-align: center;
|
||||||
|
border-top: 1px solid rgba(0, 0, 0, 0.05);
|
||||||
|
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.headline-meta::before,
|
||||||
|
.headline-meta::after {
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
top: -12.5px;
|
||||||
|
width: 25px;
|
||||||
|
height: 25px;
|
||||||
|
background-image: url(data:image/png;base64,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 truncated
|
||||||
|
background-size: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.headline-meta::after {
|
||||||
|
top: auto;
|
||||||
|
bottom: -12.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-content {
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
font-size: 1.05rem;
|
||||||
|
line-height: 2.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-content p {
|
||||||
|
margin-bottom: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* DoIt Style Code Block Wrapper */
|
||||||
|
.code-block {
|
||||||
|
position: relative;
|
||||||
|
margin: 1.5rem 0;
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
background-color: var(--code-background-color, #f5f5f5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-block-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
background-color: var(--code-header-bg, #e6e6e6);
|
||||||
|
padding: 0.4rem 0.8rem;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-title {
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-code-button {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
color: inherit;
|
||||||
|
padding: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
transition: color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-code-button:hover {
|
||||||
|
color: var(--accent-color, #bd6262);
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-block pre {
|
||||||
|
margin: 0;
|
||||||
|
padding: 1rem;
|
||||||
|
overflow: auto;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
line-height: 1.5;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-family: "Source Code Pro", Menlo, Consolas, Monaco, monospace;
|
||||||
|
background-color: transparent !important;
|
||||||
|
color: var(--text-color);
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-content code:not(pre code) {
|
||||||
|
background-color: rgba(0, 0, 0, 0.05);
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 0.2em 0.4em;
|
||||||
|
font-size: 85%;
|
||||||
|
color: #e96900;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-content blockquote {
|
||||||
|
margin: 1.5em 0;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
border-left: 0.25rem solid #6bd6fd;
|
||||||
|
background-color: rgba(107, 214, 253, 0.2);
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Admonitions (DoIt style - full 14 types) */
|
||||||
|
.admonition {
|
||||||
|
position: relative;
|
||||||
|
margin: 1rem 0;
|
||||||
|
padding: 0 0.75rem;
|
||||||
|
border-left: 0.25rem solid transparent;
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
}
|
||||||
|
|
||||||
|
.admonition .details-content {
|
||||||
|
max-height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: max-height 0.3s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admonition.open .details-content {
|
||||||
|
/* maxHeight is dynamically set by JS */
|
||||||
|
}
|
||||||
|
|
||||||
|
.admonition .admonition-title {
|
||||||
|
font-weight: bold;
|
||||||
|
margin: 0 -0.75rem;
|
||||||
|
padding: 0.25rem 1.8rem;
|
||||||
|
border-bottom: 1px solid transparent;
|
||||||
|
font-size: 1rem;
|
||||||
|
line-height: 1.6;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admonition .admonition-title::-webkit-details-marker {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admonition .admonition-title::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
right: 0.75rem;
|
||||||
|
top: 0.85rem;
|
||||||
|
width: 0.4rem;
|
||||||
|
height: 0.4rem;
|
||||||
|
border-right: 2px solid currentColor;
|
||||||
|
border-bottom: 2px solid currentColor;
|
||||||
|
transform: rotate(-45deg);
|
||||||
|
transition: transform 0.2s ease-in-out;
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admonition.open .admonition-title::after {
|
||||||
|
transform: rotate(45deg);
|
||||||
|
top: 0.7rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admonition .details-content {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admonition .admonition-content {
|
||||||
|
padding: 0.5rem 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admonition .admonition-content > p:first-child {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admonition .admonition-content > p:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admonition span.icon > svg {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
position: absolute;
|
||||||
|
top: 0.6rem;
|
||||||
|
left: 0.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admonition p {
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
font-size: 0.95rem;
|
||||||
|
line-height: 1.6;
|
||||||
|
margin-bottom: 0;
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.admonition:last-child {
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 1. note */
|
||||||
|
.admonition.note { border-left-color: #448aff; background-color: rgba(68, 138, 255, 0.1); }
|
||||||
|
.admonition.note .admonition-title { border-bottom-color: rgba(68, 138, 255, 0.1); background-color: rgba(68, 138, 255, 0.25); }
|
||||||
|
.admonition.note.open .admonition-title { background-color: rgba(68, 138, 255, 0.1); }
|
||||||
|
.admonition.note span.icon > svg { color: #448aff; }
|
||||||
|
|
||||||
|
/* 2. abstract */
|
||||||
|
.admonition.abstract { border-left-color: #00b0ff; background-color: rgba(0, 176, 255, 0.1); }
|
||||||
|
.admonition.abstract .admonition-title { border-bottom-color: rgba(0, 176, 255, 0.1); background-color: rgba(0, 176, 255, 0.25); }
|
||||||
|
.admonition.abstract.open .admonition-title { background-color: rgba(0, 176, 255, 0.1); }
|
||||||
|
.admonition.abstract span.icon > svg { color: #00b0ff; }
|
||||||
|
|
||||||
|
/* 3. info */
|
||||||
|
.admonition.info { border-left-color: #00b8d4; background-color: rgba(0, 184, 212, 0.1); }
|
||||||
|
.admonition.info .admonition-title { border-bottom-color: rgba(0, 184, 212, 0.1); background-color: rgba(0, 184, 212, 0.25); }
|
||||||
|
.admonition.info.open .admonition-title { background-color: rgba(0, 184, 212, 0.1); }
|
||||||
|
.admonition.info span.icon > svg { color: #00b8d4; }
|
||||||
|
|
||||||
|
/* 4. tip */
|
||||||
|
.admonition.tip { border-left-color: #00bfa5; background-color: rgba(0, 191, 165, 0.1); }
|
||||||
|
.admonition.tip .admonition-title { border-bottom-color: rgba(0, 191, 165, 0.1); background-color: rgba(0, 191, 165, 0.25); }
|
||||||
|
.admonition.tip.open .admonition-title { background-color: rgba(0, 191, 165, 0.1); }
|
||||||
|
.admonition.tip span.icon > svg { color: #00bfa5; }
|
||||||
|
|
||||||
|
/* 5. success */
|
||||||
|
.admonition.success { border-left-color: #00c853; background-color: rgba(0, 200, 83, 0.1); }
|
||||||
|
.admonition.success .admonition-title { border-bottom-color: rgba(0, 200, 83, 0.1); background-color: rgba(0, 200, 83, 0.25); }
|
||||||
|
.admonition.success.open .admonition-title { background-color: rgba(0, 200, 83, 0.1); }
|
||||||
|
.admonition.success span.icon > svg { color: #00c853; }
|
||||||
|
|
||||||
|
/* 6. question */
|
||||||
|
.admonition.question { border-left-color: #64dd17; background-color: rgba(100, 221, 23, 0.1); }
|
||||||
|
.admonition.question .admonition-title { border-bottom-color: rgba(100, 221, 23, 0.1); background-color: rgba(100, 221, 23, 0.25); }
|
||||||
|
.admonition.question.open .admonition-title { background-color: rgba(100, 221, 23, 0.1); }
|
||||||
|
.admonition.question span.icon > svg { color: #64dd17; }
|
||||||
|
|
||||||
|
/* 7. warning */
|
||||||
|
.admonition.warning { border-left-color: #ff9100; background-color: rgba(255, 145, 0, 0.1); }
|
||||||
|
.admonition.warning .admonition-title { border-bottom-color: rgba(255, 145, 0, 0.1); background-color: rgba(255, 145, 0, 0.25); }
|
||||||
|
.admonition.warning.open .admonition-title { background-color: rgba(255, 145, 0, 0.1); }
|
||||||
|
.admonition.warning span.icon > svg { color: #ff9100; }
|
||||||
|
|
||||||
|
/* 8. failure */
|
||||||
|
.admonition.failure { border-left-color: #ff5252; background-color: rgba(255, 82, 82, 0.1); }
|
||||||
|
.admonition.failure .admonition-title { border-bottom-color: rgba(255, 82, 82, 0.1); background-color: rgba(255, 82, 82, 0.25); }
|
||||||
|
.admonition.failure.open .admonition-title { background-color: rgba(255, 82, 82, 0.1); }
|
||||||
|
.admonition.failure span.icon > svg { color: #ff5252; }
|
||||||
|
|
||||||
|
/* 9. danger */
|
||||||
|
.admonition.danger { border-left-color: #ff1744; background-color: rgba(255, 23, 68, 0.1); }
|
||||||
|
.admonition.danger .admonition-title { border-bottom-color: rgba(255, 23, 68, 0.1); background-color: rgba(255, 23, 68, 0.25); }
|
||||||
|
.admonition.danger.open .admonition-title { background-color: rgba(255, 23, 68, 0.1); }
|
||||||
|
.admonition.danger span.icon > svg { color: #ff1744; }
|
||||||
|
|
||||||
|
/* 10. bug */
|
||||||
|
.admonition.bug { border-left-color: #f50057; background-color: rgba(245, 0, 87, 0.1); }
|
||||||
|
.admonition.bug .admonition-title { border-bottom-color: rgba(245, 0, 87, 0.1); background-color: rgba(245, 0, 87, 0.25); }
|
||||||
|
.admonition.bug.open .admonition-title { background-color: rgba(245, 0, 87, 0.1); }
|
||||||
|
.admonition.bug span.icon > svg { color: #f50057; }
|
||||||
|
|
||||||
|
/* 11. example */
|
||||||
|
.admonition.example { border-left-color: #651fff; background-color: rgba(101, 31, 255, 0.1); }
|
||||||
|
.admonition.example .admonition-title { border-bottom-color: rgba(101, 31, 255, 0.1); background-color: rgba(101, 31, 255, 0.25); }
|
||||||
|
.admonition.example.open .admonition-title { background-color: rgba(101, 31, 255, 0.1); }
|
||||||
|
.admonition.example span.icon > svg { color: #651fff; }
|
||||||
|
|
||||||
|
/* 12. quote */
|
||||||
|
.admonition.quote { border-left-color: #9e9e9e; background-color: rgba(159, 159, 159, 0.1); }
|
||||||
|
.admonition.quote .admonition-title { border-bottom-color: rgba(159, 159, 159, 0.1); background-color: rgba(159, 159, 159, 0.25); }
|
||||||
|
.admonition.quote.open .admonition-title { background-color: rgba(159, 159, 159, 0.1); }
|
||||||
|
.admonition.quote span.icon > svg { color: #9e9e9e; }
|
||||||
|
|
||||||
|
/* 13. important */
|
||||||
|
.admonition.important { border-left-color: #8957e5; background-color: rgba(137, 87, 229, 0.1); }
|
||||||
|
.admonition.important .admonition-title { border-bottom-color: rgba(137, 87, 229, 0.1); background-color: rgba(137, 87, 229, 0.25); }
|
||||||
|
.admonition.important.open .admonition-title { background-color: rgba(137, 87, 229, 0.1); }
|
||||||
|
.admonition.important span.icon > svg { color: #8957e5; }
|
||||||
|
|
||||||
|
/* 14. caution */
|
||||||
|
.admonition.caution { border-left-color: #da3633; background-color: rgba(218, 54, 51, 0.1); }
|
||||||
|
.admonition.caution .admonition-title { border-bottom-color: rgba(218, 54, 51, 0.1); background-color: rgba(218, 54, 51, 0.25); }
|
||||||
|
.admonition.caution.open .admonition-title { background-color: rgba(218, 54, 51, 0.1); }
|
||||||
|
.admonition.caution span.icon > svg { color: #da3633; }
|
||||||
|
|
||||||
|
/* Footer */
|
||||||
|
footer {
|
||||||
|
padding: 2rem 0;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: var(--text-muted);
|
||||||
|
border-top: 1px solid var(--border-color);
|
||||||
|
margin-top: 4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Floating Controllers */
|
||||||
|
#ctrler-panel {
|
||||||
|
position: fixed;
|
||||||
|
right: 20px;
|
||||||
|
bottom: 20px;
|
||||||
|
z-index: 100;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ctrler-item a {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
background-color: var(--header-bg);
|
||||||
|
color: #fff;
|
||||||
|
border-radius: 50%;
|
||||||
|
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
||||||
|
transition: 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ctrler-item a:hover {
|
||||||
|
background-color: var(--accent-color);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
Vendored
+21
@@ -0,0 +1,21 @@
|
|||||||
|
// See https://svelte.dev/docs/kit/types#app.d.ts
|
||||||
|
// for information about these interfaces
|
||||||
|
declare global {
|
||||||
|
namespace App {
|
||||||
|
interface Platform {
|
||||||
|
env: {
|
||||||
|
DB: D1Database;
|
||||||
|
} & Env;
|
||||||
|
context: ExecutionContext;
|
||||||
|
caches: CacheStorage & { default: Cache };
|
||||||
|
cf?: IncomingRequestCfProperties
|
||||||
|
}
|
||||||
|
|
||||||
|
// interface Error {}
|
||||||
|
// interface Locals {}
|
||||||
|
// interface PageData {}
|
||||||
|
// interface PageState {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export {};
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<meta name="text-scale" content="scale" />
|
||||||
|
%sveltekit.head%
|
||||||
|
</head>
|
||||||
|
<body data-sveltekit-preload-data="hover">
|
||||||
|
<div style="display: contents">%sveltekit.body%</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="107" height="128" viewBox="0 0 107 128"><title>svelte-logo</title><path d="M94.157 22.819c-10.4-14.885-30.94-19.297-45.792-9.835L22.282 29.608A29.92 29.92 0 0 0 8.764 49.65a31.5 31.5 0 0 0 3.108 20.231 30 30 0 0 0-4.477 11.183 31.9 31.9 0 0 0 5.448 24.116c10.402 14.887 30.942 19.297 45.791 9.835l26.083-16.624A29.92 29.92 0 0 0 98.235 78.35a31.53 31.53 0 0 0-3.105-20.232 30 30 0 0 0 4.474-11.182 31.88 31.88 0 0 0-5.447-24.116" style="fill:#ff3e00"/><path d="M45.817 106.582a20.72 20.72 0 0 1-22.237-8.243 19.17 19.17 0 0 1-3.277-14.503 18 18 0 0 1 .624-2.435l.49-1.498 1.337.981a33.6 33.6 0 0 0 10.203 5.098l.97.294-.09.968a5.85 5.85 0 0 0 1.052 3.878 6.24 6.24 0 0 0 6.695 2.485 5.8 5.8 0 0 0 1.603-.704L69.27 76.28a5.43 5.43 0 0 0 2.45-3.631 5.8 5.8 0 0 0-.987-4.371 6.24 6.24 0 0 0-6.698-2.487 5.7 5.7 0 0 0-1.6.704l-9.953 6.345a19 19 0 0 1-5.296 2.326 20.72 20.72 0 0 1-22.237-8.243 19.17 19.17 0 0 1-3.277-14.502 17.99 17.99 0 0 1 8.13-12.052l26.081-16.623a19 19 0 0 1 5.3-2.329 20.72 20.72 0 0 1 22.237 8.243 19.17 19.17 0 0 1 3.277 14.503 18 18 0 0 1-.624 2.435l-.49 1.498-1.337-.98a33.6 33.6 0 0 0-10.203-5.1l-.97-.294.09-.968a5.86 5.86 0 0 0-1.052-3.878 6.24 6.24 0 0 0-6.696-2.485 5.8 5.8 0 0 0-1.602.704L37.73 51.72a5.42 5.42 0 0 0-2.449 3.63 5.79 5.79 0 0 0 .986 4.372 6.24 6.24 0 0 0 6.698 2.486 5.8 5.8 0 0 0 1.602-.704l9.952-6.342a19 19 0 0 1 5.295-2.328 20.72 20.72 0 0 1 22.237 8.242 19.17 19.17 0 0 1 3.277 14.503 18 18 0 0 1-8.13 12.053l-26.081 16.622a19 19 0 0 1-5.3 2.328" style="fill:#fff"/></svg>
|
||||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1 @@
|
|||||||
|
// place files you want to import through the `$lib` alias in this folder.
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import '../app.css';
|
||||||
|
let { children } = $props();
|
||||||
|
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
import { page } from '$app/stores';
|
||||||
|
|
||||||
|
let scrolled = $state(false);
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
const handleScroll = () => {
|
||||||
|
scrolled = window.scrollY > 50;
|
||||||
|
};
|
||||||
|
window.addEventListener('scroll', handleScroll);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('scroll', handleScroll);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
function scrollToTop() {
|
||||||
|
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleSingleCol() {
|
||||||
|
document.body.classList.toggle('single-col');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<title>BlogFlare - A Pure Cloudflare Blog</title>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
|
<header class="header {scrolled ? 'scrolled' : ''}">
|
||||||
|
<div class="container">
|
||||||
|
<a href="/" class="brand">BlogFlare</a>
|
||||||
|
<nav>
|
||||||
|
<a href="/">首页</a>
|
||||||
|
<a href="/about">关于</a>
|
||||||
|
<a href="/admin">后台</a>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
{#if $page.url.pathname === '/'}
|
||||||
|
<div class="home-profile">
|
||||||
|
<div class="home-avatar">
|
||||||
|
<img src="data:image/png;base64,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 truncated
|
||||||
|
</div>
|
||||||
|
<h1 class="home-title">BlogFlare</h1>
|
||||||
|
<p class="home-subtitle">A high-performance blog powered by Cloudflare D1, R2, and KV.</p>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<main id="pjax-container" style="padding-top: {$page.url.pathname === '/' ? '30px' : '100px'}">
|
||||||
|
<div class="wrapper container wide">
|
||||||
|
{@render children()}
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<div class="container wide">
|
||||||
|
<section>
|
||||||
|
<p>© {new Date().getFullYear()} <span class="brand">BlogFlare</span></p>
|
||||||
|
<p>感谢陪伴:<span id="uptime"></span></p>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<p>Powered by <a href="https://kit.svelte.dev/">SvelteKit</a> & <a href="https://developers.cloudflare.com/">Cloudflare</a></p>
|
||||||
|
<p>Theme inspired by <a href="https://github.com/AlanDecode/Typecho-Theme-VOID">VOID</a> & <a href="https://github.com/HEIGE-PCloud/DoIt">DoIt</a></p>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<!-- Floating Controllers (VOID style) -->
|
||||||
|
<aside id="ctrler-panel">
|
||||||
|
<div class="ctrler-item" title="切换排版结构">
|
||||||
|
<a href="javascript:void(0);" onclick={toggleSingleCol}>
|
||||||
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><line x1="12" y1="3" x2="12" y2="21"></line></svg>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="ctrler-item" title="返回顶部">
|
||||||
|
<a href="javascript:void(0);" onclick={scrollToTop}>
|
||||||
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 15l-6-6-6 6"/></svg>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import type { PageServerLoad } from './$types';
|
||||||
|
|
||||||
|
export const load: PageServerLoad = async ({ platform }) => {
|
||||||
|
const db = platform?.env?.DB;
|
||||||
|
|
||||||
|
if (!db) {
|
||||||
|
// Fallback data if DB is not available (e.g. during dev without wrangler, or missing bindings)
|
||||||
|
return {
|
||||||
|
posts: [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
title: 'Hello, BlogFlare! (Fallback)',
|
||||||
|
excerpt: 'This is my first post on BlogFlare. Welcome to my new blog powered by SvelteKit.',
|
||||||
|
date: '2026-07-12',
|
||||||
|
slug: 'hello-world'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
title: 'The Beauty of Typecho Theme VOID',
|
||||||
|
excerpt: 'VOID is a minimalist, typography-oriented theme that brings elegance to your reading experience.',
|
||||||
|
date: '2026-07-11',
|
||||||
|
slug: 'beauty-of-void'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { results } = await db.prepare(
|
||||||
|
`SELECT p.id, p.slug, p.title, p.excerpt, p.published_at as date
|
||||||
|
FROM posts p
|
||||||
|
ORDER BY p.published_at DESC LIMIT 10`
|
||||||
|
).all();
|
||||||
|
|
||||||
|
// Convert date to string for serialization
|
||||||
|
const formattedPosts = results.map(post => ({
|
||||||
|
...post,
|
||||||
|
date: new Date(post.date as string).toLocaleDateString()
|
||||||
|
}));
|
||||||
|
|
||||||
|
return {
|
||||||
|
posts: formattedPosts
|
||||||
|
};
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error fetching posts from D1:', err);
|
||||||
|
return { posts: [] };
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type { PageData } from './$types';
|
||||||
|
let { data }: { data: PageData } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<section id="index-list">
|
||||||
|
<div class="post-list">
|
||||||
|
{#each data.posts as post}
|
||||||
|
<article class="card yue">
|
||||||
|
<div class="content-wrap">
|
||||||
|
<div class="post-meta-index">
|
||||||
|
<time>{post.date}</time>
|
||||||
|
</div>
|
||||||
|
<a href="/posts/{post.slug}">
|
||||||
|
<h1 class="title">{post.title}</h1>
|
||||||
|
</a>
|
||||||
|
<p class="headline">{post.excerpt}</p>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* Styles are now handled globally in app.css */
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
import type { PageServerLoad } from './$types';
|
||||||
|
import { error } from '@sveltejs/kit';
|
||||||
|
|
||||||
|
export const load: PageServerLoad = async ({ platform, params }) => {
|
||||||
|
const db = platform?.env?.DB;
|
||||||
|
const { slug } = params;
|
||||||
|
|
||||||
|
if (!db) {
|
||||||
|
const dummyMarkdown = `
|
||||||
|
欢迎来到 BlogFlare 博客系统。由于当前在 \`vite dev\` 模式下没有连接到本地数据库,您看到的是兜底(Fallback)渲染的测试内容。
|
||||||
|
|
||||||
|
### DoIt 风格的 Admonitions 测试
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> 这是一个 Note (笔记) 提示框,可以用来补充额外的说明信息。
|
||||||
|
|
||||||
|
> [!TIP]
|
||||||
|
> 这是一个 Tip (提示) 提示框,通常用来给出最佳实践或小技巧。
|
||||||
|
|
||||||
|
> [!WARNING]
|
||||||
|
> 这是一个 Warning (警告) 提示框,用来引起用户的注意,避免发生错误。
|
||||||
|
|
||||||
|
> [!DANGER]
|
||||||
|
> 这是一个 Danger (危险) 提示框,警告用户该操作可能产生严重后果(比如删除数据)。
|
||||||
|
|
||||||
|
正文测试结束。
|
||||||
|
`;
|
||||||
|
|
||||||
|
return {
|
||||||
|
post: {
|
||||||
|
title: slug === 'hello-world' ? 'Hello, BlogFlare! (Fallback)' : 'The Beauty of Typecho Theme VOID',
|
||||||
|
content: dummyMarkdown,
|
||||||
|
date: '2026-07-12',
|
||||||
|
author: 'Admin'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const post: any = await db.prepare(
|
||||||
|
`SELECT p.id, p.title, p.content, p.published_at as date, u.username as author
|
||||||
|
FROM posts p
|
||||||
|
JOIN users u ON p.author_id = u.id
|
||||||
|
WHERE p.slug = ?`
|
||||||
|
).bind(slug).first();
|
||||||
|
|
||||||
|
if (!post) {
|
||||||
|
error(404, 'Post not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
post: {
|
||||||
|
...post,
|
||||||
|
date: new Date(post.date as string).toLocaleDateString()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error fetching post:', err);
|
||||||
|
error(500, 'Internal Server Error');
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type { PageData } from './$types';
|
||||||
|
import { marked } from 'marked';
|
||||||
|
import { markedHighlight } from "marked-highlight";
|
||||||
|
import hljs from 'highlight.js';
|
||||||
|
import 'highlight.js/styles/github.css';
|
||||||
|
import DOMPurify from 'dompurify';
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
|
||||||
|
// Configure marked for code highlighting
|
||||||
|
marked.use(markedHighlight({
|
||||||
|
langPrefix: 'hljs language-',
|
||||||
|
highlight(code, lang) {
|
||||||
|
const language = hljs.getLanguage(lang) ? lang : 'plaintext';
|
||||||
|
return hljs.highlight(code, { language }).value;
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
let { data }: { data: PageData } = $props();
|
||||||
|
|
||||||
|
// Custom parsing for GitHub-style and Hugo-style Admonitions
|
||||||
|
function parseAdmonitions(markdown: string) {
|
||||||
|
const icons = {
|
||||||
|
note: '<svg viewBox="0 0 512 512" fill="currentColor" height="1em" width="1em"><path d="M290.74 93.24l128.02 128.02-277.99 277.99-114.14 12.6C11.35 513.54-1.56 500.62.14 485.34l12.7-114.22 277.9-277.88zm207.2-19.06l-60.11-60.11c-18.75-18.75-49.16-18.75-67.91 0l-56.55 56.55 128.02 128.02 56.55-56.55c18.75-18.76 18.75-49.16 0-67.91z"></path></svg>',
|
||||||
|
abstract: '<svg viewBox="0 0 512 512" fill="currentColor" height="1em" width="1em"><path d="M48 48a48 48 0 1 0 48 48 48.14 48.14 0 0 0-48-48zm0 160a48 48 0 1 0 48 48 48.14 48.14 0 0 0-48-48zm0 160a48 48 0 1 0 48 48 48.14 48.14 0 0 0-48-48zm448 16H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16z"></path></svg>',
|
||||||
|
info: '<svg viewBox="0 0 512 512" fill="currentColor" height="1em" width="1em"><path d="M256 8C119.043 8 8 119.083 8 256c0 136.997 111.043 248 248 248s248-111.003 248-248C504 119.083 392.957 8 256 8zm0 110c23.196 0 42 18.804 42 42s-18.804 42-42 42-42-18.804-42-42 18.804-42 42-42zm56 254c0 6.627-5.373 12-12 12h-88c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h12v-64h-12c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h64c6.627 0 12 5.373 12 12v100h12c6.627 0 12 5.373 12 12v24z"></path></svg>',
|
||||||
|
tip: '<svg viewBox="0 0 352 512" fill="currentColor" height="1em" width="1em"><path d="M96.06 454.35c.01 6.29 1.87 12.45 5.36 17.69l17.09 25.69a31.99 31.99 0 0 0 26.64 14.28h61.71a31.99 31.99 0 0 0 26.64-14.28l17.09-25.69a31.989 31.989 0 0 0 5.36-17.69l.04-38.35H96.01l.05 38.35zM0 176c0 44.37 16.45 84.85 43.56 115.78 16.64 18.99 42.74 58.8 52.42 92.16h160.03c9.68-33.35 35.78-73.16 52.42-92.16C335.55 260.85 352 220.37 352 176 352 78.8 273.2 0 176 0S0 78.8 0 176z"></path></svg>',
|
||||||
|
success: '<svg viewBox="0 0 512 512" fill="currentColor" height="1em" width="1em"><path d="M504 256c0 136.967-111.033 248-248 248S8 392.967 8 256 119.033 8 256 8s248 111.033 248 248zM227.314 387.314l184-184c6.248-6.248 6.248-16.379 0-22.627l-22.627-22.627c-6.248-6.249-16.379-6.249-22.628 0L216 308.118l-70.059-70.059c-6.248-6.248-16.379-6.248-22.628 0l-22.627 22.627c-6.248 6.248-6.248 16.379 0 22.627l104 104c6.249 6.249 16.379 6.249 22.628.001z"></path></svg>',
|
||||||
|
question: '<svg viewBox="0 0 512 512" fill="currentColor" height="1em" width="1em"><path d="M504 256c0 136.997-111.043 248-248 248S8 392.997 8 256C8 119.083 119.043 8 256 8s248 111.083 248 248zM262.655 90c-54.497 0-89.255 22.957-116.549 63.758-3.536 5.286-2.353 12.415 2.715 16.258l34.699 26.31c5.205 3.947 12.621 3.008 16.665-2.122 17.864-22.658 30.113-35.797 57.303-35.797 20.429 0 45.698 13.148 45.698 32.958 0 14.976-12.363 22.667-32.534 33.976C247.128 238.528 216 254.941 216 296v4c0 6.627 5.373 12 12 12h56c6.627 0 12-5.373 12-12v-1.333c0-28.462 83.186-29.647 83.186-106.667 0-58.002-60.165-102-116.531-102zM256 338c-25.365 0-46 20.635-46 46 0 25.364 20.635 46 46 46s46-20.636 46-46c0-25.365-20.635-46-46-46z"></path></svg>',
|
||||||
|
warning: '<svg viewBox="0 0 576 512" fill="currentColor" height="1em" width="1em"><path d="M569.517 440.013C587.975 472.007 564.806 512 527.94 512H48.054c-36.937 0-59.999-40.055-41.577-71.987L246.423 23.985c18.466-32.004 64.72-31.951 83.154 0l239.94 416.028zM288 354c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"></path></svg>',
|
||||||
|
failure: '<svg viewBox="0 0 512 512" fill="currentColor" height="1em" width="1em"><path d="M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm121.6 313.1c4.7 4.7 4.7 12.3 0 17L338 377.6c-4.7 4.7-12.3 4.7-17 0L256 312l-65.1 65.6c-4.7 4.7-12.3 4.7-17 0L134.4 338c-4.7-4.7-4.7-12.3 0-17l65.6-65-65.6-65.1c-4.7-4.7-4.7-12.3 0-17l39.6-39.6c4.7-4.7 12.3-4.7 17 0l65 65.7 65.1-65.6c4.7-4.7 12.3-4.7 17 0l39.6 39.6c4.7 4.7 4.7 12.3 0 17L312 256l65.6 65.1z"></path></svg>',
|
||||||
|
danger: '<svg viewBox="0 0 448 512" fill="currentColor" height="1em" width="1em"><path d="M439.15 453.06L297.17 384l141.99-69.06c7.9-3.95 11.11-13.56 7.15-21.46L432 264.85c-3.95-7.9-13.56-11.11-21.47-7.16L224 348.41 37.47 257.69c-7.9-3.95-17.51-.75-21.47 7.16L1.69 293.48c-3.96 7.9-.76 17.51 7.15 21.46L150.83 384 8.85 453.06c-7.9 3.95-11.11 13.56-7.15 21.47l14.31 28.63c3.95 7.9 13.56 11.11 21.47 7.15L224 419.59l186.53 90.72c7.9 3.95 17.51.75 21.47-7.15l14.31-28.63c3.95-7.91.75-17.52-7.16-21.47zM150 237.28l-5.48 25.87c-2.67 12.62 5.42 24.85 16.45 24.85h126.08c11.03 0 19.12-12.23 16.45-24.85l-5.5-25.87c41.78-22.41 70-62.8 70-109.28C368 61.85 303.73 0 224 0S80 61.85 80 128c0 46.48 28.22 86.87 70 109.28zM280 112c17.65 0 32 14.35 32 32s-14.35 32-32 32-32-14.35-32-32 14.35-32 32-32zm-112 0c17.65 0 32 14.35 32 32s-14.35 32-32 32-32-14.35-32-32 14.35-32 32-32z"></path></svg>',
|
||||||
|
bug: '<svg viewBox="0 0 512 512" fill="currentColor" height="1em" width="1em"><path d="M511.98 256.03c0-34.92-27.46-64-62.24-64h-38.38c-8.91-44.57-30.82-84.34-60.77-116.14l27.13-27.13c12.5-12.5 12.5-32.76 0-45.25l-22.63-22.63c-12.5-12.5-32.76-12.5-45.25 0l-43.14 43.14A220.617 220.617 0 0 0 256 16.03c-3.69 0-7.38.15-11.05.35l-43.15-43.15c-12.5-12.5-32.76-12.5-45.25 0l-22.63 22.63c-12.5 12.5-12.5 32.76 0 45.25l27.13 27.13c-29.95 31.8-51.86 71.57-60.77 116.14H62.26c-34.78 0-62.24 29.08-62.24 64 0 34.39 26.89 62.48 61 63.92V336.1c0 51.52 19.34 98.4 51.05 133.72l-25.56 25.56c-12.5 12.5-12.5 32.76 0 45.25l22.63 22.63c12.5 12.5 32.76 12.5 45.25 0l42.66-42.66c30.2 16.92 64.71 27.42 101 27.42s70.8-10.5 101-27.42l42.66 42.66c12.5 12.5 32.76 12.5 45.25 0l22.63-22.63c12.5-12.5 12.5-32.76 0-45.25l-25.56-25.56C430.68 434.5 450 387.62 450 336.1v-23.79c34.11-1.44 61.98-29.53 61.98-63.92v-8.36zM152 280.03c0-13.25 10.75-24 24-24s24 10.75 24 24v56c0 13.25-10.75 24-24 24s-24-10.75-24-24v-56zm160 0c0-13.25 10.75-24 24-24s24 10.75 24 24v56c0 13.25-10.75 24-24 24s-24-10.75-24-24v-56z"></path></svg>',
|
||||||
|
example: '<svg viewBox="0 0 512 512" fill="currentColor" height="1em" width="1em"><path d="M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h34.71L15.32 424.64A29.85 29.85 0 0 0 8 444v16c0 26.51 21.49 48 48 48h24a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H56c-8.84 0-16-7.16-16-16v-4.52a6.01 6.01 0 0 1 1.76-4.24l19.24-19.24a48.28 48.28 0 0 0 7.58 3.06L64 448c0 35.35 28.65 64 64 64s64-28.65 64-64-28.65-64-64-64c-9.67 0-18.77 2.21-27.05 6.04L75.64 361.7A29.8 29.8 0 0 0 56 352h-7.66c10.37-4.14 17.66-14.24 17.66-26.34v-3.31c0-15.46-12.54-28-28-28H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22c2.21 0 4 1.79 4 4v3.31c0 2.21-1.79 4-4 4H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h15.4c17.21 0 33.15 6.94 44.97 19.46l5.4-6.23a43.83 43.83 0 0 1 7.23-7.19h-15zM480 352H208a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h272a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM208 112h272a16 16 0 0 0 16-16V64a16 16 0 0 0-16-16H208a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16zM480 208H208a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h272a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM88 32H16C7.16 32 0 39.16 0 48v20c0 8.84 7.16 16 16 16h16v64c0 8.84 7.16 16 16 16h16c8.84 0 16-7.16 16-16V48c0-8.84-7.16-16-16-16h-48c0-8.84 7.16-16 16-16zm64 160c0-35.35-28.65-64-64-64s-64 28.65-64 64 28.65 64 64 64 64-28.65 64-64zm-32 0c0 17.67-14.33 32-32 32s-32-14.33-32-32 14.33-32 32-32 32 14.33 32 32zm0-24c0 17.67-14.33 32-32 32s-32-14.33-32-32 14.33-32 32-32 32 14.33 32 32zm0-24c0 17.67-14.33 32-32 32s-32-14.33-32-32 14.33-32 32-32 32 14.33 32 32z"></path></svg>',
|
||||||
|
quote: '<svg viewBox="0 0 512 512" fill="currentColor" height="1em" width="1em"><path d="M464 256h-80v-64c0-35.3 28.7-64 64-64h8c13.3 0 24-10.7 24-24V56c0-13.3-10.7-24-24-24h-8c-88.4 0-160 71.6-160 160v240c0 26.5 21.5 48 48 48h128c26.5 0 48-21.5 48-48V304c0-26.5-21.5-48-48-48zm-288 0H96v-64c0-35.3 28.7-64 64-64h8c13.3 0 24-10.7 24-24V56c0-13.3-10.7-24-24-24h-8C71.6 32 0 103.6 0 192v240c0 26.5 21.5 48 48 48h128c26.5 0 48-21.5 48-48V304c0-26.5-21.5-48-48-48z"></path></svg>',
|
||||||
|
important: '<svg viewBox="0 0 192 512" fill="currentColor" height="1em" width="1em"><path d="M176 432c0 44.112-35.888 80-80 80s-80-35.888-80-80 35.888-80 80-80 80 35.888 80 80zM25.26 25.33l7.12 256.33c.35 12.63 10.71 22.34 23.36 22.34h80.51c12.65 0 23.01-9.71 23.36-22.34l7.12-256.33c.37-13.37-10.36-24.33-23.75-24.33H49.02c-13.4 0-24.12 10.96-23.76 24.33z"></path></svg>',
|
||||||
|
caution: '<svg viewBox="0 0 512 512" fill="currentColor" height="1em" width="1em"><path d="M504 256c0 136.997-111.043 248-248 248S8 392.997 8 256C8 119.083 119.043 8 256 8s248 111.083 248 248zm-248 50c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"></path></svg>'
|
||||||
|
};
|
||||||
|
|
||||||
|
// Parse GitHub-style Admonitions
|
||||||
|
const githubRegex = /(?:^|\r?\n)> ?\[!(NOTE|ABSTRACT|INFO|TIP|SUCCESS|QUESTION|WARNING|FAILURE|DANGER|BUG|EXAMPLE|QUOTE|IMPORTANT|CAUTION)\][ \t]*(.*?)\r?\n((?:>.*\r?\n?)*)/gi;
|
||||||
|
let html = markdown.replace(githubRegex, (match, type, title, content) => {
|
||||||
|
const typeLower = type.toLowerCase();
|
||||||
|
const displayTitle = title ? title : type.charAt(0).toUpperCase() + type.slice(1).toLowerCase();
|
||||||
|
const icon = icons[typeLower as keyof typeof icons] || icons.note;
|
||||||
|
|
||||||
|
// Remove the leading "> " from each line of the content
|
||||||
|
const cleanContent = content.replace(/^> ?/gm, '');
|
||||||
|
|
||||||
|
return `\n<div class="details admonition ${typeLower} open">\n<div class="details-summary admonition-title"><span class="icon">${icon}</span><span>${displayTitle}</span></div>\n<div class="details-content">\n<div class="admonition-content">\n\n${cleanContent}\n\n</div>\n</div>\n</div>\n`;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Parse Hugo-style Admonitions: {{< admonition type "title" >}} content {{< /admonition >}}
|
||||||
|
const hugoRegex = /\{\{<\s*admonition\s+([a-zA-Z]+)(?:\s+"(.*?)")?\s*>\}\}\r?\n([\s\S]*?)\{\{<\s*\/admonition\s*>\}\}/gi;
|
||||||
|
html = html.replace(hugoRegex, (match, type, title, content) => {
|
||||||
|
const typeLower = type.toLowerCase();
|
||||||
|
const displayTitle = title ? title : type.charAt(0).toUpperCase() + type.slice(1).toLowerCase();
|
||||||
|
const icon = icons[typeLower as keyof typeof icons] || icons.note;
|
||||||
|
|
||||||
|
return `\n<div class="details admonition ${typeLower} open">\n<div class="details-summary admonition-title"><span class="icon">${icon}</span><span>${displayTitle}</span></div>\n<div class="details-content">\n<div class="admonition-content">\n\n${content}\n\n</div>\n</div>\n</div>\n`;
|
||||||
|
});
|
||||||
|
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rawContent = data.post.content;
|
||||||
|
const preprocessedContent = parseAdmonitions(rawContent);
|
||||||
|
|
||||||
|
// Parse markdown synchronously so it renders on the server (SSR)
|
||||||
|
// and doesn't rely on client-side effects which were causing the empty content.
|
||||||
|
const htmlContent = marked.parse(preprocessedContent) as string;
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
// Initialize details expand/collapse animation logic (DoIt style)
|
||||||
|
document.querySelectorAll('.details').forEach((detailsEl) => {
|
||||||
|
const summaryEl = detailsEl.querySelector('.details-summary') as HTMLElement;
|
||||||
|
if (!summaryEl) return;
|
||||||
|
const contentEl = summaryEl.nextElementSibling as HTMLElement;
|
||||||
|
if (!contentEl) return;
|
||||||
|
|
||||||
|
// If initially open, set max-height to none
|
||||||
|
if (detailsEl.classList.contains('open')) {
|
||||||
|
contentEl.style.maxHeight = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
summaryEl.addEventListener('click', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
if (detailsEl.classList.contains('open')) {
|
||||||
|
// Close it
|
||||||
|
// If it's none (fully expanded), set to pixel height first to allow transition
|
||||||
|
if (contentEl.style.maxHeight === 'none' || contentEl.style.maxHeight === '') {
|
||||||
|
contentEl.style.maxHeight = contentEl.scrollHeight + 'px';
|
||||||
|
// Force reflow to ensure the browser applies the pixel value before animating to 0
|
||||||
|
void contentEl.offsetHeight;
|
||||||
|
}
|
||||||
|
contentEl.style.maxHeight = '0px';
|
||||||
|
detailsEl.classList.remove('open');
|
||||||
|
} else {
|
||||||
|
// Open it
|
||||||
|
detailsEl.classList.add('open');
|
||||||
|
contentEl.style.maxHeight = contentEl.scrollHeight + 'px';
|
||||||
|
|
||||||
|
// Once transition finishes, switch to 'none' so it can resize dynamically
|
||||||
|
const transitionEndHandler = (e: Event) => {
|
||||||
|
if (e.target === contentEl && detailsEl.classList.contains('open')) {
|
||||||
|
contentEl.style.maxHeight = 'none';
|
||||||
|
}
|
||||||
|
contentEl.removeEventListener('transitionend', transitionEndHandler);
|
||||||
|
};
|
||||||
|
contentEl.addEventListener('transitionend', transitionEndHandler);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<title>{data.post.title} - BlogFlare</title>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
|
<main id="pjax-container">
|
||||||
|
<div class="wrapper container narrow">
|
||||||
|
<article class="article-detail yue">
|
||||||
|
<header class="article-header">
|
||||||
|
<h1 class="title">{data.post.title}</h1>
|
||||||
|
<div class="headline-meta">
|
||||||
|
<time>Posted on {data.post.date}</time>
|
||||||
|
<span class="word-count">• {data.post.author}</span>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<section class="articleBody article-content">
|
||||||
|
{@html htmlContent}
|
||||||
|
</section>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# allow crawling everything by default
|
||||||
|
User-agent: *
|
||||||
|
Disallow:
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"extends": "./.svelte-kit/tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"rewriteRelativeImportExtensions": true,
|
||||||
|
"allowJs": true,
|
||||||
|
"checkJs": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"strict": true,
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"types": [
|
||||||
|
"./worker-configuration.d.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import { mdsvex } from 'mdsvex';
|
||||||
|
import adapter from '@sveltejs/adapter-cloudflare';
|
||||||
|
import { sveltekit } from '@sveltejs/kit/vite';
|
||||||
|
import { defineConfig } from 'vite';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [
|
||||||
|
sveltekit({
|
||||||
|
compilerOptions: {
|
||||||
|
// Force runes mode for the project, except for libraries. Can be removed in svelte 6.
|
||||||
|
runes: ({ filename }) => filename.split(/[/\\]/).includes('node_modules') ? undefined : true
|
||||||
|
},
|
||||||
|
adapter: adapter(),
|
||||||
|
preprocess: [mdsvex({ extensions: ['.svx', '.md'] })],
|
||||||
|
extensions: ['.svelte', '.svx', '.md']
|
||||||
|
})
|
||||||
|
]
|
||||||
|
});
|
||||||
Vendored
+14704
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"$schema": "./node_modules/wrangler/config-schema.json",
|
||||||
|
"name": "blogflare",
|
||||||
|
"compatibility_date": "2026-07-12",
|
||||||
|
"compatibility_flags": [
|
||||||
|
"nodejs_als"
|
||||||
|
],
|
||||||
|
"pages_build_output_dir": ".svelte-kit/cloudflare",
|
||||||
|
"d1_databases": [
|
||||||
|
{
|
||||||
|
"binding": "DB",
|
||||||
|
"database_name": "blogflare-db",
|
||||||
|
"database_id": "local"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in new issue
Block a user