pp (>= 0.6.0)
rdoc (>= 4.0.0)
reline (>= 0.4.2)
- json (2.11.3)
+ json (2.12.0)
jsonapi-serializer (2.2.0)
activesupport (>= 4.2)
kaminari (1.2.2)
activerecord
kaminari-core (= 1.2.2)
kaminari-core (1.2.2)
- language_server-protocol (3.17.0.4)
+ language_server-protocol (3.17.0.5)
launchy (3.1.1)
addressable (~> 2.8)
childprocess (~> 5.0)
launchy (>= 2.2, < 4)
lint_roller (1.1.0)
logger (1.7.0)
- loofah (2.24.0)
+ loofah (2.24.1)
crass (~> 1.0.2)
nokogiri (>= 1.12.0)
mail (2.8.1)
marcel (1.0.4)
matrix (0.4.2)
mini_mime (1.1.5)
- mini_portile2 (2.8.8)
+ mini_portile2 (2.8.9)
minitest (5.25.5)
msgpack (1.8.0)
multi_json (1.15.0)
prettyprint
prettyprint (0.2.0)
prism (1.4.0)
- psych (5.2.5)
+ psych (5.2.6)
date
stringio
public_suffix (6.0.2)
font-style: italic;
}
+/* Atkinson Hyperlegible Next Font Family */
+
+/* Regular */
+@font-face {
+ font-family: 'Atkinson Hyperlegible Next';
+ src: url('/fonts/AtkinsonHyperlegibleNext-Regular.woff2') format('woff2');
+ font-weight: 400;
+ font-style: normal;
+ font-display: swap;
+}
+
+/* Regular Italic */
+@font-face {
+ font-family: 'Atkinson Hyperlegible Next';
+ src: url('/fonts/AtkinsonHyperlegibleNext-RegularItalic.woff2') format('woff2');
+ font-weight: 400;
+ font-style: italic;
+ font-display: swap;
+}
+
+/* Light */
+@font-face {
+ font-family: 'Atkinson Hyperlegible Next';
+ src: url('/fonts/AtkinsonHyperlegibleNext-Light.woff2') format('woff2');
+ font-weight: 300;
+ font-style: normal;
+ font-display: swap;
+}
+
+/* Light Italic */
+@font-face {
+ font-family: 'Atkinson Hyperlegible Next';
+ src: url('/fonts/AtkinsonHyperlegibleNext-LightItalic.woff2') format('woff2');
+ font-weight: 300;
+ font-style: italic;
+ font-display: swap;
+}
+
+/* Extra Light */
+@font-face {
+ font-family: 'Atkinson Hyperlegible Next';
+ src: url('/fonts/AtkinsonHyperlegibleNext-ExtraLight.woff2') format('woff2');
+ font-weight: 200;
+ font-style: normal;
+ font-display: swap;
+}
+
+/* Extra Light Italic */
+@font-face {
+ font-family: 'Atkinson Hyperlegible Next';
+ src: url('/fonts/AtkinsonHyperlegibleNext-ExtraLightItalic.woff2') format('woff2');
+ font-weight: 200;
+ font-style: italic;
+ font-display: swap;
+}
+
+/* Medium */
+@font-face {
+ font-family: 'Atkinson Hyperlegible Next';
+ src: url('/fonts/AtkinsonHyperlegibleNext-Medium.woff2') format('woff2');
+ font-weight: 500;
+ font-style: normal;
+ font-display: swap;
+}
+
+/* Medium Italic */
+@font-face {
+ font-family: 'Atkinson Hyperlegible Next';
+ src: url('/fonts/AtkinsonHyperlegibleNext-MediumItalic.woff2') format('woff2');
+ font-weight: 500;
+ font-style: italic;
+ font-display: swap;
+}
+
+/* Semi Bold */
+@font-face {
+ font-family: 'Atkinson Hyperlegible Next';
+ src: url('/fonts/AtkinsonHyperlegibleNext-SemiBold.woff2') format('woff2');
+ font-weight: 600;
+ font-style: normal;
+ font-display: swap;
+}
+
+/* Semi Bold Italic */
+@font-face {
+ font-family: 'Atkinson Hyperlegible Next';
+ src: url('/fonts/AtkinsonHyperlegibleNext-SemiBoldItalic.woff2') format('woff2');
+ font-weight: 600;
+ font-style: italic;
+ font-display: swap;
+}
+
+/* Bold */
+@font-face {
+ font-family: 'Atkinson Hyperlegible Next';
+ src: url('/fonts/AtkinsonHyperlegibleNext-Bold.woff2') format('woff2');
+ font-weight: 700;
+ font-style: normal;
+ font-display: swap;
+}
+
+/* Bold Italic */
+@font-face {
+ font-family: 'Atkinson Hyperlegible Next';
+ src: url('/fonts/AtkinsonHyperlegibleNext-BoldItalic.woff2') format('woff2');
+ font-weight: 700;
+ font-style: italic;
+ font-display: swap;
+}
+
+/* Extra Bold */
+@font-face {
+ font-family: 'Atkinson Hyperlegible Next';
+ src: url('/fonts/AtkinsonHyperlegibleNext-ExtraBold.woff2') format('woff2');
+ font-weight: 800;
+ font-style: normal;
+ font-display: swap;
+}
+
+/* Extra Bold Italic */
+@font-face {
+ font-family: 'Atkinson Hyperlegible Next';
+ src: url('/fonts/AtkinsonHyperlegibleNext-ExtraBoldItalic.woff2') format('woff2');
+ font-weight: 800;
+ font-style: italic;
+ font-display: swap;
+}
+
+
/* Root variables (Dark mode) */
:root {
--post-bg: #2b2b2b; /* Deep blue-grey for content areas */
--dark-text: #ffffff; /* Pure white for headers */
--light-text: #eeeeee; /* Muted blue for secondary text */
--code-text: #9b8aa6; /* Bright blue for code/special text */
+
+ --today-text: #c6b8b2; /* #3b0aff */
--filter-button-text: #eeeeee;
--filter-button: #3d3d3d;
/* Light mode, overwrites at the top of the CSS so it doesn't need to be repeated elsewhere */
@media (prefers-color-scheme: light) {
:root {
- --post-bg: #fdf6f0;
+ --post-bg: #fff3f1;
--body-bg: #f5e6db;
--quote-bg: #e8d1c0;
--link-color: #af794d; /* c28656 */
--accent-a: #f2a76c;
- --accent-b: #6a8693;
+ --accent-b: #42697a;
--accent-c: #9e6b57;
--body-text: #4a332c;
--dark-text: #2c1e1a;
--light-text: #7d5b4e;
--code-text: #b3461c;
+
+ --today-text: #634439;
--filter-button-text: #2c1e1a;
--filter-button: #e8d1c0;
/* Basic styles */
body {
- font-family: "Meta Correspondence Pro", sans-serif;
+ font-family: "Atkinson Hyperlegible Next", "Meta Correspondence Pro", sans-serif;
background-color: var(--body-bg);
color: var(--body-text);
line-height: 1.6;
color: var(--light-text);
}
+.home-full-content {
+ max-width: 812px;
+}
+
code {
font-family: monospace;
background-color: var(--post-bg);
font-size: 0.8em;
}
+.add-whitespace {
+ height: 6em;
+ width: 1px;
+}
+
.bookmark-buttons {
margin-bottom: -5px;
padding-bottom: 3px;
.filter-buttons {
margin: 20px auto auto;
- text-align: center;
- max-width: 945px;
+ text-align: left;
+ max-width: 952px;
}
.filter-buttons .button {
.date-heading:first-of-type {
margin-top: 0em;
margin-bottom: 0.5em;
- font-weight: 500;
- color: var(--dark-text);
+ font-weight: 400;
+ color: var(--today-text);
}
.date-heading {
margin-top: 2.5em;
margin-bottom: 0.5em;
- font-weight: 500;
+ font-weight: 400;
color: var(--dark-text);
}
padding: 0.5em;
border-left: 2px solid var(--accent-a);
background: var(--post-bg);
- word-break: break-all;
+ word-break: initial;
+ margin-top: 0.8em !important;
transition:
border-color 0.2s,
background-color 0.2s;
.side-note a {
color: var(--light-text);
text-decoration: underline;
+ word-break: break-word;
font-size: 0.9em;
}
border-left-color: var(--link-color);
background-color: var(--quote-bg);
}
+
+ .side-note p {
+ word-break: initial;
+ padding: 0; margin: 0;
+ }
/* Style reference numbers in main text */
.main-content p a[rel="nofollow"] {
before_action :set_promo, only: [:index, :post]
def index
- @per_page = 15
+ @per_page = 3
@page = params[:page].to_i || 1
@filter = params[:filter] || 'all'
renderer = Redcarpet::Render::HTML.new(options)
markdown = Redcarpet::Markdown.new(renderer, extensions)
- '<div class="post-grid">' +
- '<div class="main-content">' +
- markdown.render(text) +
- '</div>' +
- '<div class="side-notes"></div>' +
- '</div>'
+ markdown.render(text)
end
end
</div>
</div>
<br>
- <div class="field reg-form">
+ <!-- <div class="field reg-form">
<%= f.label :hide_ai_summaries do %>
<%= f.check_box :hide_ai_summaries %>
Hide AI-generated summaries
class: "form-select" %>
<p class="help-text">Please note, this is a separate email including both dispatches and bookmarks sent weekly. If you are opted-in receive dispatches via email and want to change this, you can do so <%= link_to "on this page", subscriptions_path %>.</p>
</div>
- <% end %>
+ <% end %> -->
<div class="reg-form">
<div class="field">
<div class="filter-buttons">
- <%= link_to "Dispatches", root_path(filter: 'posts'), class: "button #{@filter == 'posts' ? 'active' : ''}" %>
- <%= link_to "Bookmarks", root_path(filter: 'bookmarks'), class: "button #{@filter == 'bookmarks' ? 'active' : ''}" %>
- <%= link_to "Combined feed", root_path(filter: 'all'), class: "button #{@filter == 'all' ? 'active' : ''}" %>
+ <%= link_to "#{action_name == 'post' ? 'More dispatches' : 'Dispatches'}", root_path(filter: 'posts'), class: "button #{@filter == 'posts' ? 'active' : ''}" %>
+ <!-- <%= link_to "Bookmarks", root_path(filter: 'bookmarks'), class: "button #{@filter == 'bookmarks' ? 'active' : ''}" %>
+ <%= link_to "Combined feed", root_path(filter: 'all'), class: "button #{@filter == 'all' ? 'active' : ''}" %> -->
<%= link_to "About", public_page_path("about"), class: "button #{@page.is_a?(Page) && @page.slug == 'about' ? 'active' : ''}" %>
<% if !current_user %>
- <%= link_to "Join mind reader", join_path, class: "button" %>
+ <%= link_to "Join mind reader", join_path, class: "button #{action_name == 'join' ? 'active' : ''}" %>
<% else %>
<%= link_to "My membership", subscriptions_path, class: "button #{controller_name == 'subscriptions' ? 'active' : ''}" %>
<%= link_to "My emails", mailing_lists_path, class: "button #{controller_name == 'mailing_lists' ? 'active' : ''}" %>
<% content_for :title do %>Your mailing list membership<% end %>
<% content_for :form_content do %>
-<p>Below you can change your settings for receiving new dispatches as they are published. This is sent via a service called Buttondown, and you can manage your subscription below and via the settings at the bottom of emails sent by the service. Note, <%= link_to "members", subscriptions_path %> may also receive a digest version of mind reader which includes full-text dispatches and bookmarks via the opt-in on your <%= link_to "profile page", edit_user_registration_path %>.</p>
+<p>Below you can change your settings for receiving new dispatches as they are published. This is sent via a service called Buttondown, and you can manage your subscription below and via the settings at the bottom of emails sent by the service.</p>
<% if @buttondown_status == 'unactivated' %>
<p>You are not currently subscribed to our mailing list.</p>
<%= image_tag "icon-512.png", class: "logo", alt: "mind reader logo" %>
<% end %>
<h1 class="header">mind reader <% if @cbcom === true %><small id="valentine"><a href="https://aidan.cornelius-bell.com">click to return</a> <a href="/?from=clear" id="no-a-style">to cornelius-bell.com</a></small><% end %></h1>
- <p class="subheading"><small>An anti-capitalist blog of dispatches and links from Aidan Cornelius-Bell</small></p>
+ <p class="subheading"><small>An anti-capitalist blog of dispatches from Aidan Cornelius-Bell</small></p>
</div>
<%= render partial: "layouts/navigation_buttons" %>
<% @items.group_by { |item| item.respond_to?(:published_at) ? item.published_at&.to_date : item.created_at.to_date }.each do |date, items| %>
<li class="date-heading">
From <%= date&.strftime('%B %e, %Y') %>:
- <% if !current_user %>
- <em><%= @promo_strings.sample %> <%= link_to "Start right now", join_path %>.</em>
- <% end %>
</li>
<% items.each do |item| %>
<li class="<%= item.post_type %>-item<%= if item.short_dispatch? then " thought-item" end %>">
</span>
<span class="dash">—</span>
<span class="meta">posted <%= item.published_at.strftime('%d/%m/%y') %>, tagged as <%= raw item.format_tags %></span>
- <% if item.short_dispatch? %>
- <blockquote class="full-content">
- <%= raw item.rendered_content.html_safe %>
- </blockquote>
- <% else %>
- <blockquote class="excerpt"><%= item.generate_excerpt %>...</blockquote>
- <% end %>
+ <blockquote class="home-full-content full-content">
+ <%= raw item.rendered_content.html_safe %>
+ </blockquote>
<% else %>
<span class="post-link">
<%= link_to item.title, item.url, target: "_blank" %>
<p class="links">
<%= render partial: "layouts/home_post_links" %>
</p>
-</div>
+</div>
\ No newline at end of file
<% end %>
<% content_for :title, "Join mind reader" %>
<h1>Join mind reader</h1>
- <%= link_to "↼ Back home...", "#{root_path}" %>
+ <p class="subheading"><small>An anti-capitalist blog of dispatches from Aidan Cornelius-Bell</small></p>
</div>
+<%= render partial: "layouts/navigation_buttons" %>
+
<div class="post">
<div class="container">
<%= image_tag "growchart.svg", class: "porter" %>
<ul>
<li>☛ Direct dispatches: fresh analysis straight to your inbox.</li>
<li>☛ Community dialogue: exchange ideas and correspond directly about posts.</li>
- <li>☛ Weekly digests: curated collections of essential reading and resources.</li>
+ <!-- <li>☛ Weekly digests: curated collections of essential reading and resources.</li> -->
<li>☛ A place to support my work by becoming a financial member (free, annual and lifetime tiers — pay what you want).</li>
</ul>
<p>There’s no paywall here – just like there shouldn’t be barriers to critical thought. Joining takes two minutes and keeps this independent platform for radical analysis going strong. So, what are you waiting for? Let’s be happy together!</p>
<p>As a current member (thank you!) you can adjust your settings on these pages:</p>
<p><% if current_user&.paid_user? %><%= link_to "Financial supporter settings", subscriptions_path, class: "button" %><% else %><%= link_to "Become a financial supporter", subscriptions_path, class: "button" %><% end %> <%= link_to "Join the mailing list", mailing_lists_path, class: "button" %></p>
<% else %>
- <%= image_tag "aidan_arrow.svg", class: "aidans_arrow" %>
+ <%= image_tag "aidan_arrow.svg", class: "aidans_arrow" %>
<p>To get started with mind reader membership – <em>always free</em> – simply register for an account on this website and confirm your email. Don’t worry, I’ll guide you through each step of the process! Just Get started below:</p>
<p><%= link_to "Get started, free forever", new_user_registration_path, class: "button" %> <%= link_to "Log back in", new_user_session_path, class: "button" %></p>
<% end %>
<% if @post.post_type != "bookmark" %>
<h3 class="post_siteheading">Aidan Cornelius-Bell’s mind reader:</h3>
<h1 class="post_title"><%= @post.title %></h1>
- <div class="bookmark-buttons">
+ <%= render partial: "layouts/navigation_buttons" %>
+ <!-- <div class="bookmark-buttons">
<%= link_to "↼ More dispatches 👀", "#{root_path}?filter=posts", class: "button button-bottomless" %>
<% if !current_user %>
<%= link_to "Join mind reader 🫰🏽", join_path, class: "button button-bottomless" %>
<% end %>
- </div>
+ </div> -->
<div class="postmeta">
<p ><% if
[email protected]_dispatch?%>About a <%= @reading_time %> minute read, p<% else %>P<% end %>osted <%= @post.published_at.strftime('%B %d, %Y') %> and tagged <%= raw @post.format_tags %></p>
</div>
<% else %>
<h3 class="post_siteheading">Aidan Cornelius-Bell’s mind reader:</h3>
<h1 class="post_title"><%= @post.title %></h1>
- <div class="bookmark-buttons">
+ <!-- <div class="bookmark-buttons">
<%= link_to "👁 View linked content", @post.url, class: "button button-bottomless", target: "_BLANK" %>
<%= link_to "🗞 More from mind reader", "#{root_path}", class: "button button-bottomless" %>
- </div>
+ </div> -->
<div class="postmeta">
<p>A link to <strong>third party</strong> content property of its owner(s) first shared with commentary on mind reader on <%= @post.published_at.strftime('%B %d, %Y') %>.</p>
</div>
<% if @post.audio.present? && @post.audio.file.attached? %>
<%= render partial: "shared/audio_player", locals: { audio: @post.audio } %>
<% end %>
-
- <%= raw @rendered_content %>
+
+ <div class="post-grid">
+ <div class="main-content">
+ <%= raw @rendered_content %>
+ </div>
+ <div class="side-notes"></div>
+ </div>
+
+ <% if @post.bookmark? %>
+ <div class="add-whitespace"></div>
+ <style>.side-note {margin-top: -1.5em !important;}</style>
+ <% end %>
<% if @post.bookmark? && @post.summary? %>
<br><br>
<div class="container" id="bottom">
<%= render partial: "layouts/home_post_links" %>
</div>
-<% else %>
-<style>
-.side-note {
- margin-top: -4em !important;
-}
-</style>
<% end %>
-<script>
-document.addEventListener('DOMContentLoaded', function() {
- if (window.innerWidth >= 1024) {
- const mainContent = document.querySelector('.main-content');
- const sideNotes = document.querySelector('.side-notes');
-
- if (mainContent && sideNotes) {
- // Find all the original footnotes first and store them
- const footnotes = new Map();
- const footnoteParagraphs = Array.from(mainContent.querySelectorAll('p'))
- .filter(p => /^\[\d+\]/.test(p.textContent));
-
- // Store footnotes and hide originals
- footnoteParagraphs.forEach(p => {
- const num = p.textContent.match(/^\[(\d+)\]/)[1];
- footnotes.set(num, p.innerHTML);
- p.style.display = 'none';
- });
-
- // Find and process reference markers in the main text
- const paragraphs = Array.from(mainContent.querySelectorAll('p'))
- .filter(p => !footnoteParagraphs.includes(p)); // Exclude footnote paragraphs
-
- let lastBottom = 0;
- const padding = 30; // Increased padding between notes
-
- paragraphs.forEach(p => {
- const markers = Array.from(p.textContent.matchAll(/\[(\d+)\]/g));
- markers.forEach(match => {
- const num = match[1];
- if (footnotes.has(num)) {
- const noteContent = footnotes.get(num);
- const noteDiv = document.createElement('div');
- noteDiv.className = 'side-note';
- noteDiv.innerHTML = noteContent;
- sideNotes.appendChild(noteDiv);
-
- // Get the position of the reference
- const text = p.textContent;
- const referencePosition = text.indexOf(`[${num}]`);
- const textBefore = text.substring(0, referencePosition);
- const tempSpan = document.createElement('span');
- tempSpan.textContent = textBefore;
- p.insertBefore(tempSpan, p.firstChild);
- const spanRect = tempSpan.getBoundingClientRect();
- p.removeChild(tempSpan);
-
- // Calculate position
- const pRect = p.getBoundingClientRect();
- const containerRect = mainContent.getBoundingClientRect();
- let proposedTop = pRect.top - containerRect.top +
- (referencePosition > 0 ? spanRect.height : 0);
-
- // Ensure no overlap
- proposedTop = Math.max(proposedTop, lastBottom + padding);
-
- noteDiv.style.top = `${proposedTop}px`;
-
- // Update lastBottom after the note is rendered
- const noteRect = noteDiv.getBoundingClientRect();
- lastBottom = proposedTop + noteRect.height;
- }
- });
- });
- }
- }
-});
-</script>
+<%= render partial: "scripts/side_notes" %>
+<style>.filter-buttons{margin-left: -6px; margin-right: 0;}</style>
<% end %>
<% content_for :title, "#{@page.title} :: mind reader" %>
<h1><%= @page.title.downcase %></h1>
- <p class="subheading"><small>Aidan’s anti-capitalist posting and sharing project</small></p>
+ <p class="subheading"><small>An anti-capitalist blog of dispatches from Aidan Cornelius-Bell</small></p>
</div>
<%= render partial: "layouts/navigation_buttons" %>
--- /dev/null
+ <script>
+ document.addEventListener('DOMContentLoaded', function() {
+ if (window.innerWidth >= 1024) {
+ const mainContent = document.querySelector('.main-content');
+ const sideNotes = document.querySelector('.side-notes');
+
+ if (mainContent && sideNotes) {
+ // Find all the original footnotes first and store them
+ const footnotes = new Map();
+ const footnoteParagraphs = Array.from(mainContent.querySelectorAll('p'))
+ .filter(p => /^\[\d+\]/.test(p.textContent));
+
+ // Store footnotes and hide originals
+ footnoteParagraphs.forEach(p => {
+ const num = p.textContent.match(/^\[(\d+)\]/)[1];
+ footnotes.set(num, p.innerHTML);
+ p.style.display = 'none';
+ });
+
+ // Find and process reference markers in the main text
+ const paragraphs = Array.from(mainContent.querySelectorAll('p'))
+ .filter(p => !footnoteParagraphs.includes(p)); // Exclude footnote paragraphs
+
+ let lastBottom = 0;
+ const padding = 30; // Increased padding between notes
+
+ paragraphs.forEach(p => {
+ const markers = Array.from(p.textContent.matchAll(/\[(\d+)\]/g));
+ markers.forEach(match => {
+ const num = match[1];
+ if (footnotes.has(num)) {
+ const noteContent = footnotes.get(num);
+ const noteDiv = document.createElement('div');
+ noteDiv.className = 'side-note';
+ noteDiv.innerHTML = '<p class="side-note-text">' + noteContent + '</p>';
+ sideNotes.appendChild(noteDiv);
+
+ // Get the position of the reference
+ const text = p.textContent;
+ const referencePosition = text.indexOf(`[${num}]`);
+ const textBefore = text.substring(0, referencePosition);
+ const tempSpan = document.createElement('span');
+ tempSpan.textContent = textBefore;
+ p.insertBefore(tempSpan, p.firstChild);
+ const spanRect = tempSpan.getBoundingClientRect();
+ p.removeChild(tempSpan);
+
+ // Calculate position
+ const pRect = p.getBoundingClientRect();
+ const containerRect = mainContent.getBoundingClientRect();
+ let proposedTop = pRect.top - containerRect.top +
+ (referencePosition > 0 ? spanRect.height : 0);
+
+ // Ensure no overlap
+ proposedTop = Math.max(proposedTop, lastBottom + padding);
+
+ noteDiv.style.top = `${proposedTop}px`;
+
+ // Update lastBottom after the note is rendered
+ const noteRect = noteDiv.getBoundingClientRect();
+ lastBottom = proposedTop + noteRect.height;
+ }
+ });
+ });
+ }
+ }
+ });
+ </script>
\ No newline at end of file
# Do jobs
config.active_job.queue_adapter = :async
+
+ config.assets.compress = false
+ config.assets.debug = true
+
# Enable/disable caching. By default caching is disabled.
# Run rails dev:cache to toggle caching.