From ea77e8f02a81fc8d60a91e132b88331d219c5c12 Mon Sep 17 00:00:00 2001 From: Brendan Chen Date: Sat, 8 Aug 2026 19:53:22 -0400 Subject: [PATCH] Copy over the plugins --- .gitignore | 1 + _notes/test.md | 2 + _plugins/bidirectional_links_generator.rb | 111 +++++++++++++++++++ _plugins/empty_front_matter_note_injector.rb | 18 +++ _plugins/last_modified_at_generator.rb | 18 +++ _plugins/markdown-highlighter.rb | 18 +++ _plugins/open_external_links_in_new_tab.rb | 28 +++++ 7 files changed, 196 insertions(+) create mode 100644 _plugins/bidirectional_links_generator.rb create mode 100644 _plugins/empty_front_matter_note_injector.rb create mode 100644 _plugins/last_modified_at_generator.rb create mode 100644 _plugins/markdown-highlighter.rb create mode 100644 _plugins/open_external_links_in_new_tab.rb diff --git a/.gitignore b/.gitignore index 1a35b77..54f682b 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ vendor/ # macOS .DS_Store +/_includes/notes_graph.json diff --git a/_notes/test.md b/_notes/test.md index c3f2bd7..d1e1dd7 100644 --- a/_notes/test.md +++ b/_notes/test.md @@ -4,3 +4,5 @@ layout: default --- Hello World + +==This is some highlighted text== diff --git a/_plugins/bidirectional_links_generator.rb b/_plugins/bidirectional_links_generator.rb new file mode 100644 index 0000000..78184b5 --- /dev/null +++ b/_plugins/bidirectional_links_generator.rb @@ -0,0 +1,111 @@ +# frozen_string_literal: true +class BidirectionalLinksGenerator < Jekyll::Generator + def generate(site) + graph_nodes = [] + graph_edges = [] + + all_notes = site.collections['notes'].docs + all_pages = site.pages + + all_docs = all_notes + all_pages + + link_extension = !!site.config["use_html_extension"] ? '.html' : '' + + # Convert all Wiki/Roam-style double-bracket link syntax to plain HTML + # anchor tag elements () with "internal-link" CSS class + all_docs.each do |current_note| + all_docs.each do |note_potentially_linked_to| + note_title_regexp_pattern = Regexp.escape( + File.basename( + note_potentially_linked_to.basename, + File.extname(note_potentially_linked_to.basename) + ) + ).gsub('\_', '[ _]').gsub('\-', '[ -]').capitalize + + title_from_data = note_potentially_linked_to.data['title'] + if title_from_data + title_from_data = Regexp.escape(title_from_data) + end + + new_href = "#{site.baseurl}#{note_potentially_linked_to.url}#{link_extension}" + anchor_tag = "\\1" + + # Replace double-bracketed links with label using note title + # [[A note about cats|this is a link to the note about cats]] + current_note.content.gsub!( + /\[\[#{note_title_regexp_pattern}\|(.+?)(?=\])\]\]/i, + anchor_tag + ) + + # Replace double-bracketed links with label using note filename + # [[cats|this is a link to the note about cats]] + current_note.content.gsub!( + /\[\[#{title_from_data}\|(.+?)(?=\])\]\]/i, + anchor_tag + ) + + # Replace double-bracketed links using note title + # [[a note about cats]] + current_note.content.gsub!( + /\[\[(#{title_from_data})\]\]/i, + anchor_tag + ) + + # Replace double-bracketed links using note filename + # [[cats]] + current_note.content.gsub!( + /\[\[(#{note_title_regexp_pattern})\]\]/i, + anchor_tag + ) + end + + # At this point, all remaining double-bracket-wrapped words are + # pointing to non-existing pages, so let's turn them into disabled + # links by greying them out and changing the cursor + current_note.content = current_note.content.gsub( + /\[\[([^\]]+)\]\]/i, # match on the remaining double-bracket links + <<~HTML.delete("\n") # replace with this HTML (\\1 is what was inside the brackets) + + [[ + \\1 + ]] + HTML + ) + end + + # Identify note backlinks and add them to each note + all_notes.each do |current_note| + # Nodes: Jekyll + notes_linking_to_current_note = all_notes.filter do |e| + e.url != current_note.url && e.content.include?(current_note.url) + end + + # Nodes: Graph + graph_nodes << { + id: note_id_from_note(current_note), + path: "#{site.baseurl}#{current_note.url}#{link_extension}", + label: current_note.data['title'], + } unless current_note.path.include?('_notes/index.html') + + # Edges: Jekyll + current_note.data['backlinks'] = notes_linking_to_current_note + + # Edges: Graph + notes_linking_to_current_note.each do |n| + graph_edges << { + source: note_id_from_note(n), + target: note_id_from_note(current_note), + } + end + end + + File.write('_includes/notes_graph.json', JSON.dump({ + edges: graph_edges, + nodes: graph_nodes, + })) + end + + def note_id_from_note(note) + note.data['title'].bytes.join + end +end diff --git a/_plugins/empty_front_matter_note_injector.rb b/_plugins/empty_front_matter_note_injector.rb new file mode 100644 index 0000000..83fd4b2 --- /dev/null +++ b/_plugins/empty_front_matter_note_injector.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +EMPTY_FRONT_MATTER = <<~JEKYLL + --- + --- + +JEKYLL + +# Inject empty front matter in notes that don't have any +Jekyll::Hooks.register :site, :after_init do |site| + Dir.glob(site.collections['notes'].relative_directory + '/**/*.md').each do |filename| + raw_note_content = File.read(filename) + unless raw_note_content.start_with?('---') + raw_note_content.prepend(EMPTY_FRONT_MATTER) + File.write(filename, raw_note_content) + end + end +end diff --git a/_plugins/last_modified_at_generator.rb b/_plugins/last_modified_at_generator.rb new file mode 100644 index 0000000..ea9b1f4 --- /dev/null +++ b/_plugins/last_modified_at_generator.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +require 'fileutils' +require 'pathname' +require 'jekyll-last-modified-at' + +module Recents + # Generate change information for all markdown pages + class Generator < Jekyll::Generator + def generate(site) + items = site.collections['notes'].docs + items.each do |page| + timestamp = Jekyll::LastModifiedAt::Determinator.new(site.source, page.path, '%FT%T%:z').to_s + page.data['last_modified_at_timestamp'] = timestamp + end + end + end +end diff --git a/_plugins/markdown-highlighter.rb b/_plugins/markdown-highlighter.rb new file mode 100644 index 0000000..2ef933c --- /dev/null +++ b/_plugins/markdown-highlighter.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +# Turns ==something== in Markdown to something in output HTML + +Jekyll::Hooks.register [:notes], :pre_render do |doc| + replace(doc) +end + +Jekyll::Hooks.register [:pages], :pre_render do |doc| + # jekyll considers anything at the root as a page, + # we only want to consider actual pages + next unless doc.path.start_with?('_pages/') + replace(doc) +end + +def replace(doc) + doc.content.gsub!(/==+([^ ](.*?)?[^ .=])==+/, "\\1") +end diff --git a/_plugins/open_external_links_in_new_tab.rb b/_plugins/open_external_links_in_new_tab.rb new file mode 100644 index 0000000..8f966c5 --- /dev/null +++ b/_plugins/open_external_links_in_new_tab.rb @@ -0,0 +1,28 @@ +# If the configuration sets `open_external_links_in_new_tab` to a truthy value, +# add 'target=_blank' to anchor tags that don't have `internal-link` class + +# frozen_string_literal: true +require 'nokogiri' + +Jekyll::Hooks.register [:notes], :post_convert do |doc| + convert_links(doc) +end + +Jekyll::Hooks.register [:pages], :post_convert do |doc| + # jekyll considers anything at the root as a page, + # we only want to consider actual pages + next unless doc.path.start_with?('_pages/') + convert_links(doc) +end + +def convert_links(doc) + open_external_links_in_new_tab = !!doc.site.config["open_external_links_in_new_tab"] + + if open_external_links_in_new_tab + parsed_doc = Nokogiri::HTML::DocumentFragment.parse(doc.content) + parsed_doc.css("a:not(.internal-link):not(.footnote):not(.reversefootnote)").each do |link| + link.set_attribute('target', '_blank') + end + doc.content = parsed_doc.inner_html + end +end