24 Commits

Author SHA1 Message Date
Avraham Sakal 79deff3b6c add entry for 2024-12-07 2024-12-07 22:30:24 -05:00
Avraham Sakal f4714839dd fix: getStaticPaths doesn't run unless in prerender mode 2024-12-07 21:56:42 -05:00
Avraham Sakal 8532af4c04 gitignore .wrangler 2024-12-07 21:56:05 -05:00
Avraham Sakal 1d0f679dea upgrade astro, deploy to cloudflare 2024-12-06 12:23:11 -05:00
Avraham Sakal 345a18729a use MDX to use FullDev UI in Markdown 2024-12-01 15:09:58 -05:00
Avraham Sakal 626d94694c add content collection schemas 2024-12-01 11:50:39 -05:00
Avraham Sakal 4c1bcf02ed journal: 2024-11-24 2024-11-24 09:57:37 -05:00
Avraham Sakal 3cd0ec4d87 fix: astro build error: end of the stream or a document separator is expected 2024-11-24 09:49:40 -05:00
Avraham Sakal b7bfc8750e 0.0.3 2024-11-24 09:46:05 -05:00
Avraham Sakal 0d512a40d0 fix: Docker build error: The local project doesn't define a 'packageManager' field 2024-11-24 09:46:00 -05:00
Avraham Sakal b15ff40740 0.0.2 2024-11-24 09:44:23 -05:00
Avraham Sakal fdc2354ccc setup biome linter 2024-11-24 09:44:18 -05:00
Avraham Sakal c08fdbf7fd begin content config 2024-11-24 09:42:51 -05:00
Avraham Sakal 2d4e1fb375 begin /journal and /articles 2024-11-24 09:42:28 -05:00
Avraham Sakal fc263eeb65 journal: 2024-11-23 2024-11-24 09:42:10 -05:00
Avraham Sakal 4b537c4b04 chore: cleanup 2024-11-24 09:41:43 -05:00
Avraham Sakal cbd6ff05df fix: build error: Failed to resolve entry for package "fs" 2024-11-24 09:41:24 -05:00
Avraham Sakal 30e3a516ab begin 2024-11-21 journal entry 2024-11-23 22:17:08 -05:00
Avraham Sakal 4d41cf77ef add fulldev ui and unocss; begin ui work 2024-11-23 22:16:57 -05:00
Avraham Sakal 96ae6401c5 write jounral-entries/2024-11-20 2024-11-20 22:02:27 -05:00
Avraham Sakal 0119df3a20 my first content! 2024-11-19 21:20:04 -05:00
Avraham Sakal d090f650af wrap sample pages in Layout 2024-11-19 21:19:51 -05:00
Avraham Sakal 490ae1898e add build script 2024-11-19 21:19:31 -05:00
Avraham Sakal b46f213b2d bump Node version 2024-11-19 21:19:07 -05:00
29 changed files with 4841 additions and 610 deletions
+2
View File
@@ -22,3 +22,5 @@ pnpm-debug.log*
# jetbrains setting folder # jetbrains setting folder
.idea/ .idea/
.wrangler
+1 -1
View File
@@ -1,3 +1,3 @@
nodejs 20.15.1 nodejs 22.8.0
# python 3.12.4 # python 3.12.4
pnpm 9.7.1 pnpm 9.7.1
+1 -1
View File
@@ -13,7 +13,7 @@ COPY . /app
RUN pnpm run build RUN pnpm run build
FROM base FROM base AS final
WORKDIR /app WORKDIR /app
COPY --from=prod /app/node_modules /app/node_modules COPY --from=prod /app/node_modules /app/node_modules
COPY --from=prod /app/dist /app COPY --from=prod /app/dist /app
+57 -4
View File
@@ -1,14 +1,67 @@
// @ts-check // @ts-check
import { defineConfig } from "astro/config"; import { defineConfig } from "astro/config";
import fulldev from "fulldev-ui/integration";
import UnoCSS from "unocss/astro";
import mdx from "@astrojs/mdx";
import cloudflare from "@astrojs/cloudflare";
// import { createRequire } from "node:module";
import node from "@astrojs/node"; // const require = createRequire(import.meta.url);
// https://astro.build/config // https://astro.build/config
export default defineConfig({ export default defineConfig({
// pre-render by default; opt-in to dynamic SSR: // pre-render by default; opt-in to dynamic SSR:
output: "hybrid", output: "server",
adapter: node({ adapter: cloudflare(),
mode: "standalone",
integrations: [
mdx(),
UnoCSS({
// injectReset: true, // or a path to the reset file
// preflights: [
// {
// getCSS: ({ theme }) => `
// * {
// color: ${theme.colors.gray?.[200] ?? "#ccc"};
// padding: 0;
// margin: 0;
// }
// `,
// },
// ],
}), }),
fulldev({
// css: '/src/css/custom.css',
colors: {
theme: "dark",
dark: {
background: "#111110",
base: "#3E63DD",
brand: "#359",
},
light: {
background: "#EEEEEC",
base: "#6F6D66",
brand: "#3E63DD",
},
},
}),
],
vite: {
resolve: {
alias: {
fs: "node:fs",
// fs: require.resolve("rollup-plugin-node-builtins"),
// http: require.resolve('rollup-plugin-node-builtins'),
// util: require.resolve('rollup-plugin-node-builtins'),
// stream: require.resolve('rollup-plugin-node-builtins'),
// buffer: require.resolve('rollup-plugin-node-builtins'),
// process: require.resolve('rollup-plugin-node-builtins'),
// url: require.resolve('rollup-plugin-node-builtins'),
// querystring: require.resolve('rollup-plugin-node-builtins'),
},
},
},
}); });
+3
View File
@@ -9,6 +9,9 @@
}, },
"linter": { "linter": {
"enabled": true, "enabled": true,
"include": [
"src/**/*.{js,jsx,ts,tsx,mjs,cjs,mts,cts}"
],
"rules": { "rules": {
"recommended": true "recommended": true
} }
Executable
+11
View File
@@ -0,0 +1,11 @@
#!/bin/bash
REGISTRY=registry.sakal.us
IMAGE_NAME=blog-astro
# get version from package.json and remove quotes:
VERSION=$(pnpm pkg get version | xargs)
docker build -t "${REGISTRY}/${IMAGE_NAME}:v${VERSION}" --target final .
docker push "${REGISTRY}/${IMAGE_NAME}:v${VERSION}"
+12 -4
View File
@@ -1,7 +1,8 @@
{ {
"name": "blog-astro", "name": "blog-astro",
"type": "module", "type": "module",
"version": "0.0.1", "version": "0.0.3",
"packageManager": "pnpm@9.7.1",
"scripts": { "scripts": {
"dev": "astro dev", "dev": "astro dev",
"start": "astro dev", "start": "astro dev",
@@ -11,8 +12,15 @@
}, },
"dependencies": { "dependencies": {
"@astrojs/check": "^0.9.4", "@astrojs/check": "^0.9.4",
"@astrojs/node": "^8.3.4", "@astrojs/cloudflare": "^12.0.1",
"astro": "^4.16.13", "@astrojs/mdx": "^4.0.1",
"typescript": "^5.6.3" "astro": "^5.0.3",
"fulldev-ui": "^0.4.33",
"typescript": "^5.7.2"
},
"devDependencies": {
"@unocss/preset-uno": "^0.64.1",
"unocss": "^0.64.1",
"wrangler": "^3.93.0"
} }
} }
+4265 -369
View File
File diff suppressed because it is too large Load Diff
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 820 KiB

-61
View File
@@ -1,61 +0,0 @@
---
interface Props {
title: string;
body: string;
href: string;
}
const { href, title, body } = Astro.props;
---
<li class="link-card">
<a href={href}>
<h2>
{title}
<span>&rarr;</span>
</h2>
<p>
{body}
</p>
</a>
</li>
<style>
.link-card {
list-style: none;
display: flex;
padding: 1px;
background-color: #23262d;
background-image: none;
background-size: 400%;
border-radius: 7px;
background-position: 100%;
transition: background-position 0.6s cubic-bezier(0.22, 1, 0.36, 1);
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.1);
}
.link-card > a {
width: 100%;
text-decoration: none;
line-height: 1.4;
padding: calc(1.5rem - 1px);
border-radius: 8px;
color: white;
background-color: #23262d;
opacity: 0.8;
}
h2 {
margin: 0;
font-size: 1.25rem;
transition: color 0.6s cubic-bezier(0.22, 1, 0.36, 1);
}
p {
margin-top: 0.5rem;
margin-bottom: 0;
}
.link-card:is(:hover, :focus-within) {
background-position: 0;
background-image: var(--accent-gradient);
}
.link-card:is(:hover, :focus-within) h2 {
color: rgb(var(--accent-light));
}
</style>
+29
View File
@@ -0,0 +1,29 @@
// 1. Import utilities from `astro:content`
import { defineCollection, z } from "astro:content";
// 2. Define your collection(s)
const journalCollection = defineCollection({
type: "content",
schema: z.object({
title: z.string(),
date: z.date(),
tags: z.string().array(),
category: z.string().optional(),
description: z.string().optional(),
}),
});
const articleCollection = defineCollection({
type: "content",
schema: z.object({
title: z.string(),
date: z.date(),
tags: z.string().array(),
category: z.string().optional(),
description: z.string().optional(),
}),
});
// 3. Export a single `collections` object to register your collection(s)
// This key should match your collection directory name in "src/content"
export const collections = {
"journal-entries": journalCollection,
articles: articleCollection,
};
@@ -0,0 +1,35 @@
---
title: MySQL JSON Shenanigans
date: 2024-11-19
tags: ["mysql"]
category: "MySQL"
description: "Out of the box, there is a MySQL text encoding mismatch between VARCHAR columns and JSON columns."
---
In an effort to support out-of-date installations of our app, I had to keep a JSON column in our database. The column is obsolete, as are the values within it; but these installations continue to use it. So, knowing this, I decided to put the proper values into the column. I didn't want to pollute the code of our services to do so, though. So I made it into a `GENERATED` column.
I ran into two issues.
First, MySQL doesn't have a way to omit `NULL` values from a JSON array.
Second, the strings in the resulting array had `\u0000` interspersed between each character. This is due to how MySQL represents strings in a regular column versus how they are represented in a JSON column. I'd have to look it up, but I think I recall that by default MySQL varchars are 16-bits per character, and JSON strings are UTF-8.
I "solved" the first issue by using `IF()` statements to represent all possible combinations of values. Luckily there were only 4. The only other solution I saw involved `TRIGGER`s. No, thank you.
The second issue was solved by using `JSON_QUOTE()` followed by `JSON_UNQUOTE()` to remove the `\u0000` characters.
```sql
ALTER TABLE lectures
MODIFY COLUMN vimeo_video_links JSON GENERATED ALWAYS AS (
IF(m3u8_url IS NOT NULL AND mp4_url IS NOT NULL,
JSON_ARRAY(JSON_UNQUOTE(JSON_QUOTE(m3u8_url)), JSON_UNQUOTE(JSON_QUOTE(mp4_url))),
IF(m3u8_url IS NOT NULL AND mp4_url IS NULL,
JSON_ARRAY(JSON_UNQUOTE(JSON_QUOTE(m3u8_url))),
IF(m3u8_url IS NULL AND mp4_url IS NOT NULL,
JSON_ARRAY(JSON_UNQUOTE(JSON_QUOTE(mp4_url))),
NULL
)
)
)
) STORED;
```
@@ -0,0 +1,19 @@
---
title: Elasticsearch Ingestion Daemon
date: 2024-11-20
tags: ["elasticsearch"]
category: "Elasticsearch"
description: "Batch-processing boundaries need to be defined with enough information to point to exactly one record, taking into account records being updated between batches."
---
I still saw requests coming in through an old Cloudflare-Worker-based proxy I had set up, before I released the current one, which rewrites `m3u8` files on-the-fly, besides proxying the segment files themselves (among other features). I updated our website and app to use the new proxy; where were these requests coming from? I inspected some requests as they came in using the Cloudflare interface, and I found that the User Agent was always one of our apps; and different versions of it at that. We still had un-updated versions of our app out in the wild, but I also saw requests from the latest version! How could this be?
Then it dawned on me: I had a vague recollection that I saw in the app code that sometimes it doesn't fetch a lecture when it's chosen, because the data is already available from search results. Our search endpoints query Elasticsearch, which is _not_ the source of truth--MySQL is. So obviously it was serving stale data. Lo and behold, I ran a query, and found old-style lecture video URLs in Elasticsearch. The lecture documents had to be re-ingested from MySQL.
We had LogStash keeping Elasticsearch in sync with MySQL about a year and a half ago; but it kept running out of memory. I ended up writing a daemon in Typescript to do the job. It works by only ingesting the latest data from MySQL (i.e. rows that have an `updated_at` value that is newer than the last ingestion time). But yesterday when I re-defined the `vimeo_video_links` column in MySQL, I didn't set the `updated_at` value to the current time on all the rows. Perhaps I should have, but it didn't seem like the right thing to do, because the record was _updated_ semantically; it was just re-structured, but _represented_ the same data.
Anyway, I reset the daemon's cursor state so it would start ingesting from the beginning.
The next problem was that in order to prevent the daemon from OOMing and/or overloading MySQL, I have it fetch small batches of records (500 at a time). The next batch is supposed to begin at the row after the previous batch. I used `updated_at` as the cursor state (i.e. the next batch began with the first lecture who's `updated_at` was after the last lecture in the previous batch). I then discovered that many lectures had the same `updated_at` value (strange, yes, but many things have happened over the years including bulk-update scripts; and in MySQL the `updated_at` only had a resolution of seconds); and many batch boundaries fell squarely among such lectures; so the next batch began at the first lecture with a `updated_at` that was _newer_ than the last lecture in the previous batch (I couldn't use `>=` as the comparison operator because then script would keep getting back the old batch of lectures). So I ordered by `updated_at` _and_ by `id`, and adjusted the comparison to consider both `updated_at` and `id`. This solved the problem.
By the way, I didn't use regular `OFFSET` instead of this cursor state, because records may change between fetching batches, thus what was at "offset 501" from the point-of-view of the first query may not be at "offset 501" from the point-of-view of the second query. Namely, if a record from an already-done batch was updated (and thus its `updated_at` value changed to `NOW()`), what was at "offset 501" is now at "offset 500" in the second query, but the second query specifies to start from `OFFSET 501`, so it will skip that one record.
@@ -0,0 +1,11 @@
---
title: React-Admin Wrestling, a Little More Elasticsearch
date: 2024-11-21
tags: ["react", "react-admin", "elasticsearch"]
category: "React Admin"
description: "(This article is still incomplete. I began writing this entry after work the day it happened, and I didn't get a chance to get back to it until today (12 days later), so I forgot what I was planning on writing about!)"
---
React-admin is a wonderful framework, and is quite flexible; but if you need something that it doesn't offer, it's very difficult to dig through the docs to find out how to do it.
Today, I needed to add a conditional filter on a List page. The `List` component takes a `filters` prop, which is an Array of `*Input` components. It does not accept a component to act as a wrapper or container around the `*Input`s. The task was to have a "From Official Clipper?" filter (a boolean switch) for the Clips list; and if it's unchecked, the List should then show a "Has Potential?" filter (another boolean switch), defaulting to `false`. If "From Official Clipper?" is checked, then the "Has Potential?" filter should be disabled, because it's assumed clips made official clippers have potential.
@@ -0,0 +1,68 @@
---
title: "@astrojs/node Build Error"
tags: ["astro"]
category: "Astro"
description: "Always prefix native imports with `node:`, even in dependencies. If a dependency doesn't do it, adjust your build-step to do it for you."
date: 2024-11-23
---
Today's entry is about this very site.
I'm using [Astro](https://astro.build) to build this site, with the [Astro Node](https://github.com/withastro/astro/tree/main/packages/integrations/node) integration for SSR.
Well, I installed it, and set it up in `astro.config.mjs`:
```js
import { defineConfig } from "astro/config";
import node from "@astrojs/node";
export default defineConfig({
// pre-render by default; opt-in to dynamic SSR:
output: "hybrid",
adapter: node({
mode: "standalone",
}),
});
```
It ran fine in dev mode (`pnpm run dev`), but when I tried to build it (`pnpm run build`), I got this error:
```
23:29:44 [ERROR] [vite] x Build failed in 331ms
[commonjs--resolver] Failed to resolve entry for package "fs". The package may have incorrect main/module/exports specified in its package.json.
file: .../blog-astro/node_modules/.pnpm/send@0.19.1/node_modules/send/index.js
Stack trace:
at packageEntryFailure (file://.../blog-astro/node_modules/.pnpm/vite@5.4.11_sass@1.81.0/node_modules/vite/dist/node/chunks/dep-CB_7IfJ-.js:46637:15)
at tryNodeResolve (file://.../blog-astro/node_modules/.pnpm/vite@5.4.11_sass@1.81.0/node_modules/vite/dist/node/chunks/dep-CB_7IfJ-.js:46450:16)
at Object.handler (file://.../blog-astro/node_modules/.pnpm/vite@5.4.11_sass@1.81.0/node_modules/vite/dist/node/chunks/dep-CB_7IfJ-.js:65653:15)
at async PluginDriver.hookFirstAndGetPlugin (file://.../blog-astro/node_modules/.pnpm/rollup@4.27.2/node_modules/rollup/dist/es/shared/node-entry.js:21099:28)
at async ModuleLoader.resolveId (file://.../blog-astro/node_modules/.pnpm/rollup@4.27.2/node_modules/rollup/dist/es/shared/node-entry.js:20132:15)
ELIFECYCLE Command failed with exit code 1.
```
Of course I searched the 'net, and never found the exact same issue; only similar issues. This is surprising, because it's not like I have an edge-case setup. Anyway, long story short, the fix was not to be found on the Internet, but on a hunch I added the following to `astro.config.mjs`:
```js
import { defineConfig } from "astro/config";
import node from "@astrojs/node";
export default defineConfig({
// pre-render by default; opt-in to dynamic SSR:
output: "hybrid",
adapter: node({
mode: "standalone",
}),
vite: {
resolve: {
alias: {
fs: "node:fs",
},
},
},
});
```
This worked famously.
@@ -0,0 +1,55 @@
---
title: "Content Frontmatter Causes `astro build` Error?"
date: 2024-11-24
tags: ["astro"]
category: "Astro"
description: "Keep your eyes peeled for special characters."
---
Another entry for this site. I ran `pnpm run build` after adding yesterday's entry, and got this error:
```
✘ [ERROR] The build was canceled
end of the stream or a document separator is expected
Hint:
Browser APIs are not available on the server.
If the code is in a framework component, try to access these objects after rendering using lifecycle methods or use a `client:only` directive to make the component exclusively run on the client.
See https://docs.astro.build/en/guides/troubleshooting/#document-or-window-is-not-defined for more information.
Location:
.../blog-astro/src/content/journal-entries/2024-11-23.md:1:7
Stack trace:
at generateError (.../blog-astro/node_modules/.pnpm/js-yaml@3.14.1/node_modules/js-yaml/lib/js-yaml/loader.js:167:10)
at readDocument (.../blog-astro/node_modules/.pnpm/js-yaml@3.14.1/node_modules/js-yaml/lib/js-yaml/loader.js:1545:5)
at load (.../blog-astro/node_modules/.pnpm/js-yaml@3.14.1/node_modules/js-yaml/lib/js-yaml/loader.js:1614:19)
at module.exports (.../blog-astro/node_modules/.pnpm/gray-matter@4.0.3/node_modules/gray-matter/lib/parse.js:12:17)
at matter (.../blog-astro/node_modules/.pnpm/gray-matter@4.0.3/node_modules/gray-matter/index.js:50:10)
ELIFECYCLE Command failed with exit code 1.
```
I was obviously not using Browser APIs in an `.md` file. But, it did report that file as the culprit (specifically, the first line), so I checked it out.
```md
---
title: @astrojs/node Build Error
date: 2024-11-23
---
<... rest of the file...>
```
I noticed the `@` was a different color than the rest of the line. I thought YAML interprets any non-digit character as the start of a string; but just in case I wrapped the line in quotes, and it worked!
```md
---
title: "@astrojs/node Build Error"
date: 2024-11-23
---
<... rest of the file...>
```
It seems that the `@` character signals an import from another file, and obviously there wasn't any so-named file.
@@ -0,0 +1,64 @@
---
title: "Getting FullDev UI to Work with MDX in Astro"
tags: ["astro", "fulldev-ui"]
category: "Astro"
description: "I'm using MDX in my Astro blog, and I want to use FullDev UI's components in it."
date: 2024-12-01
---
I wanted to keep the moving parts of this blog to a minimum, so I wanted to use plain Markdown for the content. But, I also wanted to use the [FullDev UI](https://fulldev.dev) styling for headings, text, links, etc. because the rest of the site is using [FullDev UI](https://fulldev.dev).
So, I had to add the MDX integration to the blog. This allowed me to override which components to used for each Markdown element. So far, I've only overridden the `p` and `a` components:
```jsx
<Layout title={journalEntry.data.title}>
<main>
<Heading>{journalEntry.data.title}</Heading>
<Content
components={{
p: RegularText,
a: Link,
}}
/>
</main>
</Layout>
```
The `RegularText` component is a simple wrapper around `Text` from FullDev UI:
```jsx
import Text from "fulldev-ui/components/Text.astro";
<Text contrast={true}>
<slot />
</Text>;
```
I had to create this wrapper as a separate component because the Astro syntax doesn't allow defining inline components like this:
```jsx
<Layout title={journalEntry.data.title}>
<main>
<Heading>{journalEntry.data.title}</Heading>
<Content
components={{
p: () => (
<Text contrast={true}>
<slot />
</Text>
),
a: Link,
}}
/>
</main>
</Layout>
```
In that case it gives a build error:
```
15:06:29 [ERROR] Expected ">" but found "contrast"
Stack trace:
at failureErrorWithLog (.../blog-astro/node_modules/.pnpm/esbuild@0.21.5/node_modules/esbuild/lib/main.js:1472:15)
[...] See full stack trace in the browser, or rerun with --verbose.
```
@@ -0,0 +1,25 @@
---
title: "Kubernetes Crash: Node Pressure"
tags: ["kubernetes"]
category: "Kubernetes"
description: 'All pods were evicted, due to "disk pressure".'
date: 2024-12-07
---
I run my own private git server, using Gitea; and it's deployed on my one-node Kubernetes cluster. I tried pushing a commit for this very blog, and I got a `521` error response from the server.
```
The node was low on resource: ephemeral-storage. Threshold quantity: 3681937462,
available: 10701128Ki. Container frontend was using 32Ki, request is 0, has larger
consumption of ephemeral-storage.
```
Or:
```
Pod was rejected: The node had condition: [DiskPressure].
```
My disk was nowhere near full (86%), so I thought maybe clickhouse was taking up too many inodes. That wasn't the case.
I found [this on StackOverflow](https://stackoverflow.com/a/76529494), and it turned out that 85% is the threshold for `imagefs`, which is what Kubernetes uses to hold image layers. It's not that I had too many images on the disk (I had less than 3GB-worth), rather since my disk usage was at 86%, only 14% was available for more image layers, which Kubernetes says is no good. I just deleted some journal log files, which only added up to 4Gb, so this may happen again. The problem is that the disk is rather small, and Clickhouse is allocated like 50Gb due to my [stock options project](https://calendar-optimizer-frontend.sakal.us/).
+28 -23
View File
@@ -1,4 +1,5 @@
--- ---
import Header from 'fulldev-ui/components/Header.astro'
interface Props { interface Props {
title: string; title: string;
} }
@@ -10,41 +11,45 @@ const { title } = Astro.props;
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="description" content="Astro description" /> <meta name="description" content="A practical coding blog in the form of journal entries and articles." />
<meta name="viewport" content="width=device-width" /> <meta name="viewport" content="width=device-width" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" /> <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="generator" content={Astro.generator} /> <meta name="generator" content={Astro.generator} />
<title>{title}</title> <title>{title}</title>
</head> </head>
<body> <body>
<Header
heading="The Clog"
links={[
{
text: 'Journal',
href: '/journal',
},
{
text: 'Articles',
href: '/articles',
},
]}
buttons={[
{
title: 'Gitea',
icon: 'brand-git',
href: 'https://git.sakal.us/avraham/',
target: '_blank',
contrast: true,
},
]}
frame="fill"
position="relative"
color="brand"
class:list={["mb-6"]}
/>
<slot /> <slot />
</body> </body>
</html> </html>
<style is:global> <style is:global>
:root {
--accent: 136, 58, 234;
--accent-light: 224, 204, 250;
--accent-dark: 49, 10, 101;
--accent-gradient: linear-gradient(
45deg,
rgb(var(--accent)),
rgb(var(--accent-light)) 30%,
white 60%
);
}
html { html {
font-family: system-ui, sans-serif; font-family: system-ui, sans-serif;
background: #13151a; background: #13151a;
} }
code {
font-family:
Menlo,
Monaco,
Lucida Console,
Liberation Mono,
DejaVu Sans Mono,
Bitstream Vera Sans Mono,
Courier New,
monospace;
}
</style> </style>
+30
View File
@@ -0,0 +1,30 @@
---
import Layout from '../../layouts/Layout.astro';
import Section from 'fulldev-ui/components/Section.astro';
import { getCollection } from 'astro:content';
const articles = await getCollection('articles');
export const prerender = true;
---
<Layout title="Articles">
<main>
<Section
title="Articles"
text="Coming Soon!"
cards={articles.map((article)=>({
frame:"panel",
title:article.data.title,
tagline: article.data.date.toISOString().substring(0,10),
// list: ["one", "two"],
badge: article.data.category,
href: `/articles/${article.slug}`,
description: article.data.description,
}))}
align='center'
justify="start"
structure="grid"
>
</Section>
</main>
</Layout>
+4 -9
View File
@@ -1,17 +1,12 @@
--- ---
import Layout from "../layouts/Layout.astro";
export const prerender = false; export const prerender = false;
--- ---
<html lang="en"> <Layout title="Current Time">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" />
<title>Current Time</title>
</head>
<body>
<h1>Current Time</h1> <h1>Current Time</h1>
<p>This page is rendered on the server.</p> <p>This page is rendered on the server.</p>
<time datetime={new Date().toISOString()}> <time datetime={new Date().toISOString()}>
{new Date().toLocaleString()} {new Date().toLocaleString()}
</time> </time>
</body> </Layout>
</html>
+13 -115
View File
@@ -1,123 +1,21 @@
--- ---
import Layout from '../layouts/Layout.astro'; import Layout from '../layouts/Layout.astro';
import Card from '../components/Card.astro'; import Hero from 'fulldev-ui/blocks/Hero.astro';
--- ---
<Layout title="Welcome to Astro."> <Layout title="The Clog">
<main> <main>
<svg <Hero
class="astro-a" badge="The Coding Blog"
width="495" heading="Welcome to The Clog"
height="623" text="A practical coding blog in the form of journal entries and articles."
viewBox="0 0 495 623" buttons={[
fill="none" { text: 'Journal', href: '/journal', contrast: true },
xmlns="http://www.w3.org/2000/svg" { text: 'Articles', href: '/articles', contrast: true },
aria-hidden="true" ]}
> structure="split"
<path image="/hero.png"
fill-rule="evenodd" class:list={["p-space-6"]}
clip-rule="evenodd"
d="M167.19 364.254C83.4786 364.254 0 404.819 0 404.819C0 404.819 141.781 19.4876 142.087 18.7291C146.434 7.33701 153.027 0 162.289 0H332.441C341.703 0 348.574 7.33701 352.643 18.7291C352.92 19.5022 494.716 404.819 494.716 404.819C494.716 404.819 426.67 364.254 327.525 364.254L264.41 169.408C262.047 159.985 255.147 153.581 247.358 153.581C239.569 153.581 232.669 159.985 230.306 169.408L167.19 364.254ZM160.869 530.172C160.877 530.18 160.885 530.187 160.894 530.195L160.867 530.181C160.868 530.178 160.868 530.175 160.869 530.172ZM136.218 411.348C124.476 450.467 132.698 504.458 160.869 530.172C160.997 529.696 161.125 529.242 161.248 528.804C161.502 527.907 161.737 527.073 161.917 526.233C165.446 509.895 178.754 499.52 195.577 500.01C211.969 500.487 220.67 508.765 223.202 527.254C224.141 534.12 224.23 541.131 224.319 548.105C224.328 548.834 224.337 549.563 224.347 550.291C224.563 566.098 228.657 580.707 237.264 593.914C245.413 606.426 256.108 615.943 270.749 622.478C270.593 621.952 270.463 621.508 270.35 621.126C270.045 620.086 269.872 619.499 269.685 618.911C258.909 585.935 266.668 563.266 295.344 543.933C298.254 541.971 301.187 540.041 304.12 538.112C310.591 533.854 317.059 529.599 323.279 525.007C345.88 508.329 360.09 486.327 363.431 457.844C364.805 446.148 363.781 434.657 359.848 423.275C358.176 424.287 356.587 425.295 355.042 426.275C351.744 428.366 348.647 430.33 345.382 431.934C303.466 452.507 259.152 455.053 214.03 448.245C184.802 443.834 156.584 436.019 136.218 411.348Z"
fill="url(#paint0_linear_1805_24383)"></path>
<defs>
<linearGradient
id="paint0_linear_1805_24383"
x1="247.358"
y1="0"
x2="247.358"
y2="622.479"
gradientUnits="userSpaceOnUse"
>
<stop stop-opacity="0.9"></stop>
<stop offset="1" stop-opacity="0.2"></stop>
</linearGradient>
</defs>
</svg>
<h1>Welcome to <span class="text-gradient">Astro</span></h1>
<p class="instructions">
To get started, open the directory <code>src/pages</code> in your project.<br />
<strong>Code Challenge:</strong> Tweak the "Welcome to Astro" message above.
</p>
<ul role="list" class="link-card-grid">
<Card
href="https://docs.astro.build/"
title="Documentation"
body="Learn how Astro works and explore the official API docs."
/> />
<Card
href="https://astro.build/integrations/"
title="Integrations"
body="Supercharge your project with new frameworks and libraries."
/>
<Card
href="https://astro.build/themes/"
title="Themes"
body="Explore a galaxy of community-built starter themes."
/>
<Card
href="https://astro.build/chat/"
title="Community"
body="Come say hi to our amazing Discord community. ❤️"
/>
</ul>
</main> </main>
</Layout> </Layout>
<style>
main {
margin: auto;
padding: 1rem;
width: 800px;
max-width: calc(100% - 2rem);
color: white;
font-size: 20px;
line-height: 1.6;
}
.astro-a {
position: absolute;
top: -32px;
left: 50%;
transform: translatex(-50%);
width: 220px;
height: auto;
z-index: -1;
}
h1 {
font-size: 4rem;
font-weight: 700;
line-height: 1;
text-align: center;
margin-bottom: 1em;
}
.text-gradient {
background-image: var(--accent-gradient);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-size: 400%;
background-position: 0%;
}
.instructions {
margin-bottom: 2rem;
border: 1px solid rgba(var(--accent-light), 25%);
background: linear-gradient(rgba(var(--accent-dark), 66%), rgba(var(--accent-dark), 33%));
padding: 1.5rem;
border-radius: 8px;
}
.instructions code {
font-size: 0.8em;
font-weight: bold;
background: rgba(var(--accent-light), 12%);
color: rgb(var(--accent-light));
border-radius: 4px;
padding: 0.3em 0.4em;
}
.instructions strong {
color: rgb(var(--accent-light));
}
.link-card-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(24ch, 1fr));
gap: 2rem;
padding: 0;
}
</style>
+4
View File
@@ -0,0 +1,4 @@
---
import Text from 'fulldev-ui/components/Text.astro';
---
<Text contrast={true}><slot /></Text>
+31
View File
@@ -0,0 +1,31 @@
---
import { getCollection } from 'astro:content';
import Layout from '../../layouts/Layout.astro';
import Heading from 'fulldev-ui/components/Heading.astro';
import Link from 'fulldev-ui/components/Link.astro';
import RegularText from './RegularText.astro';
export const prerender = true;
// 1. Generate a new path for every collection entry
export async function getStaticPaths() {
const journalEntries = await getCollection('journal-entries');
return journalEntries.map(journalEntry => ({
params: { slug: journalEntry.slug }, props: { journalEntry },
}));
}
// 2. For your template, you can get the entry directly from the prop
const { journalEntry } = Astro.props;
const { Content } = await journalEntry.render();
---
<Layout title={journalEntry.data.title}>
<main>
<Heading>{journalEntry.data.title}</Heading>
<Content components={{
p: RegularText,
a: Link,
}} />
</main>
</Layout>
+35
View File
@@ -0,0 +1,35 @@
---
import { getCollection } from 'astro:content';
import Layout from '../../layouts/Layout.astro';
import Section from 'fulldev-ui/components/Section.astro';
import Text from 'fulldev-ui/components/Text.astro';
const journalEntries = await getCollection('journal-entries');
export const prerender = true;
---
<Layout title="Journal Entries">
<main>
<Section
title="Journal Entries"
cards={journalEntries.map((journalEntry)=>({
frame:"panel",
title:journalEntry.data.title,
tagline: journalEntry.data.date.toISOString().substring(0,10),
badge: journalEntry.data.category,
// badges: journalEntry.data.tags,
href: `/journal/${journalEntry.slug}`,
description: journalEntry.data.description,
// html: <Text>{journalEntry.data.description}</Text>,
size: "sm",
}))}
align='center'
justify="start"
structure="grid"
size="md"
>
</Section>
</main>
</Layout>
+3 -9
View File
@@ -1,16 +1,10 @@
--- ---
import Layout from '../layouts/Layout.astro';
--- ---
<html lang="en"> <Layout title='Prerendered Time'>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" />
<title>Current Time</title>
</head>
<body>
<h1>Current Time</h1> <h1>Current Time</h1>
<p>This page is rendered on the server.</p> <p>This page is rendered on the server.</p>
<time datetime={new Date().toISOString()}> <time datetime={new Date().toISOString()}>
{new Date().toLocaleString()} {new Date().toLocaleString()}
</time> </time>
</body> </Layout>
</html>
+4 -1
View File
@@ -1,3 +1,6 @@
{ {
"extends": "astro/tsconfigs/strict" "extends": "astro/tsconfigs/strict",
"compilerOptions": {
"strictNullChecks": true
}
} }
+12
View File
@@ -0,0 +1,12 @@
import { defineConfig } from "unocss";
import presetUno from "@unocss/preset-uno";
import fulldevUI from "fulldev-ui/unocss";
export default defineConfig({
injectReset: true,
presets: [
presetUno,
//@ts-ignore
fulldevUI,
],
});
+6
View File
@@ -0,0 +1,6 @@
# Generated by Wrangler on Fri Dec 06 2024 12:22:28 GMT-0500 (Eastern Standard Time)
name = "blog"
compatibility_date = "2024-12-06"
[env]
production = { }