new site style (#1)
* initial phase * remove pagefind * phase 2 * restyle blog post pages, markdown rendering, pagination, and social link components. * use dark-plus theme for markdown code * update base layout and header components, and update the remote deployment directory * use expressive code for code styling * adjust inline code style * format code * re-add pagefind * add sidebar with dev qotd * add sidebar component with dynamic quote fetching and caching * add Docker setup with Dockerfile, docker-compose, and dockerignore for the Astro site * integrate Docker Compose with Traefik proxy and remove the legacy PowerShell deployment script
This commit is contained in:
parent
bfb12ded11
commit
8d615bd421
37 changed files with 522 additions and 240 deletions
11
.dockerignore
Normal file
11
.dockerignore
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
node_modules
|
||||
dist
|
||||
.git
|
||||
.vscode
|
||||
.idea
|
||||
.astro
|
||||
.DS_Store
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
18
Dockerfile
Normal file
18
Dockerfile
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# Stage 1: Build
|
||||
FROM node:22-alpine AS build
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY package*.json ./
|
||||
RUN npm ci
|
||||
|
||||
COPY . .
|
||||
RUN npm run build
|
||||
|
||||
# Stage 2: Serve
|
||||
FROM joseluisq/static-web-server:2-alpine
|
||||
|
||||
ENV SERVER_ROOT=/public
|
||||
ENV SERVER_PORT=80
|
||||
|
||||
COPY --from=build /app/dist /public
|
||||
|
|
@ -1,13 +1,14 @@
|
|||
// @ts-check
|
||||
import tailwindcss from "@tailwindcss/vite";
|
||||
import { defineConfig } from "astro/config";
|
||||
|
||||
import tailwindcss from "@tailwindcss/vite";
|
||||
import expressiveCode from "astro-expressive-code";
|
||||
import pagefind from "astro-pagefind";
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
integrations: [pagefind()],
|
||||
vite: {
|
||||
plugins: [tailwindcss()],
|
||||
},
|
||||
integrations: [expressiveCode(), pagefind()],
|
||||
});
|
||||
|
|
|
|||
10
compose.yml
Normal file
10
compose.yml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
services:
|
||||
site-server:
|
||||
build: .
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- traefik-proxy
|
||||
|
||||
networks:
|
||||
traefik-proxy:
|
||||
external: true
|
||||
33
deploy.ps1
33
deploy.ps1
|
|
@ -1,33 +0,0 @@
|
|||
# Variables
|
||||
$remoteUser = "dhemas"
|
||||
$remoteHost = "dhemasnurjaya.com"
|
||||
$remoteDir = "/home/dhemas/apps/blog/public/"
|
||||
$localDir = "dist/"
|
||||
$keyPath = "D:\Secrets\giocloud-default.ppk"
|
||||
$winscpExecutable = "C:\Users\dhemas\AppData\Local\Programs\WinSCP\WinSCP.com" # Update this if needed
|
||||
|
||||
# Build astro site
|
||||
npm run build
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Host "Build failed!" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Generate WinSCP sync commands
|
||||
$scriptContent = @"
|
||||
open sftp://$remoteUser@$remoteHost -privatekey=$keyPath
|
||||
synchronize remote -delete -criteria=checksum $localDir $remoteDir
|
||||
exit
|
||||
"@
|
||||
|
||||
# Save the script to a temporary file
|
||||
$tempScript = [System.IO.Path]::GetTempFileName()
|
||||
Set-Content -Path $tempScript -Value $scriptContent
|
||||
|
||||
# Execute the WinSCP command
|
||||
& $winscpExecutable /script=$tempScript
|
||||
|
||||
# Clean up
|
||||
Remove-Item $tempScript
|
||||
|
||||
Write-Host "Deployed to $remoteHost!"
|
||||
11
ec.config.mjs
Normal file
11
ec.config.mjs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import { defineEcConfig } from "astro-expressive-code";
|
||||
|
||||
export default defineEcConfig({
|
||||
themes: ["dark-plus"],
|
||||
styleOverrides: {
|
||||
borderRadius: "0rem",
|
||||
frames: {
|
||||
shadowColor: "transparent",
|
||||
},
|
||||
},
|
||||
});
|
||||
237
package-lock.json
generated
237
package-lock.json
generated
|
|
@ -8,12 +8,11 @@
|
|||
"name": "site-astro",
|
||||
"version": "0.0.1",
|
||||
"dependencies": {
|
||||
"@fontsource-variable/noto-sans": "^5.2.10",
|
||||
"@fontsource/jetbrains-mono": "^5.2.8",
|
||||
"@tailwindcss/vite": "^4.1.18",
|
||||
"astro": "^5.16.10",
|
||||
"astro-expressive-code": "^0.41.6",
|
||||
"astro-pagefind": "^1.8.5",
|
||||
"lucide-astro": "^0.556.0",
|
||||
"tailwindcss": "^4.1.18"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
@ -152,6 +151,15 @@
|
|||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@ctrl/tinycolor": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-4.2.0.tgz",
|
||||
"integrity": "sha512-kzyuwOAQnXJNLS9PSyrk0CWk35nWJW/zl/6KvnTBMFK65gm7U1/Z5BqjxeapjZCIhQcM/DsrEmcbRwDyXyXK4A==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/@emnapi/runtime": {
|
||||
"version": "1.8.1",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz",
|
||||
|
|
@ -578,13 +586,49 @@
|
|||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@fontsource-variable/noto-sans": {
|
||||
"version": "5.2.10",
|
||||
"resolved": "https://registry.npmjs.org/@fontsource-variable/noto-sans/-/noto-sans-5.2.10.tgz",
|
||||
"integrity": "sha512-wyFgKkFu7jki5kEL8qv7avjQ8rxHX0J/nhLWvbR9T0hOH1HRKZEvb9EW9lMjZfWHHfEzKkYf5J+NadwgCS7TXA==",
|
||||
"license": "OFL-1.1",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ayuhito"
|
||||
"node_modules/@expressive-code/core": {
|
||||
"version": "0.41.6",
|
||||
"resolved": "https://registry.npmjs.org/@expressive-code/core/-/core-0.41.6.tgz",
|
||||
"integrity": "sha512-FvJQP+hG0jWi/FLBSmvHInDqWR7jNANp9PUDjdMqSshHb0y7sxx3vHuoOr6SgXjWw+MGLqorZyPQ0aAlHEok6g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@ctrl/tinycolor": "^4.0.4",
|
||||
"hast-util-select": "^6.0.2",
|
||||
"hast-util-to-html": "^9.0.1",
|
||||
"hast-util-to-text": "^4.0.1",
|
||||
"hastscript": "^9.0.0",
|
||||
"postcss": "^8.4.38",
|
||||
"postcss-nested": "^6.0.1",
|
||||
"unist-util-visit": "^5.0.0",
|
||||
"unist-util-visit-parents": "^6.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@expressive-code/plugin-frames": {
|
||||
"version": "0.41.6",
|
||||
"resolved": "https://registry.npmjs.org/@expressive-code/plugin-frames/-/plugin-frames-0.41.6.tgz",
|
||||
"integrity": "sha512-d+hkSYXIQot6fmYnOmWAM+7TNWRv/dhfjMsNq+mIZz8Tb4mPHOcgcfZeEM5dV9TDL0ioQNvtcqQNuzA1sRPjxg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@expressive-code/core": "^0.41.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@expressive-code/plugin-shiki": {
|
||||
"version": "0.41.6",
|
||||
"resolved": "https://registry.npmjs.org/@expressive-code/plugin-shiki/-/plugin-shiki-0.41.6.tgz",
|
||||
"integrity": "sha512-Y6zmKBmsIUtWTzdefqlzm/h9Zz0Rc4gNdt2GTIH7fhHH2I9+lDYCa27BDwuBhjqcos6uK81Aca9dLUC4wzN+ng==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@expressive-code/core": "^0.41.6",
|
||||
"shiki": "^3.2.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@expressive-code/plugin-text-markers": {
|
||||
"version": "0.41.6",
|
||||
"resolved": "https://registry.npmjs.org/@expressive-code/plugin-text-markers/-/plugin-text-markers-0.41.6.tgz",
|
||||
"integrity": "sha512-PBFa1wGyYzRExMDzBmAWC6/kdfG1oLn4pLpBeTfIRrALPjcGA/59HP3e7q9J0Smk4pC7U+lWkA2LHR8FYV8U7Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@expressive-code/core": "^0.41.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@fontsource/jetbrains-mono": {
|
||||
|
|
@ -2176,6 +2220,18 @@
|
|||
"sharp": "^0.34.0"
|
||||
}
|
||||
},
|
||||
"node_modules/astro-expressive-code": {
|
||||
"version": "0.41.6",
|
||||
"resolved": "https://registry.npmjs.org/astro-expressive-code/-/astro-expressive-code-0.41.6.tgz",
|
||||
"integrity": "sha512-l47tb1uhmVIebHUkw+HEPtU/av0G4O8Q34g2cbkPvC7/e9ZhANcjUUciKt9Hp6gSVDdIuXBBLwJQn2LkeGMOAw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"rehype-expressive-code": "^0.41.6"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"astro": "^4.0.0-beta || ^5.0.0-beta || ^3.3.0 || ^6.0.0-beta"
|
||||
}
|
||||
},
|
||||
"node_modules/astro-pagefind": {
|
||||
"version": "1.8.5",
|
||||
"resolved": "https://registry.npmjs.org/astro-pagefind/-/astro-pagefind-1.8.5.tgz",
|
||||
|
|
@ -2215,6 +2271,16 @@
|
|||
"integrity": "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/bcp-47-match": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/bcp-47-match/-/bcp-47-match-2.0.3.tgz",
|
||||
"integrity": "sha512-JtTezzbAibu8G0R9op9zb3vcWZd9JF6M0xOYGPn0fNCd7wOpRB1mU2mH9T8gaBGbAAyIIVgB2G7xG0GP98zMAQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/boolbase": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
|
||||
|
|
@ -2427,6 +2493,22 @@
|
|||
"url": "https://github.com/sponsors/fb55"
|
||||
}
|
||||
},
|
||||
"node_modules/css-selector-parser": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/css-selector-parser/-/css-selector-parser-3.3.0.tgz",
|
||||
"integrity": "sha512-Y2asgMGFqJKF4fq4xHDSlFYIkeVfRsm69lQC1q9kbEsH5XtnINTMrweLkjYMeaUgiXBy/uvKeO/a1JHTNnmB2g==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/mdevils"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://patreon.com/mdevils"
|
||||
}
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/css-tree": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.1.0.tgz",
|
||||
|
|
@ -2597,6 +2679,19 @@
|
|||
"node": ">=0.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/direction": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/direction/-/direction-2.0.1.tgz",
|
||||
"integrity": "sha512-9S6m9Sukh1cZNknO1CWAr2QAWsbKLafQiyM5gZ7VgXHeuaoUwffKN4q6NC4A/Mf9iiPlOXQEKW/Mv/mh9/3YFA==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"direction": "cli.js"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/dlv": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
|
||||
|
|
@ -2784,6 +2879,18 @@
|
|||
"integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/expressive-code": {
|
||||
"version": "0.41.6",
|
||||
"resolved": "https://registry.npmjs.org/expressive-code/-/expressive-code-0.41.6.tgz",
|
||||
"integrity": "sha512-W/5+IQbrpCIM5KGLjO35wlp1NCwDOOVQb+PAvzEoGkW1xjGM807ZGfBKptNWH6UECvt6qgmLyWolCMYKh7eQmA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@expressive-code/core": "^0.41.6",
|
||||
"@expressive-code/plugin-frames": "^0.41.6",
|
||||
"@expressive-code/plugin-shiki": "^0.41.6",
|
||||
"@expressive-code/plugin-text-markers": "^0.41.6"
|
||||
}
|
||||
},
|
||||
"node_modules/extend": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
|
||||
|
|
@ -2930,6 +3037,19 @@
|
|||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/hast-util-has-property": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/hast-util-has-property/-/hast-util-has-property-3.0.0.tgz",
|
||||
"integrity": "sha512-MNilsvEKLFpV604hwfhVStK0usFY/QmM5zX16bo7EjnAEGofr5YyI37kzopBlZJkHD4t887i+q/C8/tr5Q94cA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/hast": "^3.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/hast-util-is-element": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-3.0.0.tgz",
|
||||
|
|
@ -2981,6 +3101,33 @@
|
|||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/hast-util-select": {
|
||||
"version": "6.0.4",
|
||||
"resolved": "https://registry.npmjs.org/hast-util-select/-/hast-util-select-6.0.4.tgz",
|
||||
"integrity": "sha512-RqGS1ZgI0MwxLaKLDxjprynNzINEkRHY2i8ln4DDjgv9ZhcYVIHN9rlpiYsqtFwrgpYU361SyWDQcGNIBVu3lw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/hast": "^3.0.0",
|
||||
"@types/unist": "^3.0.0",
|
||||
"bcp-47-match": "^2.0.0",
|
||||
"comma-separated-tokens": "^2.0.0",
|
||||
"css-selector-parser": "^3.0.0",
|
||||
"devlop": "^1.0.0",
|
||||
"direction": "^2.0.0",
|
||||
"hast-util-has-property": "^3.0.0",
|
||||
"hast-util-to-string": "^3.0.0",
|
||||
"hast-util-whitespace": "^3.0.0",
|
||||
"nth-check": "^2.0.0",
|
||||
"property-information": "^7.0.0",
|
||||
"space-separated-tokens": "^2.0.0",
|
||||
"unist-util-visit": "^5.0.0",
|
||||
"zwitch": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/hast-util-to-html": {
|
||||
"version": "9.0.5",
|
||||
"resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz",
|
||||
|
|
@ -3023,6 +3170,19 @@
|
|||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/hast-util-to-string": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/hast-util-to-string/-/hast-util-to-string-3.0.1.tgz",
|
||||
"integrity": "sha512-XelQVTDWvqcl3axRfI0xSeoVKzyIFPwsAGSLIsKdJKQMXDYJS4WYrBNF/8J7RdhIcFI2BOHgAifggsvsxp/3+A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/hast": "^3.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/hast-util-to-text": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-4.0.2.tgz",
|
||||
|
|
@ -3477,16 +3637,6 @@
|
|||
"node": "20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/lucide-astro": {
|
||||
"version": "0.556.0",
|
||||
"resolved": "https://registry.npmjs.org/lucide-astro/-/lucide-astro-0.556.0.tgz",
|
||||
"integrity": "sha512-ugMjPb45AMfkLCaduNSbyy5NQEKvB1TxVVMmUS4S6L807PMESnX0Qp+DIKHjbyjJmPXOyLRbrzvR3YikTK7brg==",
|
||||
"deprecated": "Deprecated: Use `@lucide/astro`",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"astro": ">=2.7.1"
|
||||
}
|
||||
},
|
||||
"node_modules/magic-string": {
|
||||
"version": "0.30.21",
|
||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
|
||||
|
|
@ -4572,6 +4722,7 @@
|
|||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.11",
|
||||
"picocolors": "^1.1.1",
|
||||
|
|
@ -4581,6 +4732,44 @@
|
|||
"node": "^10 || ^12 || >=14"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-nested": {
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz",
|
||||
"integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/postcss/"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"postcss-selector-parser": "^6.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"postcss": "^8.2.14"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-nested/node_modules/postcss-selector-parser": {
|
||||
"version": "6.1.2",
|
||||
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
|
||||
"integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cssesc": "^3.0.0",
|
||||
"util-deprecate": "^1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-selector-parser": {
|
||||
"version": "6.0.10",
|
||||
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz",
|
||||
|
|
@ -4735,6 +4924,15 @@
|
|||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/rehype-expressive-code": {
|
||||
"version": "0.41.6",
|
||||
"resolved": "https://registry.npmjs.org/rehype-expressive-code/-/rehype-expressive-code-0.41.6.tgz",
|
||||
"integrity": "sha512-aBMX8kxPtjmDSFUdZlAWJkMvsQ4ZMASfee90JWIAV8tweltXLzkWC3q++43ToTelI8ac5iC0B3/S/Cl4Ql1y2g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"expressive-code": "^0.41.6"
|
||||
}
|
||||
},
|
||||
"node_modules/rehype-parse": {
|
||||
"version": "9.0.1",
|
||||
"resolved": "https://registry.npmjs.org/rehype-parse/-/rehype-parse-9.0.1.tgz",
|
||||
|
|
@ -5602,7 +5800,6 @@
|
|||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/vfile": {
|
||||
|
|
|
|||
|
|
@ -10,12 +10,11 @@
|
|||
"format": "prettier --write ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@fontsource-variable/noto-sans": "^5.2.10",
|
||||
"@fontsource/jetbrains-mono": "^5.2.8",
|
||||
"@tailwindcss/vite": "^4.1.18",
|
||||
"astro": "^5.16.10",
|
||||
"astro-expressive-code": "^0.41.6",
|
||||
"astro-pagefind": "^1.8.5",
|
||||
"lucide-astro": "^0.556.0",
|
||||
"tailwindcss": "^4.1.18"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
|||
1
src/assets/images/at-sign.svg
Normal file
1
src/assets/images/at-sign.svg
Normal file
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="oklch(92.2% 0 0)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-at-sign"><circle cx="12" cy="12" r="4"></circle><path d="M16 8v5a3 3 0 0 0 6 0v-1a10 10 0 1 0-3.92 7.94"></path></svg>
|
||||
|
After Width: | Height: | Size: 326 B |
1
src/assets/images/facebook.svg
Normal file
1
src/assets/images/facebook.svg
Normal file
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="oklch(92.2% 0 0)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-facebook"><path d="M18 2h-3a5 5 0 0 0-5 5v3H7v4h3v8h4v-8h3l1-4h-4V7a1 1 0 0 1 1-1h3z"></path></svg>
|
||||
|
After Width: | Height: | Size: 307 B |
1
src/assets/images/github.svg
Normal file
1
src/assets/images/github.svg
Normal file
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="oklch(92.2% 0 0)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-github"><path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"></path></svg>
|
||||
|
After Width: | Height: | Size: 531 B |
1
src/assets/images/linkedin.svg
Normal file
1
src/assets/images/linkedin.svg
Normal file
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="oklch(92.2% 0 0)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-linkedin"><path d="M16 8a6 6 0 0 1 6 6v7h-4v-7a2 2 0 0 0-2-2 2 2 0 0 0-2 2v7h-4v-7a6 6 0 0 1 6-6z"></path><rect x="2" y="9" width="4" height="12"></rect><circle cx="4" cy="4" r="2"></circle></svg>
|
||||
|
After Width: | Height: | Size: 404 B |
Binary file not shown.
|
Before Width: | Height: | Size: 15 KiB |
1
src/assets/images/twitter.svg
Normal file
1
src/assets/images/twitter.svg
Normal file
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="oklch(92.2% 0 0)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-twitter"><path d="M23 3a10.9 10.9 0 0 1-3.14 1.53 4.48 4.48 0 0 0-7.86 3v1A10.66 10.66 0 0 1 3 4s-4 9 5 13a11.64 11.64 0 0 1-7 2c9 5 20 0 20-11.5a4.5 4.5 0 0 0-.08-.83A7.72 7.72 0 0 0 23 3z"></path></svg>
|
||||
|
After Width: | Height: | Size: 412 B |
|
|
@ -1,51 +0,0 @@
|
|||
---
|
||||
import Tag from "@/components/Tag.astro";
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
description: string;
|
||||
date: Date;
|
||||
tags?: string[];
|
||||
}
|
||||
|
||||
const { title, description, date, tags } = Astro.props;
|
||||
---
|
||||
|
||||
<article data-pagefind-body>
|
||||
<header class="mb-12">
|
||||
<h1 class="text-4xl font-bold mb-2">{title}</h1>
|
||||
<h2 class="text-lg mb-2">{description}</h2>
|
||||
<div class="flex gap-4 text-gray-600 text-sm">
|
||||
<time datetime={new Date(date).toISOString()}>
|
||||
{
|
||||
date.toLocaleDateString("en-US", {
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
})
|
||||
}
|
||||
</time>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div
|
||||
class="max-w-3xl mb-8
|
||||
prose prose-invert
|
||||
prose-h1:border-b prose-h1:pb-4
|
||||
prose-a:underline prose-a:underline-offset-4
|
||||
prose-p:text-zinc-300 prose-p:text-justify prose-img:rounded-xl
|
||||
prose-headings:text-zinc-300"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
<ul class="flex flex-wrap gap-2">
|
||||
{
|
||||
tags?.map((tag) => (
|
||||
<li>
|
||||
<Tag name={tag} variant="small" />
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
</article>
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
---
|
||||
interface Props {
|
||||
id: string;
|
||||
date: Date;
|
||||
title: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
const { id, date, title, description } = Astro.props;
|
||||
---
|
||||
|
||||
<a href={`/posts/${id}`}>
|
||||
<div class="rounded-lg p-4 bg-zinc-800">
|
||||
<div class="flex items-center text-zinc-400 text-sm mb-2">
|
||||
{
|
||||
date.toLocaleDateString("en-US", {
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
})
|
||||
}
|
||||
</div>
|
||||
<h2 class="text-2xl font-semibold mb-1">{title}</h2>
|
||||
<p class="text-base text-zinc-400">{description}</p>
|
||||
</div>
|
||||
</a>
|
||||
28
src/components/BlogPostEntry.astro
Normal file
28
src/components/BlogPostEntry.astro
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
---
|
||||
import Link from "@/components/Link.astro";
|
||||
|
||||
interface Props {
|
||||
id: string;
|
||||
date: Date;
|
||||
title: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
const { id, date, title, description } = Astro.props;
|
||||
---
|
||||
|
||||
<div>
|
||||
<Link href={`/posts/${id}`}>
|
||||
<h2 class="text-lg font-semibold">{title}</h2>
|
||||
</Link>
|
||||
<div class="flex items-center text-neutral-500 text-sm">
|
||||
{
|
||||
date.toLocaleDateString("en-US", {
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
})
|
||||
}
|
||||
</div>
|
||||
<p class="text-base text-neutral-400">{description}</p>
|
||||
</div>
|
||||
|
|
@ -6,4 +6,4 @@ interface Props {
|
|||
const { message } = Astro.props;
|
||||
---
|
||||
|
||||
<p class="text-lg text-zinc-400">{message}</p>
|
||||
<p class="text-lg text-neutral-400">{message}</p>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
<footer class="w-full text-center">
|
||||
<p class="text-xs text-zinc-400">
|
||||
© {new Date().getFullYear()} Dhemas Nurjaya. All rights reserved.
|
||||
<footer class="w-full p-6 text-sm text-neutral-400">
|
||||
<p>
|
||||
© {new Date().getFullYear()} Dhemas Nurjaya.
|
||||
</p>
|
||||
<p>All rights reserved.</p>
|
||||
</footer>
|
||||
|
|
|
|||
|
|
@ -1,19 +1,27 @@
|
|||
---
|
||||
import NavLink from "@/components/NavLink.astro";
|
||||
import Link from "@/components/Link.astro";
|
||||
---
|
||||
|
||||
<header class="w-full">
|
||||
<nav class="flex flex-row justify-between" aria-label="Main navigation">
|
||||
<div class="flex gap-2 items-center">
|
||||
<a href="/" aria-label="Home">
|
||||
<p class="text-2xl font-semibold">#!</p>
|
||||
</a>
|
||||
{/* <ThemeToggle /> */}
|
||||
</div>
|
||||
<div class="flex gap-6 items-center justify-end">
|
||||
<NavLink href="/posts" ariaLabel="Posts">Posts</NavLink>
|
||||
<NavLink href="/tags" ariaLabel="Tags">Tags</NavLink>
|
||||
<NavLink href="/search" ariaLabel="Search">Search</NavLink>
|
||||
</div>
|
||||
<header class="w-full p-6">
|
||||
<nav aria-label="Main navigation">
|
||||
<ul class="flex flex-wrap gap-4 items-center">
|
||||
<li>
|
||||
<a
|
||||
href="/"
|
||||
aria-label="Home"
|
||||
class="flex before:content-['['] after:content-[']_#'] p-1 bg-green-600 text-neutral-800 hover:bg-green-500 transition-colors"
|
||||
>dhemasnurjaya</a
|
||||
>
|
||||
</li>
|
||||
<li class="flex before:content-['./'] before:text-neutral-500">
|
||||
<Link href="/posts" ariaLabel="Posts">posts</Link>
|
||||
</li>
|
||||
<li class="flex before:content-['./'] before:text-neutral-500">
|
||||
<Link href="/tags" ariaLabel="Tags">tags</Link>
|
||||
</li>
|
||||
<li class="flex before:content-['./'] before:text-neutral-500">
|
||||
<Link href="/search" ariaLabel="Search">search</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
|
|
|||
|
|
@ -1,25 +1,39 @@
|
|||
---
|
||||
import { Image } from "astro:assets";
|
||||
import profileImage from "@/assets/images/profile.webp";
|
||||
import { Linkedin, Github, Twitter, Facebook, Mail } from "lucide-astro";
|
||||
import PageTitle from "@/components/PageTitle.astro";
|
||||
import SocialLink from "@/components/SocialLink.astro";
|
||||
import Linkedin from "@/assets/images/linkedin.svg";
|
||||
import Github from "@/assets/images/github.svg";
|
||||
import Twitter from "@/assets/images/twitter.svg";
|
||||
import Facebook from "@/assets/images/facebook.svg";
|
||||
import Mail from "@/assets/images/at-sign.svg";
|
||||
---
|
||||
|
||||
<div class="flex flex-col min-h-full items-center justify-center">
|
||||
<Image
|
||||
src={profileImage}
|
||||
loading="eager"
|
||||
fetchpriority="high"
|
||||
alt="Dhemas Nurjaya"
|
||||
class="size-36 sm:size-48 rounded-2xl"
|
||||
/>
|
||||
<h1 class="text-3xl sm:text-5xl font-bold mt-4 text-center">
|
||||
Dhemas Nurjaya
|
||||
</h1>
|
||||
<h2 class="text-lg sm:text-xl mt-2 text-center">
|
||||
Passionate Software Engineer
|
||||
</h2>
|
||||
<div class="flex flex-row gap-8 mt-8" aria-label="social links">
|
||||
<div class="flex flex-col">
|
||||
<PageTitle title="Hi, I'm Dhemas 👋" />
|
||||
<p class="mb-4">
|
||||
Cross-platform developer. Linux fan stuck on Windows. Craftsman at heart.
|
||||
</p>
|
||||
<p class="mb-4">
|
||||
I write about building apps, learning new tech, and the occasional
|
||||
existential crisis that comes with debugging production code. This is where
|
||||
I share what I'm working on, what I'm learning, and thoughts that don't fit
|
||||
in a commit message.
|
||||
</p>
|
||||
<p class="mb-4">
|
||||
Currently working at a local startup where "full-stack" means Flutter 📱,
|
||||
Spring ☕, and React Router 🌐—sometimes all in the same day. I follow The
|
||||
Craftsman's Way: quality is not negotiable. Clean Architecture, TDD, and DDD
|
||||
aren't just buzzwords here; they're how I try to keep my sanity intact.
|
||||
</p>
|
||||
<p class="mb-4">
|
||||
Also, I built this blog with Astro ⚡ because life's too short for slow
|
||||
websites.
|
||||
</p>
|
||||
<p class="mb-8">
|
||||
Stick around if you're into cross-platform dev, software craftsmanship, or
|
||||
just want to see someone figure things out in public. 🚀
|
||||
</p>
|
||||
<div class="flex gap-6 items-center" aria-label="social links">
|
||||
<SocialLink
|
||||
href="https://www.linkedin.com/in/dhemas-nurjaya-030890bb"
|
||||
label="Linkedin"
|
||||
|
|
@ -42,5 +56,4 @@ import SocialLink from "@/components/SocialLink.astro";
|
|||
icon={Mail}
|
||||
/>
|
||||
</div>
|
||||
<div class="mt-24"></div>
|
||||
</div>
|
||||
|
|
|
|||
17
src/components/Link.astro
Normal file
17
src/components/Link.astro
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
---
|
||||
interface Props {
|
||||
href: string;
|
||||
ariaLabel?: string;
|
||||
class?: string;
|
||||
}
|
||||
|
||||
const { href, ariaLabel, class: className } = Astro.props;
|
||||
---
|
||||
|
||||
<a
|
||||
href={href}
|
||||
class={`text-emerald-300 hover:text-emerald-500 transition-colors underline underline-offset-4 ${className ?? ""}`}
|
||||
aria-label={ariaLabel}
|
||||
>
|
||||
<slot />
|
||||
</a>
|
||||
14
src/components/Markdown.astro
Normal file
14
src/components/Markdown.astro
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<div
|
||||
class="max-w-5xl mb-8
|
||||
prose prose-invert
|
||||
prose-p:text-neutral-300
|
||||
prose-headings:pb-4 prose-headings:text-neutral-300 prose-headings:before:mr-2 prose-headings:before:text-neutral-500
|
||||
prose-h1:border-b prose-h1:border-neutral-500 prose-h1:text-xl prose-h1:before:content-['#']
|
||||
prose-h2:font-bold prose-h2:text-lg prose-h2:before:content-['##']
|
||||
prose-h3:font-bold prose-h3:text-base prose-h3:before:content-['###']
|
||||
prose-h4:font-semibold prose-h4:text-base prose-h4:before:content-['####']
|
||||
prose-a:underline prose-a:underline-offset-4 prose-a:text-emerald-300 prose-a:hover:text-emerald-500 prose-a:transition-colors
|
||||
prose-code:before:content-none prose-code:after:content-none prose-code:bg-neutral-900 prose-code:px-1 prose-code:py-0.5 prose-code:font-normal"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
---
|
||||
interface Props {
|
||||
href: string;
|
||||
ariaLabel?: string;
|
||||
class?: string;
|
||||
}
|
||||
|
||||
const { href, ariaLabel, class: className } = Astro.props;
|
||||
---
|
||||
|
||||
<a
|
||||
href={href}
|
||||
class={`hover:underline underline-offset-4 decoration-transparent hover:decoration-current transition-colors duration-300 aria-[current]:underline aria-[current]:decoration-current aria-[current]:font-semibold ${className ?? ""}`}
|
||||
aria-label={ariaLabel}
|
||||
>
|
||||
<slot />
|
||||
</a>
|
||||
|
|
@ -6,4 +6,4 @@ interface Props {
|
|||
const { title } = Astro.props;
|
||||
---
|
||||
|
||||
<h1 class="text-4xl font-bold mb-8">{title}</h1>
|
||||
<h1 class="text-2xl font-bold mb-4">{title}</h1>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
import NavLink from "./NavLink.astro";
|
||||
import Link from "./Link.astro";
|
||||
|
||||
interface Props {
|
||||
currentPage: number;
|
||||
|
|
@ -13,14 +13,14 @@ const { currentPage, lastPage, getPageUrl } = Astro.props;
|
|||
<nav aria-label="Pagination Navigation" class="flex py-6">
|
||||
{
|
||||
currentPage > 1 && (
|
||||
<NavLink href={getPageUrl(currentPage - 1)}>« Previous</NavLink>
|
||||
<Link href={getPageUrl(currentPage - 1)}>« Previous</Link>
|
||||
)
|
||||
}
|
||||
{
|
||||
currentPage < lastPage && (
|
||||
<NavLink href={getPageUrl(currentPage + 1)} class="ml-auto">
|
||||
<Link href={getPageUrl(currentPage + 1)} class="ml-auto">
|
||||
Next »
|
||||
</NavLink>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
</nav>
|
||||
|
|
|
|||
12
src/components/Sidebar.astro
Normal file
12
src/components/Sidebar.astro
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
import { getQuote } from "@/lib/quote";
|
||||
const { quote, author } = await getQuote();
|
||||
---
|
||||
|
||||
<div class="flex flex-col sticky top-4">
|
||||
<blockquote class="border-l border-neutral-500 pl-4">
|
||||
<p class="mb-2">{quote}</p>
|
||||
<footer class="text-neutral-400 text-sm">{author}</footer>
|
||||
</blockquote>
|
||||
<span class="text-neutral-500 py-4">---</span>
|
||||
</div>
|
||||
|
|
@ -11,7 +11,7 @@ const { href, label, icon: Icon } = Astro.props;
|
|||
<a
|
||||
href={href}
|
||||
aria-label={label}
|
||||
class="text-zinc-300 hover:text-zinc-100 transition-colors"
|
||||
class="text-neutral-300 hover:text-neutral-100 transition-colors"
|
||||
>
|
||||
<Icon class="size-6" />
|
||||
<Icon class="size-5" />
|
||||
</a>
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ const textSize = isSmall ? "text-sm" : "text-base";
|
|||
|
||||
<a href={linkHref}>
|
||||
<span
|
||||
class={`${textSize} text-zinc-400 font-semibold rounded-md bg-zinc-800 px-2 py-2 hover:bg-zinc-700`}
|
||||
class={`${textSize} text-neutral-400 font-semibold bg-neutral-900 px-2 py-2 hover:bg-neutral-700`}
|
||||
>
|
||||
#{name}{count !== undefined && <sup>{count}</sup>}
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
import "@/styles/global.css";
|
||||
import Footer from "@/components/Footer.astro";
|
||||
import Header from "@/components/Header.astro";
|
||||
import Sidebar from "@/components/Sidebar.astro";
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
|
|
@ -21,10 +22,15 @@ const { title, description } = Astro.props;
|
|||
<meta name="description" content={description} />
|
||||
<meta name="generator" content={Astro.generator} />
|
||||
</head>
|
||||
<body class="h-screen max-w-5xl mx-auto flex flex-col font-sans p-4">
|
||||
<body class="max-w-5xl mx-auto flex flex-col font-default">
|
||||
<Header />
|
||||
<main class="w-full max-w-3xl mx-auto flex-1 py-12">
|
||||
<main class="flex flex-col md:flex-row">
|
||||
<section class="w-full md:flex-3 p-4 bg-neutral-800">
|
||||
<slot />
|
||||
</section>
|
||||
<aside class="w-full md:flex-1 p-4">
|
||||
<Sidebar />
|
||||
</aside>
|
||||
</main>
|
||||
<Footer />
|
||||
</body>
|
||||
|
|
|
|||
17
src/lib/quote.ts
Normal file
17
src/lib/quote.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
let cache: { quote: string; author: string } | null = null;
|
||||
|
||||
export async function getQuote() {
|
||||
if (cache) return cache;
|
||||
|
||||
console.log("\nFetching fresh quote...");
|
||||
const response = await fetch("https://quotes-github-readme.vercel.app/api");
|
||||
const svgText = await response.text();
|
||||
|
||||
cache = {
|
||||
quote: svgText.match(/<h3>([\s\S]*?)<\/h3>/)?.[1]?.trim() ?? "",
|
||||
author: svgText.match(/<p>([\s\S]*?)<\/p>/)?.[1]?.trim() ?? "",
|
||||
};
|
||||
console.log("Quote fetched: ", cache);
|
||||
|
||||
return cache;
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
import type { PaginateFunction, Page } from "astro";
|
||||
import { getCollection } from "astro:content";
|
||||
import Layout from "@/layouts/Layout.astro";
|
||||
import BlogPostCard from "@/components/BlogPostCard.astro";
|
||||
import BlogPostEntry from "@/components/BlogPostEntry.astro";
|
||||
import PageTitle from "@/components/PageTitle.astro";
|
||||
import type { CollectionEntry } from "astro:content";
|
||||
import Pagination from "@/components/Pagination.astro";
|
||||
|
|
@ -30,11 +30,11 @@ const { page } = Astro.props;
|
|||
description="Read the latest blog posts by Dhemas Nurjaya"
|
||||
>
|
||||
<PageTitle title="Posts" />
|
||||
<ul class="space-y-4">
|
||||
<ul class="space-y-6">
|
||||
{
|
||||
page.data.map((post) => (
|
||||
<li>
|
||||
<BlogPostCard
|
||||
<BlogPostEntry
|
||||
id={post.id}
|
||||
title={post.data.title}
|
||||
date={post.data.date}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,8 @@ import {
|
|||
type RenderResult,
|
||||
} from "astro:content";
|
||||
import Layout from "@/layouts/Layout.astro";
|
||||
import BlogPost from "@/components/BlogPost.astro";
|
||||
import Tag from "@/components/Tag.astro";
|
||||
import Markdown from "@/components/Markdown.astro";
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const posts = await getCollection("blog");
|
||||
|
|
@ -36,12 +37,35 @@ const { post, content } = Astro.props;
|
|||
title={`${post.data.title} - Dhemas Nurjaya`}
|
||||
description={post.data.description}
|
||||
>
|
||||
<BlogPost
|
||||
title={post.data.title}
|
||||
description={post.data.description}
|
||||
date={post.data.date}
|
||||
tags={post.data.tags}
|
||||
>
|
||||
<article data-pagefind-body>
|
||||
<header class="mb-12">
|
||||
<h1 class="text-2xl font-bold mb-2">{post.data.title}</h1>
|
||||
<h2 class="text-base mb-2">{post.data.description}</h2>
|
||||
<div class="flex gap-4 text-neutral-500 text-sm">
|
||||
<time datetime={new Date(post.data.date).toISOString()}>
|
||||
{
|
||||
post.data.date.toLocaleDateString("en-US", {
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
})
|
||||
}
|
||||
</time>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<Markdown>
|
||||
<content.Content />
|
||||
</BlogPost>
|
||||
</Markdown>
|
||||
|
||||
<ul class="flex flex-wrap gap-2">
|
||||
{
|
||||
post.data.tags?.map((tag) => (
|
||||
<li>
|
||||
<Tag name={tag} variant="small" />
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
</article>
|
||||
</Layout>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,10 @@ import Layout from "@/layouts/Layout.astro";
|
|||
import Search from "astro-pagefind/components/Search";
|
||||
---
|
||||
|
||||
<Layout title="Dhemas Nurjaya" description="Welcome to my personal website">
|
||||
<Layout
|
||||
title="Search - Dhemas Nurjaya"
|
||||
description="Search posts on Dhemas Nurjaya's website"
|
||||
>
|
||||
<PageTitle title="Search" />
|
||||
<Search
|
||||
id="search"
|
||||
|
|
@ -20,37 +23,40 @@ import Search from "astro-pagefind/components/Search";
|
|||
|
||||
<style is:global>
|
||||
.pagefind-ui {
|
||||
--pagefind-ui-scale: 1;
|
||||
--pagefind-ui-primary: #a1a1aa; /* zinc-400 */
|
||||
--pagefind-ui-text: #d4d4d8; /* zinc-300 */
|
||||
--pagefind-ui-background: #18181b; /* zinc-900 */
|
||||
--pagefind-ui-border: #3f3f46; /* zinc-700 */
|
||||
--pagefind-ui-tag: #27272a; /* zinc-800 */
|
||||
--pagefind-ui-scale: 0.9;
|
||||
--pagefind-ui-primary: #a3a3a3; /* neutral-400 */
|
||||
--pagefind-ui-text: #d4d4d8; /* neutral-300 */
|
||||
--pagefind-ui-background: #171717; /* neutral-900 */
|
||||
--pagefind-ui-border: #404040; /* neutral-700 */
|
||||
--pagefind-ui-tag: #262626; /* neutral-800 */
|
||||
--pagefind-ui-border-width: 1px;
|
||||
--pagefind-ui-border-radius: 0.5rem;
|
||||
--pagefind-ui-font: "Noto Sans Variable", sans-serif;
|
||||
--pagefind-ui-border-radius: 0rem;
|
||||
--pagefind-ui-font: "JetBrains Mono", monospace;
|
||||
}
|
||||
|
||||
.pagefind-ui__search-input {
|
||||
background: #27272a; /* zinc-800 */
|
||||
color: #d4d4d8; /* zinc-300 */
|
||||
background: #262626; /* neutral-800 */
|
||||
color: #d4d4d8; /* neutral-300 */
|
||||
border: 1px solid #404040; /* neutral-700 */
|
||||
}
|
||||
|
||||
.pagefind-ui__search-input::placeholder {
|
||||
color: #71717a; /* zinc-500 */
|
||||
color: #737373; /* neutral-500 */
|
||||
}
|
||||
|
||||
.pagefind-ui__result {
|
||||
border-radius: 0.5rem !important;
|
||||
padding: 1rem !important;
|
||||
background: #27272a !important; /* zinc-800 */
|
||||
border: none !important;
|
||||
margin-bottom: 1rem !important;
|
||||
border-bottom: 1px solid #404040 !important; /* neutral-700 */
|
||||
padding: 1rem 0 !important;
|
||||
background: transparent !important;
|
||||
border-top: none !important;
|
||||
border-left: none !important;
|
||||
border-right: none !important;
|
||||
margin-bottom: 0 !important;
|
||||
position: relative !important;
|
||||
}
|
||||
|
||||
.pagefind-ui__result:last-child {
|
||||
margin-bottom: 0 !important;
|
||||
border-bottom: none !important;
|
||||
}
|
||||
|
||||
.pagefind-ui__result-inner {
|
||||
|
|
@ -60,8 +66,14 @@ import Search from "astro-pagefind/components/Search";
|
|||
}
|
||||
|
||||
.pagefind-ui__result-link {
|
||||
color: #d4d4d8; /* zinc-300 */
|
||||
color: #d4d4d8; /* neutral-300 */
|
||||
text-decoration: none !important;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.pagefind-ui__result-link:hover {
|
||||
text-decoration: underline !important;
|
||||
color: #fafafa; /* neutral-50 */
|
||||
}
|
||||
|
||||
.pagefind-ui__result-link::after {
|
||||
|
|
@ -75,15 +87,22 @@ import Search from "astro-pagefind/components/Search";
|
|||
}
|
||||
|
||||
.pagefind-ui__result-title {
|
||||
color: #fafafa; /* zinc-50 */
|
||||
color: #fafafa; /* neutral-50 */
|
||||
}
|
||||
|
||||
.pagefind-ui__result-excerpt {
|
||||
color: #a1a1aa; /* zinc-400 */
|
||||
color: #a3a3a3; /* neutral-400 */
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.pagefind-ui__message {
|
||||
color: #a1a1aa; /* zinc-400 */
|
||||
color: #a3a3a3; /* neutral-400 */
|
||||
padding: 1rem 0;
|
||||
}
|
||||
|
||||
/* Remove default image styling if present, though showImages: false handles most */
|
||||
.pagefind-ui__result-thumb {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
import { getCollection, type CollectionEntry } from "astro:content";
|
||||
import BlogPostCard from "@/components/BlogPostCard.astro";
|
||||
import BlogPostEntry from "@/components/BlogPostEntry.astro";
|
||||
import Layout from "@/layouts/Layout.astro";
|
||||
import PageTitle from "@/components/PageTitle.astro";
|
||||
import EmptyState from "@/components/EmptyState.astro";
|
||||
|
|
@ -44,7 +44,7 @@ const { filteredPosts } = Astro.props;
|
|||
<ul class="space-y-4">
|
||||
{filteredPosts.map((post) => (
|
||||
<li>
|
||||
<BlogPostCard
|
||||
<BlogPostEntry
|
||||
id={post.id}
|
||||
title={post.data.title}
|
||||
date={post.data.date}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ const tags = Array.from(tagsMap.entries()).sort((a, b) =>
|
|||
|
||||
<Layout title="Tags - Dhemas Nurjaya" description="Browse blog posts by tags">
|
||||
<PageTitle title="Tags" />
|
||||
<ul class="flex flex-wrap gap-4">
|
||||
<ul class="flex flex-wrap gap-4 mt-8">
|
||||
{
|
||||
tags?.map(([tag, count]) => (
|
||||
<li class="mb-4">
|
||||
|
|
|
|||
|
|
@ -1,15 +1,13 @@
|
|||
@import "@fontsource-variable/noto-sans";
|
||||
@import "@fontsource/jetbrains-mono";
|
||||
@import "tailwindcss";
|
||||
@plugin "@tailwindcss/typography";
|
||||
|
||||
@theme {
|
||||
--font-sans: "Noto Sans Variable", sans-serif;
|
||||
--font-mono: "JetBrains Mono", monospace;
|
||||
--font-default: "JetBrains Mono", monospace;
|
||||
}
|
||||
|
||||
@layer base {
|
||||
body {
|
||||
@apply bg-zinc-900 text-zinc-300;
|
||||
@apply bg-neutral-900 text-neutral-300;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue