diff --git a/_layouts/base.html b/_layouts/base.html index 50de135..7f5a78a 100644 --- a/_layouts/base.html +++ b/_layouts/base.html @@ -109,7 +109,13 @@ figure.captioned-media { counter-increment: captioned-figure; } figure img, figure > a, - figure > video { display: block; } + figure > video, + figure > svg.inline-svg { display: block; } + figure > svg.inline-svg { + width: 100%; + max-width: 100%; + height: auto; + } figure.captioned-media figcaption { margin-top: .55em; padding-top: .5em; @@ -212,6 +218,15 @@ figure.captioned-media figcaption::before { color: #fff; } code { background: transparent; } thead { border-color: currentColor; } + mjx-container[jax="SVG"] rect[data-bgcolor="true"][fill="#fff3cd"] { fill: #51431c; } + mjx-container[jax="SVG"] rect[data-bgcolor="true"][fill="#cfe2ff"] { fill: #203b5e; } + mjx-container[jax="SVG"] rect[data-bgcolor="true"][fill="#e2d9f3"] { fill: #41305a; } + mjx-container[jax="SVG"] rect[data-bgcolor="true"][fill="#f8d7da"] { fill: #542b35; } + mjx-container[jax="SVG"] rect[data-bgcolor="true"][fill="#d1e7dd"] { fill: #1d453b; } + mjx-container[jax="SVG"] g[data-mml-node="mpadded"] > g[fill="#1c1c1a"] { + fill: #f5f5f5; + stroke: #f5f5f5; + } } diff --git a/_plugins/inline_svg.rb b/_plugins/inline_svg.rb new file mode 100644 index 0000000..332a1b2 --- /dev/null +++ b/_plugins/inline_svg.rb @@ -0,0 +1,79 @@ +require "cgi" + +module InlineSvg + LOCAL_SVG_IMAGE = /[^>]*\bsrc=(?["'])(?[^"']+\.svg(?:[?#][^"']*)?)\k[^>]*)\/?\s*>/i + + module_function + + def apply(document) + return unless document.output + + document.output = document.output.gsub(LOCAL_SVG_IMAGE) do |image| + source = Regexp.last_match[:src] + image_attributes = Regexp.last_match[:attributes] + path = local_svg_path(document.site, source) + next image unless path && File.file?(path) + + inline(File.read(path), image_attributes) || image + end + end + + def local_svg_path(site, source) + path = source.split(/[?#]/, 2).first + return unless path.start_with?("/") + + candidate = File.expand_path(path.delete_prefix("/"), site.source) + return unless candidate.start_with?("#{File.expand_path(site.source)}/") + + candidate + end + + def inline(svg, image_attributes) + alt = image_attributes[/\balt=(['"])(.*?)\1/i, 2] + svg.sub(/\A\s*[^>]*)>/i) do + attributes = Regexp.last_match[:attributes] + attributes = add_class(attributes, "inline-svg") + attributes = add_print_size(attributes) + attributes = add_accessible_name(attributes, alt) if alt && !alt.empty? + "" + end + end + + def add_class(attributes, class_name) + if attributes =~ /\bclass=(['"])(.*?)\1/i + attributes.sub(/\bclass=(['"])(.*?)\1/i) { %(class="#{$2} #{class_name}") } + else + %(#{attributes} class="#{class_name}") + end + end + + def add_accessible_name(attributes, alt) + return attributes if attributes.match?(/\baria-(?:label|labelledby)=/i) + + %(#{attributes} role="img" aria-label="#{CGI.escapeHTML(alt)}") + end + + def add_print_size(attributes) + width = attributes[/\bwidth=(['"])([\d.]+)(?:px)?\1/i, 2] + height = attributes[/\bheight=(['"])([\d.]+)(?:px)?\1/i, 2] + view_box = attributes.match(/\bviewBox=(['"])[\d.]+\s+[\d.]+\s+([\d.]+)\s+([\d.]+)\1/i) + width ||= view_box && view_box[2] + height ||= view_box && view_box[3] + return attributes unless width && height + + properties = "--inline-svg-print-width: #{width}px; --inline-svg-print-height: #{height}px;" + if attributes =~ /\bstyle=(['"])(.*?)\1/i + attributes.sub(/\bstyle=(['"])(.*?)\1/i) { %(style="#{$2}; #{properties}") } + else + %(#{attributes} style="#{properties}") + end + end +end + +Jekyll::Hooks.register :documents, :post_render do |document| + InlineSvg.apply(document) +end + +Jekyll::Hooks.register :pages, :post_render do |page| + InlineSvg.apply(page) +end diff --git a/_posts/2024-08-03-hotwire-outside-rails.md b/_posts/2024-08-03-hotwire-outside-rails.md index 8084f9e..d4209fa 100644 --- a/_posts/2024-08-03-hotwire-outside-rails.md +++ b/_posts/2024-08-03-hotwire-outside-rails.md @@ -66,7 +66,7 @@ Our approach involves implementing the **multiple publisher - multiple subscribe Here is a simple sequence diagram showing the core idea of this project: -![Sequence diagram of the client-server interactions](/assets/blog/hotwire-sequence-diagram.webp) +![Sequence diagram of the client-server interactions](/assets/blog/hotwire-sequence-diagram.svg) _Multiple publisher / multiple subscriber pattern over WebSockets_ ### Implement the server diff --git a/_posts/2026-05-27-system-prompts-for-humans.md b/_posts/2026-05-27-system-prompts-for-humans.md index 06e3037..671fd47 100644 --- a/_posts/2026-05-27-system-prompts-for-humans.md +++ b/_posts/2026-05-27-system-prompts-for-humans.md @@ -48,8 +48,12 @@ The difference is easiest to see as two message paths. Talking to an LLM has low ![Low social overhead communication with an LLM](/assets/communication/low_social_ov.svg) +*A compressed prompt goes straight to the LLM, with little social overhead around the exchange.* + ![High social overhead communication with a human](/assets/communication/high_social_ov.svg) +*The same core message takes extra work to encode, send, and decode when social expectations enter the exchange.* + So why not do the same for people approaching me? ## Human communication has hidden state diff --git a/_posts/2026-09-05-comparing-six-and-seven-from-scratch.md b/_posts/2026-09-05-comparing-six-and-seven-from-scratch.md index 6aadbe6..5e7ff13 100644 --- a/_posts/2026-09-05-comparing-six-and-seven-from-scratch.md +++ b/_posts/2026-09-05-comparing-six-and-seven-from-scratch.md @@ -490,7 +490,7 @@ theorem le_total (x y : ℕ) : x ≤ y ∨ y ≤ x := by the indentation records the nesting, but the code still reads as one vertical sequence. the proof state branches: `induction y` creates two obligations, then `cases hd` and `cases c` split them again. -![Lean proof state overview](/assets/blog/lean_state_overview.png) +![Lean proof state overview](/assets/blog/lean_proof_state.svg) _the diagram puts that shape on the page and shows where each branch closes._ every node in the graphviz diagram is one of the two-column states from earlier, and every edge is a tactic. three things it shows that the linear listing hides. diff --git a/assets/blog/hotwire-sequence-diagram.svg b/assets/blog/hotwire-sequence-diagram.svg new file mode 100644 index 0000000..2932110 --- /dev/null +++ b/assets/blog/hotwire-sequence-diagram.svg @@ -0,0 +1,12 @@ +ServerServerServerServerClientServerClientClientServerServerServerServerServerServerGET /Render layout fileChatroom HTMLAfter page load,initiate WebSocket connectionws://localhost:8080/subscribeTurbo Stream (update UI with connection message)Message SubmissionPOST /submit (Message)Process messageTurbo Stream (update UI with user message)Client DisconnectionOn disconnectClient disconnectHandle disconnectionTurbo Stream (update UI with disconnection message) diff --git a/assets/blog/hotwire-sequence-diagram.webp b/assets/blog/hotwire-sequence-diagram.webp deleted file mode 100644 index dbc4689..0000000 Binary files a/assets/blog/hotwire-sequence-diagram.webp and /dev/null differ diff --git a/assets/blog/lean_dependency_graph.svg b/assets/blog/lean_dependency_graph.svg index 9efe3e4..1c410ac 100644 --- a/assets/blog/lean_dependency_graph.svg +++ b/assets/blog/lean_dependency_graph.svg @@ -1,2 +1,11 @@ - -ℕa + succ(d) = succ(a + d)a + 0 = a1 = succ(0)succ(n) = n + 10 + n = nsucc(a) + b = succ(a + b)a + b = b + aa + b + c = a + (b + c)0 ≤ xx ≤ succ(x)x ≤ y ∨ y ≤ x \ No newline at end of file +ℕa + succ(d) = succ(a + d)a + 0 = a1 = succ(0)succ(n) = n + 10 + n = nsucc(a) + b = succ(a + b)a + b = b + aa + b + c = a + (b + c)0 ≤ xx ≤ succ(x)x ≤ y ∨ y ≤ x diff --git a/assets/blog/lean_numberline_two_cases.svg b/assets/blog/lean_numberline_two_cases.svg index 345208c..f75c2e0 100644 --- a/assets/blog/lean_numberline_two_cases.svg +++ b/assets/blog/lean_numberline_two_cases.svg @@ -1,19 +1,23 @@ - + + svg.lean-numberline .lbl { font-size: 17px; font-style: italic; } + svg.lean-numberline .num { font-size: 16px; } + svg.lean-numberline .case { font-size: 17px; } + svg.lean-numberline .ax { stroke: #181818; stroke-width: 1.6; fill: none; } + svg.lean-numberline .tick { stroke: #181818; stroke-width: 1.4; } + svg.lean-numberline .brace{ stroke: #181818; stroke-width: 1.4; fill: none; } + svg.lean-numberline .dot { fill: #181818; } + + + + @media (prefers-color-scheme: dark) { + svg.inline-svg.lean-numberline { background: #000 !important; color: #f5f5f5; fill: #f5f5f5; } + svg.inline-svg.lean-numberline :is(.ax, .tick, .brace) { stroke: #f5f5f5; } + svg.inline-svg.lean-numberline .dot, svg.inline-svg.lean-numberline marker path { fill: #f5f5f5; } + svg.inline-svg.lean-numberline .svg-background { fill: #000; } +} + + +Lean proof state overview +The proof of total ordering splits by induction on y, then by cases of the induction hypothesis and the witness. Four branches close with exact or reflexivity. + + + + +succ d (hd : x≤d ∨ d≤x) + +inl + +inr + + +induction y (succ d, hd) + +induction y (0) + +right + +exact zero_le x + + +cases hd (inl hl) + +cases hd (inr hr) + + +cases' hl with c hc + +rewrite[hc] + +left + +rewrite[succ_eq_add_one] + +use c+1 + +rewrite[← add_assoc] + +rfl + + +cases' hr with c hc + +cases c (0) + +cases c (succ a) + +rewrite[add_zero d] at hc + +rewrite[add_succ] at hc + +left + +right + +rewrite[hc] + +rewrite[hc] + +use a + +rewrite[succ_add] + +exact le_succ_self d + +rfl + + + + +x ≤ y ∨ y ≤ x + +x ≤ 0 ∨ 0 ≤ x + +0 ≤ x + +x ≤ succ d ∨ succ d ≤ x + +…∨… +(hr : d≤x) + +…∨… +(hc : x = d+c) + +…∨… +(hl : x≤d) + +…∨… +(hc : d = x+c) + +…∨… +(hc : x = d+0) + +…∨… +(hc : x = d + succ a) + +x ≤ succ(x+c) ∨ succ(x+c) ≤ x + +…∨… +(hc : x = d) + +…∨… +(hc : x = succ(d+a)) + +x ≤ succ(x+c) + +x ≤ succ d + +succ d ≤ x + +x ≤ (x+c)+1 + +succ d ≤ succ(d+a) + +(x+c)+1 = x+(c+1) + +d ≤ succ d + +succ(d+a) = succ d + a + +(x+c)+1 = (x+c)+1 + +succ(d+a) = succ(d+a) + + + + + + + + + diff --git a/assets/blog/lean_state_overview.png b/assets/blog/lean_state_overview.png deleted file mode 100644 index b38be45..0000000 Binary files a/assets/blog/lean_state_overview.png and /dev/null differ diff --git a/assets/communication/high_social_ov.svg b/assets/communication/high_social_ov.svg index 22b84fa..14d4147 100644 --- a/assets/communication/high_social_ov.svg +++ b/assets/communication/high_social_ov.svg @@ -1 +1,13 @@ -HumanDaniHumanDaniDaniHumanHumanHumanDani to human: high social overheadideawrite core messageremember human protocolencode carefully to avoid hurting feelingsadd fluffproofreadget anxioussend messagedecode intentskip over fluffunderstand corewrite core responseadd fluffsend responsedecode core through fluff \ No newline at end of file +HumanDaniHumanDaniDaniHumanHumanHumanDani to human: high social overheadideawrite core messageremember human protocolencode carefully to avoid hurting feelingsadd fluffproofreadget anxioussend messagedecode intentskip over fluffunderstand corewrite core responseadd fluffsend responsedecode core through fluff diff --git a/assets/communication/low_social_ov.svg b/assets/communication/low_social_ov.svg index 04bdefe..ca85c73 100644 --- a/assets/communication/low_social_ov.svg +++ b/assets/communication/low_social_ov.svg @@ -1 +1,13 @@ -LLMDaniLLMDaniDaniLLMLLMLLMDani to LLM: low social overheadideawrite compressed promptsend promptdo model magicrespondcontinue thinking \ No newline at end of file +LLMDaniLLMDaniDaniLLMLLMLLMDani to LLM: low social overheadideawrite compressed promptsend promptdo model magicrespondcontinue thinking diff --git a/bin/pdf.css b/bin/pdf.css index 546eee1..bbc8a24 100644 --- a/bin/pdf.css +++ b/bin/pdf.css @@ -73,6 +73,12 @@ } p img { width: auto; } img[src$=".svg"], img.pixelated-image { max-height: none; } + figure > svg.inline-svg { + width: auto !important; + height: min(120mm, calc(var(--inline-svg-print-height) * .8)) !important; + max-width: 100% !important; + margin-inline: auto; + } pre { border: 1px solid #ccc; line-height: 1.3; white-space: pre-wrap; } code { background: transparent; }