remove so much stuff
|
Before Width: | Height: | Size: 294 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 7.9 KiB |
|
Before Width: | Height: | Size: 9.7 KiB |
|
|
@ -1,259 +0,0 @@
|
|||
(() => {
|
||||
if (window.__BT_INDEX_INIT) return;
|
||||
window.__BT_INDEX_INIT = true;
|
||||
|
||||
const TERMINAL_PROMPT = "barrett@ruth:~$ ";
|
||||
let typing = false;
|
||||
|
||||
function promptEl() {
|
||||
return document.querySelector(".terminal-prompt");
|
||||
}
|
||||
function promptTail() {
|
||||
const el = promptEl();
|
||||
if (!el) return "";
|
||||
const s = el.textContent || "";
|
||||
return s.startsWith(TERMINAL_PROMPT) ? s.slice(TERMINAL_PROMPT.length) : s;
|
||||
}
|
||||
function setPromptTailImmediate(tail) {
|
||||
const el = promptEl();
|
||||
if (!el) return;
|
||||
el.textContent = TERMINAL_PROMPT + tail;
|
||||
}
|
||||
function persistPrompt() {
|
||||
const el = promptEl();
|
||||
if (el) sessionStorage.setItem("terminalPromptText", el.textContent);
|
||||
}
|
||||
(function restorePrompt() {
|
||||
const saved = sessionStorage.getItem("terminalPromptText");
|
||||
const el = promptEl();
|
||||
if (saved && el) el.textContent = saved;
|
||||
sessionStorage.removeItem("terminalPromptText");
|
||||
})();
|
||||
|
||||
function normalizeDisplayPath(pathname) {
|
||||
let p = pathname.replace(/\/index\.html$/, "/").replace(/\.html$/, "");
|
||||
p = p.replace(/\/{2,}/g, "/");
|
||||
if (p !== "/" && p.endsWith("/")) p = p.slice(0, -1);
|
||||
return p === "/" ? "" : p;
|
||||
}
|
||||
function displayPathFromHref(href) {
|
||||
const url = new URL(href, location.origin);
|
||||
return normalizeDisplayPath(url.pathname);
|
||||
}
|
||||
function currentDisplayPath() {
|
||||
return normalizeDisplayPath(location.pathname);
|
||||
}
|
||||
function animateToDisplayPath(displayPath, totalMs, done) {
|
||||
if (typing) return;
|
||||
typing = true;
|
||||
|
||||
const el = promptEl();
|
||||
if (!el) {
|
||||
typing = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const targetTail = displayPath ? " " + displayPath : "";
|
||||
const curTail = promptTail();
|
||||
|
||||
let i = 0;
|
||||
const max = Math.min(curTail.length, targetTail.length);
|
||||
while (i < max && curTail.charAt(i) === targetTail.charAt(i)) i++;
|
||||
|
||||
const delSteps = curTail.length - i;
|
||||
const typeSteps = targetTail.length - i;
|
||||
const totalSteps = delSteps + typeSteps;
|
||||
|
||||
if (totalSteps === 0) {
|
||||
typing = false;
|
||||
done && done();
|
||||
return;
|
||||
}
|
||||
|
||||
const stepMs = totalMs / totalSteps;
|
||||
|
||||
let delCount = 0;
|
||||
function tickDelete() {
|
||||
if (delCount < delSteps) {
|
||||
setPromptTailImmediate(curTail.slice(0, curTail.length - delCount - 1));
|
||||
delCount++;
|
||||
setTimeout(tickDelete, stepMs);
|
||||
} else {
|
||||
let j = 0;
|
||||
function tickType() {
|
||||
if (j < typeSteps) {
|
||||
setPromptTailImmediate(
|
||||
curTail.slice(0, i) + targetTail.slice(i, i + j + 1),
|
||||
);
|
||||
j++;
|
||||
setTimeout(tickType, stepMs);
|
||||
} else {
|
||||
typing = false;
|
||||
done && done();
|
||||
}
|
||||
}
|
||||
tickType();
|
||||
}
|
||||
}
|
||||
tickDelete();
|
||||
}
|
||||
|
||||
function renderPosts(topic) {
|
||||
if (!window.postsByCategory) return;
|
||||
const posts = document.getElementById("posts");
|
||||
if (!posts) return;
|
||||
posts.innerHTML = "";
|
||||
const arr = window.postsByCategory[topic];
|
||||
if (!arr) return;
|
||||
for (const post of arr) {
|
||||
const div = document.createElement("div");
|
||||
div.className = "post";
|
||||
const a = document.createElement("a");
|
||||
const slug =
|
||||
post.slug ||
|
||||
post.id
|
||||
?.split("/")
|
||||
.pop()
|
||||
?.replace(/\.mdx?$/, "");
|
||||
a.href = `/${topic}/${slug}.html`;
|
||||
a.textContent = post.data.title;
|
||||
a.style.textDecoration = "underline";
|
||||
div.appendChild(a);
|
||||
posts.appendChild(div);
|
||||
}
|
||||
}
|
||||
|
||||
function handleDataTopicClick(e) {
|
||||
const link = e.target.closest("[data-topic]");
|
||||
if (!link) return;
|
||||
e.preventDefault();
|
||||
if (typing) return;
|
||||
|
||||
const path = window.location.pathname;
|
||||
const isHome = path === "/" || path === "/index.html";
|
||||
const topic = link.dataset.topic?.toLowerCase() || "";
|
||||
const href = link.getAttribute("href") || "/";
|
||||
const delay = 500;
|
||||
|
||||
const colorFn = window.getTopicColor || (() => "");
|
||||
const topicKey = topic.split("/")[0];
|
||||
const willNavigateAway = !(
|
||||
isHome &&
|
||||
topic &&
|
||||
window.postsByCategory &&
|
||||
window.postsByCategory[topic]
|
||||
);
|
||||
|
||||
if (willNavigateAway) {
|
||||
link.classList.add("active");
|
||||
const c = colorFn(topicKey);
|
||||
if (c) link.style.color = c;
|
||||
} else {
|
||||
document.querySelectorAll("[data-topic]").forEach((t) => {
|
||||
t.classList.remove("active");
|
||||
t.style.color = "";
|
||||
});
|
||||
link.classList.add("active");
|
||||
const c = colorFn(topicKey);
|
||||
if (c) link.style.color = c;
|
||||
}
|
||||
|
||||
const displayPath = isHome ? `/${topic}` : displayPathFromHref(href);
|
||||
|
||||
animateToDisplayPath(displayPath, delay, () => {
|
||||
if (
|
||||
isHome &&
|
||||
topic &&
|
||||
window.postsByCategory &&
|
||||
window.postsByCategory[topic]
|
||||
) {
|
||||
renderPosts(topic);
|
||||
return;
|
||||
}
|
||||
persistPrompt();
|
||||
|
||||
const isMail = href.startsWith("mailto:");
|
||||
if (isMail) {
|
||||
window.location.href = href;
|
||||
return;
|
||||
}
|
||||
if (link.target === "_blank") {
|
||||
window.open(href, "_blank");
|
||||
return;
|
||||
}
|
||||
window.location.href = href;
|
||||
});
|
||||
}
|
||||
|
||||
function handleHomeClick(e) {
|
||||
const home = e.target.closest(".home-link");
|
||||
if (!home) return;
|
||||
e.preventDefault();
|
||||
const isHome =
|
||||
window.location.pathname === "/" ||
|
||||
window.location.pathname === "/index.html";
|
||||
const delay = 500;
|
||||
if (isHome) {
|
||||
animateToDisplayPath("", delay, () => {
|
||||
const posts = document.getElementById("posts");
|
||||
if (posts) posts.innerHTML = "";
|
||||
document.querySelectorAll("[data-topic].active").forEach((t) => {
|
||||
t.classList.remove("active");
|
||||
t.style.color = "";
|
||||
});
|
||||
document.title = "";
|
||||
});
|
||||
} else {
|
||||
persistPrompt();
|
||||
animateToDisplayPath("", delay, () => {
|
||||
window.location.href = "/";
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const initial = currentDisplayPath();
|
||||
if (initial) setPromptTailImmediate(" " + initial);
|
||||
else setPromptTailImmediate("");
|
||||
|
||||
document.body.addEventListener("click", (e) => {
|
||||
if (e.target.closest(".home-link")) return handleHomeClick(e);
|
||||
if (e.target.closest(".topics [data-topic]"))
|
||||
return handleDataTopicClick(e);
|
||||
});
|
||||
|
||||
document.body.addEventListener(
|
||||
"mouseenter",
|
||||
(e) => {
|
||||
const link = e.target.closest(".topics [data-topic]");
|
||||
if (!link) return;
|
||||
const raw = link.dataset.topic || "";
|
||||
const key = raw.split("/")[0].toLowerCase();
|
||||
const color = (window.getTopicColor && window.getTopicColor(key)) || "";
|
||||
if (color) {
|
||||
link.style.color = color;
|
||||
link.style.textDecorationColor = color;
|
||||
}
|
||||
},
|
||||
true,
|
||||
);
|
||||
|
||||
document.body.addEventListener(
|
||||
"mouseleave",
|
||||
(e) => {
|
||||
const link = e.target.closest(".topics [data-topic]");
|
||||
if (!link) return;
|
||||
if (!link.classList.contains("active")) {
|
||||
link.style.color = "";
|
||||
link.style.textDecorationColor = "";
|
||||
}
|
||||
},
|
||||
true,
|
||||
);
|
||||
|
||||
window.addEventListener("beforeunload", () => {
|
||||
const el = promptEl();
|
||||
if (el) el.textContent = TERMINAL_PROMPT;
|
||||
});
|
||||
});
|
||||
})();
|
||||
|
|
@ -1,666 +0,0 @@
|
|||
function urlToTopic() {
|
||||
const path = window.location.pathname;
|
||||
const pathParts = path.split("/");
|
||||
return pathParts[2];
|
||||
}
|
||||
|
||||
function getTextColor() {
|
||||
return getComputedStyle(document.documentElement)
|
||||
.getPropertyValue("--text")
|
||||
.trim();
|
||||
}
|
||||
|
||||
function setUpParameters(render, parameters, modelPrefix) {
|
||||
parameters.forEach((param) => {
|
||||
const slider = document.getElementById(`slider${modelPrefix}${param}`);
|
||||
slider.oninput = function () {
|
||||
slider.previousElementSibling.innerText = this.value;
|
||||
render();
|
||||
};
|
||||
});
|
||||
return parameters.map((param) => {
|
||||
return parseFloat(
|
||||
document.getElementById(`output${modelPrefix}${param}`).textContent,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function drawSolowGraph() {
|
||||
const L = 150,
|
||||
K_MAX = 500,
|
||||
margin = { top: 50, right: 55, bottom: 40, left: 75 };
|
||||
|
||||
const [A, d, s, alpha] = setUpParameters(
|
||||
drawSolowGraph,
|
||||
["A", "d", "s", "alpha"],
|
||||
"S",
|
||||
);
|
||||
const solowOutput = (K) => A * Math.pow(K, alpha) * Math.pow(L, 1 - alpha);
|
||||
const solowDepreciation = (K) => d * K;
|
||||
const solowInvestment = (Y) => s * Y;
|
||||
|
||||
const container = document.getElementById("solow-visualization");
|
||||
|
||||
const width = container.clientWidth - margin.left - margin.right;
|
||||
const height = container.clientHeight - margin.top - margin.bottom;
|
||||
|
||||
container.innerHTML = "";
|
||||
|
||||
const svg = d3
|
||||
.select("#solow-visualization")
|
||||
.append("svg")
|
||||
.attr("width", width + margin.left + margin.right)
|
||||
.attr("height", height + margin.top + margin.bottom)
|
||||
.append("g")
|
||||
.attr("transform", `translate(${margin.left}, ${margin.top})`);
|
||||
|
||||
const x = d3.scaleLinear().domain([0, K_MAX]).range([0, width]);
|
||||
const xAxis = svg
|
||||
.append("g")
|
||||
.attr("transform", `translate(0, ${height})`)
|
||||
.call(d3.axisBottom(x));
|
||||
xAxis.selectAll("text").style("font-size", "1.5em");
|
||||
xAxis
|
||||
.append("text")
|
||||
.attr("x", width + 10)
|
||||
.attr("y", -10)
|
||||
.style("text-anchor", "end")
|
||||
.style("font-size", "2em")
|
||||
.text("K");
|
||||
|
||||
const Y_MAX = solowOutput(K_MAX) + K_MAX / 10;
|
||||
const y = d3.scaleLinear().domain([0, Y_MAX]).range([height, 0]);
|
||||
const yAxis = svg.append("g").call(d3.axisLeft(y));
|
||||
yAxis.selectAll("text").style("font-size", "1.5em");
|
||||
yAxis
|
||||
.append("text")
|
||||
.attr("x", 0)
|
||||
.attr("y", -10)
|
||||
.style("text-anchor", "start")
|
||||
.style("font-size", "2em")
|
||||
.text("Y");
|
||||
|
||||
const outputData = Array.from({ length: K_MAX }, (_, k) => ({
|
||||
K: k,
|
||||
Y: solowOutput(k),
|
||||
}));
|
||||
svg
|
||||
.append("path")
|
||||
.datum(outputData)
|
||||
.attr("fill", "none")
|
||||
.attr("stroke", window.getTopicColor(urlToTopic()))
|
||||
.attr("stroke-width", 2)
|
||||
.attr(
|
||||
"d",
|
||||
d3
|
||||
.line()
|
||||
.x((d) => x(d.K))
|
||||
.y((d) => y(d.Y)),
|
||||
);
|
||||
svg
|
||||
.append("foreignObject")
|
||||
.attr("width", "2em")
|
||||
.attr("height", "2em")
|
||||
.attr("x", x(K_MAX))
|
||||
.attr("y", y(outputData[K_MAX - 1].Y))
|
||||
.append("xhtml:body")
|
||||
.style("font-size", "0.75em")
|
||||
.text("Y");
|
||||
|
||||
const depreciationData = Array.from({ length: K_MAX }, (_, k) => ({
|
||||
K: k,
|
||||
Y: solowDepreciation(k),
|
||||
}));
|
||||
svg
|
||||
.append("path")
|
||||
.datum(depreciationData)
|
||||
.attr("fill", "none")
|
||||
.attr("stroke", "red")
|
||||
.attr("stroke-width", 2)
|
||||
.attr(
|
||||
"d",
|
||||
d3
|
||||
.line()
|
||||
.x((d) => x(d.K))
|
||||
.y((d) => y(d.Y)),
|
||||
);
|
||||
|
||||
svg
|
||||
.append("foreignObject")
|
||||
.attr("width", "2em")
|
||||
.attr("height", "2em")
|
||||
.attr("x", x(K_MAX))
|
||||
.attr("y", y(depreciationData[K_MAX - 1].Y))
|
||||
.append("xhtml:body")
|
||||
.style("font-size", "0.75em")
|
||||
.append("xhtml:div")
|
||||
.text("dK");
|
||||
|
||||
const investmentData = outputData.map((d) => ({
|
||||
K: d.K,
|
||||
Y: solowInvestment(d.Y),
|
||||
}));
|
||||
svg
|
||||
.append("path")
|
||||
.datum(investmentData)
|
||||
.attr("fill", "none")
|
||||
.attr("stroke", "purple")
|
||||
.attr("stroke-width", 2)
|
||||
.attr(
|
||||
"d",
|
||||
d3
|
||||
.line()
|
||||
.x((d) => x(d.K))
|
||||
.y((d) => y(d.Y)),
|
||||
);
|
||||
|
||||
svg
|
||||
.append("text")
|
||||
.attr("x", x(K_MAX))
|
||||
.attr("y", y(investmentData[K_MAX - 1].Y))
|
||||
.style("font-size", "0.75em")
|
||||
.text("I");
|
||||
|
||||
const k_star = L * Math.pow((s * A) / d, 1 / (1 - alpha));
|
||||
svg
|
||||
.append("line")
|
||||
.attr("x1", x(k_star))
|
||||
.attr("y1", y((d * k_star) / s))
|
||||
.attr("x2", x(k_star))
|
||||
.attr("y2", y(0))
|
||||
.attr("stroke-width", 1)
|
||||
.attr("stroke-dasharray", "5,5");
|
||||
|
||||
const y_star = solowOutput(k_star);
|
||||
svg
|
||||
.append("text")
|
||||
.attr("x", x(k_star) - 40)
|
||||
.attr("y", y(y_star) - 40)
|
||||
.style("font-size", "0.75em")
|
||||
.text(`(${k_star.toFixed(0)}, ${y_star.toFixed(0)})`);
|
||||
}
|
||||
|
||||
const formatNumber = (num) => {
|
||||
return `~${num.toExponential(0)}`;
|
||||
};
|
||||
|
||||
const normalFont = `style="font-weight: normal"`;
|
||||
|
||||
const updateRomerTable = (romerData) => {
|
||||
const tableHeader = document.getElementById("romer-table-header");
|
||||
const rowA_t = document.getElementById("row-A_t");
|
||||
const rowY_t = document.getElementById("row-Y_t");
|
||||
|
||||
tableHeader.innerHTML = `<th ${normalFont}>t</th>`;
|
||||
rowA_t.innerHTML = `<td class="romer-table-at">A_t</td>`;
|
||||
rowY_t.innerHTML = `<td class="romer-table-yt">Y_t</td>`;
|
||||
|
||||
romerData.forEach((d) => {
|
||||
if (d.year % 20 === 0 || d.year === 1) {
|
||||
tableHeader.innerHTML += `<th ${normalFont}>${d.year}</th>`;
|
||||
rowA_t.innerHTML += `<td>${formatNumber(d.A)}</td>`;
|
||||
rowY_t.innerHTML += `<td>${formatNumber(d.Y)}</td>`;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
function drawRomerGraph() {
|
||||
const T_MAX = 100,
|
||||
margin = { top: 50, right: 110, bottom: 40, left: 75 };
|
||||
|
||||
const [z, L, l, A0] = setUpParameters(
|
||||
drawRomerGraph,
|
||||
["z", "L", "l", "A0"],
|
||||
"R",
|
||||
);
|
||||
|
||||
const container = document.getElementById("romer-visualization");
|
||||
const width = container.clientWidth - margin.left - margin.right;
|
||||
const height = container.clientHeight - margin.top - margin.bottom;
|
||||
|
||||
container.innerHTML = "";
|
||||
|
||||
const svg = d3
|
||||
.select("#romer-visualization")
|
||||
.append("svg")
|
||||
.attr("width", width + margin.left + margin.right)
|
||||
.attr("height", height + margin.top + margin.bottom)
|
||||
.append("g")
|
||||
.attr("transform", `translate(${margin.left}, ${margin.top})`);
|
||||
|
||||
let A = A0;
|
||||
const romerData = [];
|
||||
|
||||
for (let t = 1; t <= T_MAX; ++t) {
|
||||
const A_t = A * (1 + z * l * L);
|
||||
const Y_t = A_t * (1 - l) * L;
|
||||
romerData.push({ year: t, A: A_t, Y: Math.log10(Y_t) });
|
||||
A = A_t;
|
||||
}
|
||||
|
||||
const x = d3.scaleLinear().domain([1, T_MAX]).range([0, width]);
|
||||
const xAxis = svg
|
||||
.append("g")
|
||||
.attr("transform", `translate(0, ${height})`)
|
||||
.call(d3.axisBottom(x));
|
||||
xAxis.selectAll("text").style("font-size", "1.5em");
|
||||
xAxis
|
||||
.append("text")
|
||||
.attr("x", width + 10)
|
||||
.attr("y", -10)
|
||||
.style("text-anchor", "end")
|
||||
.style("font-size", "2em")
|
||||
.text("t");
|
||||
|
||||
const y = d3
|
||||
.scaleLinear()
|
||||
.domain([0, romerData[romerData.length - 1].Y])
|
||||
.range([height, 0]);
|
||||
const yAxis = svg
|
||||
.append("g")
|
||||
.call(d3.axisLeft(y).ticks(10, d3.format(".1s")));
|
||||
yAxis.selectAll("text").style("font-size", "1.5em");
|
||||
yAxis
|
||||
.append("text")
|
||||
.attr("x", 0)
|
||||
.attr("y", -10)
|
||||
.style("text-anchor", "start")
|
||||
.style("font-size", "2em")
|
||||
.text("log(Y)");
|
||||
|
||||
svg
|
||||
.append("path")
|
||||
.datum(romerData)
|
||||
.attr("fill", "none")
|
||||
.attr("stroke", window.getTopicColor(urlToTopic()))
|
||||
.attr("stroke-width", 2)
|
||||
.attr(
|
||||
"d",
|
||||
d3
|
||||
.line()
|
||||
.x((d) => x(d.year))
|
||||
.y((d) => y(d.Y)),
|
||||
);
|
||||
|
||||
svg
|
||||
.append("text")
|
||||
.attr("x", x(T_MAX))
|
||||
.attr("y", y(romerData[T_MAX - 1].Y))
|
||||
.style("font-size", "0.75em")
|
||||
.text("log10(Y)");
|
||||
|
||||
updateRomerTable(romerData);
|
||||
}
|
||||
|
||||
function drawRomerlGraph() {
|
||||
const T_MAX = 100,
|
||||
z = 0.01,
|
||||
L = 50,
|
||||
A0 = 50,
|
||||
margin = { top: 50, right: 110, bottom: 40, left: 75 };
|
||||
|
||||
const [l, t0] = setUpParameters(drawRomerlGraph, ["lChange", "t0"], "");
|
||||
|
||||
const container = document.getElementById("romer-lchange-visualization");
|
||||
const width = container.clientWidth - margin.left - margin.right;
|
||||
const height = container.clientHeight - margin.top - margin.bottom;
|
||||
|
||||
container.innerHTML = "";
|
||||
|
||||
const svg = d3
|
||||
.select("#romer-lchange-visualization")
|
||||
.append("svg")
|
||||
.attr("width", width + margin.left + margin.right)
|
||||
.attr("height", height + margin.top + margin.bottom)
|
||||
.append("g")
|
||||
.attr("transform", `translate(${margin.left}, ${margin.top})`);
|
||||
|
||||
let A = A0,
|
||||
l_ = 0.1;
|
||||
const romerData = [];
|
||||
|
||||
for (let t = 1; t <= t0; ++t) {
|
||||
const A_t = A * (1 + z * l_ * L);
|
||||
const Y_t = A_t * (1 - l_) * L;
|
||||
romerData.push({ year: t, A: A_t, Y: Math.log10(Y_t) });
|
||||
A = A_t;
|
||||
}
|
||||
|
||||
for (let t = t0 + 1; t <= T_MAX; ++t) {
|
||||
const A_t = A * (1 + z * l * L);
|
||||
const Y_t = A_t * (1 - l) * L;
|
||||
romerData.push({ year: t, A: A_t, Y: Math.log10(Y_t) });
|
||||
A = A_t;
|
||||
}
|
||||
|
||||
const x = d3.scaleLinear().domain([1, T_MAX]).range([0, width]);
|
||||
const xAxis = svg
|
||||
.append("g")
|
||||
.attr("transform", `translate(0, ${height})`)
|
||||
.call(d3.axisBottom(x));
|
||||
xAxis.selectAll("text").style("font-size", "1.5em");
|
||||
xAxis
|
||||
.append("text")
|
||||
.attr("x", width + 10)
|
||||
.attr("y", -10)
|
||||
.style("text-anchor", "end")
|
||||
.style("font-size", "2em")
|
||||
.text("t");
|
||||
|
||||
const y = d3
|
||||
.scaleLinear()
|
||||
.domain([0, romerData[romerData.length - 1].Y])
|
||||
.range([height, 0]);
|
||||
const yAxis = svg
|
||||
.append("g")
|
||||
.call(d3.axisLeft(y).ticks(10, d3.format(".1s")));
|
||||
yAxis.selectAll("text").style("font-size", "1.5em");
|
||||
yAxis
|
||||
.append("text")
|
||||
.attr("x", 0)
|
||||
.attr("y", -10)
|
||||
.style("text-anchor", "start")
|
||||
.style("font-size", "2em")
|
||||
.text("log(Y)");
|
||||
|
||||
svg
|
||||
.append("path")
|
||||
.datum(romerData)
|
||||
.attr("fill", "none")
|
||||
.attr("stroke", window.getTopicColor(urlToTopic()))
|
||||
.attr("stroke-width", 2)
|
||||
.attr(
|
||||
"d",
|
||||
d3
|
||||
.line()
|
||||
.x((d) => x(d.year))
|
||||
.y((d) => y(d.Y)),
|
||||
);
|
||||
|
||||
svg
|
||||
.append("line")
|
||||
.attr("x1", x(t0))
|
||||
.attr("y1", y(romerData[T_MAX - 1].Y))
|
||||
.attr("x2", x(t0))
|
||||
.attr("y2", height)
|
||||
.attr("stroke-width", 1)
|
||||
.attr("stroke-dasharray", "4");
|
||||
|
||||
svg
|
||||
.append("text")
|
||||
.attr("x", x(0) + 15)
|
||||
.attr("y", y(romerData[0].Y))
|
||||
.style("font-size", "0.6em")
|
||||
.text(`l0=${l_}`);
|
||||
|
||||
svg
|
||||
.append("text")
|
||||
.attr("x", x(t0) + 15)
|
||||
.attr("y", y(romerData[t0].Y))
|
||||
.style("font-size", "0.6em")
|
||||
.text(`l1=${l}`);
|
||||
|
||||
svg
|
||||
.append("text")
|
||||
.attr("x", x(T_MAX))
|
||||
.attr("y", y(romerData[T_MAX - 1].Y))
|
||||
.style("font-size", "0.75em")
|
||||
.text("log10(Y)");
|
||||
}
|
||||
|
||||
function calculateRomerSolowData(
|
||||
T_MAX,
|
||||
L,
|
||||
l,
|
||||
A0,
|
||||
alpha,
|
||||
s,
|
||||
d,
|
||||
z,
|
||||
t0 = Infinity,
|
||||
L0,
|
||||
l0,
|
||||
alpha0,
|
||||
z0,
|
||||
) {
|
||||
let A = A0,
|
||||
K_t = 1,
|
||||
romerSolowData = [];
|
||||
|
||||
for (let t = 1; t <= T_MAX; ++t) {
|
||||
if (t > t0) {
|
||||
alpha = alpha0;
|
||||
z = z0;
|
||||
l = l0;
|
||||
L = L0;
|
||||
}
|
||||
|
||||
const Y_t = A * Math.pow(K_t, alpha) * Math.pow((1 - l) * L, 1 - alpha);
|
||||
const A_t = A * (1 + z * l * L);
|
||||
K_t = K_t + s * Y_t - d * K_t;
|
||||
romerSolowData.push({ year: t, A: A_t, K: K_t, Y: Math.log10(Y_t) });
|
||||
A = A_t;
|
||||
}
|
||||
|
||||
return romerSolowData;
|
||||
}
|
||||
|
||||
function drawRomerSolowGraph() {
|
||||
const T_MAX = 100,
|
||||
margin = { top: 50, right: 110, bottom: 40, left: 75 };
|
||||
|
||||
const [z, l, L, A0, s, d, alpha] = setUpParameters(
|
||||
drawRomerSolowGraph,
|
||||
["z", "l", "L", "A0", "s", "d", "alpha"],
|
||||
"RS",
|
||||
);
|
||||
|
||||
const container = document.getElementById("romer-solow-visualization");
|
||||
const width = container.clientWidth - margin.left - margin.right;
|
||||
const height = container.clientHeight - margin.top - margin.bottom;
|
||||
|
||||
container.innerHTML = "";
|
||||
|
||||
const svg = d3
|
||||
.select("#romer-solow-visualization")
|
||||
.append("svg")
|
||||
.attr("width", width + margin.left + margin.right)
|
||||
.attr("height", height + margin.top + margin.bottom)
|
||||
.append("g")
|
||||
.attr("transform", `translate(${margin.left}, ${margin.top})`);
|
||||
|
||||
const romerSolowData = calculateRomerSolowData(
|
||||
T_MAX,
|
||||
L,
|
||||
l,
|
||||
A0,
|
||||
alpha,
|
||||
s,
|
||||
d,
|
||||
z,
|
||||
);
|
||||
|
||||
const x = d3.scaleLinear().domain([1, T_MAX]).range([0, width]);
|
||||
const xAxis = svg
|
||||
.append("g")
|
||||
.attr("transform", `translate(0, ${height})`)
|
||||
.call(d3.axisBottom(x));
|
||||
xAxis.selectAll("text").style("font-size", "1.5em");
|
||||
xAxis
|
||||
.append("text")
|
||||
.attr("x", width + 10)
|
||||
.attr("y", -10)
|
||||
.style("text-anchor", "end")
|
||||
.style("font-size", "2em")
|
||||
.text("t");
|
||||
|
||||
const y = d3
|
||||
.scaleLinear()
|
||||
.domain([0, romerSolowData[romerSolowData.length - 1].Y])
|
||||
.range([height, 0]);
|
||||
const yAxis = svg
|
||||
.append("g")
|
||||
.call(d3.axisLeft(y).ticks(10, d3.format(".1s")));
|
||||
yAxis.selectAll("text").style("font-size", "1.5em");
|
||||
yAxis
|
||||
.append("text")
|
||||
.attr("x", 0)
|
||||
.attr("y", -10)
|
||||
.style("text-anchor", "start")
|
||||
.style("font-size", "2em")
|
||||
.text("log(Y)");
|
||||
|
||||
svg
|
||||
.append("path")
|
||||
.datum(romerSolowData)
|
||||
.attr("fill", "none")
|
||||
.attr("stroke", window.getTopicColor(urlToTopic()))
|
||||
.attr("stroke-width", 2)
|
||||
.attr(
|
||||
"d",
|
||||
d3
|
||||
.line()
|
||||
.x((d) => x(d.year))
|
||||
.y((d) => y(d.Y)),
|
||||
);
|
||||
|
||||
svg
|
||||
.append("text")
|
||||
.attr("x", x(T_MAX))
|
||||
.attr("y", y(romerSolowData[T_MAX - 1].Y))
|
||||
.style("font-size", "0.75em")
|
||||
.text("log10(Y)");
|
||||
}
|
||||
|
||||
function drawRomerSolowChangeGraph() {
|
||||
const T_MAX = 100,
|
||||
margin = { top: 20, right: 100, bottom: 20, left: 50 },
|
||||
s = 0.2,
|
||||
d = 0.2,
|
||||
A0 = 50,
|
||||
alpha = 0.33,
|
||||
l = 0.5,
|
||||
L = 100,
|
||||
z = 0.5;
|
||||
|
||||
const [z0, l0, L0, alpha0, t0] = setUpParameters(
|
||||
drawRomerSolowChangeGraph,
|
||||
["z0", "l0", "L0", "alpha0", "t0"],
|
||||
"RSC",
|
||||
);
|
||||
|
||||
const container = document.getElementById("romer-solow-change-visualization");
|
||||
const width = container.clientWidth - margin.left - margin.right;
|
||||
const height = container.clientHeight - margin.top - margin.bottom;
|
||||
|
||||
container.innerHTML = "";
|
||||
|
||||
const svg = d3
|
||||
.select("#romer-solow-change-visualization")
|
||||
.append("svg")
|
||||
.attr("width", width + margin.left + margin.right)
|
||||
.attr("height", height + margin.top + margin.bottom)
|
||||
.append("g")
|
||||
.attr("transform", `translate(${margin.left}, ${margin.top})`);
|
||||
|
||||
const romerSolowData = calculateRomerSolowData(
|
||||
T_MAX,
|
||||
L,
|
||||
l,
|
||||
A0,
|
||||
alpha,
|
||||
s,
|
||||
d,
|
||||
z,
|
||||
t0,
|
||||
L0,
|
||||
l0,
|
||||
alpha0,
|
||||
z0,
|
||||
);
|
||||
|
||||
const x = d3.scaleLinear().domain([1, T_MAX]).range([0, width]);
|
||||
const xAxis = svg
|
||||
.append("g")
|
||||
.attr("transform", `translate(0, ${height})`)
|
||||
.call(d3.axisBottom(x));
|
||||
xAxis.selectAll("text").style("font-size", "1.5em");
|
||||
xAxis
|
||||
.append("text")
|
||||
.attr("x", width + 10)
|
||||
.attr("y", -10)
|
||||
.style("text-anchor", "end")
|
||||
.style("font-size", "2em")
|
||||
.text("t");
|
||||
|
||||
const y = d3
|
||||
.scaleLinear()
|
||||
.domain([0, romerSolowData[romerSolowData.length - 1].Y])
|
||||
.range([height, 0]);
|
||||
const yAxis = svg
|
||||
.append("g")
|
||||
.call(d3.axisLeft(y).ticks(10, d3.format(".1s")));
|
||||
yAxis.selectAll("text").style("font-size", "1.5em");
|
||||
yAxis
|
||||
.append("text")
|
||||
.attr("x", 0)
|
||||
.attr("y", -10)
|
||||
.style("text-anchor", "start")
|
||||
.style("font-size", "2em")
|
||||
.text("log(Y)");
|
||||
|
||||
svg
|
||||
.append("path")
|
||||
.datum(romerSolowData)
|
||||
.attr("fill", "none")
|
||||
.attr("stroke", window.getTopicColor(urlToTopic()))
|
||||
.attr("stroke-width", 2)
|
||||
.attr(
|
||||
"d",
|
||||
d3
|
||||
.line()
|
||||
.x((d) => x(d.year))
|
||||
.y((d) => y(d.Y)),
|
||||
);
|
||||
|
||||
svg
|
||||
.append("line")
|
||||
.attr("x1", x(t0))
|
||||
.attr("y1", y(romerSolowData[T_MAX - 1].Y))
|
||||
.attr("x2", x(t0))
|
||||
.attr("y2", height)
|
||||
.attr("stroke-width", 1)
|
||||
.attr("stroke-dasharray", "4");
|
||||
|
||||
svg
|
||||
.append("text")
|
||||
.attr("x", x(T_MAX))
|
||||
.attr("y", y(romerSolowData[T_MAX - 1].Y))
|
||||
.style("font-size", "0.75em")
|
||||
.text("log10(Y)");
|
||||
}
|
||||
|
||||
drawSolowGraph();
|
||||
drawRomerGraph();
|
||||
drawRomerlGraph();
|
||||
drawRomerSolowGraph();
|
||||
drawRomerSolowChangeGraph();
|
||||
|
||||
window.onresize = () => {
|
||||
drawSolowGraph();
|
||||
drawRomerGraph();
|
||||
drawRomerlGraph();
|
||||
drawRomerSolowGraph();
|
||||
drawRomerSolowChangeGraph();
|
||||
};
|
||||
|
||||
new MutationObserver(() => {
|
||||
drawSolowGraph();
|
||||
drawRomerGraph();
|
||||
drawRomerlGraph();
|
||||
drawRomerSolowGraph();
|
||||
drawRomerSolowChangeGraph();
|
||||
}).observe(document.documentElement, {
|
||||
attributes: true,
|
||||
attributeFilter: ["data-theme"],
|
||||
});
|
||||
|
|
@ -1,264 +0,0 @@
|
|||
@font-face {
|
||||
font-family: "Signifier";
|
||||
src: url("/fonts/signifier/Signifier-Thin.ttf") format("truetype");
|
||||
font-weight: 100;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Signifier";
|
||||
src: url("/fonts/signifier/Signifier-ThinItalic.ttf") format("truetype");
|
||||
font-weight: 100;
|
||||
font-style: italic;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Signifier";
|
||||
src: url("/fonts/signifier/Signifier-Extralight.ttf") format("truetype");
|
||||
font-weight: 200;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Signifier";
|
||||
src: url("/fonts/signifier/Signifier-ExtralightItalic.ttf") format("truetype");
|
||||
font-weight: 200;
|
||||
font-style: italic;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Signifier";
|
||||
src: url("/fonts/signifier/Signifier-Light.ttf") format("truetype");
|
||||
font-weight: 300;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Signifier";
|
||||
src: url("/fonts/signifier/Signifier-LightItalic.ttf") format("truetype");
|
||||
font-weight: 300;
|
||||
font-style: italic;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Signifier";
|
||||
src: url("/fonts/signifier/Signifier-Regular.ttf") format("truetype");
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Signifier";
|
||||
src: url("/fonts/signifier/Signifier-RegularItalic.ttf") format("truetype");
|
||||
font-weight: 400;
|
||||
font-style: italic;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Signifier";
|
||||
src: url("/fonts/signifier/Signifier-Medium.ttf") format("truetype");
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Signifier";
|
||||
src: url("/fonts/signifier/Signifier-MediumItalic.ttf") format("truetype");
|
||||
font-weight: 500;
|
||||
font-style: italic;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Signifier";
|
||||
src: url("/fonts/signifier/Signifier-Bold.ttf") format("truetype");
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Signifier";
|
||||
src: url("/fonts/signifier/Signifier-BoldItalic.ttf") format("truetype");
|
||||
font-weight: 700;
|
||||
font-style: italic;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Signifier";
|
||||
src: url("/fonts/signifier/Signifier-Black.ttf") format("truetype");
|
||||
font-weight: 900;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Signifier";
|
||||
src: url("/fonts/signifier/Signifier-BlackItalic.ttf") format("truetype");
|
||||
font-weight: 900;
|
||||
font-style: italic;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Apercu Mono";
|
||||
src: url("/fonts/apercu-mono/ApercuMonoProLight.ttf") format("truetype");
|
||||
font-weight: 300;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Apercu Mono";
|
||||
src: url("/fonts/apercu-mono/ApercuMonoProRegular.ttf") format("truetype");
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Apercu Mono";
|
||||
src: url("/fonts/apercu-mono/ApercuMonoProMedium.ttf") format("truetype");
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Apercu Mono";
|
||||
src: url("/fonts/apercu-mono/ApercuMonoProBold.ttf") format("truetype");
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
:root {
|
||||
--bg: #fff;
|
||||
--text: #000;
|
||||
--code-bg: #f4f4f4;
|
||||
--border: #e1e1e1;
|
||||
--grid-color: rgba(200, 200, 200, 0.4);
|
||||
}
|
||||
|
||||
[data-theme="light"] {
|
||||
--bg: #fff;
|
||||
--text: #000;
|
||||
--code-bg: #f4f4f4;
|
||||
--border: #e1e1e1;
|
||||
--grid-color: rgba(200, 200, 200, 0.4);
|
||||
}
|
||||
|
||||
pre,
|
||||
code,
|
||||
pre code,
|
||||
.astro-code,
|
||||
code[class*="language-"] {
|
||||
font-family: "Apercu Mono", monospace !important;
|
||||
font-size: 0.95em;
|
||||
line-height: 1.5;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
font-family: "Signifier", serif;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
header,
|
||||
footer {
|
||||
padding: 30px 20px;
|
||||
font-size: 1.5em;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.main {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
li {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.graph-background {
|
||||
background-image:
|
||||
linear-gradient(to right, var(--grid-color) 1px, transparent 1px),
|
||||
linear-gradient(to bottom, var(--grid-color) 1px, transparent 1px);
|
||||
background-size: 3vw 3vw;
|
||||
}
|
||||
|
||||
html:has(body.graph-background) {
|
||||
background-image:
|
||||
linear-gradient(to right, var(--grid-color) 1px, transparent 1px),
|
||||
linear-gradient(to bottom, var(--grid-color) 1px, transparent 1px);
|
||||
background-size: 3vw 3vw;
|
||||
background-color: var(--bg);
|
||||
}
|
||||
|
||||
.terminal-cursor {
|
||||
display: inline-block;
|
||||
width: 10px;
|
||||
height: 1em;
|
||||
background-color: var(--bg);
|
||||
border: 1px solid var(--text);
|
||||
vertical-align: text-top;
|
||||
animation: blink 1s step-start infinite;
|
||||
}
|
||||
|
||||
@keyframes blink {
|
||||
50% {
|
||||
background-color: transparent;
|
||||
border-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.terminal-container {
|
||||
font-family: "Courier New", monospace;
|
||||
}
|
||||
|
||||
pre {
|
||||
font-feature-settings:
|
||||
"liga" 0,
|
||||
"calt" 0;
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
header,
|
||||
footer {
|
||||
font-size: 1.2em;
|
||||
padding: 20px 15px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
header,
|
||||
footer {
|
||||
font-size: 1em;
|
||||
padding: 15px 10px;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
.clone-line {
|
||||
font-family: "Apercu Mono", monospace;
|
||||
font-size: 1.2em;
|
||||
margin: 0 1em 0 0;
|
||||
text-align: left;
|
||||
user-select: all;
|
||||
}
|
||||
|
||||
.clone-line code {
|
||||
background: transparent !important;
|
||||
all: unset;
|
||||
font-family: "Apercu Mono", monospace;
|
||||
white-space: normal;
|
||||
word-break: break-word;
|
||||
overflow-wrap: anywhere;
|
||||
line-height: 1.5;
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
.clone-line .prompt {
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
|
|
@ -1,103 +0,0 @@
|
|||
.graph {
|
||||
height: 50vh;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin: 50px 0;
|
||||
}
|
||||
|
||||
.graph div {
|
||||
height: 100%;
|
||||
width: 60%;
|
||||
}
|
||||
|
||||
.graph svg line,
|
||||
.graph svg path.domain,
|
||||
.graph svg .tick line {
|
||||
stroke: var(--text);
|
||||
}
|
||||
|
||||
.graph svg text {
|
||||
fill: var(--text);
|
||||
}
|
||||
|
||||
.slider {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.slider label {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.slider input {
|
||||
margin-left: 20px;
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
background: var(--text);
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
transform: translateY(-50%);
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
.slider input::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
background: var(--text);
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.slider input::-moz-range-thumb {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
background: var(--text);
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.slider input::-webkit-slider-runnable-track,
|
||||
.slider input::-moz-range-track {
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
background: var(--text);
|
||||
border: none;
|
||||
}
|
||||
|
||||
.sliders {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.sliders li {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.romer-table-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin: 20px 0;
|
||||
}
|
||||
#romer-table {
|
||||
text-align: center;
|
||||
margin-top: 20px;
|
||||
margin: 0;
|
||||
font-size: 0.8em;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
#romer-table th,
|
||||
#romer-table td {
|
||||
border: 1px solid var(--text);
|
||||
background: var(--bg);
|
||||
text-align: center;
|
||||
padding: 5px;
|
||||
}
|
||||
|
|
@ -1,164 +1,165 @@
|
|||
/* List pages only - no scroll */
|
||||
body:has(.content) {
|
||||
@font-face {
|
||||
font-family: "Signifier";
|
||||
src: url("/fonts/signifier/Signifier-Thin.ttf") format("truetype");
|
||||
font-weight: 100;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Signifier";
|
||||
src: url("/fonts/signifier/Signifier-ThinItalic.ttf") format("truetype");
|
||||
font-weight: 100;
|
||||
font-style: italic;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Signifier";
|
||||
src: url("/fonts/signifier/Signifier-Extralight.ttf") format("truetype");
|
||||
font-weight: 200;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Signifier";
|
||||
src: url("/fonts/signifier/Signifier-ExtralightItalic.ttf") format("truetype");
|
||||
font-weight: 200;
|
||||
font-style: italic;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Signifier";
|
||||
src: url("/fonts/signifier/Signifier-Light.ttf") format("truetype");
|
||||
font-weight: 300;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Signifier";
|
||||
src: url("/fonts/signifier/Signifier-LightItalic.ttf") format("truetype");
|
||||
font-weight: 300;
|
||||
font-style: italic;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Signifier";
|
||||
src: url("/fonts/signifier/Signifier-Regular.ttf") format("truetype");
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Signifier";
|
||||
src: url("/fonts/signifier/Signifier-RegularItalic.ttf") format("truetype");
|
||||
font-weight: 400;
|
||||
font-style: italic;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Signifier";
|
||||
src: url("/fonts/signifier/Signifier-Medium.ttf") format("truetype");
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Signifier";
|
||||
src: url("/fonts/signifier/Signifier-MediumItalic.ttf") format("truetype");
|
||||
font-weight: 500;
|
||||
font-style: italic;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Signifier";
|
||||
src: url("/fonts/signifier/Signifier-Bold.ttf") format("truetype");
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Signifier";
|
||||
src: url("/fonts/signifier/Signifier-BoldItalic.ttf") format("truetype");
|
||||
font-weight: 700;
|
||||
font-style: italic;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Signifier";
|
||||
src: url("/fonts/signifier/Signifier-Black.ttf") format("truetype");
|
||||
font-weight: 900;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Signifier";
|
||||
src: url("/fonts/signifier/Signifier-BlackItalic.ttf") format("truetype");
|
||||
font-weight: 900;
|
||||
font-style: italic;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Apercu Mono";
|
||||
src: url("/fonts/apercu-mono/ApercuMonoProLight.ttf") format("truetype");
|
||||
font-weight: 300;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Apercu Mono";
|
||||
src: url("/fonts/apercu-mono/ApercuMonoProRegular.ttf") format("truetype");
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Apercu Mono";
|
||||
src: url("/fonts/apercu-mono/ApercuMonoProMedium.ttf") format("truetype");
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Apercu Mono";
|
||||
src: url("/fonts/apercu-mono/ApercuMonoProBold.ttf") format("truetype");
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
pre,
|
||||
code,
|
||||
pre code,
|
||||
.astro-code,
|
||||
code[class*="language-"] {
|
||||
font-family: "Apercu Mono", monospace !important;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
font-family: "Signifier", serif;
|
||||
}
|
||||
|
||||
header,
|
||||
footer {
|
||||
padding: 30px 20px;
|
||||
font-size: 1.5em;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
body:has(.content) > header {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
body:has(.content) > .main {
|
||||
flex: 1 1 auto;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
body:has(.content) > footer {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 40px;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
gap: 80px;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.topics {
|
||||
font-size: clamp(1.5em, 4vw, 3em);
|
||||
max-height: 80vh;
|
||||
column-width: 300px;
|
||||
column-gap: clamp(20px, 4vw, 60px);
|
||||
column-fill: auto;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.topics li:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.topics li:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.posts {
|
||||
font-size: clamp(1.2em, 3vw, 2.2em);
|
||||
text-align: right;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.posts:empty {
|
||||
display: none;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.post {
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.topic a {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
margin-bottom: 25px;
|
||||
transition: color 0.5s ease;
|
||||
}
|
||||
|
||||
.topic a::after {
|
||||
content: "";
|
||||
height: 2px;
|
||||
display: block;
|
||||
margin-top: 4px;
|
||||
background: currentColor;
|
||||
width: 0;
|
||||
right: 100%;
|
||||
position: absolute;
|
||||
transition:
|
||||
width 0.5s ease,
|
||||
right 0.5s ease;
|
||||
}
|
||||
|
||||
.topic a:hover::after,
|
||||
.topic a.active::after {
|
||||
width: 100%;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
body:has(#repo-list) .content,
|
||||
body:has(.topics:only-child) .content {
|
||||
justify-content: flex-start;
|
||||
display: block;
|
||||
}
|
||||
|
||||
body:has(#repo-list) .topics,
|
||||
body:has(.topics:only-child) .topics {
|
||||
column-width: 350px;
|
||||
column-fill: auto;
|
||||
height: calc(100vh - 200px);
|
||||
overflow-y: hidden;
|
||||
overflow-x: auto;
|
||||
max-height: calc(100vh - 200px);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
body:has(#repo-list) .posts {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
body:has(.content) {
|
||||
height: auto;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
body:has(.content) > .main {
|
||||
display: block;
|
||||
min-height: calc(100vh - 200px);
|
||||
}
|
||||
|
||||
.content {
|
||||
display: block;
|
||||
padding: 20px;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.topics {
|
||||
font-size: 1.8em;
|
||||
column-count: 1;
|
||||
max-height: none;
|
||||
}
|
||||
|
||||
.posts {
|
||||
font-size: 1.4em;
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.topic a {
|
||||
display: block;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,192 +0,0 @@
|
|||
li {
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
.post-wrapper {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.toc-column {
|
||||
position: absolute;
|
||||
left: calc((100vw - min(65vw, 800px)) / 4 - 100px);
|
||||
top: 0;
|
||||
width: 200px;
|
||||
padding-top: 190px;
|
||||
}
|
||||
|
||||
.post-header {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.post-container {
|
||||
width: 65%;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.post-title {
|
||||
font-weight: normal;
|
||||
font-size: 2.2em;
|
||||
margin: 50px 0;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.post-title::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
background-color: var(--topic-color, var(--text));
|
||||
height: 30px;
|
||||
width: 2px;
|
||||
bottom: -10px;
|
||||
left: -20px;
|
||||
}
|
||||
|
||||
.post-title::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
background-color: var(--topic-color, var(--text));
|
||||
width: 200px;
|
||||
height: 2px;
|
||||
bottom: -10px;
|
||||
left: -20px;
|
||||
}
|
||||
|
||||
.post-meta {
|
||||
font-size: 1.6em;
|
||||
color: var(--text);
|
||||
margin-left: 100px;
|
||||
}
|
||||
|
||||
.post-article {
|
||||
font-size: 1.5em;
|
||||
line-height: 1.3;
|
||||
padding-bottom: 50px;
|
||||
}
|
||||
|
||||
.post-article a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
article h1,
|
||||
article h2,
|
||||
article h3,
|
||||
.post-article h1,
|
||||
.post-article h2,
|
||||
.post-article h3 {
|
||||
font-weight: normal;
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
article h1,
|
||||
.post-article h1 {
|
||||
font-size: 1.8em;
|
||||
padding-left: 1.5em;
|
||||
}
|
||||
|
||||
article h2,
|
||||
.post-article h2 {
|
||||
font-size: 1.2em;
|
||||
padding-left: 2em;
|
||||
}
|
||||
|
||||
article h3,
|
||||
.post-article h3 {
|
||||
padding-left: 2.5em;
|
||||
}
|
||||
|
||||
article h1::before,
|
||||
.post-article h1::before {
|
||||
content: "#";
|
||||
}
|
||||
|
||||
article h2::before,
|
||||
.post-article h2::before {
|
||||
content: "##";
|
||||
}
|
||||
|
||||
article h3::before,
|
||||
.post-article h3::before {
|
||||
content: "###";
|
||||
}
|
||||
|
||||
article h1::before,
|
||||
article h2::before,
|
||||
article h3::before,
|
||||
.post-article h1::before,
|
||||
.post-article h2::before,
|
||||
.post-article h3::before {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
color: var(--topic-color, var(--text));
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
|
||||
.date {
|
||||
font-size: 0.67em;
|
||||
}
|
||||
|
||||
article img {
|
||||
display: block;
|
||||
margin: 2rem auto;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
article pre {
|
||||
padding: 1rem;
|
||||
overflow-x: auto;
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .astro-code,
|
||||
[data-theme="dark"] .astro-code span {
|
||||
color: var(--shiki-dark) !important;
|
||||
background-color: var(--shiki-dark-bg) !important;
|
||||
}
|
||||
|
||||
:not(pre) > code {
|
||||
font-family: "Courier New", Courier, monospace;
|
||||
padding: 4px;
|
||||
margin: 0 5px;
|
||||
white-space: nowrap;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 4px;
|
||||
background: var(--code-bg);
|
||||
}
|
||||
|
||||
.astro-code {
|
||||
font-size: 0.8em !important;
|
||||
}
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
.toc-column {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.post-container {
|
||||
width: 85%;
|
||||
}
|
||||
|
||||
.post-title {
|
||||
font-size: 1.8em;
|
||||
margin: 30px 0;
|
||||
}
|
||||
|
||||
.post-meta {
|
||||
font-size: 1.2em;
|
||||
margin-left: 50px;
|
||||
}
|
||||
|
||||
.post-article {
|
||||
font-size: 1.1em;
|
||||
}
|
||||
}
|
||||