Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce Topics and bidirectional links between pages #639

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
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
2 changes: 2 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,5 @@ gem 'color_pound_spec_reporter'

# HTTP bindings to libcurl - https://github.com/taf2/curb
gem 'curb'

gem "nokogiri", "~> 1.13.10"
8 changes: 7 additions & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,20 @@ GEM
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
mercenary (0.3.6)
mini_portile2 (2.8.1)
minitest (5.14.2)
minitest-reporters (1.4.2)
ansi
builder
minitest (>= 5.0)
ruby-progressbar
nokogiri (1.13.10)
mini_portile2 (~> 2.8.0)
racc (~> 1.4)
pathutil (0.16.2)
forwardable-extended (~> 2.6)
public_suffix (5.0.0)
racc (1.6.2)
rake (13.0.1)
rb-fsevent (0.10.4)
rb-inotify (0.10.1)
Expand Down Expand Up @@ -92,7 +97,8 @@ DEPENDENCIES
kramdown-parser-gfm
minitest
minitest-reporters
nokogiri (~> 1.13.10)
rake

BUNDLED WITH
2.1.4
2.3.26
75 changes: 71 additions & 4 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ desc 'Create a new post file'
namespace :posts do
task :new do
# Fetch user command line args. Exit if required args are missing.
pr, host, date = get_cli_options
pr, host, date = get_cli_options_posts
handle_missing_required_arg('pr') unless pr
handle_missing_required_arg('host') unless host

Expand Down Expand Up @@ -120,7 +120,7 @@ namespace :posts do
end
end

def get_cli_options(options = {})
def get_cli_options_posts(options = {})
OptionParser.new do |opts|
opts.banner = 'Usage: rake posts:new -- <options>'
opts.on('-p', '--pr NUMBER', 'Pull request number (required)') do
Expand All @@ -143,8 +143,57 @@ def get_cli_options(options = {})
[options[:pr], options[:host], options[:date]]
end

def handle_missing_required_arg(name)
puts "Error: Missing required --#{name} argument. Run `rake posts:new -- --help` for info."
# To see the rake topics:new help, run:
# rake topics:new -- -H
#
desc 'Create a new topics file'
namespace :topics do
task :new do
# Fetch user command line args. Exit if required args are missing.
title, slug = get_cli_options_topics

handle_missing_required_arg('title', 'topics') unless title

# Slugify title if slug not supplied by user.
unless slug
slug = title.downcase.strip.gsub(' ', '-').gsub(/[^\w-]/, '')
puts "Topic '#{title}' has been slugified and filename set to : #{slug}"
end
# Create a new topic file if none exists, otherwise exit.
filename = "#{'_topics/' if File.directory?('_topics')}#{slug}.md"

if File.file?(filename)
puts "Filename #{filename} already exists. Nothing done, exiting."
else
create_topic_file!(filename, title)
puts "New file #{filename} created successfully."
end
exit
end
end

def get_cli_options_topics(options = {})
OptionParser.new do |opts|
opts.banner = 'Usage: rake topics:new -- <options>'
opts.on('-t', '--title TOPIC_LONG', 'Long name for topic (required)') do
|title| options[:title] = title
end
opts.on('-s', '--slug TOPIC_SHORT',
'Short name for topic (optional, defaults to slugified title)') do
|slug| options[:slug] = slug
end
opts.on('-H', '--help', 'Display this help') do
puts opts
exit
end
args = opts.order!(ARGV) {}
opts.parse!(args)
end
[options[:title], options[:slug]]
end

def handle_missing_required_arg(name, rake = "posts")
puts "Error: Missing required --#{name} argument. Run `rake #{rake}:new -- --help` for info."
exit
end

Expand Down Expand Up @@ -225,6 +274,24 @@ def create_post_file!(filename, response, date, host)
end
end

def create_topic_file!(filename, title)
def comment_out(line, header)
line.puts "<!-- uncomment to add"
line.puts "## #{header}"
line.puts "-->"
end

File.open(filename, 'w') do |line|
line.puts '---'
line.puts 'layout: topic'
line.puts "title: #{title}"
line.puts "---\n\n"
comment_out(line, "Notes")
comment_out(line, "History")
comment_out(line, "Resources")
end
end

def parse_title(title)
first, *rest = title.split # e.g. if title = "a b c", first = "a", rest = ["b", "c"]
first.downcase! # mutate first word to lowercase in place
Expand Down
5 changes: 5 additions & 0 deletions _config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ exclude:
- Rakefile
- test/

collections:
topics:
output: true
permalink: /topics/:slug

defaults:
- scope:
path: "" ## all pages
Expand Down
1 change: 1 addition & 0 deletions _data/settings.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ author:
menu:
- {name: 'Home', url: ''}
- {name: 'Meetings', url: 'meetings/'}
- {name: 'Topics', url: 'topics/'}
- {name: 'Code of Conduct', url: 'code-of-conduct/'}
- {name: 'Hosting', url: 'hosting/'}
glozow marked this conversation as resolved.
Show resolved Hide resolved
20 changes: 20 additions & 0 deletions _includes/backlinks.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<side style="font-size: 0.9em">
{% if page.backlinks.size > 0 %}
<h3 style="margin-bottom: 1em; color: #828282">{{ page.backlinks.size }} Linked pages</h3>
<div style="display: grid; grid-gap: 1em; grid-template-columns: repeat(1fr);">
{% for backlink in page.backlinks %}
<div >
<a class="internal-link" style="font-size: 1.2em" href="{{ backlink.doc.url }}">{%- if backlink.doc.pr -%}#{{ backlink.doc.pr }}&nbsp;{%- endif -%}{{ backlink.doc.title }}</a><br>
<div style="margin-top: 0.3em; display: grid; grid-gap: 0.7em; grid-template-columns: repeat(1fr);">
{% for snippet in backlink.snippets %}
<div class="backlink-box">
<div class="backlink-header">{{ snippet.header }}</div>
{{ snippet.paragraph | link_to_anchor: backlink.doc.url | markdownify }}
</div>
{% endfor %}
</div>
</div>
{% endfor %}
</div>
{% endif %}
</side>
1 change: 1 addition & 0 deletions _layouts/pr.html
Original file line number Diff line number Diff line change
Expand Up @@ -65,5 +65,6 @@ <h1>{{ page.title }} ({{components}})</h1>
<p>
{{ content }}
</p>
{% include backlinks.html %}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I'd add this to the PR page. Perhaps wait until the "related meetings" feature is added and just link to related meetings?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea here is that you can reference a PR at another PR's page and it will show the backlink in the same way that is currently displayed at the topics pages. In my mind, "related meetings" could be broader than just meetings that reference each other, therefore backlinks in PRs pages is just another way to navigate around.
Based on the recent changes, if no backlinks exist, nothing will be displayed at the PR page.

</div>
</section>
22 changes: 22 additions & 0 deletions _layouts/topic.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
layout: default
---

<section>
<div class="post-content">
{% if page.code %}
<h1><code>{{ page.title }}</code></h1>
{% else %}
<h1>{{ page.title }}</h1>
{% endif %}

<time class="dt-published" datetime="{{ page.last-modified-date | date_to_xmlschema }}">
Last updated: {{ page.last-modified-date | date: '%B %d, %Y' }}
</time>

<p>
{{ content }}
</p>
{% include backlinks.html %}
</div>
</section>
61 changes: 61 additions & 0 deletions _plugins/auto-anchor.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# frozen_string_literal: true
# This file is based on code from https://github.com/bitcoinops/bitcoinops.github.io

# Automatically adds id tags (anchors) to list items
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about making the bullet points (slightly) larger so that they're more visible as clickable elements.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to keep the size the same as the regular bullets, but it's true that, in comparison to Optech's larger colored bullets, this doesn't make it obvious that they are clickable.
Just to point out, that clickable bullets are used in two places in the page:

  1. at each paragraph of the main page
  2. at each backlink snippet

I intentionally made (1) the same size as the previous(regular) bullets to not change the reader's experience and as I think that the clickability of the bullets in the main page doesn't offer so much value. I was also thinking of removing those, but as they are nonintrusive, I went with the extra utility for those that will notice.

For (2), it makes sense to make it a bit more obvious that they are clickable, so I just change them based on your suggestion.

Let me know if base on my reasoning you still think that (1) should also be bigger.


require 'digest/md5'

def generate_slug(text)
# Remove double-quotes from titles before attempting to slugify
text.gsub!('"', '')
# Remove whitespace character from the end and use Liquid/Jekyll slugify filter
slug_text = "{{ \"#{text.rstrip}\" | slugify: 'latin' }}"
# use the digest library to create deterministic ids based on text
id = Digest::MD5.hexdigest(slug_text)[0...7]
slug = "\#b#{id}" # prefix with 'b', ids cannot start with a number
Comment on lines +13 to +15
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is interesting (and different from the optech version). What's the rationale for using a digest here rather than the text?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optech has a very specific pattern of writing paragraphs that allows for the extraction of the title that is later used for the slug creation. This is not true here, therefore using just the text results to very long slugs, and if try to concatenate it (e.g. first 10 characters) does not always result to unique ids.

slug
end

def generate_anchor_list_link(anchor_link, class_name='anchor-list-link')
# custom clickable bullet linking to an anchor
"<a href=\"#{anchor_link}\" class=\"#{class_name}\">●</a>"
end

def auto_anchor(content)
# finds “bulleted” list items that start with hyphen (-) or asterisk (*)
# adds anchor and clickable bullet
content.gsub!(/^ *[\*-] .*?(?:\n\n|\z)/m) do |bulleted_paragraph|
slug = generate_slug(bulleted_paragraph)
bullet_character = bulleted_paragraph.match(/^ *([\*-])/)[1] # bullet can be '-' or '*'
id_prefix = "#{bullet_character} {:#{slug} .anchor-list} #{generate_anchor_list_link(slug)}"
bulleted_paragraph.sub!(/#{Regexp.quote(bullet_character)}/, id_prefix)
end
# finds “numbered” list items that start with number (1.)
# adds anchor only
content.gsub!(/^ *\d+\. .*?(?:\n\n|\z)/m) do |numbered_paragraph|
slug = generate_slug(numbered_paragraph)
id_prefix = "1. {:#{slug} .anchor-list .anchor-numbered}"
numbered_paragraph.sub!(/\d+\./, id_prefix)
end
end

## Run automatically on all documents
Jekyll::Hooks.register :documents, :pre_render do |post|
## Don't process documents if YAML headers say: "auto_id: false"
unless post.data["auto_id"] == false
auto_anchor(post.content)
end
end

module TextFilter
# This is a custom filter used in backlinks.html to
# add anchor links to each backlink snippet
def link_to_anchor(text, url)
slug = generate_slug(text)
id_prefix = generate_anchor_list_link("#{url}#{slug}", "backlink-link")
text.sub!(/(?:-|\*|\d+\.)/, id_prefix) # this targets both “bulleted” and “numbered” list items
text
end
end

Liquid::Template.register_filter(TextFilter)
Loading