Skip to content

Fix mobile layout for mods table and pagination#6

Merged
AgentKush merged 30 commits into
mainfrom
fix/mobile-mods-layout
Apr 5, 2026
Merged

Fix mobile layout for mods table and pagination#6
AgentKush merged 30 commits into
mainfrom
fix/mobile-mods-layout

Conversation

@AgentKush
Copy link
Copy Markdown
Owner

Summary

  • Table: On mobile (< 640px), the Name column was pinched to ~20% width (w-1/5) even though Author/Version/Week/Description columns are hidden. Now it fills available space on mobile. Download button shows only the file type (PAK/EXMODZ) on small screens to save space.
  • Pagination: The "Showing X to Y" text and page controls were side-by-side on all screens, causing the page numbers to get cut off on mobile. Now stacks vertically on small screens, and Prev/Next labels are hidden (chevrons only) to keep it compact.

Changes

  • _mods.html.erb — Make Name column width responsive (sm:w-1/5 instead of w-1/5), Download column auto-width on mobile
  • _mod.html.erb — Hide "Download" label text on mobile (hidden sm:inline), reduce cell padding
  • _pagination.html.erb — Stack layout vertically on mobile (flex-col sm:flex-row), flex-wrap page numbers, compact padding

Test plan

  • Verify mods table renders correctly on mobile (~375px wide)
  • Verify table still looks correct on desktop with all columns
  • Verify pagination wraps cleanly and all page numbers are accessible
  • Run existing view specs

🤖 Generated with Claude Code

AgentKush and others added 30 commits April 4, 2026 18:10
The Gemfile specifies ruby "3.4.9" but .ruby-version was still
3.4.8, causing CI to install the wrong Ruby version and fail with
"Your Ruby version is 3.4.8, but your Gemfile specified 3.4.9".

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The CSP initializer was entirely commented out, leaving the application
with no Content-Security-Policy headers. This is a security risk as it
allows unrestricted loading of scripts, styles, and other resources.

Configure CSP with sensible defaults for this app's needs:
- default_src/script_src/connect_src: self + https
- img_src/font_src: self + https + data URIs
- object_src: none (blocks Flash/Java embeds)
- style_src includes unsafe_inline for Tailwind compatibility
- Nonce-based script protection via importmap

Starts in report-only mode so violations are logged without breaking
the site. Once verified in production, report_only can be removed.

Fixes the medium-severity CSP bug reported in DonovanMods#76.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tool#filename calls url.split("/") without checking if url is nil.
When a Firestore document lacks a fileURL field, url is nil and
calling .split on it raises NoMethodError, crashing the tools
index page.

Add a nil guard that returns nil early when url is absent, and
use URI parsing for consistency with how Mod#filename handles URLs.

Fixes the medium-severity Tool#filename crash reported in DonovanMods#76.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The author_slug method in the Displayable concern calls
author.parameterize without checking if author is nil. If a single
Firestore document lacks an author field, it raises NoMethodError
and can crash the entire mods or tools index page since author_slug
is called during rendering of every record.

Return "unknown" as a safe fallback slug when author is nil, keeping
the page rendering and producing a valid URL segment.

Fixes the medium-severity author_slug crash reported in DonovanMods#76.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use string splitting instead of URI() to avoid URI::InvalidURIError
on malformed Firestore URLs, matching the safer approach already used
in Mod#filename (commit 2b66be7).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When switching Tailwind from darkMode: "media" to "class", the OS
preference is no longer automatically respected. This adds a
matchMedia check as fallback when no localStorage preference is
saved, preserving the original behavior for first-time visitors.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implements array-based pagination for the mods index when displaying
all mods. Search results and author-filtered views show all matches
without pagination, as requested in issue DonovanMods#55.

- Add PaginationHelper with page windowing and ellipsis support
- Add _pagination.html.erb partial with Prev/Next and page numbers
- Paginate at 20 mods per page (configurable via DEFAULT_PER_PAGE)
- Show "page X of Y" counter when paginated
- Styled with existing Tailwind classes for light/dark mode
Covers paginate_array edge cases (empty collection, page clamping,
custom per_page) and PaginationResult methods (first/last page,
navigation, page_range with ellipsis).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The .gitignore only had .env.* (matching .env.local, .env.production,
etc.) but not .env itself, so the base .env file was committed and
tracked. While it currently contains non-secret config (project IDs,
bucket names), tracking .env files is a security risk as developers
may add secrets to it later.

Changes:
- Add .env to .gitignore so it is no longer tracked
- Remove .env from git index (file stays on disk for existing devs)
- Add .env.example template so new developers know which vars to set
- Whitelist .env.example in .gitignore so the template is tracked

Fixes the low-severity .env tracking bug reported in DonovanMods#76.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The _mod.html.erb partial passes mod.author (which contains spaces
and mixed case) to mod_detail_path, producing ugly URLs with encoded
spaces like /mods/Donovan%20Young/some-mod.

Change to mod.author_slug which produces clean, parameterized URLs
like /mods/donovan-young/some-mod, consistent with how the show
action and author filtering already work.

Fixes the low-severity ugly URL bug reported in DonovanMods#76.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The search input debounce was set to 200ms, firing a Turbo Frame
request on nearly every keystroke. Increase to 400ms which still
feels responsive but significantly reduces unnecessary requests
and Firestore reads during active typing.

Fixes the low-severity search debounce bug reported in DonovanMods#76.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The markdown helper created new CodeRayify and Redcarpet::Markdown
instances on every call. On the mods index this means allocating
these objects once per mod with a README.

Extract into a memoized private method so objects are created once
per request and reused for all subsequent renders.

Fixes the low-severity markdown renderer performance bug in DonovanMods#76.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The require_master_key setting was commented out, meaning the app
can boot in production without a master key. Without it, Rails
credentials (including Firebase keyfile) can't be decrypted, causing
confusing Firestore errors instead of a clear startup failure.

Uncomment so production deploys fail fast if RAILS_MASTER_KEY is
missing. The Kamal deploy config already provides this as a secret.

Fixes the low-severity require_master_key bug reported in DonovanMods#76.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The firestore class method creates a new Google::Cloud::Firestore
client on every call. Memoize with @FireStore ||= so the client is
created once per class and reused. The Google Cloud Firestore client
is designed to be long-lived and thread-safe.

Fixes the low-severity Firestore client performance bug in DonovanMods#76.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Reset class-level @FireStore instance variable after each test
to prevent RSpec doubles from persisting via Firestorable's
@FireStore ||= memoization pattern. This fixes 8 test failures
where leaked doubles caused errors in subsequent examples.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Use .positive? instead of > 0
- Break long line in PaginationResult.new
- Use described_class instead of explicit class names in specs
- Fix context descriptions to start with when/with/without

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The template now uses mod.author_slug instead of mod.author for
cleaner URLs, so the test expectation needs to match.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
These are local MCP plugin directories that shouldn't be tracked.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replaces inline toggleTheme() and DOMContentLoaded listener with a
Stimulus theme_controller.js. Uses data-action and data-target
attributes instead of global functions and getElementById, preventing
potential conflicts with other scripts.

The early-load FOUC prevention script in <head> is kept since Stimulus
connects after DOMContentLoaded.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Merge nested conditional into outer if to satisfy RuboCop
Style/SoleNestedConditional rule.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extract filter_by_author, filter_by_query, paginate_mods, and
render_index private methods to bring ABC size and method length
below RuboCop thresholds.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace `return unless params[:x].present?` with `return if
params[:x].blank?` per Rails/Blank cop preference.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…smatch

Fix .ruby-version to match Gemfile ruby requirement (3.4.9)
…nil-guards-and-csp

Fix/medium severity nil guards and csp
…anup-and-performance

Fix: Code clean-up, security hardening, and performance improvements
Add dark/light theme toggle to header nav
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Table:
- Remove fixed w-1/5 on Name column at mobile breakpoint so it fills
  available space when Author/Version/Description are hidden
- Set Download column to w-auto on mobile, w-28 at sm+
- Show only file type (PAK/EXMODZ) on mobile, full "Download PAK" at sm+
- Reduce cell padding on mobile (p-2 -> sm:p-3)
- Add whitespace-nowrap on download cell to prevent button wrapping

Pagination:
- Stack "Showing X to Y" and page controls vertically on mobile,
  side-by-side at sm+ (flex-col -> sm:flex-row)
- Use flex-wrap + justify-center on page numbers so they wrap cleanly
- Hide "Prev"/"Next" labels on mobile, keep chevrons only
- Reduce padding on page number buttons for small screens

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@AgentKush AgentKush merged commit 5c9c2d3 into main Apr 5, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants