Merge branch 'main' into public-description-lists

* main: (21 commits)
  Ruby 3.4.7 (#1274)
  bin/setup: enpurple step headings
  bin/setup: ensure PATH is prepared after a new Ruby is installed
  Drop defunct bin/bundle that interferes with Bundler's own version management
  bin/setup: use Bash so we can bootstrap Ruby (#1273)
  Don't try to wrap with a download link, every file already has one in the caption
  bundle lock --normalize-platforms
  Show column title on public collections
  Docker: explicit libssl dep
  Ruby 3.4.6 (#1272)
  Check the selected color
  Style
  Add column color to cache keys
  Make the edit form the same dimensions as the add form
  Don't let the column button effect apply to buttons in the picker
  Set and change column colors
  Prom metrics in dev (#1269)
  Cleanup prototyping
  Properly scroll and center content
  Sorta working x-scroll
  ...
This commit is contained in:
Andy Smith
2025-10-09 16:08:16 -05:00
17 changed files with 155 additions and 195 deletions
+2
View File
@@ -0,0 +1,2 @@
[env]
PROMETHEUS_EXPORTER_URL = "http://127.0.0.1:9306/metrics"
+1 -1
View File
@@ -1 +1 @@
3.4.5
3.4.7
+3 -3
View File
@@ -1,7 +1,7 @@
# syntax = docker/dockerfile:1
# Make sure RUBY_VERSION matches the Ruby version in .ruby-version and Gemfile
ARG RUBY_VERSION=3.4.5
# Make sure RUBY_VERSION matches the Ruby version in .ruby-version
ARG RUBY_VERSION=3.4.7
FROM registry.docker.com/library/ruby:$RUBY_VERSION-slim AS base
# Rails app lives here
@@ -19,7 +19,7 @@ FROM base AS build
# Install packages needed to build gems
RUN apt-get update -qq && \
apt-get install -y --no-install-recommends -y build-essential pkg-config git libvips libyaml-dev && \
apt-get install -y --no-install-recommends -y build-essential pkg-config git libvips libyaml-dev libssl-dev && \
rm -rf /var/lib/apt/lists /var/cache/apt/archives
# Install application gems
+43 -30
View File
@@ -171,8 +171,8 @@ GEM
ruby-next-core (~> 1.0)
ast (2.4.3)
aws-eventstream (1.4.0)
aws-partitions (1.1159.0)
aws-sdk-core (3.232.0)
aws-partitions (1.1170.0)
aws-sdk-core (3.233.0)
aws-eventstream (~> 1, >= 1.3.0)
aws-partitions (~> 1, >= 1.992.0)
aws-sigv4 (~> 1.9)
@@ -180,10 +180,10 @@ GEM
bigdecimal
jmespath (~> 1, >= 1.6.1)
logger
aws-sdk-kms (1.112.0)
aws-sdk-kms (1.113.0)
aws-sdk-core (~> 3, >= 3.231.0)
aws-sigv4 (~> 1.5)
aws-sdk-s3 (1.199.0)
aws-sdk-s3 (1.199.1)
aws-sdk-core (~> 3, >= 3.231.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.5)
@@ -193,7 +193,7 @@ GEM
bcrypt (3.1.20)
bcrypt_pbkdf (1.1.1)
benchmark (0.4.1)
bigdecimal (3.2.3)
bigdecimal (3.3.0)
bootsnap (1.18.6)
msgpack (~> 1.2)
brakeman (7.1.0)
@@ -228,12 +228,12 @@ GEM
drb (2.2.3)
dry-initializer (3.2.0)
ed25519 (1.4.0)
erb (5.0.2)
erb (5.0.3)
erubi (1.13.1)
et-orbi (1.3.0)
et-orbi (1.4.0)
tzinfo
event_stream_parser (1.0.0)
faraday (2.13.4)
faraday (2.14.0)
faraday-net_http (>= 2.0, < 3.5)
json
logger
@@ -247,13 +247,14 @@ GEM
ffi (1.17.2-arm64-darwin)
ffi (1.17.2-x86_64-darwin)
ffi (1.17.2-x86_64-linux-gnu)
ffi (1.17.2-x86_64-linux-musl)
fugit (1.11.2)
et-orbi (~> 1, >= 1.2.11)
raabro (~> 1.4)
geared_pagination (1.2.0)
activesupport (>= 5.0)
addressable (>= 2.5.0)
globalid (1.2.1)
globalid (1.3.0)
activesupport (>= 6.1)
hashdiff (1.2.1)
i18n (1.14.7)
@@ -274,7 +275,7 @@ GEM
actionview (>= 7.0.0)
activesupport (>= 7.0.0)
jmespath (1.6.2)
json (2.13.2)
json (2.15.1)
jwt (3.1.2)
base64
kamal (2.7.0)
@@ -307,13 +308,13 @@ GEM
net-imap
net-pop
net-smtp
marcel (1.0.4)
marcel (1.1.0)
matrix (0.4.3)
mini_magick (5.3.1)
logger
mini_mime (1.1.5)
mini_portile2 (2.8.9)
minitest (5.25.5)
minitest (5.26.0)
mission_control-jobs (1.1.0)
actioncable (>= 7.1)
actionpack (>= 7.1)
@@ -332,7 +333,7 @@ GEM
uri
net-http-persistent (4.0.6)
connection_pool (~> 2.2, >= 2.2.4)
net-imap (0.5.10)
net-imap (0.5.12)
date
net-protocol
net-pop (0.1.2)
@@ -356,7 +357,9 @@ GEM
racc (~> 1.4)
nokogiri (1.18.10-x86_64-linux-gnu)
racc (~> 1.4)
openssl (3.3.0)
nokogiri (1.18.10-x86_64-linux-musl)
racc (~> 1.4)
openssl (3.3.1)
ostruct (0.6.3)
parallel (1.27.0)
parser (3.3.9.0)
@@ -365,10 +368,10 @@ GEM
platform_agent (1.0.1)
activesupport (>= 5.2.0)
useragent (~> 0.16.3)
pp (0.6.2)
pp (0.6.3)
prettyprint
prettyprint (0.2.0)
prism (1.5.1)
prism (1.4.0)
prometheus-client-mmap (1.2.10)
base64
bigdecimal
@@ -389,6 +392,11 @@ GEM
bigdecimal
logger
rb_sys (~> 0.9.109)
prometheus-client-mmap (1.2.10-x86_64-linux-musl)
base64
bigdecimal
logger
rb_sys (~> 0.9.109)
propshaft (1.3.1)
actionpack (>= 7.0.0)
activesupport (>= 7.0.0)
@@ -397,11 +405,11 @@ GEM
date
stringio
public_suffix (6.0.2)
puma (7.0.3)
puma (7.0.4)
nio4r (~> 2.0)
raabro (1.4.0)
racc (1.8.1)
rack (3.2.1)
rack (3.2.2)
rack-session (2.1.1)
base64 (>= 0.1.0)
rack (>= 3.0.0)
@@ -421,20 +429,21 @@ GEM
rake-compiler-dock (1.9.1)
rb_sys (0.9.117)
rake-compiler-dock (= 1.9.1)
rdoc (6.14.2)
rdoc (6.15.0)
erb
psych (>= 4.0.0)
tsort
redcarpet (3.6.1)
regexp_parser (2.11.3)
reline (0.6.2)
io-console (~> 0.5)
rexml (3.4.4)
rouge (4.6.0)
rouge (4.6.1)
rqrcode (3.1.0)
chunky_png (~> 1.0)
rqrcode_core (~> 2.0)
rqrcode_core (2.0.0)
rubocop (1.80.2)
rubocop (1.81.1)
json (~> 2.3)
language_server-protocol (~> 3.17.0.2)
lint_roller (~> 1.1.0)
@@ -442,17 +451,17 @@ GEM
parser (>= 3.3.0.2)
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 2.9.3, < 3.0)
rubocop-ast (>= 1.46.0, < 2.0)
rubocop-ast (>= 1.47.1, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 4.0)
rubocop-ast (1.46.0)
rubocop-ast (1.47.1)
parser (>= 3.3.7.2)
prism (~> 1.4)
rubocop-performance (1.26.0)
lint_roller (~> 1.1)
rubocop (>= 1.75.0, < 2.0)
rubocop-ast (>= 1.44.0, < 2.0)
rubocop-rails (2.33.3)
rubocop-rails (2.33.4)
activesupport (>= 4.2.0)
lint_roller (~> 1.1)
rack (>= 1.1)
@@ -468,7 +477,7 @@ GEM
ffi (~> 1.12)
logger
ruby2_keywords (0.0.5)
rubyzip (3.1.0)
rubyzip (3.1.1)
securerandom (0.4.1)
selenium-webdriver (4.35.0)
base64 (~> 0.2)
@@ -476,10 +485,10 @@ GEM
rexml (~> 3.2, >= 3.2.5)
rubyzip (>= 1.2.2, < 4.0)
websocket (~> 1.0)
sentry-rails (5.27.0)
sentry-rails (5.28.0)
railties (>= 5.0)
sentry-ruby (~> 5.27.0)
sentry-ruby (5.27.0)
sentry-ruby (~> 5.28.0)
sentry-ruby (5.28.0)
bigdecimal
concurrent-ruby (~> 1.0, >= 1.0.2)
sniffer (0.5.0)
@@ -511,6 +520,7 @@ GEM
sqlite3 (2.7.4-arm64-darwin)
sqlite3 (2.7.4-x86_64-darwin)
sqlite3 (2.7.4-x86_64-linux-gnu)
sqlite3 (2.7.4-x86_64-linux-musl)
sshkit (1.24.0)
base64
logger
@@ -531,6 +541,7 @@ GEM
tiktoken_ruby (0.0.12-arm64-darwin)
tiktoken_ruby (0.0.12-x86_64-darwin)
tiktoken_ruby (0.0.12-x86_64-linux)
tiktoken_ruby (0.0.12-x86_64-linux-musl)
timeout (0.4.3)
tsort (0.2.0)
turbo-rails (2.0.17)
@@ -572,7 +583,7 @@ GEM
yabeda-prometheus-mmap (0.4.0)
prometheus-client-mmap
yabeda (~> 0.10)
yabeda-puma-plugin (0.7.1)
yabeda-puma-plugin (0.8.0)
json
puma
yabeda (~> 0.5)
@@ -588,6 +599,8 @@ PLATFORMS
arm64-linux
x86_64-darwin
x86_64-linux
x86_64-linux-gnu
x86_64-linux-musl
x86_64-mingw32
DEPENDENCIES
@@ -648,4 +661,4 @@ DEPENDENCIES
yabeda-rails
BUNDLED WITH
2.7.0
2.7.2
+10 -10
View File
@@ -2,7 +2,7 @@
/* Column container
/* ------------------------------------------------------------------------ */
.card-columns {
#main:has(.card-columns) {
--bubble-size: 3.5rem;
--cards-gap: min(1.2cqi, 1.7rem);
--column-gap: 8px;
@@ -13,18 +13,22 @@
--progress-max-cards: 20;
--progress-max-height: 50dvh;
align-items: stretch;
padding-inline: var(--column-gap);
}
.card-columns {
container-type: inline-size;
display: grid;
gap: var(--column-gap);
grid-template-columns: 1fr var(--column-width-expanded) 1fr;
justify-content: center;
margin: var(--cards-gap) auto 0;
margin-inline: auto;
max-inline-size: var(--main-width);
overflow-x: auto;
position: relative;
/* When it has something expanded */
&:has(.card-columns__left .cards:not(.is-collapsed), .card-columns__right .cards:not(.is-collapsed)) {
grid-template-columns: auto auto auto;
grid-template-columns: auto var(--column-width-expanded) auto;
}
&:has(.cards) {
@@ -209,7 +213,7 @@
@media (any-hover: hover) {
.btn--circle,
~ .btn--circle,
~ .cards__expander-menu .btn--circle {
~ .cards__expander-menu .btn--circle:not(label) {
transition: opacity 300ms ease-in-out;
opacity: 0;
@@ -277,10 +281,6 @@
inline-size: var(--column-width-collapsed);
}
.panel {
--panel-padding: 1ch;
}
.cards.is-collapsed & {
display: none;
}
@@ -26,6 +26,6 @@ class Collections::ColumnsController < ApplicationController
end
def column_params
params.require(:column).permit(:name)
params.require(:column).permit(:name, :color)
end
end
+1 -1
View File
@@ -35,6 +35,6 @@ module CardsHelper
end
def cacheable_preview_parts_for(card, *options)
[ card, card.collection, card.collection.entropy_configuration, card.collection.publication, *options ]
[ card, card.collection, card.collection.entropy_configuration, card.collection.publication, card.column&.color, *options ]
end
end
+1 -1
View File
@@ -21,7 +21,7 @@ module Card::Cacheable
end
def for_preview(*other)
[ card, card.collection.entropy_configuration, card.collection.publication, *other ]
[ card, card.collection.entropy_configuration, card.collection.publication, card.column&.color, *other ]
end
end
end
@@ -18,9 +18,7 @@
</figure>
<% else %>
<div class="attachment attachment--file attachment--<%= blob.filename.extension -%>">
<%= link_to rails_blob_path(blob, disposition: :attachment), class: "attachment__figure", download: blob.filename, title: "Download #{blob.filename}" do %>
<%= render "active_storage/blobs/web/representation", blob: blob %>
<% end %>
<%= render "active_storage/blobs/web/representation", blob: blob %>
<div class="attachment__caption">
<div>
+1 -1
View File
@@ -1,4 +1,4 @@
json.cache! card do
json.cache! [ card, card.column&.color ] do
json.(card, :id, :title, :status)
json.image_url card.image.presence && url_for(card.image)
@@ -8,7 +8,6 @@
dragenter->drag-and-strum#dragEnter
drop->drag-and-drop#drop
dragend->drag-and-drop#dragEnd" } do %>
<div class="card-columns" data-controller="collapsible-columns" data-collapsible-columns-collapsed-class="is-collapsed">
<div class="card-columns__left">
<%= render "collections/show/not_now", collection: collection %>
@@ -7,8 +7,8 @@
<ul class="popup__list">
<li class="popup__item">
<button class="popup__btn btn" data-action="click->clicker#click">
<%= icon_tag "rename" %>
<span>Rename column</span>
<%= icon_tag "pencil" %>
<span>Edit column</span>
</button>
</li>
<li class="popup__item">
@@ -25,9 +25,9 @@
</dialog>
<div data-controller="dialog" data-action="keydown.esc->dialog#close click@document->dialog#closeOnClickOutside">
<button id="<%= dom_id(column, :manage_menu) %>" class="popup__btn btn" data-action="click->dialog#open:stop" data-clicker-target="clickable" hidden aria-hidden>Rename column</button>
<button id="<%= dom_id(column, :manage_menu) %>" class="popup__btn btn" data-action="click->dialog#open:stop" data-clicker-target="clickable" hidden aria-hidden>Save changes</button>
<dialog class="popup panel flex-column gap-half fill-white shadow txt-small margin-block-double" data-dialog-target="dialog">
<%= render "collections/show/menu/column_form", collection: column.collection, column: column, label: "Rename column" %>
<%= render "collections/show/menu/column_form", collection: column.collection, column: column, label: "Save changes" %>
</dialog>
</div>
</div>
@@ -1,5 +1,15 @@
<%= form_with model: [collection, column], data: { controller: "form", action: "turbo:submit-end->dialog#close turbo:submit-end->form#reset" } do |form| %>
<%= form.text_field :name, class: "input", placeholder: "Column name", value: column.name,
required: true, autocomplete: "off", pattern: ".*\\S.*", title: "Column name cannot be blank" %>
<div class="flex flex-wrap gap-half justify-space-between margin-block-half">
<% Card::COLORS.each do |color| %>
<label class="btn btn--circle txt-small borderless" style="--btn-background: <%= color %>" title="<%= color %>">
<%= form.radio_button :color, color, checked: (column.color == color) %>
<%= icon_tag "check", class: "checked" %>
<span class="for-screen-reader"><%= color %></span>
</label>
<% end %>
</div>
<%= form.submit label, class: "btn btn--link" %>
<% end %>
@@ -2,6 +2,10 @@
data-action="turbo:before-morph-attribute->collapsible-columns#preventToggle">
<div class="cards__decoration"></div>
<header class="cards__expander">
<h2 class="cards__expander-title">The Stream</h2>
</header>
<% if page.used? %>
<%= with_automatic_pagination "the-stream", @page do %>
<%= render "public/collections/columns/list", cards: page.records %>
-113
View File
@@ -1,113 +0,0 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
#
# This file was generated by Bundler.
#
# The application 'bundle' is installed as part of a gem, and
# this file is here to facilitate running it.
#
require 'rubygems'
m = Module.new do
module_function
def invoked_as_script?
File.expand_path($0) == File.expand_path(__FILE__)
end
def env_var_version
ENV['BUNDLER_VERSION']
end
def cli_arg_version
return unless invoked_as_script? # don't want to hijack other binstubs
return unless 'update'.start_with?(ARGV.first || ' ') # must be running `bundle update`
bundler_version = nil
update_index = nil
ARGV.each_with_index do |a, i|
bundler_version = a if update_index && update_index.succ == i && a.match?(Gem::Version::ANCHORED_VERSION_PATTERN)
next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/
bundler_version = Regexp.last_match(1)
update_index = i
end
bundler_version
end
def gemfile
gemfile = ENV['BUNDLE_GEMFILE']
return gemfile if gemfile && !gemfile.empty?
File.expand_path('../Gemfile', __dir__)
end
def lockfile
lockfile =
case File.basename(gemfile)
when 'gems.rb' then gemfile.sub(/\.rb$/, '.locked')
else "#{gemfile}.lock"
end
File.expand_path(lockfile)
end
def lockfile_version
return unless File.file?(lockfile)
lockfile_contents = File.read(lockfile)
return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/
Regexp.last_match(1)
end
def bundler_requirement
@bundler_requirement ||=
env_var_version ||
cli_arg_version ||
bundler_requirement_for(lockfile_version)
end
def bundler_requirement_for(version)
return "#{Gem::Requirement.default}.a" unless version
bundler_gem_version = Gem::Version.new(version)
bundler_gem_version.approximate_recommendation
end
def load_bundler!
ENV['BUNDLE_GEMFILE'] ||= gemfile
activate_bundler
end
def activate_bundler
gem_error = activation_error_handling do
gem 'bundler', bundler_requirement
end
return if gem_error.nil?
require_error = activation_error_handling do
require 'bundler/version'
end
if require_error.nil? && Gem::Requirement.new(bundler_requirement).satisfied_by?(Gem::Version.new(Bundler::VERSION))
return
end
warn "Activating bundler (#{bundler_requirement}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_requirement}'`"
exit 42
end
def activation_error_handling
yield
nil
rescue StandardError, LoadError => e
e
end
end
m.load_bundler!
load Gem.bin_path('bundler', 'bundle') if m.invoked_as_script?
+67 -20
View File
@@ -1,26 +1,73 @@
#!/usr/bin/env ruby
#!/usr/bin/env bash
set -eo pipefail
require "mkmf"
def system!(*args) system(*args, exception: true) end
# Prefer app executables
app_root="$(cd "$(dirname "$0")/.."; pwd)"
export PATH="$app_root/bin:$PATH"
puts "\n== Installing Ruby and Node =="
system("mise install -y")
# Install gum if needed
if ! command -v gum &> /dev/null; then
echo
echo "▸ Installing gum"
if command -v pacman &> /dev/null; then
sudo pacman -S --noconfirm gum
elif command -v brew &> /dev/null; then
brew install gum
else
echo "Please install gum: https://github.com/charmbracelet/gum"
exit 1
fi
echo
fi
puts "\n== Installing dependencies =="
if MakeMakefile.find_executable0("pacman")
packages = "imagemagick openslide"
system("pacman -Q #{packages} || sudo pacman -S --noconfirm --needed #{packages}")
end
system("gem install bundler --conservative")
system("bundle config set --local auto_install true")
system("bundle check") || system!("bundle install")
step() {
local no_spin=false
if [[ "$1" == "--no-spin" ]]; then
no_spin=true
shift
fi
puts "\n== Preparing database =="
if ARGV.include?("--reset")
system! "bin/rails db:drop db:create db:schema:load db:prepare db:seed"
local step_name="$1"
shift
gum style --foreground 135 --bold "▸ $step_name"
gum style --foreground 240 "$*"
if $no_spin; then
"$@"
else
gum spin --spinner dot --title "" --show-output -- "$@"
fi
local exit_code=$?
echo
return $exit_code
}
echo
gum style --foreground 153 " ˚ ∘ ∘ ˚ "
gum style --foreground 111 --bold " ∘˚˳°∘° 𝒻𝒾𝓏𝓏𝓎 °∘°˳˚∘ "
echo
step --no-spin "Installing Ruby" mise install --yes
eval "$(mise hook-env)"
if which pacman >/dev/null 2>&1; then
packages=(imagemagick openslide)
if ! pacman -Q "${packages[@]}" >/dev/null 2>&1; then
step "Installing packages" sudo pacman -S --noconfirm --needed "${packages[@]}"
fi
fi
bundle config set --local auto_install true
step "Installing RubyGems" bundle install
if [[ $* == *--reset* ]]; then
step "Resetting the database" rails db:reset
else
system! "bin/rails db:prepare"
end
step "Preparing the database" rails db:prepare
fi
puts "\n== Removing old logs and tempfiles =="
system! "bin/rails log:clear tmp:clear"
step "Cleaning up logs and tempfiles" rails log:clear tmp:clear
gum style --foreground 46 "✓ Done (${SECONDS} sec)"
+6 -6
View File
@@ -23,9 +23,9 @@ plugin :tmp_restart
# Run Solid Queue with Puma
plugin :solid_queue if ENV["SOLID_QUEUE_IN_PUMA"]
if ENV.fetch("PREFORK") { !Rails.env.local? }
# Expose prometheus metrics on port 9394
activate_control_app
plugin :yabeda
plugin :yabeda_prometheus
end
# Expose Prometheus metrics at http://0.0.0.0:9394/metrics.
# In dev, overridden to http://127.0.0.1:9306/metrics in .mise.toml.
control_uri = Rails.env.local? ? "unix://tmp/pumactl.sock" : "auto"
activate_control_app control_uri, no_token: true
plugin :yabeda
plugin :yabeda_prometheus