fixing gitignore
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
output/
|
||||
@@ -1,7 +0,0 @@
|
||||
#+TITLE: About Page
|
||||
#+DATE: <2025-08-04 Mon>
|
||||
#+OPTIONS: toc:nil num:nil title:nil
|
||||
|
||||
* About
|
||||
|
||||
TODO
|
||||
BIN
assets/.DS_Store
vendored
|
After Width: | Height: | Size: 508 KiB |
BIN
assets/images/competencies/competencies-09-11-25.png
Normal file
|
After Width: | Height: | Size: 254 KiB |
BIN
assets/images/competencies/dev-plan-09-11-25.png
Normal file
|
After Width: | Height: | Size: 230 KiB |
BIN
assets/images/diagrams/airflow.png
Normal file
|
After Width: | Height: | Size: 88 KiB |
BIN
assets/images/hzone/patpat.png
Normal file
|
After Width: | Height: | Size: 203 KiB |
BIN
assets/images/owasp/Screenshot 2025-10-16 134403.png
Normal file
|
After Width: | Height: | Size: 86 KiB |
BIN
assets/images/owasp/Screenshot 2025-10-16 134438.png
Normal file
|
After Width: | Height: | Size: 118 KiB |
BIN
assets/images/owasp/Screenshot 2025-10-16 134457.png
Normal file
|
After Width: | Height: | Size: 62 KiB |
BIN
assets/images/owasp/Screenshot 2025-10-16 134512.png
Normal file
|
After Width: | Height: | Size: 96 KiB |
BIN
assets/images/owasp/swappy-20251106-214613.png
Normal file
|
After Width: | Height: | Size: 103 KiB |
BIN
assets/images/owasp/swappy-20251106-214648.png
Normal file
|
After Width: | Height: | Size: 104 KiB |
BIN
assets/images/owasp/swappy-20251106-214700.png
Normal file
|
After Width: | Height: | Size: 113 KiB |
BIN
assets/images/owasp/swappy-20251106-214709.png
Normal file
|
After Width: | Height: | Size: 98 KiB |
BIN
assets/images/owasp/swappy-20251106-214717.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
assets/images/retrospective_boat_model.png
Normal file
|
After Width: | Height: | Size: 291 KiB |
BIN
assets/images/reviews/02-12-25-retros.jpg
Normal file
|
After Width: | Height: | Size: 284 KiB |
BIN
assets/images/reviews/07-12-25-daily-retros.jpg
Normal file
|
After Width: | Height: | Size: 1.4 MiB |
BIN
assets/images/reviews/15-11-2025-guac.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
assets/images/reviews/16-11-25-daily-retros.jpg
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
BIN
assets/images/reviews/23-11-2025-netdata.png
Normal file
|
After Width: | Height: | Size: 142 KiB |
BIN
assets/images/reviews/23-11-2025-reader.png
Normal file
|
After Width: | Height: | Size: 1.4 MiB |
BIN
assets/images/reviews/23-11-2025-vnc.png
Normal file
|
After Width: | Height: | Size: 783 KiB |
BIN
assets/images/reviews/23-11-25-daily-retros.jpg
Normal file
|
After Width: | Height: | Size: 3.2 MiB |
BIN
assets/images/reviews/30-11-2025-acutal.png
Normal file
|
After Width: | Height: | Size: 147 KiB |
BIN
assets/images/reviews/30-11-25-daily-retros.jpg
Normal file
|
After Width: | Height: | Size: 2.4 MiB |
|
After Width: | Height: | Size: 128 KiB |
|
After Width: | Height: | Size: 128 KiB |
|
After Width: | Height: | Size: 123 KiB |
|
After Width: | Height: | Size: 118 KiB |
|
After Width: | Height: | Size: 32 KiB |
BIN
assets/images/stress-in-workplace.png
Normal file
|
After Width: | Height: | Size: 66 KiB |
BIN
assets/images/syntax/assets.png
Normal file
|
After Width: | Height: | Size: 226 KiB |
BIN
assets/images/syntax/filebrowser.png
Normal file
|
After Width: | Height: | Size: 564 KiB |
BIN
assets/images/syntax/not_clutter.png
Normal file
|
After Width: | Height: | Size: 192 KiB |
BIN
assets/images/user_stories.jpg
Normal file
|
After Width: | Height: | Size: 929 KiB |
BIN
assets/images/wireframes/Screenshot 2025-10-31 112139.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
assets/images/wireframes/Screenshot 2025-10-31 112206.png
Normal file
|
After Width: | Height: | Size: 59 KiB |
BIN
assets/images/wireframes/Screenshot 2025-10-31 112427.png
Normal file
|
After Width: | Height: | Size: 57 KiB |
BIN
assets/images/wireframes/Screenshot 2025-10-31 115621.png
Normal file
|
After Width: | Height: | Size: 46 KiB |
BIN
assets/images/wireframes/Screenshot 2025-10-31 115949.png
Normal file
|
After Width: | Height: | Size: 65 KiB |
BIN
assets/images/wireframes/Screenshot 2025-10-31 120038.png
Normal file
|
After Width: | Height: | Size: 75 KiB |
BIN
assets/images/wireframes/Screenshot 2025-10-31 120203.png
Normal file
|
After Width: | Height: | Size: 68 KiB |
BIN
assets/rnote/07-12-2025-retrospectives.rnote
Normal file
BIN
assets/rnote/09-11-2025-nginx-architecture.rnote
Normal file
BIN
assets/rnote/09-11-2025-retrospectives.rnote
Normal file
BIN
assets/rnote/10-08-2025-weekly-review.rnote
Normal file
BIN
assets/rnote/23-11-2025-retrospectives.rnote
Normal file
BIN
assets/rnote/30-11-2025-retrospectives.rnote
Normal file
BIN
assets/rnote/retros-template.rnote
Normal file
197
assets/scripts/comments.js
Normal file
@@ -0,0 +1,197 @@
|
||||
let pageSlug = null;
|
||||
|
||||
/* -----------------------------
|
||||
* DOM builders
|
||||
* ----------------------------- */
|
||||
|
||||
|
||||
function showReplyForm(container, parentId) {
|
||||
if (container.querySelector(".reply-form")) return;
|
||||
|
||||
const form = document.createElement("form");
|
||||
form.className = "reply-form";
|
||||
|
||||
form.innerHTML = `
|
||||
<input type="text" name="author" placeholder="Your name (optional)">
|
||||
<textarea name="content" required placeholder="Write a reply..."></textarea>
|
||||
<button type="submit">Post reply</button>
|
||||
`;
|
||||
|
||||
form.addEventListener("submit", async e => {
|
||||
e.preventDefault();
|
||||
|
||||
const author = form.author.value.trim();
|
||||
const content = form.content.value.trim();
|
||||
if (!content) return;
|
||||
|
||||
const ok = await postComment(author, content, parentId);
|
||||
if (ok) loadComments();
|
||||
else alert("Failed to post reply");
|
||||
});
|
||||
|
||||
container.appendChild(form);
|
||||
}
|
||||
|
||||
function createComment(comment) {
|
||||
const wrapper = document.createElement("div");
|
||||
wrapper.className = "comment";
|
||||
wrapper.dataset.id = comment.id;
|
||||
|
||||
const author = document.createElement("strong");
|
||||
author.textContent = comment.author || "Anonymous";
|
||||
|
||||
const body = document.createElement("p");
|
||||
body.textContent = comment.content;
|
||||
|
||||
const time = document.createElement("time");
|
||||
time.className = "comment-date";
|
||||
time.dateTime = comment.created_at;
|
||||
time.textContent = new Date(comment.created_at).toLocaleString();
|
||||
|
||||
const replyBtn = document.createElement("button");
|
||||
replyBtn.className = "comment-reply";
|
||||
replyBtn.textContent = "Reply";
|
||||
replyBtn.onclick = () =>
|
||||
showReplyForm(wrapper, Number(comment.id));
|
||||
|
||||
wrapper.append(author, body, time, replyBtn);
|
||||
|
||||
if (comment.children?.length) {
|
||||
const replies = document.createElement("div");
|
||||
replies.className = "comment-children";
|
||||
|
||||
comment.children.forEach(child => {
|
||||
replies.appendChild(createComment(child));
|
||||
});
|
||||
|
||||
wrapper.appendChild(replies);
|
||||
}
|
||||
console.log("PARENT_ID RECEIVED:", comment.parent_id)
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function buildCommentTree(comments) {
|
||||
const byId = {};
|
||||
const roots = [];
|
||||
|
||||
comments.forEach(c => {
|
||||
c.children = [];
|
||||
byId[c.id] = c;
|
||||
});
|
||||
|
||||
comments.forEach(c => {
|
||||
if (c.parent_id) {
|
||||
const parent = byId[c.parent_id];
|
||||
if (parent) parent.children.push(c);
|
||||
} else {
|
||||
roots.push(c);
|
||||
}
|
||||
});
|
||||
|
||||
return roots;
|
||||
}
|
||||
|
||||
|
||||
/* -----------------------------
|
||||
* Rendering
|
||||
* ----------------------------- */
|
||||
|
||||
function renderComments(comments) {
|
||||
const list = document.getElementById("comments-list");
|
||||
list.innerHTML = "";
|
||||
|
||||
if (!comments.length) {
|
||||
const empty = document.createElement("p");
|
||||
empty.className = "comments-empty";
|
||||
empty.textContent = "No comments yet.";
|
||||
list.appendChild(empty);
|
||||
return;
|
||||
}
|
||||
|
||||
const tree = buildCommentTree(comments);
|
||||
tree.forEach(c => list.appendChild(createComment(c)));
|
||||
}
|
||||
|
||||
|
||||
/* -----------------------------
|
||||
* API
|
||||
* ----------------------------- */
|
||||
|
||||
async function fetchComments() {
|
||||
const res = await fetch(`/api/comments/${pageSlug}`);
|
||||
if (!res.ok) {
|
||||
console.error("Failed to fetch comments");
|
||||
return [];
|
||||
}
|
||||
return await res.json();
|
||||
}
|
||||
|
||||
async function postComment(author, content, parentId = null) {
|
||||
const res = await fetch("/api/comments", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
page_slug: pageSlug,
|
||||
author: author || null,
|
||||
content,
|
||||
parent_id: parentId
|
||||
})
|
||||
});
|
||||
|
||||
return res.ok;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* -----------------------------
|
||||
* Form handling
|
||||
* ----------------------------- */
|
||||
|
||||
function wireCommentForm() {
|
||||
const form = document.getElementById("comment-form");
|
||||
|
||||
form.addEventListener("submit", async e => {
|
||||
e.preventDefault();
|
||||
|
||||
const authorInput = form.querySelector("input[name='author']");
|
||||
const textarea = form.querySelector("textarea[name='content']");
|
||||
|
||||
const author = authorInput.value.trim();
|
||||
const content = textarea.value.trim();
|
||||
|
||||
if (!content) return;
|
||||
|
||||
const ok = await postComment(author, content);
|
||||
if (ok) {
|
||||
textarea.value = "";
|
||||
loadComments(); // same pattern as your Kanban board
|
||||
} else {
|
||||
alert("Failed to post comment");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/* -----------------------------
|
||||
* Entry point
|
||||
* ----------------------------- */
|
||||
|
||||
async function loadComments() {
|
||||
const comments = await fetchComments();
|
||||
renderComments(comments);
|
||||
}
|
||||
|
||||
function initComments() {
|
||||
const section = document.getElementById("comments");
|
||||
if (!section) return;
|
||||
|
||||
pageSlug = section.dataset.slug;
|
||||
|
||||
wireCommentForm();
|
||||
loadComments();
|
||||
}
|
||||
|
||||
initComments();
|
||||
127
assets/scripts/comments.js~
Normal file
@@ -0,0 +1,127 @@
|
||||
let pageSlug = null;
|
||||
|
||||
/* -----------------------------
|
||||
* DOM builders
|
||||
* ----------------------------- */
|
||||
|
||||
function createComment(comment) {
|
||||
const wrapper = document.createElement("div");
|
||||
wrapper.className = "comment";
|
||||
wrapper.dataset.id = comment.id;
|
||||
|
||||
const author = document.createElement("strong");
|
||||
author.textContent = comment.author || "Anonymous";
|
||||
|
||||
const body = document.createElement("p");
|
||||
body.textContent = comment.content;
|
||||
|
||||
const time = document.createElement("time");
|
||||
time.className = "comment-date";
|
||||
time.dateTime = comment.created_at;
|
||||
time.textContent = new Date(comment.created_at).toLocaleString();
|
||||
|
||||
wrapper.appendChild(author);
|
||||
wrapper.appendChild(body);
|
||||
wrapper.appendChild(time);
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
/* -----------------------------
|
||||
* Rendering
|
||||
* ----------------------------- */
|
||||
|
||||
function renderComments(comments) {
|
||||
const list = document.getElementById("comments-list");
|
||||
list.innerHTML = "";
|
||||
|
||||
if (!comments.length) {
|
||||
const empty = document.createElement("p");
|
||||
empty.className = "comments-empty";
|
||||
empty.textContent = "No comments yet.";
|
||||
list.appendChild(empty);
|
||||
return;
|
||||
}
|
||||
|
||||
comments.forEach(c => {
|
||||
list.appendChild(createComment(c));
|
||||
});
|
||||
}
|
||||
|
||||
/* -----------------------------
|
||||
* API
|
||||
* ----------------------------- */
|
||||
|
||||
async function fetchComments() {
|
||||
const res = await fetch(`/api/comments/${pageSlug}`);
|
||||
if (!res.ok) {
|
||||
console.error("Failed to fetch comments");
|
||||
return [];
|
||||
}
|
||||
return await res.json();
|
||||
}
|
||||
|
||||
async function postComment(author, content) {
|
||||
const res = await fetch("/api/comments", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
page_slug: pageSlug,
|
||||
author: author || null,
|
||||
content: content
|
||||
})
|
||||
});
|
||||
|
||||
return res.ok;
|
||||
}
|
||||
|
||||
|
||||
/* -----------------------------
|
||||
* Form handling
|
||||
* ----------------------------- */
|
||||
|
||||
function wireCommentForm() {
|
||||
const form = document.getElementById("comment-form");
|
||||
|
||||
form.addEventListener("submit", async e => {
|
||||
e.preventDefault();
|
||||
|
||||
const authorInput = form.querySelector("input[name='author']");
|
||||
const textarea = form.querySelector("textarea[name='content']");
|
||||
|
||||
const author = authorInput.value.trim();
|
||||
const content = textarea.value.trim();
|
||||
|
||||
if (!content) return;
|
||||
|
||||
const ok = await postComment(author, content);
|
||||
if (ok) {
|
||||
textarea.value = "";
|
||||
loadComments(); // same pattern as your Kanban board
|
||||
} else {
|
||||
alert("Failed to post comment");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/* -----------------------------
|
||||
* Entry point
|
||||
* ----------------------------- */
|
||||
|
||||
async function loadComments() {
|
||||
const comments = await fetchComments();
|
||||
renderComments(comments);
|
||||
}
|
||||
|
||||
function initComments() {
|
||||
const section = document.getElementById("comments");
|
||||
if (!section) return;
|
||||
|
||||
pageSlug = section.dataset.slug;
|
||||
|
||||
wireCommentForm();
|
||||
loadComments();
|
||||
}
|
||||
|
||||
initComments();
|
||||
169
assets/scripts/competency-status-board.js
Normal file
@@ -0,0 +1,169 @@
|
||||
const STATES = [
|
||||
{ key: "completed", label: "Completed" },
|
||||
{ key: "manager_review", label: "Manager Review" },
|
||||
{ key: "in_progress", label: "In Progress" },
|
||||
{ key: "not_started", label: "Not Started" },
|
||||
{ key: "comments", label: "Comments" }
|
||||
];
|
||||
|
||||
function isMobile() {
|
||||
return window.matchMedia("(max-width: 600px)").matches;
|
||||
}
|
||||
|
||||
function populateMobileControls(items) {
|
||||
if (!isMobile()) return;
|
||||
|
||||
const panel = document.getElementById("mobile-move-panel");
|
||||
const itemSelect = document.getElementById("move-item");
|
||||
const fromSelect = document.getElementById("move-from");
|
||||
const toSelect = document.getElementById("move-to");
|
||||
|
||||
itemSelect.innerHTML = "<option value=''>Select competency</option>";
|
||||
fromSelect.innerHTML = "<option value=''>From</option>";
|
||||
toSelect.innerHTML = "<option value=''>To</option>";
|
||||
|
||||
items.forEach(item => {
|
||||
const opt = document.createElement("option");
|
||||
opt.value = item.id;
|
||||
opt.textContent = item.title;
|
||||
opt.dataset.state = item.state;
|
||||
itemSelect.appendChild(opt);
|
||||
});
|
||||
|
||||
STATES.forEach(s => {
|
||||
fromSelect.appendChild(new Option(s.label, s.key));
|
||||
toSelect.appendChild(new Option(s.label, s.key));
|
||||
});
|
||||
|
||||
// Auto-fill "from" when item selected
|
||||
itemSelect.onchange = () => {
|
||||
const selected = itemSelect.selectedOptions[0];
|
||||
if (selected?.dataset.state) {
|
||||
fromSelect.value = selected.dataset.state;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
document.getElementById("move-confirm").addEventListener("click", async () => {
|
||||
const itemId = document.getElementById("move-item").value;
|
||||
const from = document.getElementById("move-from").value;
|
||||
const to = document.getElementById("move-to").value;
|
||||
|
||||
if (!itemId || !to) {
|
||||
alert("Select an item and target column");
|
||||
return;
|
||||
}
|
||||
|
||||
if (from === to) {
|
||||
alert("Item is already in that column");
|
||||
return;
|
||||
}
|
||||
|
||||
const res = await fetch(`/api/competencies/items/${itemId}/state`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ state: to })
|
||||
});
|
||||
|
||||
if (res.ok) {
|
||||
loadBoard();
|
||||
} else {
|
||||
alert("Failed to move competency");
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
let draggedItemId = null;
|
||||
|
||||
function countByState(items) {
|
||||
return items.reduce((acc, item) => {
|
||||
acc[item.state] = (acc[item.state] || 0) + 1;
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
|
||||
function createCard(item) {
|
||||
const card = document.createElement("div");
|
||||
card.className = "kanban-card";
|
||||
card.draggable = true;
|
||||
card.dataset.id = item.id;
|
||||
card.textContent = item.title;
|
||||
|
||||
card.addEventListener("dragstart", () => {
|
||||
draggedItemId = item.id;
|
||||
card.classList.add("dragging");
|
||||
});
|
||||
|
||||
card.addEventListener("dragend", () => {
|
||||
draggedItemId = null;
|
||||
card.classList.remove("dragging");
|
||||
});
|
||||
|
||||
return card;
|
||||
}
|
||||
|
||||
function createColumn(state, items, counts) {
|
||||
const col = document.createElement("div");
|
||||
col.className = "kanban-column";
|
||||
col.dataset.state = state.key;
|
||||
|
||||
const header = document.createElement("h3");
|
||||
header.innerHTML = `
|
||||
<span class="kanban-title">${state.label}</span>
|
||||
<span class="kanban-count">${counts[state.key] || 0}</span>
|
||||
`;
|
||||
|
||||
const list = document.createElement("div");
|
||||
list.className = "kanban-list";
|
||||
|
||||
list.addEventListener("dragover", e => e.preventDefault());
|
||||
|
||||
list.addEventListener("drop", async () => {
|
||||
if (!draggedItemId) return;
|
||||
|
||||
const res = await fetch(`/api/competencies/items/${draggedItemId}/state`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ state: state.key })
|
||||
});
|
||||
|
||||
if (res.ok) {
|
||||
loadBoard(); // simple & safe re-render
|
||||
} else {
|
||||
alert("Failed to update state");
|
||||
}
|
||||
});
|
||||
|
||||
items
|
||||
.filter(i => i.state === state.key)
|
||||
.forEach(i => list.appendChild(createCard(i)));
|
||||
|
||||
col.appendChild(header);
|
||||
col.appendChild(list);
|
||||
|
||||
return col;
|
||||
}
|
||||
|
||||
function renderBoard(items) {
|
||||
const board = document.getElementById("kanban-board");
|
||||
if (!board) return;
|
||||
board.innerHTML = "";
|
||||
|
||||
const counts = countByState(items);
|
||||
|
||||
STATES.forEach(state => {
|
||||
board.appendChild(createColumn(state, items, counts));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
async function loadBoard() {
|
||||
const res = await fetch("/api/competencies/items");
|
||||
const items = await res.json();
|
||||
renderBoard(items);
|
||||
populateMobileControls(items);
|
||||
}
|
||||
|
||||
|
||||
loadBoard();
|
||||
168
assets/scripts/competency-status-board.js~
Normal file
@@ -0,0 +1,168 @@
|
||||
const STATES = [
|
||||
{ key: "completed", label: "Completed" },
|
||||
{ key: "manager_review", label: "Manager Review" },
|
||||
{ key: "in_progress", label: "In Progress" },
|
||||
{ key: "not_started", label: "Not Started" },
|
||||
{ key: "comments", label: "Comments" }
|
||||
];
|
||||
|
||||
function isMobile() {
|
||||
return window.matchMedia("(max-width: 600px)").matches;
|
||||
}
|
||||
|
||||
function populateMobileControls(items) {
|
||||
if (!isMobile()) return;
|
||||
|
||||
const panel = document.getElementById("mobile-move-panel");
|
||||
const itemSelect = document.getElementById("move-item");
|
||||
const fromSelect = document.getElementById("move-from");
|
||||
const toSelect = document.getElementById("move-to");
|
||||
|
||||
itemSelect.innerHTML = "<option value=''>Select competency</option>";
|
||||
fromSelect.innerHTML = "<option value=''>From</option>";
|
||||
toSelect.innerHTML = "<option value=''>To</option>";
|
||||
|
||||
items.forEach(item => {
|
||||
const opt = document.createElement("option");
|
||||
opt.value = item.id;
|
||||
opt.textContent = item.title;
|
||||
opt.dataset.state = item.state;
|
||||
itemSelect.appendChild(opt);
|
||||
});
|
||||
|
||||
STATES.forEach(s => {
|
||||
fromSelect.appendChild(new Option(s.label, s.key));
|
||||
toSelect.appendChild(new Option(s.label, s.key));
|
||||
});
|
||||
|
||||
// Auto-fill "from" when item selected
|
||||
itemSelect.onchange = () => {
|
||||
const selected = itemSelect.selectedOptions[0];
|
||||
if (selected?.dataset.state) {
|
||||
fromSelect.value = selected.dataset.state;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
document.getElementById("move-confirm").addEventListener("click", async () => {
|
||||
const itemId = document.getElementById("move-item").value;
|
||||
const from = document.getElementById("move-from").value;
|
||||
const to = document.getElementById("move-to").value;
|
||||
|
||||
if (!itemId || !to) {
|
||||
alert("Select an item and target column");
|
||||
return;
|
||||
}
|
||||
|
||||
if (from === to) {
|
||||
alert("Item is already in that column");
|
||||
return;
|
||||
}
|
||||
|
||||
const res = await fetch(`/api/competencies/items/${itemId}/state`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ state: to })
|
||||
});
|
||||
|
||||
if (res.ok) {
|
||||
loadBoard();
|
||||
} else {
|
||||
alert("Failed to move competency");
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
let draggedItemId = null;
|
||||
|
||||
function countByState(items) {
|
||||
return items.reduce((acc, item) => {
|
||||
acc[item.state] = (acc[item.state] || 0) + 1;
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
|
||||
function createCard(item) {
|
||||
const card = document.createElement("div");
|
||||
card.className = "kanban-card";
|
||||
card.draggable = true;
|
||||
card.dataset.id = item.id;
|
||||
card.textContent = item.title;
|
||||
|
||||
card.addEventListener("dragstart", () => {
|
||||
draggedItemId = item.id;
|
||||
card.classList.add("dragging");
|
||||
});
|
||||
|
||||
card.addEventListener("dragend", () => {
|
||||
draggedItemId = null;
|
||||
card.classList.remove("dragging");
|
||||
});
|
||||
|
||||
return card;
|
||||
}
|
||||
|
||||
function createColumn(state, items, counts) {
|
||||
const col = document.createElement("div");
|
||||
col.className = "kanban-column";
|
||||
col.dataset.state = state.key;
|
||||
|
||||
const header = document.createElement("h3");
|
||||
header.innerHTML = `
|
||||
<span class="kanban-title">${state.label}</span>
|
||||
<span class="kanban-count">${counts[state.key] || 0}</span>
|
||||
`;
|
||||
|
||||
const list = document.createElement("div");
|
||||
list.className = "kanban-list";
|
||||
|
||||
list.addEventListener("dragover", e => e.preventDefault());
|
||||
|
||||
list.addEventListener("drop", async () => {
|
||||
if (!draggedItemId) return;
|
||||
|
||||
const res = await fetch(`/api/competencies/items/${draggedItemId}/state`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ state: state.key })
|
||||
});
|
||||
|
||||
if (res.ok) {
|
||||
loadBoard(); // simple & safe re-render
|
||||
} else {
|
||||
alert("Failed to update state");
|
||||
}
|
||||
});
|
||||
|
||||
items
|
||||
.filter(i => i.state === state.key)
|
||||
.forEach(i => list.appendChild(createCard(i)));
|
||||
|
||||
col.appendChild(header);
|
||||
col.appendChild(list);
|
||||
|
||||
return col;
|
||||
}
|
||||
|
||||
function renderBoard(items) {
|
||||
const board = document.getElementById("kanban-board");
|
||||
board.innerHTML = "";
|
||||
|
||||
const counts = countByState(items);
|
||||
|
||||
STATES.forEach(state => {
|
||||
board.appendChild(createColumn(state, items, counts));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
async function loadBoard() {
|
||||
const res = await fetch("/api/competencies/items");
|
||||
const items = await res.json();
|
||||
renderBoard(items);
|
||||
populateMobileControls(items);
|
||||
}
|
||||
|
||||
|
||||
loadBoard();
|
||||
255
assets/scripts/gallery-init.js
Normal file
@@ -0,0 +1,255 @@
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
if (typeof window.BiggerPicture !== 'function') {
|
||||
console.error('[gallery-init] BiggerPicture not found. Check script path.');
|
||||
return;
|
||||
}
|
||||
|
||||
// 1) Wrap Org-exported images so they’re clickable
|
||||
const imgs = document.querySelectorAll('.figure img, img.org-svg');
|
||||
imgs.forEach((img) => {
|
||||
if (img.closest('a')) return; // already wrapped
|
||||
const a = document.createElement('a');
|
||||
const href = img.currentSrc || img.src;
|
||||
a.href = href;
|
||||
a.dataset.img = href; // lets BP pre-size/raster slides
|
||||
a.dataset.alt = img.alt || '';
|
||||
const setDims = () => {
|
||||
a.dataset.width = img.naturalWidth || img.width || 1920;
|
||||
a.dataset.height = img.naturalHeight || img.height || 1080;
|
||||
};
|
||||
if (img.complete) setDims(); else img.addEventListener('load', setDims);
|
||||
img.style.cursor = 'zoom-in';
|
||||
img.parentElement.insertBefore(a, img);
|
||||
a.appendChild(img);
|
||||
});
|
||||
|
||||
// 2) One global BP instance
|
||||
const bp = BiggerPicture({ target: document.body });
|
||||
|
||||
// SVG pan/zoom handle
|
||||
let activePanZoom = null;
|
||||
const destroyPanZoom = () => { try { activePanZoom?.destroy(); } catch(_){} activePanZoom = null; };
|
||||
|
||||
// Simple rotate state (for non-SVG images)
|
||||
let activeContainer = null;
|
||||
let currentRotation = 0;
|
||||
let rotateControls = null;
|
||||
|
||||
|
||||
// 3) Build galleries per content container
|
||||
const containers = document.querySelectorAll('main, article, .content, body');
|
||||
containers.forEach((container) => {
|
||||
const links = Array.from(container.querySelectorAll('.figure a, a:has(img.org-svg)'));
|
||||
if (!links.length) return;
|
||||
|
||||
// Start the lightbox on click
|
||||
links.forEach((link, index) => {
|
||||
link.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
document.querySelectorAll(".theme-toggle").forEach(el => {
|
||||
el.classList.add("hidden");
|
||||
});
|
||||
|
||||
bp.open({
|
||||
// IMPORTANT: pass the anchor ELEMENTS, not custom objects
|
||||
items: links,
|
||||
el: link,
|
||||
caption: (el) => el.querySelector('img')?.alt || el.title || '',
|
||||
maxZoom: 40, // for raster images (PNG/JPG); SVG handled separately
|
||||
|
||||
// Fade-out polish + cleanup
|
||||
onClose(containerEl) {
|
||||
destroyPanZoom();
|
||||
teardownRotation();
|
||||
if (containerEl) containerEl.classList.add('bp-fadeout');
|
||||
const themeToggle = document.querySelector(".theme-toggle");
|
||||
if (themeToggle) {
|
||||
themeToggle.classList.remove("hidden");
|
||||
}
|
||||
},
|
||||
|
||||
// Called once after open and on every slide change
|
||||
onOpen(containerEl) { setupRotation(containerEl); enhanceSVG(containerEl); },
|
||||
onUpdate(containerEl){ setupRotation(containerEl); enhanceSVG(containerEl); }
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// 4) Simple rotate buttons for raster images
|
||||
function ensureRotateControls() {
|
||||
if (rotateControls) return rotateControls;
|
||||
|
||||
const wrapper = document.createElement('div');
|
||||
wrapper.className = 'bp-rotate-controls';
|
||||
Object.assign(wrapper.style, {
|
||||
position: 'fixed',
|
||||
bottom: '1.5rem',
|
||||
right: '1.5rem',
|
||||
display: 'flex',
|
||||
gap: '0.5rem',
|
||||
zIndex: '9999',
|
||||
pointerEvents: 'auto'
|
||||
});
|
||||
|
||||
const mkBtn = (label, title) => {
|
||||
const btn = document.createElement('button');
|
||||
btn.type = 'button';
|
||||
btn.textContent = label;
|
||||
btn.title = title;
|
||||
btn.setAttribute('aria-label', title);
|
||||
Object.assign(btn.style, {
|
||||
padding: '0.4rem 0.6rem',
|
||||
borderRadius: '999px',
|
||||
border: 'none',
|
||||
fontSize: '1.2rem',
|
||||
cursor: 'pointer',
|
||||
background: 'rgba(30,30,30,0.8)',
|
||||
color: '#fff'
|
||||
});
|
||||
return btn;
|
||||
};
|
||||
|
||||
const left = mkBtn('⟲', 'Rotate image 90° left');
|
||||
const right = mkBtn('⟳', 'Rotate image 90° right');
|
||||
|
||||
left.addEventListener('click', (e) => {
|
||||
e.stopPropagation(); // don’t close the lightbox
|
||||
if (!activeContainer) return;
|
||||
currentRotation = (currentRotation - 90 + 360) % 360;
|
||||
applyRotation();
|
||||
});
|
||||
|
||||
right.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
if (!activeContainer) return;
|
||||
currentRotation = (currentRotation + 90) % 360;
|
||||
applyRotation();
|
||||
});
|
||||
|
||||
wrapper.append(left, right);
|
||||
document.body.appendChild(wrapper);
|
||||
rotateControls = wrapper;
|
||||
return rotateControls;
|
||||
}
|
||||
|
||||
function ensureRotateWrapper() {
|
||||
if (!activeContainer) return null;
|
||||
|
||||
const imgRoot = activeContainer.querySelector('.bp-img');
|
||||
if (!imgRoot) return null;
|
||||
|
||||
let imgEl = imgRoot.querySelector('img');
|
||||
if (!imgEl) return null;
|
||||
|
||||
const src = (imgEl.currentSrc || imgEl.src || '').toLowerCase();
|
||||
// We only rotate raster images; SVGs are handled via svg-pan-zoom
|
||||
if (src.endsWith('.svg')) return null;
|
||||
|
||||
let wrapper = imgRoot.querySelector('.bp-rotate-wrapper');
|
||||
if (!wrapper) {
|
||||
wrapper = document.createElement('div');
|
||||
wrapper.className = 'bp-rotate-wrapper';
|
||||
wrapper.style.display = 'inline-block';
|
||||
wrapper.style.transformOrigin = 'center center';
|
||||
|
||||
imgRoot.appendChild(wrapper);
|
||||
wrapper.appendChild(imgEl);
|
||||
} else if (!wrapper.contains(imgEl)) {
|
||||
// Slide changed and BiggerPicture replaced the <img>
|
||||
wrapper.innerHTML = '';
|
||||
wrapper.appendChild(imgEl);
|
||||
}
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
function applyRotation() {
|
||||
const wrapper = ensureRotateWrapper();
|
||||
if (!wrapper) return;
|
||||
wrapper.style.transform = `rotate(${currentRotation}deg)`;
|
||||
}
|
||||
|
||||
function setupRotation(containerEl) {
|
||||
activeContainer = containerEl;
|
||||
currentRotation = 0;
|
||||
const controls = ensureRotateControls();
|
||||
controls.style.display = 'flex';
|
||||
applyRotation();
|
||||
}
|
||||
|
||||
function teardownRotation() {
|
||||
activeContainer = null;
|
||||
currentRotation = 0;
|
||||
if (rotateControls) {
|
||||
rotateControls.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 4) If current slide is an SVG, swap to inline + enable svg-pan-zoom
|
||||
async function enhanceSVG(containerEl) {
|
||||
try {
|
||||
destroyPanZoom();
|
||||
|
||||
const imgEl = containerEl.querySelector('.bp-img img');
|
||||
if (!imgEl) return;
|
||||
|
||||
const src = imgEl.currentSrc || imgEl.src || '';
|
||||
const isSVG = src.toLowerCase().endsWith('.svg');
|
||||
const htmlLayer = containerEl.querySelector('.bp-html');
|
||||
if (!isSVG || !htmlLayer) {
|
||||
// ensure any previous holder is removed and bitmap is visible
|
||||
const old = htmlLayer?.querySelector('.bp-svg-holder');
|
||||
if (old) old.remove();
|
||||
imgEl.style.visibility = '';
|
||||
return;
|
||||
}
|
||||
|
||||
// Create/clear holder
|
||||
let holder = htmlLayer.querySelector('.bp-svg-holder');
|
||||
if (!holder) {
|
||||
holder = document.createElement('div');
|
||||
holder.className = 'bp-svg-holder';
|
||||
holder.style.maxWidth = '95vw';
|
||||
holder.style.maxHeight = '95vh';
|
||||
htmlLayer.appendChild(holder);
|
||||
}
|
||||
holder.innerHTML = '';
|
||||
|
||||
// Hide the bitmap so only the inline SVG shows
|
||||
imgEl.style.visibility = 'hidden';
|
||||
|
||||
// Inline the SVG
|
||||
const res = await fetch(src, { cache: 'force-cache' });
|
||||
const text = await res.text();
|
||||
holder.innerHTML = text;
|
||||
|
||||
const svg = holder.querySelector('svg');
|
||||
if (!svg) { imgEl.style.visibility = ''; return; }
|
||||
|
||||
svg.style.maxWidth = '95vw';
|
||||
svg.style.maxHeight = '95vh';
|
||||
svg.style.display = 'block';
|
||||
|
||||
if (typeof window.svgPanZoom === 'function') {
|
||||
activePanZoom = svgPanZoom(svg, {
|
||||
zoomEnabled: true,
|
||||
controlIconsEnabled: true,
|
||||
fit: true,
|
||||
center: true,
|
||||
minZoom: 0.05,
|
||||
maxZoom: 400, // effectively "unlimited"
|
||||
zoomScaleSensitivity: 0.25,
|
||||
dblClickZoomEnabled: true
|
||||
});
|
||||
// Keep wheel inside lightbox
|
||||
holder.addEventListener('wheel', (e) => e.stopPropagation(), { passive: true });
|
||||
} else {
|
||||
console.warn('[gallery-init] svg-pan-zoom not loaded');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[gallery-init] SVG enhance failed:', err);
|
||||
}
|
||||
}
|
||||
});
|
||||
102
assets/scripts/notes.js
Normal file
@@ -0,0 +1,102 @@
|
||||
(function () {
|
||||
const wall = document.getElementById("notes-wall");
|
||||
const form = document.getElementById("notes-form");
|
||||
const authorInput = document.getElementById("note-author");
|
||||
const contentInput = document.getElementById("note-content");
|
||||
|
||||
if (!wall) return;
|
||||
|
||||
function escapeHtml(str) {
|
||||
return str
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """)
|
||||
.replace(/'/g, "'");
|
||||
}
|
||||
|
||||
function renderNote(note) {
|
||||
const el = document.createElement("div");
|
||||
el.className = "note";
|
||||
|
||||
el.innerHTML = `
|
||||
<div class="note-meta">
|
||||
<span class="note-author">${escapeHtml(note.author_name)}</span>
|
||||
<time datetime="${note.created_at}">
|
||||
${new Date(note.created_at).toLocaleString()}
|
||||
</time>
|
||||
</div>
|
||||
<div class="note-content">
|
||||
${escapeHtml(note.content).replace(/\n/g, "<br>")}
|
||||
</div>
|
||||
`;
|
||||
|
||||
return el;
|
||||
}
|
||||
|
||||
async function loadNotes() {
|
||||
wall.innerHTML = "<p>Loading notes…</p>";
|
||||
|
||||
try {
|
||||
const res = await fetch("/api/notes");
|
||||
if (!res.ok) throw new Error("Failed to fetch notes");
|
||||
|
||||
const notes = await res.json();
|
||||
wall.innerHTML = "";
|
||||
|
||||
if (notes.length === 0) {
|
||||
wall.innerHTML = "<p>No notes yet.</p>";
|
||||
return;
|
||||
}
|
||||
|
||||
notes.forEach(note => {
|
||||
wall.appendChild(renderNote(note));
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
wall.innerHTML = "<p>Could not load notes.</p>";
|
||||
}
|
||||
}
|
||||
|
||||
async function submitNote(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const author = authorInput.value.trim();
|
||||
const content = contentInput.value.trim();
|
||||
|
||||
if (!author || !content) return;
|
||||
|
||||
form.querySelector("button").disabled = true;
|
||||
|
||||
try {
|
||||
const res = await fetch("/api/notes", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
author_name: author,
|
||||
content: content,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const err = await res.json();
|
||||
throw new Error(err.detail || "Failed to post note");
|
||||
}
|
||||
|
||||
contentInput.value = "";
|
||||
await loadNotes();
|
||||
} catch (err) {
|
||||
alert(err.message);
|
||||
} finally {
|
||||
form.querySelector("button").disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (form) {
|
||||
form.addEventListener("submit", submitNote);
|
||||
}
|
||||
|
||||
loadNotes();
|
||||
})();
|
||||
@@ -94,6 +94,47 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
})();
|
||||
|
||||
|
||||
// Simple, timezone-safe if you pass UTC (…Z) in the datetime
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const els = document.querySelectorAll('time.countdown');
|
||||
if (!els.length) return;
|
||||
|
||||
const plural = (n, w) => `${n} ${w}${n === 1 ? '' : 's'}`;
|
||||
|
||||
const render = (el) => {
|
||||
const raw = el.getAttribute('datetime');
|
||||
const label = el.dataset.label || '';
|
||||
const target = new Date(raw); // Prefer ISO like 2025-12-31T00:00:00Z
|
||||
if (isNaN(target)) { el.textContent = '—'; return; }
|
||||
|
||||
const now = new Date();
|
||||
let diff = target - now;
|
||||
|
||||
if (diff <= 0) {
|
||||
el.textContent = `${label ? label + ' ' : ''}today`;
|
||||
el.classList.add('expired');
|
||||
return;
|
||||
}
|
||||
|
||||
const d = Math.floor(diff / 86400000); diff -= d * 86400000;
|
||||
const h = Math.floor(diff / 3600000); diff -= h * 3600000;
|
||||
const m = Math.floor(diff / 60000); diff -= m * 60000;
|
||||
const s = Math.floor(diff / 1000);
|
||||
|
||||
const pieces = [];
|
||||
if (d) pieces.push(plural(d, 'day'));
|
||||
pieces.push(`${h}h ${m}m ${s}s`);
|
||||
|
||||
el.textContent = `${label ? label + ' in : ' : ''}${pieces.join(' ')}`;
|
||||
};
|
||||
|
||||
const tick = () => els.forEach(render);
|
||||
tick();
|
||||
setInterval(tick, 1000); // update every second
|
||||
});
|
||||
|
||||
|
||||
|
||||
/* Event listener for scrolling and changing the active label on the TOC */
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const toc = document.querySelector("#text-table-of-contents");
|
||||
@@ -182,3 +223,5 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
history.pushState(null, "", `#${id}`);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
98
assets/scripts/status-board.js~
Normal file
@@ -0,0 +1,98 @@
|
||||
const STATES = [
|
||||
{ key: "completed", label: "Completed" },
|
||||
{ key: "manager_review", label: "Manager Review" },
|
||||
{ key: "in_progress", label: "In Progress" },
|
||||
{ key: "not_started", label: "Not Started" },
|
||||
{ key: "comments", label: "Comments" }
|
||||
];
|
||||
|
||||
let draggedItemId = null;
|
||||
|
||||
function countByState(items) {
|
||||
return items.reduce((acc, item) => {
|
||||
acc[item.state] = (acc[item.state] || 0) + 1;
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
|
||||
function createCard(item) {
|
||||
const card = document.createElement("div");
|
||||
card.className = "kanban-card";
|
||||
card.draggable = true;
|
||||
card.dataset.id = item.id;
|
||||
card.textContent = item.title;
|
||||
|
||||
card.addEventListener("dragstart", () => {
|
||||
draggedItemId = item.id;
|
||||
card.classList.add("dragging");
|
||||
});
|
||||
|
||||
card.addEventListener("dragend", () => {
|
||||
draggedItemId = null;
|
||||
card.classList.remove("dragging");
|
||||
});
|
||||
|
||||
return card;
|
||||
}
|
||||
|
||||
function createColumn(state, items, counts) {
|
||||
const col = document.createElement("div");
|
||||
col.className = "kanban-column";
|
||||
col.dataset.state = state.key;
|
||||
|
||||
const header = document.createElement("h3");
|
||||
header.innerHTML = `
|
||||
<span class="kanban-title">${state.label}</span>
|
||||
<span class="kanban-count">${counts[state.key] || 0}</span>
|
||||
`;
|
||||
|
||||
const list = document.createElement("div");
|
||||
list.className = "kanban-list";
|
||||
|
||||
list.addEventListener("dragover", e => e.preventDefault());
|
||||
|
||||
list.addEventListener("drop", async () => {
|
||||
if (!draggedItemId) return;
|
||||
|
||||
const res = await fetch(`/api/competencies/items/${draggedItemId}/state`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ state: state.key })
|
||||
});
|
||||
|
||||
if (res.ok) {
|
||||
loadBoard(); // simple & safe re-render
|
||||
} else {
|
||||
alert("Failed to update state");
|
||||
}
|
||||
});
|
||||
|
||||
items
|
||||
.filter(i => i.state === state.key)
|
||||
.forEach(i => list.appendChild(createCard(i)));
|
||||
|
||||
col.appendChild(header);
|
||||
col.appendChild(list);
|
||||
|
||||
return col;
|
||||
}
|
||||
|
||||
function renderBoard(items) {
|
||||
const board = document.getElementById("kanban-board");
|
||||
board.innerHTML = "";
|
||||
|
||||
const counts = countByState(items);
|
||||
|
||||
STATES.forEach(state => {
|
||||
board.appendChild(createColumn(state, items, counts));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
async function loadBoard() {
|
||||
const res = await fetch("/api/competencies/items");
|
||||
const items = await res.json();
|
||||
renderBoard(items);
|
||||
}
|
||||
|
||||
loadBoard();
|
||||
4
assets/sketchnotes/07-12-2025-retrospectives.svg
Normal file
|
After Width: | Height: | Size: 568 KiB |
4
assets/sketchnotes/09-11-2025-nginx-architecture.svg
Normal file
|
After Width: | Height: | Size: 1.8 MiB |
4
assets/sketchnotes/09-11-2025-retrospectives.svg
Normal file
|
After Width: | Height: | Size: 1024 KiB |
|
Before Width: | Height: | Size: 2.2 MiB After Width: | Height: | Size: 2.2 MiB |
4
assets/sketchnotes/16-11-2025-retrospectives.svg
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
4
assets/sketchnotes/23-11-2025-retrospectives.svg
Normal file
|
After Width: | Height: | Size: 711 KiB |
4
assets/sketchnotes/30-11-2025-retrospectives.svg
Normal file
|
After Width: | Height: | Size: 802 KiB |
265
assets/sketchnotes/elt-yed.svg
Normal file
@@ -0,0 +1,265 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill-opacity="1" color-rendering="auto" color-interpolation="auto" text-rendering="auto" stroke="black" stroke-linecap="square" width="857" stroke-miterlimit="10" shape-rendering="auto" stroke-opacity="1" fill="black" stroke-dasharray="none" font-weight="normal" stroke-width="1" height="378" font-family="'Dialog'" font-style="normal" stroke-linejoin="miter" font-size="12px" stroke-dashoffset="0" image-rendering="auto">
|
||||
<!--Generated by ySVG 2.6-->
|
||||
<defs id="genericDefs"/>
|
||||
<g>
|
||||
<g fill="white" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="translate(-198,-76)" stroke="white">
|
||||
<rect x="198" width="857" height="378" y="76" clip-path="url(#clipPath2)" stroke="none"/>
|
||||
</g>
|
||||
<g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-198,-76)" stroke-linecap="butt">
|
||||
<rect fill="none" x="213.5" width="826" height="347" y="91.278" clip-path="url(#clipPath2)"/>
|
||||
<text x="552.2202" font-size="24px" y="134.9674" clip-path="url(#clipPath2)" font-family="sans-serif" stroke="none" xml:space="preserve">ELT PROCESS</text>
|
||||
<line y2="141.9994" fill="none" x1="552.2202" clip-path="url(#clipPath2)" x2="700.7798" y1="141.9994"/>
|
||||
</g>
|
||||
<g fill="rgb(245,245,245)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-198,-76)" stroke="rgb(245,245,245)">
|
||||
<rect x="481" y="156" clip-path="url(#clipPath2)" width="324" rx="4" ry="4" height="228" stroke="none"/>
|
||||
<rect x="481" y="156" clip-path="url(#clipPath2)" fill="rgb(235,235,235)" width="324" height="24.4301" stroke="none"/>
|
||||
</g>
|
||||
<g font-size="15px" stroke-linecap="butt" transform="matrix(1,0,0,1,-198,-76)" text-rendering="geometricPrecision" font-family="sans-serif" shape-rendering="geometricPrecision" stroke-miterlimit="1.45">
|
||||
<text x="655.654" xml:space="preserve" y="174.0351" clip-path="url(#clipPath2)" stroke="none">Transformation (ELT)</text>
|
||||
<rect x="481" y="156" clip-path="url(#clipPath2)" fill="none" width="324" stroke-dasharray="6,2" rx="4" ry="4" height="228"/>
|
||||
</g>
|
||||
<g text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(3.4968,0,0,4.9946,33.375,75.0968)">
|
||||
<g clip-path="url(#clipPath3)">
|
||||
<svg xml:space="preserve" opacity="1" writing-mode="lr-tb" stop-color="rgb(0, 0, 0)" shape-rendering="auto" glyph-orientation-horizontal="0deg" color-profile="auto" lighting-color="rgb(255, 255, 255)" color="rgb(0, 0, 0)" font-weight="400" alignment-baseline="auto" font-style="normal" version="1.1" color-interpolation-filters="linearrgb" text-anchor="start" stroke-linecap="butt" color-interpolation="srgb" font-variant="normal" word-spacing="normal" fill-opacity="1" text-rendering="auto" clip-path="none" text-decoration="none" letter-spacing="normal" viewBox="0 0 40 48" glyph-orientation-vertical="auto" display="inline" font-size-adjust="none" overflow="hidden" fill="rgb(0, 0, 0)" font-stretch="normal" stroke-dasharray="none" stroke-miterlimit="4" stop-opacity="1" color-rendering="auto" font-size="12" pointer-events="visiblepainted" mask="none" direction="ltr" baseline-shift="baseline" enable-background="new 0 0 40 48" fill-rule="nonzero" image-rendering="auto" stroke-dashoffset="0" width="40px" marker-end="none" clip="auto" cursor="auto" stroke="none" filter="none" visibility="visible" kerning="auto" stroke-width="1" font-family=""Arial","Helvetica",sans-serif" flood-opacity="1" clip-rule="nonzero" src="none" height="48px" unicode-bidi="normal" stroke-linejoin="miter" stroke-opacity="1" flood-color="rgb(0, 0, 0)" dominant-baseline="auto" marker-start="none" x="0px" marker-mid="none" y="0px">
|
||||
<defs>
|
||||
<clipPath clipPathUnits="userSpaceOnUse" id="clipPath1"><path d="M0 0 L857 0 L857 378 L0 378 L0 0 Z"/></clipPath><clipPath clipPathUnits="userSpaceOnUse" id="clipPath2"><path d="M198 76 L1055 76 L1055 454 L198 454 L198 76 Z"/></clipPath><clipPath clipPathUnits="userSpaceOnUse" id="clipPath3"><path d="M-9.5444 -15.0357 L235.5358 -15.0357 L235.5358 60.6465 L-9.5444 60.6465 L-9.5444 -15.0357 Z"/></clipPath><clipPath clipPathUnits="userSpaceOnUse" id="clipPath4"><path d="M-194.2336 -15.6952 L50.8532 -15.6952 L50.8532 62.1148 L-194.2336 62.1148 L-194.2336 -15.6952 Z"/></clipPath></defs>
|
||||
<linearGradient gradientTransform="matrix(1 0 0 1 -370.2002 -614.5742)" x1="370.2002" x2="409.4502" gradientUnits="userSpaceOnUse" y1="655.0938" y2="655.0938" id="svg1.SVGID_1_">
|
||||
<stop offset="0" style="stop-color:#4D4D4D"/>
|
||||
<stop offset="0.0558" style="stop-color:#5F5F5F"/>
|
||||
<stop offset="0.2103" style="stop-color:#8D8D8D"/>
|
||||
<stop offset="0.3479" style="stop-color:#AEAEAE"/>
|
||||
<stop offset="0.4623" style="stop-color:#C2C2C2"/>
|
||||
<stop offset="0.5394" style="stop-color:#C9C9C9"/>
|
||||
<stop offset="0.6247" style="stop-color:#C5C5C5"/>
|
||||
<stop offset="0.7072" style="stop-color:#BABABA"/>
|
||||
<stop offset="0.7885" style="stop-color:#A6A6A6"/>
|
||||
<stop offset="0.869" style="stop-color:#8B8B8B"/>
|
||||
<stop offset="0.9484" style="stop-color:#686868"/>
|
||||
<stop offset="1" style="stop-color:#4D4D4D"/>
|
||||
</linearGradient>
|
||||
<path fill="url(#svg1.SVGID_1_)" d="M19.625,37.613C8.787,37.613,0,35.738,0,33.425v10c0,2.313,8.787,4.188,19.625,4.188 c10.839,0,19.625-1.875,19.625-4.188v-10C39.25,35.738,30.464,37.613,19.625,37.613z"/>
|
||||
<linearGradient gradientTransform="matrix(1 0 0 1 -370.2002 -614.5742)" x1="370.2002" x2="409.4502" gradientUnits="userSpaceOnUse" y1="649.0938" y2="649.0938" id="svg1.SVGID_2_">
|
||||
<stop offset="0" style="stop-color:#B3B3B3"/>
|
||||
<stop offset="0.0171" style="stop-color:#B6B6B6"/>
|
||||
<stop offset="0.235" style="stop-color:#D7D7D7"/>
|
||||
<stop offset="0.4168" style="stop-color:#EBEBEB"/>
|
||||
<stop offset="0.5394" style="stop-color:#F2F2F2"/>
|
||||
<stop offset="0.6579" style="stop-color:#EEEEEE"/>
|
||||
<stop offset="0.7724" style="stop-color:#E3E3E3"/>
|
||||
<stop offset="0.8853" style="stop-color:#CFCFCF"/>
|
||||
<stop offset="0.9965" style="stop-color:#B4B4B4"/>
|
||||
<stop offset="1" style="stop-color:#B3B3B3"/>
|
||||
</linearGradient>
|
||||
<path fill="url(#svg1.SVGID_2_)" d="M19.625,37.613c10.839,0,19.625-1.875,19.625-4.188l-1.229-2c0,2.168-8.235,3.927-18.396,3.927 c-9.481,0-17.396-1.959-18.396-3.927l-1.229,2C0,35.738,8.787,37.613,19.625,37.613z"/>
|
||||
<linearGradient gradientTransform="matrix(1 0 0 1 -370.2002 -614.5742)" x1="371.4297" x2="408.2217" gradientUnits="userSpaceOnUse" y1="646" y2="646" id="svg1.SVGID_3_">
|
||||
<stop offset="0" style="stop-color:#C9C9C9"/>
|
||||
<stop offset="1" style="stop-color:#808080"/>
|
||||
</linearGradient>
|
||||
<ellipse rx="18.396" fill="url(#svg1.SVGID_3_)" ry="3.926" cx="19.625" cy="31.425"/>
|
||||
<linearGradient gradientTransform="matrix(1 0 0 1 -370.2002 -614.5742)" x1="370.2002" x2="409.4502" gradientUnits="userSpaceOnUse" y1="641.0938" y2="641.0938" id="svg1.SVGID_4_">
|
||||
<stop offset="0" style="stop-color:#4D4D4D"/>
|
||||
<stop offset="0.0558" style="stop-color:#5F5F5F"/>
|
||||
<stop offset="0.2103" style="stop-color:#8D8D8D"/>
|
||||
<stop offset="0.3479" style="stop-color:#AEAEAE"/>
|
||||
<stop offset="0.4623" style="stop-color:#C2C2C2"/>
|
||||
<stop offset="0.5394" style="stop-color:#C9C9C9"/>
|
||||
<stop offset="0.6247" style="stop-color:#C5C5C5"/>
|
||||
<stop offset="0.7072" style="stop-color:#BABABA"/>
|
||||
<stop offset="0.7885" style="stop-color:#A6A6A6"/>
|
||||
<stop offset="0.869" style="stop-color:#8B8B8B"/>
|
||||
<stop offset="0.9484" style="stop-color:#686868"/>
|
||||
<stop offset="1" style="stop-color:#4D4D4D"/>
|
||||
</linearGradient>
|
||||
<path fill="url(#svg1.SVGID_4_)" d="M19.625,23.613C8.787,23.613,0,21.738,0,19.425v10c0,2.313,8.787,4.188,19.625,4.188 c10.839,0,19.625-1.875,19.625-4.188v-10C39.25,21.738,30.464,23.613,19.625,23.613z"/>
|
||||
<linearGradient gradientTransform="matrix(1 0 0 1 -370.2002 -614.5742)" x1="370.2002" x2="409.4502" gradientUnits="userSpaceOnUse" y1="635.0938" y2="635.0938" id="svg1.SVGID_5_">
|
||||
<stop offset="0" style="stop-color:#B3B3B3"/>
|
||||
<stop offset="0.0171" style="stop-color:#B6B6B6"/>
|
||||
<stop offset="0.235" style="stop-color:#D7D7D7"/>
|
||||
<stop offset="0.4168" style="stop-color:#EBEBEB"/>
|
||||
<stop offset="0.5394" style="stop-color:#F2F2F2"/>
|
||||
<stop offset="0.6579" style="stop-color:#EEEEEE"/>
|
||||
<stop offset="0.7724" style="stop-color:#E3E3E3"/>
|
||||
<stop offset="0.8853" style="stop-color:#CFCFCF"/>
|
||||
<stop offset="0.9965" style="stop-color:#B4B4B4"/>
|
||||
<stop offset="1" style="stop-color:#B3B3B3"/>
|
||||
</linearGradient>
|
||||
<path fill="url(#svg1.SVGID_5_)" d="M19.625,23.613c10.839,0,19.625-1.875,19.625-4.188l-1.229-2c0,2.168-8.235,3.926-18.396,3.926 c-9.481,0-17.396-1.959-18.396-3.926l-1.229,2C0,21.738,8.787,23.613,19.625,23.613z"/>
|
||||
<linearGradient gradientTransform="matrix(1 0 0 1 -370.2002 -614.5742)" x1="371.4297" x2="408.2217" gradientUnits="userSpaceOnUse" y1="632" y2="632" id="svg1.SVGID_6_">
|
||||
<stop offset="0" style="stop-color:#C9C9C9"/>
|
||||
<stop offset="1" style="stop-color:#808080"/>
|
||||
</linearGradient>
|
||||
<ellipse rx="18.396" fill="url(#svg1.SVGID_6_)" ry="3.926" cx="19.625" cy="17.426"/>
|
||||
<linearGradient gradientTransform="matrix(1 0 0 1 -370.2002 -614.5742)" x1="370.2002" x2="409.4502" gradientUnits="userSpaceOnUse" y1="627.5938" y2="627.5938" id="svg1.SVGID_7_">
|
||||
<stop offset="0" style="stop-color:#4D4D4D"/>
|
||||
<stop offset="0.0558" style="stop-color:#5F5F5F"/>
|
||||
<stop offset="0.2103" style="stop-color:#8D8D8D"/>
|
||||
<stop offset="0.3479" style="stop-color:#AEAEAE"/>
|
||||
<stop offset="0.4623" style="stop-color:#C2C2C2"/>
|
||||
<stop offset="0.5394" style="stop-color:#C9C9C9"/>
|
||||
<stop offset="0.6247" style="stop-color:#C5C5C5"/>
|
||||
<stop offset="0.7072" style="stop-color:#BABABA"/>
|
||||
<stop offset="0.7885" style="stop-color:#A6A6A6"/>
|
||||
<stop offset="0.869" style="stop-color:#8B8B8B"/>
|
||||
<stop offset="0.9484" style="stop-color:#686868"/>
|
||||
<stop offset="1" style="stop-color:#4D4D4D"/>
|
||||
</linearGradient>
|
||||
<path fill="url(#svg1.SVGID_7_)" d="M19.625,10.113C8.787,10.113,0,8.238,0,5.925v10c0,2.313,8.787,4.188,19.625,4.188 c10.839,0,19.625-1.875,19.625-4.188v-10C39.25,8.238,30.464,10.113,19.625,10.113z"/>
|
||||
<linearGradient gradientTransform="matrix(1 0 0 1 -370.2002 -614.5742)" x1="370.2002" x2="409.4502" gradientUnits="userSpaceOnUse" y1="621.5938" y2="621.5938" id="svg1.SVGID_8_">
|
||||
<stop offset="0" style="stop-color:#B3B3B3"/>
|
||||
<stop offset="0.0171" style="stop-color:#B6B6B6"/>
|
||||
<stop offset="0.235" style="stop-color:#D7D7D7"/>
|
||||
<stop offset="0.4168" style="stop-color:#EBEBEB"/>
|
||||
<stop offset="0.5394" style="stop-color:#F2F2F2"/>
|
||||
<stop offset="0.6579" style="stop-color:#EEEEEE"/>
|
||||
<stop offset="0.7724" style="stop-color:#E3E3E3"/>
|
||||
<stop offset="0.8853" style="stop-color:#CFCFCF"/>
|
||||
<stop offset="0.9965" style="stop-color:#B4B4B4"/>
|
||||
<stop offset="1" style="stop-color:#B3B3B3"/>
|
||||
</linearGradient>
|
||||
<path fill="url(#svg1.SVGID_8_)" d="M19.625,10.113c10.839,0,19.625-1.875,19.625-4.188l-1.229-2c0,2.168-8.235,3.926-18.396,3.926 c-9.481,0-17.396-1.959-18.396-3.926L0,5.925C0,8.238,8.787,10.113,19.625,10.113z"/>
|
||||
<linearGradient gradientTransform="matrix(1 0 0 1 -370.2002 -614.5742)" x1="371.4297" x2="408.2217" gradientUnits="userSpaceOnUse" y1="618.5" y2="618.5" id="svg1.SVGID_9_">
|
||||
<stop offset="0" style="stop-color:#C9C9C9"/>
|
||||
<stop offset="1" style="stop-color:#808080"/>
|
||||
</linearGradient>
|
||||
<ellipse rx="18.396" fill="url(#svg1.SVGID_9_)" ry="3.926" cx="19.625" cy="3.926"/>
|
||||
<path fill="#FFFFFF" d="M31.291,46.792c0,0-4.313,0.578-7.249,0.694 C20.917,47.613,15,47.613,15,47.613l-2.443-10.279l-0.119-2.283l-1.231-1.842L9.789,23.024l-0.082-0.119L9.3,20.715l-1.45-1.44 L5.329,8.793c0,0,5.296,0.882,7.234,1.07s8.375,0.25,8.375,0.25l3,9.875l-0.25,1.313l1.063,2.168l2.312,9.644l-0.375,1.875 l1.627,2.193L31.291,46.792z" enable-background="new " opacity="0.24"/>
|
||||
</svg>
|
||||
</g>
|
||||
</g>
|
||||
<g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" font-family="sans-serif" transform="matrix(1,0,0,1,-198,-76)" stroke-linecap="butt">
|
||||
<text x="280.6259" xml:space="preserve" y="407.7314" clip-path="url(#clipPath2)" stroke="none">Source</text>
|
||||
</g>
|
||||
<g text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(3.4967,0,0,4.858,679.1806,76.247)">
|
||||
<g clip-path="url(#clipPath4)">
|
||||
<svg xml:space="preserve" opacity="1" writing-mode="lr-tb" stop-color="rgb(0, 0, 0)" shape-rendering="auto" glyph-orientation-horizontal="0deg" color-profile="auto" lighting-color="rgb(255, 255, 255)" color="rgb(0, 0, 0)" font-weight="400" alignment-baseline="auto" font-style="normal" version="1.1" color-interpolation-filters="linearrgb" text-anchor="start" stroke-linecap="butt" color-interpolation="srgb" font-variant="normal" word-spacing="normal" fill-opacity="1" text-rendering="auto" clip-path="none" text-decoration="none" letter-spacing="normal" viewBox="-0.875 -0.887 41 48" glyph-orientation-vertical="auto" display="inline" font-size-adjust="none" overflow="hidden" fill="rgb(0, 0, 0)" font-stretch="normal" stroke-dasharray="none" stroke-miterlimit="4" stop-opacity="1" color-rendering="auto" font-size="12" pointer-events="visiblepainted" mask="none" direction="ltr" baseline-shift="baseline" enable-background="new -0.875 -0.887 41 48" fill-rule="nonzero" image-rendering="auto" stroke-dashoffset="0" width="41px" marker-end="none" clip="auto" cursor="auto" stroke="none" filter="none" visibility="visible" kerning="auto" stroke-width="1" font-family=""Arial","Helvetica",sans-serif" flood-opacity="1" clip-rule="nonzero" src="none" height="48px" unicode-bidi="normal" stroke-linejoin="miter" stroke-opacity="1" flood-color="rgb(0, 0, 0)" dominant-baseline="auto" marker-start="none" x="0px" marker-mid="none" y="0px">
|
||||
<defs>
|
||||
</defs>
|
||||
<linearGradient gradientTransform="matrix(1 0 0 -1 -642.8008 -939.4756)" x1="642.8008" x2="682.0508" gradientUnits="userSpaceOnUse" y1="-979.1445" y2="-979.1445" id="svg2.SVGID_1_">
|
||||
<stop offset="0" style="stop-color:#3C89C9"/>
|
||||
<stop offset="0.1482" style="stop-color:#60A6DD"/>
|
||||
<stop offset="0.3113" style="stop-color:#81C1F0"/>
|
||||
<stop offset="0.4476" style="stop-color:#95D1FB"/>
|
||||
<stop offset="0.5394" style="stop-color:#9CD7FF"/>
|
||||
<stop offset="0.636" style="stop-color:#98D4FD"/>
|
||||
<stop offset="0.7293" style="stop-color:#8DCAF6"/>
|
||||
<stop offset="0.8214" style="stop-color:#79BBEB"/>
|
||||
<stop offset="0.912" style="stop-color:#5EA5DC"/>
|
||||
<stop offset="1" style="stop-color:#3C89C9"/>
|
||||
</linearGradient>
|
||||
<path fill="url(#svg2.SVGID_1_)" d="M19.625,36.763C8.787,36.763,0,34.888,0,32.575v10c0,2.313,8.787,4.188,19.625,4.188 c10.839,0,19.625-1.875,19.625-4.188v-10C39.25,34.888,30.464,36.763,19.625,36.763z"/>
|
||||
<linearGradient gradientTransform="matrix(1 0 0 -1 -642.8008 -939.4756)" x1="642.8008" x2="682.0508" gradientUnits="userSpaceOnUse" y1="-973.1445" y2="-973.1445" id="svg2.SVGID_2_">
|
||||
<stop offset="0" style="stop-color:#9CD7FF"/>
|
||||
<stop offset="0.0039" style="stop-color:#9DD7FF"/>
|
||||
<stop offset="0.2273" style="stop-color:#BDE5FF"/>
|
||||
<stop offset="0.4138" style="stop-color:#D1EEFF"/>
|
||||
<stop offset="0.5394" style="stop-color:#D9F1FF"/>
|
||||
<stop offset="0.6155" style="stop-color:#D5EFFE"/>
|
||||
<stop offset="0.6891" style="stop-color:#C9E7FA"/>
|
||||
<stop offset="0.7617" style="stop-color:#B6DAF3"/>
|
||||
<stop offset="0.8337" style="stop-color:#9AC8EA"/>
|
||||
<stop offset="0.9052" style="stop-color:#77B0DD"/>
|
||||
<stop offset="0.9754" style="stop-color:#4D94CF"/>
|
||||
<stop offset="1" style="stop-color:#3C89C9"/>
|
||||
</linearGradient>
|
||||
<path fill="url(#svg2.SVGID_2_)" d="M19.625,36.763c10.839,0,19.625-1.875,19.625-4.188l-1.229-2c0,2.168-8.235,3.927-18.396,3.927 c-9.481,0-17.396-1.959-18.396-3.927l-1.229,2C0,34.888,8.787,36.763,19.625,36.763z"/>
|
||||
<path fill="#3C89C9" d="M19.625,26.468c10.16,0,19.625,2.775,19.625,2.775c-0.375,2.721-5.367,5.438-19.554,5.438 c-12.125,0-18.467-2.484-19.541-4.918C-0.127,29.125,9.465,26.468,19.625,26.468z"/>
|
||||
<linearGradient gradientTransform="matrix(1 0 0 -1 -642.8008 -939.4756)" x1="642.8008" x2="682.0508" gradientUnits="userSpaceOnUse" y1="-965.6948" y2="-965.6948" id="svg2.SVGID_3_">
|
||||
<stop offset="0" style="stop-color:#3C89C9"/>
|
||||
<stop offset="0.1482" style="stop-color:#60A6DD"/>
|
||||
<stop offset="0.3113" style="stop-color:#81C1F0"/>
|
||||
<stop offset="0.4476" style="stop-color:#95D1FB"/>
|
||||
<stop offset="0.5394" style="stop-color:#9CD7FF"/>
|
||||
<stop offset="0.636" style="stop-color:#98D4FD"/>
|
||||
<stop offset="0.7293" style="stop-color:#8DCAF6"/>
|
||||
<stop offset="0.8214" style="stop-color:#79BBEB"/>
|
||||
<stop offset="0.912" style="stop-color:#5EA5DC"/>
|
||||
<stop offset="1" style="stop-color:#3C89C9"/>
|
||||
</linearGradient>
|
||||
<path fill="url(#svg2.SVGID_3_)" d="M19.625,23.313C8.787,23.313,0,21.438,0,19.125v10c0,2.313,8.787,4.188,19.625,4.188 c10.839,0,19.625-1.875,19.625-4.188v-10C39.25,21.438,30.464,23.313,19.625,23.313z"/>
|
||||
<linearGradient gradientTransform="matrix(1 0 0 -1 -642.8008 -939.4756)" x1="642.8008" x2="682.0508" gradientUnits="userSpaceOnUse" y1="-959.6948" y2="-959.6948" id="svg2.SVGID_4_">
|
||||
<stop offset="0" style="stop-color:#9CD7FF"/>
|
||||
<stop offset="0.0039" style="stop-color:#9DD7FF"/>
|
||||
<stop offset="0.2273" style="stop-color:#BDE5FF"/>
|
||||
<stop offset="0.4138" style="stop-color:#D1EEFF"/>
|
||||
<stop offset="0.5394" style="stop-color:#D9F1FF"/>
|
||||
<stop offset="0.6155" style="stop-color:#D5EFFE"/>
|
||||
<stop offset="0.6891" style="stop-color:#C9E7FA"/>
|
||||
<stop offset="0.7617" style="stop-color:#B6DAF3"/>
|
||||
<stop offset="0.8337" style="stop-color:#9AC8EA"/>
|
||||
<stop offset="0.9052" style="stop-color:#77B0DD"/>
|
||||
<stop offset="0.9754" style="stop-color:#4D94CF"/>
|
||||
<stop offset="1" style="stop-color:#3C89C9"/>
|
||||
</linearGradient>
|
||||
<path fill="url(#svg2.SVGID_4_)" d="M19.625,23.313c10.839,0,19.625-1.875,19.625-4.188l-1.229-2c0,2.168-8.235,3.926-18.396,3.926 c-9.481,0-17.396-1.959-18.396-3.926l-1.229,2C0,21.438,8.787,23.313,19.625,23.313z"/>
|
||||
<path fill="#3C89C9" d="M19.476,13.019c10.161,0,19.625,2.775,19.625,2.775c-0.375,2.721-5.367,5.438-19.555,5.438 c-12.125,0-18.467-2.485-19.541-4.918C-0.277,15.674,9.316,13.019,19.476,13.019z"/>
|
||||
<linearGradient gradientTransform="matrix(1 0 0 -1 -642.8008 -939.4756)" x1="642.8008" x2="682.0508" gradientUnits="userSpaceOnUse" y1="-952.4946" y2="-952.4946" id="svg2.SVGID_5_">
|
||||
<stop offset="0" style="stop-color:#3C89C9"/>
|
||||
<stop offset="0.1482" style="stop-color:#60A6DD"/>
|
||||
<stop offset="0.3113" style="stop-color:#81C1F0"/>
|
||||
<stop offset="0.4476" style="stop-color:#95D1FB"/>
|
||||
<stop offset="0.5394" style="stop-color:#9CD7FF"/>
|
||||
<stop offset="0.636" style="stop-color:#98D4FD"/>
|
||||
<stop offset="0.7293" style="stop-color:#8DCAF6"/>
|
||||
<stop offset="0.8214" style="stop-color:#79BBEB"/>
|
||||
<stop offset="0.912" style="stop-color:#5EA5DC"/>
|
||||
<stop offset="1" style="stop-color:#3C89C9"/>
|
||||
</linearGradient>
|
||||
<path fill="url(#svg2.SVGID_5_)" d="M19.625,10.113C8.787,10.113,0,8.238,0,5.925v10c0,2.313,8.787,4.188,19.625,4.188 c10.839,0,19.625-1.875,19.625-4.188v-10C39.25,8.238,30.464,10.113,19.625,10.113z"/>
|
||||
<linearGradient gradientTransform="matrix(1 0 0 -1 -642.8008 -939.4756)" x1="642.8008" x2="682.0508" gradientUnits="userSpaceOnUse" y1="-946.4946" y2="-946.4946" id="svg2.SVGID_6_">
|
||||
<stop offset="0" style="stop-color:#9CD7FF"/>
|
||||
<stop offset="0.0039" style="stop-color:#9DD7FF"/>
|
||||
<stop offset="0.2273" style="stop-color:#BDE5FF"/>
|
||||
<stop offset="0.4138" style="stop-color:#D1EEFF"/>
|
||||
<stop offset="0.5394" style="stop-color:#D9F1FF"/>
|
||||
<stop offset="0.6155" style="stop-color:#D5EFFE"/>
|
||||
<stop offset="0.6891" style="stop-color:#C9E7FA"/>
|
||||
<stop offset="0.7617" style="stop-color:#B6DAF3"/>
|
||||
<stop offset="0.8337" style="stop-color:#9AC8EA"/>
|
||||
<stop offset="0.9052" style="stop-color:#77B0DD"/>
|
||||
<stop offset="0.9754" style="stop-color:#4D94CF"/>
|
||||
<stop offset="1" style="stop-color:#3C89C9"/>
|
||||
</linearGradient>
|
||||
<path fill="url(#svg2.SVGID_6_)" d="M19.625,10.113c10.839,0,19.625-1.875,19.625-4.188l-1.229-2c0,2.168-8.235,3.926-18.396,3.926 c-9.481,0-17.396-1.959-18.396-3.926L0,5.925C0,8.238,8.787,10.113,19.625,10.113z"/>
|
||||
<linearGradient gradientTransform="matrix(1 0 0 -1 -642.8008 -939.4756)" x1="644.0293" x2="680.8223" gradientUnits="userSpaceOnUse" y1="-943.4014" y2="-943.4014" id="svg2.SVGID_7_">
|
||||
<stop offset="0" style="stop-color:#9CD7FF"/>
|
||||
<stop offset="1" style="stop-color:#3C89C9"/>
|
||||
</linearGradient>
|
||||
<ellipse rx="18.396" fill="url(#svg2.SVGID_7_)" ry="3.926" cx="19.625" cy="3.926"/>
|
||||
<path fill="#FFFFFF" d="M31.04,45.982c0,0-4.354,0.664-7.29,0.781 c-3.125,0.125-8.952,0-8.952,0l-2.384-10.292l0.044-2.108l-1.251-1.154L9.789,23.024l-0.082-0.119L9.5,20.529l-1.65-1.254 L5.329,8.793c0,0,4.213,0.903,7.234,1.07s8.375,0.25,8.375,0.25l3,9.875l-0.25,1.313l1.063,2.168l2.312,9.645l-0.521,1.416 l1.46,1.834L31.04,45.982z" enable-background="new " opacity="0.24"/>
|
||||
</svg>
|
||||
</g>
|
||||
</g>
|
||||
<g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" font-family="sans-serif" transform="matrix(1,0,0,1,-198,-76)" stroke-linecap="butt">
|
||||
<text x="916.3833" xml:space="preserve" y="402.8281" clip-path="url(#clipPath2)" stroke="none">Destination</text>
|
||||
</g>
|
||||
<g fill="rgb(255,204,0)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-198,-76)" stroke="rgb(255,204,0)">
|
||||
<rect x="495" width="81" height="174" y="195" clip-path="url(#clipPath2)" stroke="none"/>
|
||||
</g>
|
||||
<g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-198,-76)" stroke-linecap="butt">
|
||||
<rect fill="none" x="495" width="81" height="174" y="195" clip-path="url(#clipPath2)"/>
|
||||
<text x="515.9338" xml:space="preserve" y="286.656" clip-path="url(#clipPath2)" font-family="sans-serif" stroke="none">Extract</text>
|
||||
</g>
|
||||
<g fill="rgb(255,204,0)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-198,-76)" stroke="rgb(255,204,0)">
|
||||
<rect x="602.5" width="81" height="174" y="195" clip-path="url(#clipPath2)" stroke="none"/>
|
||||
</g>
|
||||
<g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-198,-76)" stroke-linecap="butt">
|
||||
<rect fill="none" x="602.5" width="81" height="174" y="195" clip-path="url(#clipPath2)"/>
|
||||
<text x="629.1699" xml:space="preserve" y="286.656" clip-path="url(#clipPath2)" font-family="sans-serif" stroke="none">Load</text>
|
||||
</g>
|
||||
<g fill="rgb(255,204,0)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-198,-76)" stroke="rgb(255,204,0)">
|
||||
<rect x="709.5" width="81" height="174" y="195" clip-path="url(#clipPath2)" stroke="none"/>
|
||||
</g>
|
||||
<g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-198,-76)" stroke-linecap="butt">
|
||||
<rect fill="none" x="709.5" width="81" height="174" y="195" clip-path="url(#clipPath2)"/>
|
||||
<text x="720.4558" xml:space="preserve" y="286.656" clip-path="url(#clipPath2)" font-family="sans-serif" stroke="none">Transform</text>
|
||||
<path fill="none" d="M368.6251 270 L472.9942 270" clip-path="url(#clipPath2)"/>
|
||||
<path d="M480.9942 270 L468.9942 265 L471.9942 270 L468.9942 275 Z" clip-path="url(#clipPath2)" stroke="none"/>
|
||||
<path fill="none" d="M805.0035 270.278 L872.269 270.278" clip-path="url(#clipPath2)"/>
|
||||
<path d="M880.269 270.278 L868.269 265.278 L871.269 270.278 L868.269 275.278 Z" clip-path="url(#clipPath2)" stroke="none"/>
|
||||
<path fill="none" d="M576 282 L594.5 282" clip-path="url(#clipPath2)"/>
|
||||
<path d="M602.5 282 L590.5 277 L593.5 282 L590.5 287 Z" clip-path="url(#clipPath2)" stroke="none"/>
|
||||
<path fill="none" d="M683.5 282 L701.5 282" clip-path="url(#clipPath2)"/>
|
||||
<path d="M709.5 282 L697.5 277 L700.5 282 L697.5 287 Z" clip-path="url(#clipPath2)" stroke="none"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 23 KiB |
118
assets/styles/comments.css
Normal file
@@ -0,0 +1,118 @@
|
||||
.comments {
|
||||
margin-top: 4rem;
|
||||
padding-top: 2rem;
|
||||
border-top: 1px solid var(--border-muted, #ddd);
|
||||
}
|
||||
|
||||
.comments h2 {
|
||||
margin: 0 0 1rem 0;
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
|
||||
|
||||
.comments-list {
|
||||
margin: 1.5rem 0 2rem;
|
||||
}
|
||||
|
||||
.comments-empty {
|
||||
font-style: italic;
|
||||
color: var(--text-muted, #777);
|
||||
}
|
||||
|
||||
|
||||
.comment {
|
||||
margin-bottom: 1.5rem;
|
||||
padding-bottom: 1.25rem;
|
||||
border-bottom: 1px solid var(--border-muted, #eee);
|
||||
}
|
||||
|
||||
.comment:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.comment strong {
|
||||
display: block;
|
||||
font-weight: 600;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.comment p {
|
||||
margin: 0.25rem 0 0.5rem;
|
||||
}
|
||||
|
||||
.comment-date {
|
||||
display: block;
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-muted, #777);
|
||||
}
|
||||
|
||||
.comment-children {
|
||||
margin-top: 1rem;
|
||||
margin-left: 1.25rem;
|
||||
padding-left: 1rem;
|
||||
border-left: 2px solid var(--border-muted, #ddd);
|
||||
}
|
||||
|
||||
.comment-reply {
|
||||
margin-top: 0.4rem;
|
||||
padding: 0;
|
||||
font-size: 0.85rem;
|
||||
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--text-muted, #555);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.comment-reply:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.comment-form {
|
||||
max-width: 40rem;
|
||||
}
|
||||
|
||||
.comment-author {
|
||||
display: block;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.comment-form span {
|
||||
display: block;
|
||||
margin-bottom: 0.25rem;
|
||||
font-size: 0.85rem;
|
||||
color: var(--fg);
|
||||
}
|
||||
|
||||
.comment-form input,
|
||||
.comment-form textarea {
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
|
||||
font-family: inherit;
|
||||
font-size: 1rem;
|
||||
|
||||
color: var(--fg);
|
||||
background: var(--bg);
|
||||
border: 1px solid var(--border-muted, #ccc);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.comment-form textarea {
|
||||
min-height: 6rem;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
.comment-form button {
|
||||
margin-top: 0.75rem;
|
||||
padding: 0.45rem 1rem;
|
||||
|
||||
font-family: inherit;
|
||||
font-size: 0.9rem;
|
||||
|
||||
color: var(--fg);
|
||||
|
||||
background: transparent;
|
||||
border: 1px solid var(--border-muted, #aaa);
|
||||
cursor: pointer;
|
||||
}
|
||||
183
assets/styles/kanban.css
Normal file
@@ -0,0 +1,183 @@
|
||||
#kanban-board {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
||||
gap: 1rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.kanban-column {
|
||||
background: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
padding: 0.6rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.kanban-column h3 {
|
||||
text-align: center;
|
||||
font-size: 0.9rem;
|
||||
margin: 0.4rem 0 0.6rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.kanban-list {
|
||||
flex-grow: 1;
|
||||
min-height: 3rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.kanban-card {
|
||||
background: var(--bg);
|
||||
border-radius: 6px;
|
||||
padding: 0.5rem 0.6rem;
|
||||
font-size: 0.85rem;
|
||||
cursor: grab;
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,0.1);
|
||||
color: var(--fg);
|
||||
}
|
||||
|
||||
.kanban-card.dragging {
|
||||
opacity: 0.5;
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
|
||||
.kanban-column[data-state="completed"] {
|
||||
background: var(--kb-bg-completed);
|
||||
}
|
||||
|
||||
.kanban-column[data-state="manager_review"] {
|
||||
background: var(--kb-bg-manager-review);
|
||||
}
|
||||
|
||||
.kanban-column[data-state="in_progress"] {
|
||||
background: var(--kb-bg-in-progress);
|
||||
}
|
||||
|
||||
.kanban-column[data-state="not_started"] {
|
||||
background: var(--kb-bg-not-started);
|
||||
}
|
||||
|
||||
.kanban-column[data-state="comments"] {
|
||||
background: var(--kb-bg-comments);
|
||||
}
|
||||
|
||||
|
||||
#mobile-move-panel {
|
||||
display: none;
|
||||
max-width: 22rem;
|
||||
margin: 1.5rem auto 0 auto;
|
||||
padding: 1rem;
|
||||
|
||||
box-sizing: border-box;
|
||||
|
||||
border: 1px solid var(--border-color, #ddd);
|
||||
border-radius: 0.5rem;
|
||||
background: var(--bg);
|
||||
color: var(--fg);
|
||||
|
||||
overflow: hidden;
|
||||
|
||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
|
||||
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
#mobile-move-panel * {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
#mobile-move-panel h4 {
|
||||
margin: 0 0 0.75rem 0;
|
||||
font-size: 0.95rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
#mobile-move-panel label {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.35rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#mobile-move-panel select,
|
||||
#mobile-move-panel button {
|
||||
font-size: 0.9rem;
|
||||
padding: 0.4rem 0.5rem;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
#mobile-move-panel button {
|
||||
margin-top: 0.5rem;
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
#mobile-move-panel select {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
|
||||
padding: 0.45rem 2rem 0.45rem 0.6rem;
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.2;
|
||||
|
||||
border: 1px solid var(--border-color, #ccc);
|
||||
border-radius: 0.4rem;
|
||||
background-color: var(--bg, #fff);
|
||||
|
||||
appearance: none;
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
background-image:
|
||||
linear-gradient(45deg, transparent 50%, #666 50%),
|
||||
linear-gradient(135deg, #666 50%, transparent 50%),
|
||||
linear-gradient(to right, transparent, transparent);
|
||||
|
||||
background-position:
|
||||
calc(100% - 1.2rem) 50%,
|
||||
calc(100% - 0.9rem) 50%,
|
||||
100% 0;
|
||||
|
||||
background-size:
|
||||
6px 6px,
|
||||
6px 6px,
|
||||
2.5rem 100%;
|
||||
|
||||
background-repeat: no-repeat;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
#mobile-move-panel select:focus {
|
||||
outline: none;
|
||||
border-color: #666;
|
||||
box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
#mobile-move-panel button {
|
||||
width: 100%;
|
||||
padding: 0.5rem 0.6rem;
|
||||
font-size: 0.9rem;
|
||||
|
||||
border-radius: 0.4rem;
|
||||
border: 1px solid #888;
|
||||
background: #f8f8f8;
|
||||
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#mobile-move-panel button:active {
|
||||
background: #eee;
|
||||
}
|
||||
#mobile-move-panel select:disabled {
|
||||
background-color: #f3f3f3;
|
||||
color: #555;
|
||||
border-color: #ccc;
|
||||
opacity: 1;
|
||||
cursor: default;
|
||||
background-image: none;
|
||||
}
|
||||
106
assets/styles/media.css
Normal file
@@ -0,0 +1,106 @@
|
||||
@media (max-width: 600px){
|
||||
.banner-header{ flex-direction: column; align-items: center; text-align: center; }
|
||||
.banner-logo{ margin: 0 0 .5rem 0; }
|
||||
nav{ flex-wrap: wrap; justify-content: center; gap: .5rem; font-size: 1rem; }
|
||||
|
||||
|
||||
.theme-toggle {
|
||||
position: static;
|
||||
order: 2;
|
||||
margin-left: .5rem;
|
||||
padding: .3rem .6rem;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.banner-header {
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
}
|
||||
.banner-header nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
body.no-sidenotes {
|
||||
margin-left: 1em;
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
#mobile-move-panel {
|
||||
display: block;
|
||||
}
|
||||
|
||||
}
|
||||
@media (prefers-color-scheme: dark){
|
||||
:root:not([data-theme="light"]){
|
||||
--bg: #0f1115;
|
||||
--fg: #e6e6e6;
|
||||
--link: #8ab4ff;
|
||||
--heading: #9ecbff;
|
||||
--code-bg: #1a1d24;
|
||||
|
||||
--note-color: #c2c7cf;
|
||||
--note-bg: transparent;
|
||||
|
||||
--border: #2a2f3a;
|
||||
--chip-bg: #1f2330;
|
||||
--chip-fg: #cfd3da;
|
||||
--muted: #a9b0bb;
|
||||
}
|
||||
.countdown-wrap {
|
||||
color: var(--fg, #ddd);
|
||||
}
|
||||
time.countdown {
|
||||
color: var(--accent, #7abfff);
|
||||
background: color-mix(in srgb, var(--accent, #7abfff) 15%, transparent);
|
||||
box-shadow: 0 0 6px rgba(255,255,255,0.05);
|
||||
}
|
||||
time.countdown.expired {
|
||||
color: #777;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1250px){
|
||||
#preamble.status{
|
||||
padding-right: var(--body-pad);
|
||||
}
|
||||
|
||||
#content.content{
|
||||
padding-right: var(--body-pad);
|
||||
}
|
||||
.sidenote,
|
||||
.marginnote{
|
||||
float: none;
|
||||
clear: both;
|
||||
width: auto;
|
||||
display: block;
|
||||
margin: 0.5rem 0 0.75rem;
|
||||
padding-left: 0.75rem;
|
||||
border-left: 3px solid rgba(0,0,0,.08);
|
||||
margin-right: 0;
|
||||
}
|
||||
.fullwidth{
|
||||
max-width: calc(100vw - 2 * var(--body-pad));
|
||||
}
|
||||
.sidenote, .marginnote{
|
||||
border-left: 3px solid color-mix(in oklab, var(--fg) 12%, transparent);
|
||||
}
|
||||
.sidenote .mn-img,
|
||||
.marginnote .mn-img{
|
||||
border-color: color-mix(in oklab, var(--fg) 12%, transparent);
|
||||
}
|
||||
|
||||
#table-of-contents{
|
||||
float: none;
|
||||
position: static;
|
||||
width: auto;
|
||||
margin: 0 0 1rem;
|
||||
padding: .5rem .75rem;
|
||||
border-right: 0;
|
||||
border-left: 3px solid color-mix(in oklab, var(--fg) 12%, transparent);
|
||||
background: color-mix(in oklab, var(--bg) 96%, var(--fg) 4%);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
}
|
||||
213
assets/styles/misc.css
Normal file
@@ -0,0 +1,213 @@
|
||||
|
||||
#updated{
|
||||
font-size: .8rem; color: var(--fg);
|
||||
margin: .5rem 0 1rem; font-style: italic;
|
||||
}
|
||||
|
||||
.sidenote,
|
||||
.marginnote{
|
||||
float: right;
|
||||
width: var(--margin);
|
||||
/* margin-right: calc(-1 * ( (95vw - var(--content)) / 2 )); */
|
||||
margin-right: calc(-1 * ((100vi - var(--content)) / 2));
|
||||
padding-right: var(--gutter);
|
||||
overflow-wrap: break-word;
|
||||
word-wrap: break-word;
|
||||
word-break: break-word;
|
||||
margin-top: .3rem;
|
||||
margin-bottom: .6rem;
|
||||
font-size: .95rem;
|
||||
line-height: 1.35;
|
||||
color: var(--note-color);
|
||||
background: var(--note-bg);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.sidenote,
|
||||
.marginnote{
|
||||
display: block;
|
||||
clear: right;
|
||||
}
|
||||
|
||||
.sidenote + .sidenote,
|
||||
.sidenote + .marginnote,
|
||||
.marginnote + .sidenote,
|
||||
.marginnote + .marginnote{
|
||||
margin-top: .4rem;
|
||||
}
|
||||
|
||||
.footnote-sidenote::before{
|
||||
content: attr(data-fn) " ";
|
||||
font-size: 0.85em;
|
||||
vertical-align: super;
|
||||
margin-right: 0.2rem;
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
body{ counter-reset: sidenote-counter; }
|
||||
.sidenote-number:after{
|
||||
counter-increment: sidenote-counter;
|
||||
content: counter(sidenote-counter);
|
||||
font-size: .85em;
|
||||
vertical-align: super;
|
||||
margin-left: .15rem;
|
||||
}
|
||||
|
||||
.margin-toggle{
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* #content .figure, */
|
||||
/* #content .org-src-container, */
|
||||
/* #content img.fullwidth{ */
|
||||
/* //clear: both; */
|
||||
/* } */
|
||||
|
||||
.fullwidth{
|
||||
display: block;
|
||||
position: relative;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
height: auto;
|
||||
margin: 1.25rem 0 1.75rem;
|
||||
width: 100%;
|
||||
max-width: min(
|
||||
calc(var(--content) + var(--bleed) * 2),
|
||||
var(--fullwidth-cap),
|
||||
calc(100vw - 2 * var(--body-pad))
|
||||
);
|
||||
}
|
||||
.figure .fullwidth{ width: inherit; }
|
||||
.figure figcaption{
|
||||
text-align: center; font-size: .95rem; color: #666; margin-top: .4rem;
|
||||
}
|
||||
|
||||
|
||||
.post-date{ color: pink; font-size: .9em; margin-left: 8px; }
|
||||
|
||||
.post-tag{
|
||||
float: right;
|
||||
display: inline-block;
|
||||
background-color: #f0f0f0;
|
||||
color: #444;
|
||||
border-radius: 5px;
|
||||
padding: 2px 6px;
|
||||
margin-left: 6px;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
.filetags {
|
||||
margin-top: .5rem;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: .3rem .4rem;
|
||||
font-size: .9rem;
|
||||
color: #666;
|
||||
}
|
||||
.filetags .tag {
|
||||
display: inline-block;
|
||||
padding: .1rem .45rem;
|
||||
border-radius: .5rem;
|
||||
background: #f0f0f0;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.figure figcaption{ color: var(--muted); }
|
||||
.post-tag{
|
||||
background-color: var(--chip-bg);
|
||||
color: var(--chip-fg);
|
||||
}
|
||||
|
||||
.org-src-container{
|
||||
border: 1px solid var(--border);
|
||||
box-shadow: 3px 3px 3px rgba(0,0,0,.15);
|
||||
}
|
||||
|
||||
:not(pre) > code{
|
||||
border: 1px solid var(--border);
|
||||
background-color: var(--code-bg);
|
||||
color: var(--fg);
|
||||
}
|
||||
|
||||
.theme-toggle {
|
||||
position: fixed;
|
||||
top: 0.75rem;
|
||||
right: 1rem;
|
||||
z-index: 1000;
|
||||
border: 1px solid var(--border, #ccc);
|
||||
background: transparent;
|
||||
color: var(--fg);
|
||||
padding: .35rem .7rem;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font: inherit;
|
||||
transition: background-color 0.3s, color 0.3s, border-color 0.3s;
|
||||
}
|
||||
.theme-toggle:hover {
|
||||
background-color: rgba(0,0,0,0.05);
|
||||
color: var(--heading);
|
||||
border-color: var(--heading);
|
||||
}
|
||||
|
||||
|
||||
|
||||
.sidenote .mn-fig,
|
||||
.marginnote .mn-fig{
|
||||
margin: 0;
|
||||
}
|
||||
.sidenote .mn-img,
|
||||
.marginnote .mn-img{
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
border: 1px solid var(--border, #d7d7d7);
|
||||
border-radius: 6px;
|
||||
background: var(--bg);
|
||||
}
|
||||
.sidenote .mn-fig figcaption,
|
||||
.marginnote .mn-fig figcaption{
|
||||
margin-top: .35rem;
|
||||
font-size: .85rem;
|
||||
color: var(--muted, #666);
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
|
||||
.sidenote img {
|
||||
margin-top: 0.35rem;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.countdown-wrap {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 1em 0;
|
||||
font-family: system-ui, sans-serif;
|
||||
font-size: 1.2rem;
|
||||
color: var(--fg, #222);
|
||||
background: var(--bg-alt, transparent);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
time.countdown {
|
||||
font-weight: 600;
|
||||
color: var(--accent, #2a7fff);
|
||||
font-variant-numeric: tabular-nums;
|
||||
letter-spacing: 0.02em;
|
||||
padding: 0.25em 0.6em;
|
||||
border-radius: 0.5em;
|
||||
background: color-mix(in srgb, var(--accent, #2a7fff) 10%, transparent);
|
||||
box-shadow: 0 0 8px rgba(0,0,0,0.1);
|
||||
transition: color 0.3s ease, background 0.3s ease;
|
||||
}
|
||||
|
||||
time.countdown.expired {
|
||||
color: #999;
|
||||
background: none;
|
||||
font-weight: 500;
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
72
assets/styles/org-syntax.css
Normal file
@@ -0,0 +1,72 @@
|
||||
.org-comment { color: #b22222; }
|
||||
.org-string { color: #8b2252; }
|
||||
.org-keyword { color: #a020f0; }
|
||||
.org-builtin { color: #483d8b; }
|
||||
.org-constant { color: #008b8b; }
|
||||
.org-function-name { color: #0000ff; }
|
||||
.org-variable-name { color: sienna; }
|
||||
.org-type { color: #228b22; }
|
||||
.org-preprocessor { color: #483d8b; }
|
||||
.org-doc { color: #8b2252; }
|
||||
.org-warning { color: #ff8c00; font-weight: 700; }
|
||||
|
||||
:not(pre) > code{
|
||||
padding: 2px 5px; margin: 0 1px;
|
||||
border: 1px solid #ddd; border-radius: 3px;
|
||||
background-clip: padding-box;
|
||||
color: #333; font-size: 80%;
|
||||
}
|
||||
pre {
|
||||
background-color: var(--bg);
|
||||
}
|
||||
|
||||
.org-src-container{
|
||||
border: 1px solid #ccc;
|
||||
box-shadow: 3px 3px 3px #eee;
|
||||
font-family: Lucida Console, monospace;
|
||||
font-size: 80%;
|
||||
margin: 1em auto;
|
||||
padding: .1em .5em;
|
||||
position: relative;
|
||||
}
|
||||
.org-src-container > pre{ overflow: auto; }
|
||||
|
||||
.org-src-container > pre:before{
|
||||
display: block; position: absolute; top: 0; right: 0;
|
||||
background-color: #b3b3b3; color: #fff;
|
||||
padding: 0 .5em; border-bottom-left-radius: 8px; border: 0;
|
||||
font-size: 80%;
|
||||
}
|
||||
.org-src-container > pre.src-bash:before { content: "bash"; }
|
||||
.org-src-container > pre.src-sh:before { content: "sh"; }
|
||||
.org-src-container > pre.src-shell:before { content: "shell"; }
|
||||
.org-src-container > pre.src-python:before { content: "Python"; }
|
||||
.org-src-container > pre.src-emacs-lisp:before { content: "Emacs Lisp"; }
|
||||
.org-src-container > pre.src-js:before,
|
||||
.org-src-container > pre.src-javascript:before { content: "Javascript"; }
|
||||
.org-src-container > pre.src-typescript:before { content: "Typescript"; }
|
||||
.org-src-container > pre.src-html:before { content: "HTML"; }
|
||||
.org-src-container > pre.src-css:before { content: "CSS"; }
|
||||
.org-src-container > pre.src-c:before { content: "C"; }
|
||||
.org-src-container > pre.src-cpp:before { content: "C++"; }
|
||||
.org-src-container > pre.src-java:before { content: "Java"; }
|
||||
.org-src-container > pre.src-rust:before { content: "Rust"; }
|
||||
.org-src-container > pre.src-R:before { content: "R"; }
|
||||
|
||||
.copy-btn{
|
||||
position: absolute; top: .4em; right: .4em;
|
||||
background-color: var(--code-bg); color: var(--heading);
|
||||
border: 1px solid var(--heading); border-radius: 4px;
|
||||
padding: .2em .6em; font-size: .8rem; cursor: pointer; z-index: 10;
|
||||
transition: background-color .3s;
|
||||
}
|
||||
.copy-btn:hover{ background-color: var(--heading); color: var(--code-bg); }
|
||||
|
||||
pre.src::before{ content: none !important; }
|
||||
pre.src{
|
||||
padding: 1.5em 1em 1em;
|
||||
font-family: "Fira Mono","Courier New",monospace;
|
||||
font-size: .9rem; line-height: 1.4;
|
||||
overflow-x: auto; white-space: pre-wrap;
|
||||
}
|
||||
|
||||
53
assets/styles/toc.css
Normal file
@@ -0,0 +1,53 @@
|
||||
#table-of-contents{
|
||||
float: left;
|
||||
width: var(--margin);
|
||||
/* margin-left: calc(-1 * ( (95vw - var(--content)) / 2 )); */
|
||||
margin-left: calc(-1 * ((100vi - var(--content)) / 2));
|
||||
padding-left: var(--gutter);
|
||||
box-sizing: border-box;
|
||||
|
||||
position: sticky;
|
||||
top: 5.5rem;
|
||||
max-height: calc(100vh - 6rem);
|
||||
overflow: auto;
|
||||
|
||||
font-size: .95rem;
|
||||
border-right: 1px solid var(--border, #d7d7d7);
|
||||
padding-top: .25rem;
|
||||
}
|
||||
|
||||
#table-of-contents > h2{
|
||||
margin: 0 0 .5rem;
|
||||
font-size: 1rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .02em;
|
||||
color: var(--muted, #666);
|
||||
}
|
||||
|
||||
#text-table-of-contents ul{
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
#text-table-of-contents li{
|
||||
margin: .25rem 0;
|
||||
}
|
||||
#text-table-of-contents a{
|
||||
text-decoration: none;
|
||||
}
|
||||
#text-table-of-contents a:hover{
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
#text-table-of-contents ul ul { margin-left: .75rem; opacity: .9; }
|
||||
|
||||
h2[id], h3[id], h4[id] { scroll-margin-top: 6.5rem; }
|
||||
|
||||
#text-table-of-contents a.is-active{
|
||||
/* font-weight: 700; */
|
||||
text-decoration: underline;
|
||||
background-color: var(--active-toc);
|
||||
/* border: 1px solid var(--border, #ccc); */
|
||||
|
||||
}
|
||||
|
||||
27
blogs/2025/08-august/benefits-of-reading.org
Normal file
@@ -0,0 +1,27 @@
|
||||
#+TITLE: Benefits of Reading
|
||||
#+OPTIONS: num:nil tags:t
|
||||
#+DATE: <2025-08-14 Thu 23:36>
|
||||
#+FILETAGS: :reading:insights:
|
||||
#+COMMENTS: t
|
||||
#+SLUG: benefits-of-reading
|
||||
|
||||
* Benefits [fn:1]
|
||||
|
||||
At times, I find myself being able to digest material more quickly and efficiently, and so I thought to myself as to what the probable cause of this was. The answer transpired to be the inculcating of reading in my daily routine. As humans, we are constantly surrounded with information, some are noisy, some are useful. We can choose to /filter/ out noisy information by limiting exposure of their avenues, however the topic I wanted to briefly touch upon is what are the (cognitive and non-cognitive) benefits of increasing the intake of useful information through the medium of reading?
|
||||
|
||||
** Mental Stimulation
|
||||
The brain is a muscle, in order to keep any muscle strong and healthy, it needs stimulation and attention. Thus, reading is an excellent way of ensuring the brain is /fit and healthy/.
|
||||
|
||||
** Stress Reduction
|
||||
A well written novel or informative book can go a long way in making you forget about the worries of the world.
|
||||
|
||||
** Increase in Knowledge
|
||||
Reading will always fill your head with new information.
|
||||
|
||||
** Entertainment
|
||||
A plethora of genres can keep anyone entertained!
|
||||
|
||||
** Better writing skills
|
||||
Being exposed to different writing styles allows you to be influenced to obtaining your own unique writing style.
|
||||
|
||||
[fn:1] Check the following [[https://www.crawshawacademy.org.uk/seecmsfile/?id=165][link]]
|
||||
25
blogs/2025/08-august/benefits-of-reading.org~
Normal file
@@ -0,0 +1,25 @@
|
||||
#+TITLE: Benefits of Reading
|
||||
#+OPTIONS: num:nil tags:t
|
||||
#+DATE: <2025-08-14 Thu 23:36>
|
||||
#+FILETAGS: :reading:insights:
|
||||
|
||||
* Benefits [fn:1]
|
||||
|
||||
At times, I find myself being able to digest material more quickly and efficiently, and so I thought to myself as to what the probable cause of this was. The answer transpired to be the inculcating of reading in my daily routine. As humans, we are constantly surrounded with information, some are noisy, some are useful. We can choose to /filter/ out noisy information by limiting exposure of their avenues, however the topic I wanted to briefly touch upon is what are the (cognitive and non-cognitive) benefits of increasing the intake of useful information through the medium of reading?
|
||||
|
||||
** Mental Stimulation
|
||||
The brain is a muscle, in order to keep any muscle strong and healthy, it needs stimulation and attention. Thus, reading is an excellent way of ensuring the brain is /fit and healthy/.
|
||||
|
||||
** Stress Reduction
|
||||
A well written novel or informative book can go a long way in making you forget about the worries of the world.
|
||||
|
||||
** Increase in Knowledge
|
||||
Reading will always fill your head with new information.
|
||||
|
||||
** Entertainment
|
||||
A plethora of genres can keep anyone entertained!
|
||||
|
||||
** Better writing skills
|
||||
Being exposed to different writing styles allows you to be influenced to obtaining your own unique writing style.
|
||||
|
||||
[fn:1] Check the following [[https://www.crawshawacademy.org.uk/seecmsfile/?id=165][link]]
|
||||
10
blogs/2025/08-august/hilberts.hotel.org
Normal file
@@ -0,0 +1,10 @@
|
||||
#+TITLE: Hilbert's Hotel
|
||||
#+OPTIONS: num:nil tags:t
|
||||
#+DATE: <2025-08-11 Mon 18:39>
|
||||
#+FILETAGS: :maths:insights:
|
||||
#+COMMENTS: t
|
||||
#+SLUG: hilberts-hotel
|
||||
|
||||
I recently stumbled across a thought experiment in mathematics, where the counterintuitive nature of infinity is portrayed. There is a hotel with an infinite number of rooms, all of which are occupied with a guest. There is a paradox upon the arrival of a new guest, the hotel is still shown to be able to accomodate this guest, despite it being previously coined as /full/.
|
||||
|
||||
See: [[https://plus.maths.org/content/hilberts-hotel][this link]] for more details on the full experimental thought and the proofs alongside.
|
||||
8
blogs/2025/08-august/hilberts.hotel.org~
Normal file
@@ -0,0 +1,8 @@
|
||||
#+TITLE: Hilbert's Hotel
|
||||
#+OPTIONS: num:nil tags:t
|
||||
#+DATE: <2025-08-11 Mon 18:39>
|
||||
#+FILETAGS: :maths:insights:
|
||||
|
||||
I recently stumbled across a thought experiment in mathematics, where the counterintuitive nature of infinity is portrayed. There is a hotel with an infinite number of rooms, all of which are occupied with a guest. There is a paradox upon the arrival of a new guest, the hotel is still shown to be able to accomodate this guest, despite it being previously coined as /full/.
|
||||
|
||||
See: [[https://plus.maths.org/content/hilberts-hotel][this link]] for more details on the full experimental thought and the proofs alongside.
|
||||
@@ -1,7 +1,9 @@
|
||||
#+TITLE: 09-08-2025: Website Changes
|
||||
#+OPTIONS: num:nil title:nil
|
||||
#+OPTIONS: num:nil toc:nil
|
||||
#+DATE: <2025-08-09 Sat>
|
||||
#+FILETAGS: :emacs:website:
|
||||
#+COMMENTS: t
|
||||
#+SLUG: spending-all-day-on-this-website
|
||||
|
||||
* What was worked on
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
#+TITLE: 09-08-2025: Website Changes
|
||||
#+OPTIONS: num:nil toc:nil
|
||||
#+DATE: <2025-08-09 Sat>
|
||||
#+FILETAGS: :emacs:website:
|
||||
|
||||
* What was worked on
|
||||
|
||||
I saw a few websites that used marginal notes on the right side of the screen like [[https://ogbe.net/blog/emacs_org_static_site][this one]], so I decided to try and implement this feature in this wesbite. In my mind I thought of using an external pre-configured CSS that I can just insert and ta-da it would work.
|
||||
|
||||
How wrong I was.
|
||||
|
||||
See, this website is published with ~org-publish~, meaning the org files that are written are converted to HTML through a transcoder. And as I already had a ~org.css~[fn:1] stylesheet which was primarily used for syntax highlighting, it caused some layout and formatting issue when I tried to use something like ~tufte.css~[fn:2]. Anyways, long story short, the main thing I worked on today was styling this website, and ensuring certain macros worked:
|
||||
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
|
||||
(setq org-export-global-macros
|
||||
(append
|
||||
'(("sidenote"
|
||||
. "@@html:<label for=\"sn$1\" class=\"margin-toggle sidenote-number\"></label><input type=\"checkbox\" id=\"sn$1\" class=\"margin-toggle\"/><span class=\"sidenote\">$2</span>@@")
|
||||
("epigraph" . "@@html:<div class=\"epigraph\"><blockquote>$1<footer>$2</footer></blockquote></div>@@")
|
||||
("epigraph_single" . "@@html:<div class=\"epigraph\"><blockquote>$1</blockquote></div>@@")
|
||||
("epigraph3" . "@@html:<div class=\"epigraph\"><blockquote>$1<footer>$2, <cite>$3</cite></footer></blockquote></div>@@")
|
||||
("kbd" . "@@html:<kbd>$1</kbd>@@@@latex:\\texttt{$1}@@")
|
||||
("margimg"
|
||||
. "@@html:<aside class=\"marginnote\"><figure class=\"mn-fig\"><img src=\"$1\" alt=\"$2\" class=\"mn-img\" loading=\"lazy\" decoding=\"async\"/>$3</figure></aside>@@")
|
||||
|
||||
org-export-global-macros)))
|
||||
|
||||
#+END_SRC
|
||||
|
||||
This worked quite nicely, and I made sure to make these changes accessible on smaller devices. Anyways, I quite like this style of posting. Nothing formal nor technical, just a bunch of rambling on about random things.
|
||||
|
||||
Oh, and also there's a darkmode theme switch now for you night owls.
|
||||
|
||||
[fn:1] See: https://gongzhitaao.org/orgcss/
|
||||
[fn:2] See: https://edwardtufte.github.io/tufte-css/
|
||||
20
blogs/2025/08-august/third-time.org
Normal file
@@ -0,0 +1,20 @@
|
||||
#+TITLE: Third Time
|
||||
#+OPTIONS: num:nil tags:t
|
||||
#+DATE: <2025-08-28 Thu 17:08>
|
||||
#+FILETAGS: :insights:
|
||||
#+COMMENTS: t
|
||||
#+SLUG: third-time
|
||||
|
||||
* A more structured way of working
|
||||
{{{sidenote(1, Useful for people who don't like the constrained working limit of 25 minutes for instance.)}}}
|
||||
|
||||
Whilst researching for different ways to use pomodoro [fn:1] on Emacs [fn:2], I came across this package titled: ~pomm.el~. In this package it had an interesting feature called /pomm-third-time/[fn:3]. The ideology behind is as follows:
|
||||
- You work as long as you want.
|
||||
- Your break is calculated using the formula: ~Time of break = 1/3 x Time of work~.
|
||||
- If you take a shorter break, the remaining break time is accumulated and added to the next break (within the same session).
|
||||
|
||||
[fn:1] - https://en.wikipedia.org/wiki/Pomodoro_Technique
|
||||
|
||||
[fn:2] - https://www.gnu.org/software/emacs/
|
||||
|
||||
[fn:3] - https://www.lesswrong.com/posts/RWu8eZqbwgB9zaerh/third-time-a-better-way-to-work
|
||||
18
blogs/2025/08-august/third-time.org~
Normal file
@@ -0,0 +1,18 @@
|
||||
#+TITLE: Third Time
|
||||
#+OPTIONS: num:nil tags:t
|
||||
#+DATE: <2025-08-28 Thu 17:08>
|
||||
#+FILETAGS: :insights:
|
||||
|
||||
* A more structured way of working
|
||||
{{{sidenote(1, Useful for people who don't like the constrained working limit of 25 minutes for instance.)}}}
|
||||
|
||||
Whilst researching for different ways to use pomodoro [fn:1] on Emacs [fn:2], I came across this package titled: ~pomm.el~. In this package it had an interesting feature called /pomm-third-time/[fn:3]. The ideology behind is as follows:
|
||||
- You work as long as you want.
|
||||
- Your break is calculated using the formula: ~Time of break = 1/3 x Time of work~.
|
||||
- If you take a shorter break, the remaining break time is accumulated and added to the next break (within the same session).
|
||||
|
||||
[fn:1] - https://en.wikipedia.org/wiki/Pomodoro_Technique
|
||||
|
||||
[fn:2] - https://www.gnu.org/software/emacs/
|
||||
|
||||
[fn:3] - https://www.lesswrong.com/posts/RWu8eZqbwgB9zaerh/third-time-a-better-way-to-work
|
||||
@@ -1,9 +1,11 @@
|
||||
#+TITLE: Wacom With Arch
|
||||
#+DATE: <2025-08-08 Fri>
|
||||
#+OPTIONS: toc:nil num:nil title:nil
|
||||
#+OPTIONS: toc:nil num:nil
|
||||
#+FILETAGS: :insights:
|
||||
#+COMMENTS: t
|
||||
#+SLUG: wacom-with-arch
|
||||
|
||||
* Wacom With Arch
|
||||
* Setting up Wacom Tablet in Arch Hyprland
|
||||
|
||||
I purchased a [[https://www.wacom.com/en-gb/products/one-by-wacom][One by Wacom]] tablet almost 2 years ago now and it works flawlessly in both Windows and MacOS and even in most Linux distros with some configuration.
|
||||
|
||||
18
blogs/2025/08-august/wacom-with-arch.org~
Normal file
@@ -0,0 +1,18 @@
|
||||
#+TITLE: Wacom With Arch
|
||||
#+DATE: <2025-08-08 Fri>
|
||||
#+OPTIONS: toc:nil num:nil
|
||||
#+FILETAGS: :insights:
|
||||
|
||||
* Setting up Wacom Tablet in Arch Hyprland
|
||||
|
||||
I purchased a [[https://www.wacom.com/en-gb/products/one-by-wacom][One by Wacom]] tablet almost 2 years ago now and it works flawlessly in both Windows and MacOS and even in most Linux distros with some configuration.
|
||||
|
||||
However with Arch and Hyprland, there were some issues related to the ~xserver~ it was running on. The X input driver module (e.g. xf86-input-wacom) is linked into the X server which in turn is in charge of rendering and generic input events. Hyprland on the other hand uses the Wayland display server protocol (replacement for the X protocol) hence why there is difficulty in input mapping.
|
||||
|
||||
I came across this issue as I was able to get the tablet to work across multiple monitors (mapping to a single one) through the ~xsetwacom~ function - which, at the time I was using Ubuntu. But with Hyprland there needs to be some manual intervention. I came across this [[https://thomaspurnell.com/003_wacom_graphics_hyprland/wacom_hyprland.html][post]] which explains nicely how to map the tablet to a single monitor. I took this approach with a slight configuration; in my ~hyprland.conf~ file, I simply added the following line:
|
||||
|
||||
#+BEGIN_SRC
|
||||
exec-once = hyprctl keyword "device[wacom-one-by-wacom-m-pen]:output" "DP-1"
|
||||
#+END_SRC
|
||||
|
||||
which mapped the device to the primary monitor DP-1.
|
||||
@@ -2,6 +2,8 @@
|
||||
#+DATE: <2025-08-08 Fri>
|
||||
#+OPTIONS: toc:nil num:nil
|
||||
#+FILETAGS: :emacs:review:
|
||||
#+COMMENTS: t
|
||||
#+SLUG: what-i-want-to-do-with-emacs
|
||||
|
||||
I first started using Emacs (and Linux for that matter) since around the end of 2023, and it's been well over a year of trial and error with this text editor. At first I was using this for all sorts of things such as:
|
||||
|
||||
@@ -17,7 +19,7 @@ One thing I realised is just how powerful Emacs really is, just recently I found
|
||||
|
||||
After some thinking I narrowed it down and collated it in the following diagram:
|
||||
|
||||
[[../../../assets/2025-08-08-emacs.svg]]
|
||||
[[../../../assets/sketchnotes/2025-08-08-emacs.svg]]
|
||||
|
||||
Of course this is the current workflow, where the text in red are items I /currently/ use Emacs for (high percentage likelihood this will increase), and the items in blue text are ones I plan on adding to my workflow. The main thing I want from this is to ensure consistency in daily practices.
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
#+DATE: <2025-08-07 Thu>
|
||||
#+OPTIONS: toc:nil num:nil
|
||||
#+FILETAGS: :education:
|
||||
#+COMMENTS: t
|
||||
#+SLUG: zettlekasten
|
||||
|
||||
* The Zettelkasten Method
|
||||
|
||||
@@ -9,9 +11,10 @@ This Zettelkasten method is growing in popularity for taking notes. Ever felt as
|
||||
|
||||
The [[https://zettelkasten.de/introduction/][official website]] describes in detail exactly what the Zettelkasten method is, providing use cases and benefits to this method. The part I wanted to highlighted in this is the following paragraph, where the author depicts how this concept would be explained to someone unfamiliar with it:
|
||||
|
||||
#+BEGIN_QUOTE
|
||||
{{{epigraph_single(A Zettelkasten is a personal tool for thinking and writing. It has hypertextual features to make a web of thoughts possible. The difference to other systems is that you create a web of thoughts instead of notes of arbitrary size and form\, and emphasize connection\, not a collection.
|
||||
)}}}
|
||||
A Zettelkasten is a personal tool for thinking and writing. It has hypertextual features to make a web of thoughts possible. The difference to other systems is that you create a web of thoughts instead of notes of arbitrary size and form, and emphasize connection, not a collection.
|
||||
#+END_QUOTE
|
||||
|
||||
|
||||
This web of thoughts is exactly what makes it useful, it takes on the seemingly cluttered /mess of information/ and is able to link the nodes together to create something meaningful and useful.
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
#+TITLE: Wacom With Arch
|
||||
#+DATE: <2025-08-08 Fri>
|
||||
#+OPTIONS: toc:nil num:nil title:nil
|
||||
#+FILETAGS: :insights:
|
||||
|
||||
* Wacom With Arch
|
||||
22
blogs/2025/11-november/02-11-week-review.org
Normal file
@@ -0,0 +1,22 @@
|
||||
#+TITLE: [02-11-2025] - Weekly Review
|
||||
#+OPTIONS: num:nil
|
||||
#+DATE: <2025-11-02 Sun>
|
||||
#+filetags: :review:
|
||||
#+COMMENTS: t
|
||||
#+SLUG: 02-11-25-week-review
|
||||
|
||||
[Was a mid week file, then moved it to a review file]
|
||||
|
||||
* Mid Week Review
|
||||
- Mostly work related updates. Working from home on monday 10th (for trial, so we get used to it and connecting to the VPN)
|
||||
- Subsequently we will be able to work from home on mondays and fridays for the following weeks (beginning either 17th or 24th?)
|
||||
- Still working on powerbi (not a fan)
|
||||
- Upskilling needs to take more this week and the next
|
||||
- Probation meeting on thursday next week [06/11]
|
||||
- It's getting darker earlier and more windy
|
||||
|
||||
* TODOS
|
||||
** DONE
|
||||
Write the retrospective notes
|
||||
** DONE
|
||||
More competencies
|
||||
20
blogs/2025/11-november/02-11-week-review.org~
Normal file
@@ -0,0 +1,20 @@
|
||||
#+TITLE: [02-11-2025] - Weekly Review
|
||||
#+OPTIONS: num:nil
|
||||
#+DATE: <2025-11-02 Sun>
|
||||
#+filetags: :review:
|
||||
|
||||
[Was a mid week file, then moved it to a review file]
|
||||
|
||||
* Mid Week Review
|
||||
- Mostly work related updates. Working from home on monday 10th (for trial, so we get used to it and connecting to the VPN)
|
||||
- Subsequently we will be able to work from home on mondays and fridays for the following weeks (beginning either 17th or 24th?)
|
||||
- Still working on powerbi (not a fan)
|
||||
- Upskilling needs to take more this week and the next
|
||||
- Probation meeting on thursday next week [06/11]
|
||||
- It's getting darker earlier and more windy
|
||||
|
||||
* TODOS
|
||||
** DONE
|
||||
Write the retrospective notes
|
||||
** DONE
|
||||
More competencies
|
||||
44
blogs/2025/11-november/09-11-week-review.org
Normal file
@@ -0,0 +1,44 @@
|
||||
#+TITLE: [09-11-2025] - Weekly Review
|
||||
#+OPTIONS: num:nil
|
||||
#+DATE:<2025-11-09 Sun 20:08>
|
||||
#+filetags: :review:
|
||||
#+COMMENTS: t
|
||||
#+SLUG: 09-11-25-week-review
|
||||
|
||||
* Weekly review
|
||||
|
||||
- Week 5 work finished!
|
||||
- Working from home on monday 10th.
|
||||
- Maybe do weekly work retrospectives? Using the start stop continue model.
|
||||
- Probation meeting went quite well Alhamdulillah. Keep doing what you're doing was the message.
|
||||
- Should start to record successes, either here or on post-it notes. Helps with imposter syndrome.
|
||||
- Led a meeting for the first time (07/11). Was with Lloyd and Oli from team monopoly (SecDevOps). They understood the problem statement clearly and everything was explained properly to them.
|
||||
- Bowling Kata was reduced to 29 minutes.
|
||||
|
||||
Some thoughts about the nginx reverse proxying architecture. This week I transferred all the docker services to a subdomain.
|
||||
|
||||
[[../../../assets/sketchnotes/09-11-2025-nginx-architecture.svg]]
|
||||
|
||||
Weekly retrospective using the start stop continue model:
|
||||
|
||||
[[../../../assets/sketchnotes/09-11-2025-retrospectives.svg]]
|
||||
|
||||
Here is the current state of the competencies (20.5%):
|
||||
|
||||
[[../../../assets/images/competencies/competencies-09-11-25.png]]
|
||||
|
||||
During the meeting, we were told to achieve 60% by the end of January.
|
||||
|
||||
And here is the dev plan:
|
||||
|
||||
[[../../../assets/images/competencies/dev-plan-09-11-25.png]]
|
||||
|
||||
|
||||
* Next week:
|
||||
|
||||
- Competencies to do:
|
||||
- Aware of the Benefits of Normalisation
|
||||
- Aware of PowerShell, Python, Bash and Pester
|
||||
- Aware of Lean, Agile & extreme programming Principles
|
||||
- Understands Server Operating Systems (might be tough)
|
||||
- Understanding of TCP/IP
|
||||
42
blogs/2025/11-november/09-11-week-review.org~
Normal file
@@ -0,0 +1,42 @@
|
||||
#+TITLE: [09-11-2025] - Weekly Review
|
||||
#+OPTIONS: num:nil
|
||||
#+DATE:<2025-11-09 Sun 20:08>
|
||||
#+filetags: :review:
|
||||
|
||||
* Weekly review
|
||||
|
||||
- Week 5 work finished!
|
||||
- Working from home on monday 10th.
|
||||
- Maybe do weekly work retrospectives? Using the start stop continue model.
|
||||
- Probation meeting went quite well Alhamdulillah. Keep doing what you're doing was the message.
|
||||
- Should start to record successes, either here or on post-it notes. Helps with imposter syndrome.
|
||||
- Led a meeting for the first time (07/11). Was with Lloyd and Oli from team monopoly (SecDevOps). They understood the problem statement clearly and everything was explained properly to them.
|
||||
- Bowling Kata was reduced to 29 minutes.
|
||||
|
||||
Some thoughts about the nginx reverse proxying architecture. This week I transferred all the docker services to a subdomain.
|
||||
|
||||
[[../../../assets/sketchnotes/09-11-2025-nginx-architecture.svg]]
|
||||
|
||||
Weekly retrospective using the start stop continue model:
|
||||
|
||||
[[../../../assets/sketchnotes/09-11-2025-retrospectives.svg]]
|
||||
|
||||
Here is the current state of the competencies (20.5%):
|
||||
|
||||
[[../../../assets/images/competencies/competencies-09-11-25.png]]
|
||||
|
||||
During the meeting, we were told to achieve 60% by the end of January.
|
||||
|
||||
And here is the dev plan:
|
||||
|
||||
[[../../../assets/images/competencies/dev-plan-09-11-25.png]]
|
||||
|
||||
|
||||
* Next week:
|
||||
|
||||
- Competencies to do:
|
||||
- Aware of the Benefits of Normalisation
|
||||
- Aware of PowerShell, Python, Bash and Pester
|
||||
- Aware of Lean, Agile & extreme programming Principles
|
||||
- Understands Server Operating Systems (might be tough)
|
||||
- Understanding of TCP/IP
|
||||