Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions blog/src/_data/media.json
Original file line number Diff line number Diff line change
Expand Up @@ -3275,6 +3275,29 @@
},
"inline": {}
},
"agent-tools-today-2026-11-03": {
"title": "Agent Tools Today, 3 Nov 2026: Astra Edits Weights, Claude Code 2.1.317, Momentic Mo",
"category": "news",
"hero": {
"file": "assets/media/agent-tools-today/hero.png",
"alt": "Hand drawn sketch: a front page with five numbered lines, a clock, and a yellow character reading it out",
"prompt": "Series hero for the Agent Tools Today daily brief, drawn in code (media-src/topic-tiles.html?t=news). Reused by every brief.",
"status": "ready"
},
"inline": {},
"youtube": []
},
"t3-code-alternatives": {
"title": "T3 Code Alternatives: 6 Picks by Why You Are Leaving (Nov 2026)",
"category": "comparisons",
"hero": {
"file": "assets/media/t3-code-alternatives/hero.png",
"alt": "Title card on white. A pill reading Alternatives, the title T3 Code alternatives, the line 6 picks by why you are leaving and a Checked 3 Nov 2026 line. Six small tiles named Claude Code desktop, Munder Difflin, Codex app, Orca, Conductor and OpenCode desktop slide in under a grey tile labelled T3 Code.",
"prompt": "Title card: the hero scene in assets/media/t3-code-alternatives/motion.js, shot at 1600 by 900.",
"status": "ready"
},
"inline": {}
},
"the-hook-shim-pattern": {
"title": "The Hook Shim Pattern: Wiring Agent Hooks to a Live Process",
"category": "internals",
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 13 additions & 0 deletions blog/src/assets/media/agent-tools-today-2026-11-03/motion.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/* Bright coded scenes for "Agent tools today, 3 Nov 2026". Pairs with motion.js. */
.prose figure.mg { margin: 42px 0; }
.prose figure.mg img, .mg .mg-svg { display: block; width: 100%; height: auto; border: 1px solid #1A1320; border-radius: 18px; background: #fff; }
.mg figcaption { font: 500 13px/1.5 "Space Grotesk", system-ui, sans-serif; color: #6B5878; padding-top: 10px; text-align: center; }
.mg .mg-svg { touch-action: manipulation; -webkit-tap-highlight-color: transparent; }
.mg .mg-svg text { -webkit-user-select: none; user-select: none; pointer-events: none; }
.mg .mg-hit { cursor: pointer; outline: none; }
.mg [role="slider"] { cursor: grab; touch-action: none; }
.mg .mg-ring { stroke-opacity: 0; transition: stroke-opacity 0.15s; }
.mg .mg-hit:focus-visible .mg-ring { stroke-opacity: 1; }
.mg [role="slider"]:focus-visible circle:nth-child(2) { stroke-width: 5; }
@media (hover: hover) { .mg .mg-hit:hover .mg-ring { stroke-opacity: 0.5; } }
@media (prefers-reduced-motion: reduce) { .mg .mg-ring { transition: none; } }
97 changes: 97 additions & 0 deletions blog/src/assets/media/agent-tools-today-2026-11-03/motion.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/* Bright coded scenes for "Agent tools today, 3 Nov 2026". Vanilla JS, no library.
<figure class="mg" data-scene="NAME"> holds a still image; this script swaps in a live SVG.
Plays only in view, honours reduced motion, "?mgstill" freezes each scene on its still frame. */
(() => {
const NS = "http://www.w3.org/2000/svg";
const INK = "#1A1320", YEL = "#FFCA54", BLUE = "#6C8EF5", LIL = "#B69CFF", MINT = "#7FD8AE", SKY = "#9AD8FF", SOFT = "#F1ECF9";
const SANS = '"Space Grotesk", system-ui, sans-serif', MONO = '"JetBrains Mono", ui-monospace, monospace';
const el = (t, a = {}, p) => { const n = document.createElementNS(NS, t); for (const k in a) n.setAttribute(k, a[k]); if (p) p.appendChild(n); return n; };
const txt = (p, x, y, s, a = {}) => { const n = el("text", Object.assign({ x, y, "font-family": SANS, "font-size": 24, "font-weight": 600, fill: INK }, a), p); n.textContent = s; return n; };
const cl = (v, a = 0, b = 1) => Math.max(a, Math.min(b, v));
const seg = (t, a, b) => cl((t - a) / (b - a));
const ob = (x) => { const c = 1.70158; return 1 + (c + 1) * Math.pow(x - 1, 3) + c * Math.pow(x - 1, 2); };
const io = (x) => (x < 0.5 ? 4 * x * x * x : 1 - Math.pow(-2 * x + 2, 3) / 2);
const button = (g, label, fn) => {
g.setAttribute("role", "button"); g.setAttribute("tabindex", "0"); g.setAttribute("aria-label", label); g.classList.add("mg-hit");
g.addEventListener("click", fn);
g.addEventListener("keydown", (e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); fn(); } });
};

const SC = {};

/* ---- a small cabinet whose three drawers slide out one after another ---- */
SC.drawers = {
dur: 10, still: 7,
build(svg, api) {
const X0 = 190, W = 420, H = 118, PX = -78, PY = 52;
const ROWS = [["Astra", "Edits weights", YEL], ["Claude Code", "2.1.317", LIL], ["Momentic Mo", "269 bugs found", MINT]];
el("path", { d: "M110 550 H730", stroke: SOFT, "stroke-width": 12, "stroke-linecap": "round" }, svg);
[196, 576].forEach((x) => el("rect", { x, y: 520, width: 28, height: 26, rx: 5, fill: INK }, svg));
el("path", { d: "M170 104 L230 64 H690 L630 104 Z", fill: SOFT, stroke: INK, "stroke-width": 4, "stroke-linejoin": "round" }, svg);
el("path", { d: "M630 104 L690 64 V488 L630 528 Z", fill: BLUE, stroke: INK, "stroke-width": 4, "stroke-linejoin": "round" }, svg);
el("rect", { x: 170, y: 104, width: 460, height: 424, rx: 6, fill: SKY, stroke: INK, "stroke-width": 4 }, svg);
const ys = [124, 258, 392];
ys.forEach((y) => el("rect", { x: X0, y, width: W, height: H, rx: 8, fill: INK }, svg));
const taps = [-99, -99, -99];
const drawers = ROWS.map(([name, tag, col], i) => ({ name, tag, col, i, y: ys[i] })).reverse().map((d) => {
const g = el("g", {}, svg);
const top = el("path", { fill: SOFT, stroke: INK, "stroke-width": 3, "stroke-linejoin": "round" }, g);
const side = el("path", { fill: d.col, stroke: INK, "stroke-width": 3, "stroke-linejoin": "round" }, g);
const front = el("g", {}, g);
el("rect", { x: X0 - 7, y: d.y - 7, width: W + 14, height: H + 14, rx: 13, fill: "none", stroke: INK, "stroke-width": 3, class: "mg-ring" }, front);
el("rect", { x: X0, y: d.y, width: W, height: H, rx: 8, fill: d.col, stroke: INK, "stroke-width": 4 }, front);
txt(front, X0 + 26, d.y + 54, d.name, { "font-family": MONO, "font-size": 28, "font-weight": 700 });
el("rect", { x: X0 + W - 188, y: d.y + 22, width: 168, height: 42, rx: 21, fill: "#fff", stroke: INK, "stroke-width": 2.5 }, front);
txt(front, X0 + W - 104, d.y + 50, d.tag, { "text-anchor": "middle", "font-size": 19, "font-weight": 700 });
el("rect", { x: X0 + W / 2 - 40, y: d.y + 84, width: 80, height: 14, rx: 7, fill: "#fff", stroke: INK, "stroke-width": 3 }, front);
button(g, `${d.name}: ${d.tag}`, () => { taps[d.i] = api.now(); api.poke(); });
return Object.assign(d, { top, side, front });
});
return (t, now) => {
const shut = io(seg(t, 8.4, 9.3));
drawers.forEach((d) => {
const a = 0.8 + d.i * 1.4, tl = ob(seg(t, a, a + 0.8)) * (1 - shut);
const s = now - taps[d.i], user = s >= 0 && s < 4 ? ob(seg(s, 0, 0.7)) * (1 - io(seg(s, 3.2, 4))) : 0;
const nudge = tl > 0.95 && s >= 0 && s < 0.5 ? Math.sin(seg(s, 0, 0.5) * Math.PI) * 0.18 : 0;
const k = Math.max(tl, user) + nudge, ox = PX * k, oy = PY * k, x1 = X0 + W, y = d.y;
d.front.setAttribute("transform", `translate(${ox} ${oy})`);
const vis = k > 0.01 ? 1 : 0;
d.top.setAttribute("opacity", vis); d.side.setAttribute("opacity", vis);
d.top.setAttribute("d", `M${X0 + ox} ${y + oy} H${x1 + ox} L${x1} ${y} H${X0} Z`);
d.side.setAttribute("d", `M${x1 + ox} ${y + oy} L${x1} ${y} V${y + H} L${x1 + ox} ${y + H + oy} Z`);
});
};
},
};

/* ---- player ---- */
const q = new URLSearchParams(location.search), frozen = q.has("mgstill"), fixed = parseFloat(q.get("mgstill"));
const calm = matchMedia("(prefers-reduced-motion: reduce)").matches;
const mount = (fig) => {
const sc = SC[fig.dataset.scene], img = fig.querySelector("img"); if (!sc || !img) return;
const W = sc.w || 800, H = sc.h || 600;
const svg = el("svg", { viewBox: `0 0 ${W} ${H}`, class: "mg-svg", role: "group", "aria-label": img.alt || "" });
svg.style.aspectRatio = `${W} / ${H}`;
el("rect", { width: W, height: H, fill: "#fff" }, svg);
img.replaceWith(svg);
let off = 0, raf = 0, seen = false, held = calm;
const base = performance.now(), clock = () => (performance.now() - base) / 1000;
let draw = () => {};
const frame = () => { const now = clock(); draw(held ? sc.still : (((now + off) % sc.dur) + sc.dur) % sc.dur, now); };
const tick = () => { cancelAnimationFrame(raf); frame(); if (seen && !held) raf = requestAnimationFrame(tick); };
const api = { now: clock, seek: (s) => { off = s - clock(); held = false; tick(); }, poke: () => { held = false; tick(); } };
draw = sc.build(svg, api);
if (frozen) { draw(isNaN(fixed) ? sc.still : fixed, 0); return; }
new IntersectionObserver(([e]) => {
seen = e.isIntersecting;
if (seen) { tick(); if (!fig.dataset.sent && window.posthog) { fig.dataset.sent = 1; window.posthog.capture("blog_scene_view", { slug: location.pathname.split("/").filter(Boolean).pop(), scene: fig.dataset.scene }); } }
else cancelAnimationFrame(raf);
}, { threshold: 0.25 }).observe(fig);
svg.addEventListener("click", () => { if (!fig.dataset.hit && window.posthog) { fig.dataset.hit = 1; window.posthog.capture("blog_scene_interact", { slug: location.pathname.split("/").filter(Boolean).pop(), scene: fig.dataset.scene }); } });
frame();
};
const go = () => document.querySelectorAll("figure.mg[data-scene]").forEach(mount);
const fonts = document.fonts && document.fonts.load ? Promise.all([document.fonts.load('700 32px "Space Grotesk"'), document.fonts.load('500 20px "Space Grotesk"'), document.fonts.load('700 20px "JetBrains Mono"')]).catch(() => {}) : Promise.resolve();
const start = () => fonts.then(go, go);
if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", start); else start();
})();
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 13 additions & 0 deletions blog/src/assets/media/t3-code-alternatives/motion.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/* Bright coded scenes for "T3 Code alternatives". Pairs with motion.js. */
.prose figure.mg { margin: 42px 0; }
.prose figure.mg img, .mg .mg-svg { display: block; width: 100%; height: auto; border: 1px solid #1A1320; border-radius: 18px; background: #fff; }
.mg figcaption { font: 500 13px/1.5 "Space Grotesk", system-ui, sans-serif; color: #6B5878; padding-top: 10px; text-align: center; }
.mg .mg-svg { touch-action: manipulation; -webkit-tap-highlight-color: transparent; }
.mg .mg-svg text { -webkit-user-select: none; user-select: none; pointer-events: none; }
.mg .mg-hit { cursor: pointer; outline: none; }
.mg [role="slider"] { cursor: grab; touch-action: none; }
.mg .mg-ring { stroke-opacity: 0; transition: stroke-opacity 0.15s; }
.mg .mg-hit:focus-visible .mg-ring { stroke-opacity: 1; }
.mg [role="slider"]:focus-visible circle:nth-child(2) { stroke-width: 5; }
@media (hover: hover) { .mg .mg-hit:hover .mg-ring { stroke-opacity: 0.5; } }
@media (prefers-reduced-motion: reduce) { .mg .mg-ring { transition: none; } }
Loading
Loading