mirror of
https://gitlab.com/Anson-Projects/projects.git
synced 2026-06-03 21:00:27 +00:00
Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 85adfdf067 | |||
| c9e0264208 | |||
| d3966eaf53 | |||
| 21ad5cb862 | |||
| 9e2596c070 | |||
| f93746e2c0 | |||
| ae1be54f8f | |||
| e479c96e44 | |||
| 890775b2bc | |||
| 788052233a | |||
| 1a4773b3ef | |||
| 84f4e48386 | |||
| 52229040c6 | |||
| b70c57e23e | |||
| f6532e4fb6 | |||
| 0675f1f1b7 | |||
| b5a4b33b56 | |||
| 9fc6a9bae1 | |||
| 05474b986d | |||
| cdb96a50b7 | |||
| e233a96f55 |
@@ -16,8 +16,6 @@ staging:
|
|||||||
script:
|
script:
|
||||||
- echo "Building the main website with Quarto..."
|
- echo "Building the main website with Quarto..."
|
||||||
- quarto render --to html --output-dir public
|
- quarto render --to html --output-dir public
|
||||||
- echo "Checking for RSS feed after render..."
|
|
||||||
- ls -la public/ | grep -E "\.xml|index\.xml" || echo "No XML files found in public directory"
|
|
||||||
- echo "Building Ghost-optimized version..."
|
- echo "Building Ghost-optimized version..."
|
||||||
- quarto render --profile ghost --to html --output-dir public/ghost-content
|
- quarto render --profile ghost --to html --output-dir public/ghost-content
|
||||||
artifacts:
|
artifacts:
|
||||||
|
|||||||
@@ -2,7 +2,8 @@ const kProgressiveAttr = "data-src";
|
|||||||
let categoriesLoaded = false;
|
let categoriesLoaded = false;
|
||||||
|
|
||||||
window.quartoListingCategory = (category) => {
|
window.quartoListingCategory = (category) => {
|
||||||
category = atob(category);
|
// category is URI encoded in EJS template for UTF-8 support
|
||||||
|
category = decodeURIComponent(atob(category));
|
||||||
if (categoriesLoaded) {
|
if (categoriesLoaded) {
|
||||||
activateCategory(category);
|
activateCategory(category);
|
||||||
setCategoryHash(category);
|
setCategoryHash(category);
|
||||||
|
|||||||
-11
@@ -1,18 +1,12 @@
|
|||||||
project:
|
project:
|
||||||
type: website
|
type: website
|
||||||
|
|
||||||
website:
|
|
||||||
title: "Anson's Projects"
|
|
||||||
site-url: https://projects.ansonbiggs.com
|
|
||||||
description: A Blog for Technical Topics
|
|
||||||
|
|
||||||
profiles:
|
profiles:
|
||||||
default:
|
default:
|
||||||
website:
|
website:
|
||||||
title: "Anson's Projects"
|
title: "Anson's Projects"
|
||||||
site-url: https://projects.ansonbiggs.com
|
site-url: https://projects.ansonbiggs.com
|
||||||
description: A Blog for Technical Topics
|
description: A Blog for Technical Topics
|
||||||
author: "Anson Biggs"
|
|
||||||
navbar:
|
navbar:
|
||||||
left:
|
left:
|
||||||
- text: "About"
|
- text: "About"
|
||||||
@@ -23,11 +17,6 @@ profiles:
|
|||||||
# - icon: gitlab
|
# - icon: gitlab
|
||||||
# href: https://gitlab.com/MisterBiggs
|
# href: https://gitlab.com/MisterBiggs
|
||||||
open-graph: true
|
open-graph: true
|
||||||
feed:
|
|
||||||
title: "Anson's Projects"
|
|
||||||
description: "A Blog for Technical Topics"
|
|
||||||
author: "Anson Biggs"
|
|
||||||
items: 10
|
|
||||||
format:
|
format:
|
||||||
html:
|
html:
|
||||||
theme: zephyr
|
theme: zephyr
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ publish:
|
|||||||
- cd ./ghost-upload
|
- cd ./ghost-upload
|
||||||
- cargo run
|
- cargo run
|
||||||
needs:
|
needs:
|
||||||
- pages
|
- job: pages
|
||||||
|
optional: true
|
||||||
rules:
|
rules:
|
||||||
- if: "$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH"
|
- if: "$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH"
|
||||||
|
- if: "$CI_COMMIT_BRANCH == 'ghost-content-extraction'" # Allow testing on this branch
|
||||||
|
|||||||
@@ -227,12 +227,48 @@ async fn get_existing_post_id(slug: &str, token: &str) -> Option<String> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn fetch_feed(path: &str) -> Vec<Entry> {
|
async fn fetch_feed(url: &str) -> Vec<Entry> {
|
||||||
// Read from local file instead of HTTP request
|
println!("Fetching RSS feed from: {}", url);
|
||||||
let content = std::fs::read_to_string(path).expect("Failed to read RSS feed file");
|
|
||||||
|
|
||||||
let feed = parser::parse(content.as_bytes()).expect("Failed to parse RSS feed");
|
let response = reqwest::get(url).await;
|
||||||
|
let response = match response {
|
||||||
|
Ok(resp) => resp,
|
||||||
|
Err(e) => {
|
||||||
|
println!("Failed to fetch RSS feed: {}", e);
|
||||||
|
return vec![];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if !response.status().is_success() {
|
||||||
|
println!("RSS feed request failed with status: {}", response.status());
|
||||||
|
return vec![];
|
||||||
|
}
|
||||||
|
|
||||||
|
let content = match response.text().await {
|
||||||
|
Ok(text) => text,
|
||||||
|
Err(e) => {
|
||||||
|
println!("Failed to read RSS feed content: {}", e);
|
||||||
|
return vec![];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if content.trim().is_empty() {
|
||||||
|
println!("RSS feed content is empty");
|
||||||
|
return vec![];
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("RSS feed content preview: {}", &content[..content.len().min(200)]);
|
||||||
|
|
||||||
|
let feed = match parser::parse(content.as_bytes()) {
|
||||||
|
Ok(f) => f,
|
||||||
|
Err(e) => {
|
||||||
|
println!("Failed to parse RSS feed: {:?}", e);
|
||||||
|
println!("Feed content starts with: {}", &content[..content.len().min(500)]);
|
||||||
|
return vec![];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
println!("Successfully parsed RSS feed with {} entries", feed.entries.len());
|
||||||
feed.entries
|
feed.entries
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -297,7 +333,7 @@ async fn main() {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
let feed = "../public/index.xml";
|
let feed = "https://projects.ansonbiggs.com/index.xml";
|
||||||
|
|
||||||
// Split the key into ID and SECRET
|
// Split the key into ID and SECRET
|
||||||
let (id, secret) = ghost_admin_api_key
|
let (id, secret) = ghost_admin_api_key
|
||||||
@@ -330,6 +366,13 @@ async fn main() {
|
|||||||
// Prepare the post data
|
// Prepare the post data
|
||||||
let entries = fetch_feed(feed).await;
|
let entries = fetch_feed(feed).await;
|
||||||
|
|
||||||
|
if entries.is_empty() {
|
||||||
|
println!("No entries found in RSS feed or feed parsing failed. Exiting.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Processing {} entries from RSS feed", entries.len());
|
||||||
|
|
||||||
let post_exists_futures = entries.into_iter().map(|entry| {
|
let post_exists_futures = entries.into_iter().map(|entry| {
|
||||||
let entry_clone = entry.clone();
|
let entry_clone = entry.clone();
|
||||||
let token_clone = token.clone();
|
let token_clone = token.clone();
|
||||||
|
|||||||
@@ -1,114 +0,0 @@
|
|||||||
---
|
|
||||||
title: "Brachistochrone"
|
|
||||||
description: |
|
|
||||||
Lets create a double pendulum in Observable JS!
|
|
||||||
date: 2025-05-09
|
|
||||||
categories:
|
|
||||||
- Observable JS
|
|
||||||
- Code
|
|
||||||
- Math
|
|
||||||
---
|
|
||||||
|
|
||||||
This is a bunch of text This is a bunch of text This is a bunch of text This is a bunch of text This is a bunch of text This is a bunch of text This is a bunch of text This is a bunch of text This is a bunch of text This is a bunch of text This is a bunch of text
|
|
||||||
|
|
||||||
:::{.column-screen}
|
|
||||||
```{ojs}
|
|
||||||
//| echo: false
|
|
||||||
|
|
||||||
|
|
||||||
viewof canvas = {
|
|
||||||
const height = 500;
|
|
||||||
const canvas = html`<canvas width=${width} height="${height}" style="border: 1px solid #ccc; cursor: crosshair;"></canvas>`;
|
|
||||||
const ctx = canvas.getContext('2d');
|
|
||||||
|
|
||||||
let isDrawing = false;
|
|
||||||
let lastX = 0;
|
|
||||||
let lastY = 0;
|
|
||||||
let pathLength = 0;
|
|
||||||
let pathHistory = [{time: Date.now(), length: 0}];
|
|
||||||
|
|
||||||
// Drawing settings
|
|
||||||
ctx.strokeStyle = '#000';
|
|
||||||
ctx.lineWidth = 2;
|
|
||||||
ctx.lineCap = 'round';
|
|
||||||
ctx.lineJoin = 'round';
|
|
||||||
|
|
||||||
canvas.addEventListener('mousedown', (e) => {
|
|
||||||
if (!isDrawing) {
|
|
||||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
||||||
ctx.arc(300, 100, 5, 0, 2 * Math.PI);
|
|
||||||
ctx.fillStyle = 'red';
|
|
||||||
ctx.fill();
|
|
||||||
ctx.arc(600, 300, 5, 0, 2 * Math.PI);
|
|
||||||
ctx.fillStyle = 'red';
|
|
||||||
ctx.fill();
|
|
||||||
pathLength = 0;
|
|
||||||
pathHistory = [{time: Date.now(), length: 0}];
|
|
||||||
canvas.value = {length: pathLength, history: pathHistory};
|
|
||||||
canvas.dispatchEvent(new CustomEvent("input"));
|
|
||||||
}
|
|
||||||
isDrawing = true;
|
|
||||||
[lastX, lastY] = [e.offsetX, e.offsetY];
|
|
||||||
});
|
|
||||||
|
|
||||||
canvas.addEventListener('mousemove', (e) => {
|
|
||||||
if (!isDrawing) return;
|
|
||||||
|
|
||||||
// Calculate distance
|
|
||||||
const distance = Math.sqrt(
|
|
||||||
Math.pow(e.offsetX - lastX, 2) +
|
|
||||||
Math.pow(e.offsetY - lastY, 2)
|
|
||||||
);
|
|
||||||
const angle = Math.atan2(e.offsetY - lastY, e.offsetX - lastX)
|
|
||||||
pathLength += distance;
|
|
||||||
|
|
||||||
// Draw
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.moveTo(lastX, lastY);
|
|
||||||
ctx.lineTo(e.offsetX, e.offsetY);
|
|
||||||
ctx.stroke();
|
|
||||||
[lastX, lastY] = [e.offsetX, e.offsetY];
|
|
||||||
|
|
||||||
// Update value and emit event
|
|
||||||
pathHistory.push({time: Date.now(), length: pathLength});
|
|
||||||
canvas.value = {length: pathLength, history: pathHistory};
|
|
||||||
canvas.dispatchEvent(new CustomEvent("input"));
|
|
||||||
});
|
|
||||||
|
|
||||||
canvas.addEventListener('mouseup', () => isDrawing = false);
|
|
||||||
canvas.addEventListener('mouseout', () => isDrawing = false);
|
|
||||||
|
|
||||||
// Initial value
|
|
||||||
canvas.value = {length: 0, history: pathHistory};
|
|
||||||
return canvas;
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
```{ojs}
|
|
||||||
//| echo: false
|
|
||||||
md`Current path length: **${canvas.length?.toFixed(0) || 0} pixels**`
|
|
||||||
```
|
|
||||||
|
|
||||||
```{ojs}
|
|
||||||
//| echo: false
|
|
||||||
import {Plot} from "@observablehq/plot"
|
|
||||||
|
|
||||||
pathChart = Plot.plot({
|
|
||||||
width: width,
|
|
||||||
height: 200,
|
|
||||||
y: {label: "Path Length (pixels)"},
|
|
||||||
x: {label: "Time", type: "time"},
|
|
||||||
marks: [
|
|
||||||
Plot.line(canvas.history || [], {
|
|
||||||
x: "time",
|
|
||||||
y: "length",
|
|
||||||
stroke: "steelblue",
|
|
||||||
strokeWidth: 2
|
|
||||||
})
|
|
||||||
]
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
This is a bunch of text This is a bunch of text This is a bunch of text This is a bunch of text This is a bunch of text This is a bunch of text This is a bunch of text This is a bunch of text This is a bunch of text This is a bunch of text This is a bunch of text
|
|
||||||
Reference in New Issue
Block a user