···11<!DOCTYPE html>
22-<html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><meta name="description" content="oppiliappan's μsings" /><meta name="author" content="Akshay Oppiliappan" /><meta name="color-scheme" content="light dark" /><meta name="twitter:card" content="summary" /><title>configuring jujutsu</title><link rel="stylesheet" href="/style.css?v=b6dddbe3" /><style>html { font-size-adjust: ex-height 0.53; }</style><meta property="og:type" content="website" /><meta property="og:site_name" content="oppi.li" /><meta property="og:title" content="configuring jujutsu" /><meta property="og:description" content="oppiliappan's μsings" /></head><body><div><nav class="max-w-xl mx-auto"><a href="/">home</a><span> :: <a href="/posts">posts</a></span><span> :: <a href="#">configuring jujutsu</a></span></nav><main class="px-2"><div class="max-w-xl mx-auto text-pretty mb-12"><h1 class="text-center">configuring jujutsu</h1><div class="text-center space-x-2"><span>7 min</span><span>·</span><span>79 sentences</span><span>·</span><span>188.48 cm</span><span>·</span><span>24.05.2025</span></div><p>There are a lot of reasons to use
22+<html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><meta name="description" content="oppiliappan's μsings" /><meta name="author" content="Akshay Oppiliappan" /><meta name="color-scheme" content="light dark" /><meta name="twitter:card" content="summary" /><title>configuring jujutsu</title><link rel="stylesheet" href="/style.css?v=215baaaf" /><style>html { font-size-adjust: ex-height 0.53; }</style><meta property="og:type" content="website" /><meta property="og:site_name" content="oppi.li" /><meta property="og:title" content="configuring jujutsu" /><meta property="og:description" content="oppiliappan's μsings" /></head><body><div class="min-h-screen flex flex-col"><nav class="px-2"><a href="/">home</a><span> :: <a href="/posts">posts</a></span><span> :: <a href="#">configuring jujutsu</a></span></nav><main class="px-2"><div class="max-w-xl mx-auto text-pretty mb-12"><h1 class="text-center">configuring jujutsu</h1><div class="text-center space-x-2"><span>7 min</span><span>·</span><span>79 sentences</span><span>·</span><span>188.48 cm</span><span>·</span><span>24.05.2025</span></div><p>There are a lot of reasons to use
33<a href="https://github.com/jj-vcs/jj">jujutsu</a>, but this post is
44not about that. I have this terrible habit of turning every
55knob on a tool before I even fully absorb how it works; and
+1-1
_site/posts/curing_a_case_of_git-UX/index.html
···11<!DOCTYPE html>
22-<html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><meta name="description" content="oppiliappan's μsings" /><meta name="author" content="Akshay Oppiliappan" /><meta name="color-scheme" content="light dark" /><meta name="twitter:card" content="summary" /><title>curing a case of git-UX</title><link rel="stylesheet" href="/style.css?v=b6dddbe3" /><style>html { font-size-adjust: ex-height 0.53; }</style><meta property="og:type" content="website" /><meta property="og:site_name" content="oppi.li" /><meta property="og:title" content="curing a case of git-UX" /><meta property="og:description" content="oppiliappan's μsings" /></head><body><div><nav class="max-w-xl mx-auto"><a href="/">home</a><span> :: <a href="/posts">posts</a></span><span> :: <a href="#">curing a case of git-UX</a></span></nav><main class="px-2"><div class="max-w-xl mx-auto text-pretty mb-12"><h1 class="text-center">curing a case of git-UX</h1><div class="text-center space-x-2"><span>7 min</span><span>·</span><span>143 sentences</span><span>·</span><span>167.65 cm</span><span>·</span><span>03.09.2022</span></div><p>Git worktrees are great, but they fall behind the venerable
22+<html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><meta name="description" content="oppiliappan's μsings" /><meta name="author" content="Akshay Oppiliappan" /><meta name="color-scheme" content="light dark" /><meta name="twitter:card" content="summary" /><title>curing a case of git-UX</title><link rel="stylesheet" href="/style.css?v=215baaaf" /><style>html { font-size-adjust: ex-height 0.53; }</style><meta property="og:type" content="website" /><meta property="og:site_name" content="oppi.li" /><meta property="og:title" content="curing a case of git-UX" /><meta property="og:description" content="oppiliappan's μsings" /></head><body><div class="min-h-screen flex flex-col"><nav class="px-2"><a href="/">home</a><span> :: <a href="/posts">posts</a></span><span> :: <a href="#">curing a case of git-UX</a></span></nav><main class="px-2"><div class="max-w-xl mx-auto text-pretty mb-12"><h1 class="text-center">curing a case of git-UX</h1><div class="text-center space-x-2"><span>7 min</span><span>·</span><span>143 sentences</span><span>·</span><span>167.65 cm</span><span>·</span><span>03.09.2022</span></div><p>Git worktrees are great, but they fall behind the venerable
33<code>git checkout</code> sometimes. I attempted to fix that with
44<a href="https://github.com/junegunn/fzf">fzf</a> and
55a bit of bash.</p>
+1-1
_site/posts/font_size_fallacies/index.html
···11<!DOCTYPE html>
22-<html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><meta name="description" content="oppiliappan's μsings" /><meta name="author" content="Akshay Oppiliappan" /><meta name="color-scheme" content="light dark" /><meta name="twitter:card" content="summary" /><title>font size fallacies</title><link rel="stylesheet" href="/style.css?v=b6dddbe3" /><style>html { font-size-adjust: ex-height 0.53; }</style><meta property="og:type" content="website" /><meta property="og:site_name" content="oppi.li" /><meta property="og:title" content="font size fallacies" /><meta property="og:description" content="oppiliappan's μsings" /></head><body><div><nav class="max-w-xl mx-auto"><a href="/">home</a><span> :: <a href="/posts">posts</a></span><span> :: <a href="#">font size fallacies</a></span></nav><main class="px-2"><div class="max-w-xl mx-auto text-pretty mb-12"><h1 class="text-center">font size fallacies</h1><div class="text-center space-x-2"><span>2 min</span><span>·</span><span>44 sentences</span><span>·</span><span>41.15 cm</span><span>·</span><span>16.03.2020</span></div><p>I am not an expert with fonts, but I do have some
22+<html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><meta name="description" content="oppiliappan's μsings" /><meta name="author" content="Akshay Oppiliappan" /><meta name="color-scheme" content="light dark" /><meta name="twitter:card" content="summary" /><title>font size fallacies</title><link rel="stylesheet" href="/style.css?v=215baaaf" /><style>html { font-size-adjust: ex-height 0.53; }</style><meta property="og:type" content="website" /><meta property="og:site_name" content="oppi.li" /><meta property="og:title" content="font size fallacies" /><meta property="og:description" content="oppiliappan's μsings" /></head><body><div class="min-h-screen flex flex-col"><nav class="px-2"><a href="/">home</a><span> :: <a href="/posts">posts</a></span><span> :: <a href="#">font size fallacies</a></span></nav><main class="px-2"><div class="max-w-xl mx-auto text-pretty mb-12"><h1 class="text-center">font size fallacies</h1><div class="text-center space-x-2"><span>2 min</span><span>·</span><span>44 sentences</span><span>·</span><span>41.15 cm</span><span>·</span><span>16.03.2020</span></div><p>I am not an expert with fonts, but I do have some
33experience <a href="https://github.com/nerdypepper/scientifica">^exp</a>, and common sense. This post aims to debunk some
44misconceptions about font sizes!</p>
55<p>11 px on your display is <em>probably not</em> 11 px on my display.
···11+module Index (P : Site.Content.Page) :
22+ Site.Category.Index with type page = P.t = struct
33+ type page = P.t
44+55+ let compare_date a b =
66+ Option.compare CalendarLib.Date.compare (P.date b) (P.date a)
77+88+ let img_url p = Site.Layout.cdn_url ^ "/art/" ^ P.slug p ^ ".png"
99+1010+ let card ?(base = "") post =
1111+ Tw_html.(
1212+ a
1313+ ~at:At.[ href (img_url post) ]
1414+ ~tw:Tw.[ no_underline ]
1515+ [
1616+ img ~at:At.[ src (img_url post); alt (P.title post); loading_lazy ] ();
1717+ ])
1818+1919+ let render posts =
2020+ let sorted = List.sort compare_date posts in
2121+ Tw_html.(
2222+ div
2323+ ~tw:Tw.[ max_w_xl; mx_auto ]
2424+ [
2525+ h1 ~tw:Tw.[ text_center ] [ txt "art" ];
2626+ div
2727+ ~tw:Tw.[ text_center; space_x 2. ]
2828+ [ span [ txt (Printf.sprintf "%d pieces" (List.length posts)) ] ];
2929+ div
3030+ ~tw:Tw.[ grid; grid_cols 3; mt 4 ]
3131+ ~at:At.[ style "gap:1ch" ]
3232+ (List.map card sorted);
3333+ ])
3434+3535+ let preview ?(base = "") ~limit posts =
3636+ let recent = List.sort compare_date posts |> List.take limit in
3737+ Tw_html.(div ~tw:Tw.[ grid; grid_cols 1 ] (List.map (card ~base) recent))
3838+end
3939+4040+module Cat =
4141+ Site.Category.Make
4242+ (struct
4343+ let name = "art"
4444+ end)
4545+ (Site.Content.ArtPage)
4646+ (Index (Site.Content.ArtPage))
+17-15
bin/home.ml
···5454 ~at:At.[ v "class" "sphere-clip" ]
5555 [ div ~at:At.[ v "class" "sphere" ] [] ])
56565757-let about =
5757+let about ~art_preview =
5858 Tw_html.(
5959 div
6060- ~tw:
6161- Tw.[ flex; flex_col; items_center; md [ flex_row ]; p 2; mt 12; gap 12 ]
6060+ ~tw:Tw.[ flex; items_center; gap 4 ]
6261 [
6363- dither_svg;
6464- div
6565- ~tw:Tw.[ aspect_ratio 1 1; w 48; flex; items_center; justify_center ]
6666- [ sphere ];
6262+ div ~tw:Tw.[ block; w_fit; h_fit; md [ hidden ] ] [ art_preview ];
6763 div
6868- ~tw:Tw.[ text_justify ]
6464+ ~tw:Tw.[ text_pretty ]
6965 [
7066 p
7167 [
···122118 posts;
123119 ])
124120125125-let render ~posts_preview ~weeklies_preview =
121121+let render ~posts_preview ~weeklies_preview ~art_preview =
126122 Tw_html.(
127123 div
128128- ~tw:Tw.[ max_w_xl; mx_auto ]
124124+ ~tw:Tw.[ grid; max_w_4xl; m_auto; gap 4; md [ grid_cols 2 ] ]
129125 [
130130- sphere_css;
131131- about;
132132- div [ posts_p posts_preview; weeklies_p weeklies_preview ];
126126+ div
127127+ ~tw:Tw.[ items_center; justify_center; hidden; md [ flex ] ]
128128+ [ art_preview ];
129129+ div
130130+ [
131131+ about ~art_preview;
132132+ div [ posts_p posts_preview; weeklies_p weeklies_preview ];
133133+ ];
133134 ])
134135135135-let build ~posts_preview ~weeklies_preview =
136136- Site.Layout.page ~title:"oppi.li" (render ~posts_preview ~weeklies_preview)
136136+let build ~posts_preview ~weeklies_preview ~art_preview =
137137+ Site.Layout.landing ~title:"oppi.li"
138138+ (render ~posts_preview ~weeklies_preview ~art_preview)
137139138140let write ~out_dir p = Site.Category.write_page ~out_dir "" p
139141let css p = snd (Tw_html.css p)
+18-2
bin/main.ml
···11let () =
22 let posts = Posts.Cat.build ~path:"content/posts" in
33 Printf.printf "[OK] %s: %d\n%!" (Posts.Cat.name posts) (Posts.Cat.count posts);
44+45 let weeklies = Weeklies.Cat.build ~path:"content/weeklies" in
56 Printf.printf "[OK] %s: %d\n%!"
67 (Weeklies.Cat.name weeklies)
78 (Weeklies.Cat.count weeklies);
99+1010+ let art = Art.Cat.build ~path:"content/art" in
1111+ Printf.printf "[OK] %s: %d\n%!" (Art.Cat.name art) (Art.Cat.count art);
1212+813 let home =
914 Home.build
1015 ~posts_preview:(Posts.Cat.preview ~limit:3 posts)
1116 ~weeklies_preview:(Weeklies.Cat.preview ~limit:1 weeklies)
1717+ ~art_preview:(Art.Cat.preview ~limit:1 art)
1218 in
1319 Posts.Cat.write ~out_dir:"_site" posts;
2020+ Weeklies.Cat.write ~out_dir:"_site" weeklies;
2121+ Art.Cat.write ~out_dir:"_site" art;
2222+1423 Posts.Cat.write_rss ~out_dir:"_site" posts;
1515- Weeklies.Cat.write ~out_dir:"_site" weeklies;
1624 Weeklies.Cat.write_rss ~out_dir:"_site" weeklies;
2525+ Art.Cat.write_rss ~out_dir:"_site" art;
2626+1727 Home.write ~out_dir:"_site" home;
2828+1829 let css =
1930 Tw.Css.concat
2020- [ Posts.Cat.css posts; Weeklies.Cat.css weeklies; Home.css home ]
3131+ [
3232+ Posts.Cat.css posts;
3333+ Weeklies.Cat.css weeklies;
3434+ Art.Cat.css art;
3535+ Home.css home;
3636+ ]
2137 in
2238 let css_path = "_site/style.css" in
2339 Out_channel.with_open_text css_path (fun oc ->
+1-5
bin/posts.ml
···33 type page = P.t
4455 let compare_date a b =
66- match (P.date a, P.date b) with
77- | Some da, Some db -> CalendarLib.Date.compare db da
88- | Some _, None -> -1
99- | None, Some _ -> 1
1010- | None, None -> 0
66+ Option.compare CalendarLib.Date.compare (P.date b) (P.date a)
117128 let view_row ?(base = "") post =
139 Tw_html.(
···9797 :: List.map (fun (_, page) -> snd (Tw_html.css page)) t.pages)
98989999 let compare_date a b =
100100- match (P.date a, P.date b) with
101101- | Some da, Some db -> CalendarLib.Date.compare db da
102102- | Some _, None -> -1
103103- | None, Some _ -> 1
104104- | None, None -> 0
100100+ Option.compare CalendarLib.Date.compare (P.date b) (P.date a)
105101106102 let rss t =
107103 let cat = N.name in
+84-38
lib/content.ml
···11type toc_item = { text : string; children : toc_item list }
2233+let fm_string key fm =
44+ match fm with
55+ | `O fields -> (
66+ match List.assoc_opt key fields with
77+ | Some (`String s) -> Some s
88+ | _ -> None)
99+ | _ -> None
1010+1111+let parse_date s =
1212+ match String.split_on_char '-' s with
1313+ | [ dd; mm; yyyy ] -> (
1414+ try
1515+ Some
1616+ (CalendarLib.Date.make (int_of_string yyyy) (int_of_string mm)
1717+ (int_of_string dd))
1818+ with _ -> None)
1919+ | _ -> None
2020+2121+let title_of_path path =
2222+ path |> Filename.basename |> Filename.remove_extension
2323+ |> String.map (function '_' -> ' ' | x -> x)
2424+2525+let xml_escape s =
2626+ let b = Buffer.create (String.length s) in
2727+ String.iter
2828+ (function
2929+ | '&' -> Buffer.add_string b "&"
3030+ | '<' -> Buffer.add_string b "<"
3131+ | '>' -> Buffer.add_string b ">"
3232+ | c -> Buffer.add_char b c)
3333+ s;
3434+ Buffer.contents b
3535+336module type Page = sig
437 type t
538···3366 go [] rest
3467 | _ -> ([], lines)
35683636- let fm_string key fm =
3737- match fm with
3838- | `O fields -> (
3939- match List.assoc_opt key fields with
4040- | Some (`String s) -> Some s
4141- | _ -> None)
4242- | _ -> None
4343-4469 let frontmatter t = t.frontmatter
4545-4646- let title_of_path path =
4747- path |> Filename.basename |> Filename.remove_extension
4848- |> String.map (function '_' -> ' ' | x -> x)
4949-5050- let title' fm path =
5151- Option.value (fm_string "title" fm) ~default:(title_of_path path)
5252-7070+ let title' fm path = Option.value (fm_string "title" fm) ~default:(title_of_path path)
5371 let title t = title' t.frontmatter t.path
5454-5555- let parse_date s =
5656- match String.split_on_char '-' s with
5757- | [ dd; mm; yyyy ] -> (
5858- try
5959- Some
6060- (CalendarLib.Date.make (int_of_string yyyy) (int_of_string mm)
6161- (int_of_string dd))
6262- with _ -> None)
6363- | _ -> None
6464-6572 let date' fm = Option.bind (fm_string "date" fm) parse_date
6673 let date t = date' t.frontmatter
6774···157164158165 let html t = t.html
159166160160- let xml_escape s =
161161- let b = Buffer.create (String.length s) in
162162- String.iter
163163- (function
164164- | '&' -> Buffer.add_string b "&"
165165- | '<' -> Buffer.add_string b "<"
166166- | '>' -> Buffer.add_string b ">"
167167- | c -> Buffer.add_char b c)
168168- s;
169169- Buffer.contents b
170170-171167 let rss_item ~link t =
172168 let title = xml_escape (title t) in
173169 let description = xml_escape (Omd.to_html t.doc) in
···187183 </item>|}
188184 title description link pub_date link
189185end
186186+187187+module ArtPage : Page = struct
188188+ type t = {
189189+ slug : string;
190190+ frontmatter : Yaml.value;
191191+ title : string;
192192+ date : CalendarLib.Date.t option;
193193+ }
194194+195195+ let make ~path =
196196+ let content = In_channel.with_open_text path In_channel.input_all in
197197+ let frontmatter = Yaml.of_string_exn content in
198198+ let slug = path |> Filename.basename |> Filename.remove_extension in
199199+ let title = Option.value (fm_string "title" frontmatter) ~default:(title_of_path path) in
200200+ let date = Option.bind (fm_string "date" frontmatter) parse_date in
201201+ { slug; frontmatter; title; date }
202202+203203+ let slug t = t.slug
204204+ let frontmatter t = t.frontmatter
205205+ let title t = t.title
206206+ let date t = t.date
207207+ let toc _ = []
208208+209209+ let img_url slug = Layout.cdn_url ^ "/art/" ^ slug ^ ".png"
210210+211211+ let html t =
212212+ Tw_html.(img ~at:At.[ src (img_url t.slug); alt t.title ] ())
213213+214214+ let rss_item ~link t =
215215+ let title = xml_escape t.title in
216216+ let description =
217217+ xml_escape
218218+ (Printf.sprintf {|<img src="%s" alt="%s"/>|} (img_url t.slug) t.title)
219219+ in
220220+ let pub_date =
221221+ match t.date with
222222+ | None -> ""
223223+ | Some d ->
224224+ CalendarLib.Printer.Date.sprint "%a, %d %b %Y 00:00:00 +0000" d
225225+ in
226226+ Printf.sprintf
227227+ {|<item>
228228+ <title>%s</title>
229229+ <description>%s</description>
230230+ <link>%s</link>
231231+ <pubDate>%s</pubDate>
232232+ <guid>%s</guid>
233233+ </item>|}
234234+ title description link pub_date link
235235+end