Improve commit and diff views, show real commit feed on profile
- Redesign commit diff view to group changes by file, show additions/deletions, and improve layout - Show real recent commits on user profile, not hardcoded list - Refactor syntax highlighting to highlight lines for better display - Update registration/login branding to "siGit Code & Deploy" - Update dev:seed_repo to always point to project .git for local dev - Add .smb/config.toml for project metadata - Set format: false on blob/raw routes to avoid .html extension issues
Seto Elkahfi committed
Apr 12, 2026 at 14:02 UTC
9e5813f06e6e3d6f2c07976731efb538bbb150dc
11 files changed
+191
-73
.smb/config.toml
+11
new file mode 100644
index 0000000..99e6f56
--- /dev/null
+++ b/.smb/config.toml
@@ -0,0 +1,11 @@
+name = "sigitsi"
+description = "sigitsi"
+
+[project]
+id = 56
+runner = 0
+name = "sigitsi"
+repository = "sigitsi"
+description = "siGit Si"
+created_at = "2025-06-28T22:43:02.646Z"
+updated_at = "2025-06-28T22:43:02.646Z"
app/controllers/blobs_controller.rb
+23
-5
index 8817e31..dab4fc8 100644
--- a/app/controllers/blobs_controller.rb
+++ b/app/controllers/blobs_controller.rb
@@ -21,7 +21,7 @@ class BlobsController < ApplicationController
@file_size = content.bytesize
unless @is_binary
- @highlighted = highlight_code(content, @filename)
+ @highlighted_lines = highlight_lines(content, @filename)
@line_count = content.lines.count
end
end
@@ -66,11 +66,29 @@ class BlobsController < ApplicationController
content.bytes.first(8192).include?(0)
end
- def highlight_code(content, filename)
+ def highlight_lines(content, filename)
lexer = Rouge::Lexer.guess(filename: filename, source: content)
- formatter = Rouge::Formatters::HTML.new
- formatter.format(lexer.lex(content)).html_safe
+ html_formatter = Rouge::Formatters::HTML.new
+
+ # Tokenize the entire file at once (preserves stateful lexer context across
+ # lines — e.g. multiline strings, heredocs) then split the token stream on
+ # newline boundaries to get one HTML fragment per source line.
+ lines = [[]]
+ lexer.lex(content).each do |token, value|
+ value.split(/(\n)/, -1).each do |part|
+ if part == "\n"
+ lines << []
+ else
+ lines.last << [token, part] unless part.empty?
+ end
+ end
+ end
+
+ # Drop a trailing empty entry when the file ends with a newline.
+ lines.pop if lines.last&.empty?
+
+ lines.map { |line_tokens| html_formatter.format(line_tokens).html_safe }
rescue StandardError
- Rouge::Formatters::HTML.new.format(Rouge::Lexers::PlainText.new.lex(content)).html_safe
+ content.lines.map { |l| ERB::Util.html_escape(l.chomp).html_safe }
end
end
app/controllers/users_controller.rb
+10
index d3d1204..8054724 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -8,6 +8,16 @@ class UsersController < ApplicationController
else
@profile_user.repositories.where(is_private: false).order(updated_at: :desc)
end
+
+ feed_repo = @repositories.find { |r| r.name == "sigit-si" } || @repositories.first
+ if feed_repo&.initialized?
+ @feed_repo = feed_repo
+ @feed_commits = GitRepositoryService.commits(
+ feed_repo.disk_path,
+ feed_repo.default_branch,
+ limit: 3
+ )
+ end
rescue ActiveRecord::RecordNotFound
render file: Rails.public_path.join("404.html"), status: :not_found, layout: false
end
app/services/git_repository_service.rb
+19
-13
index b8dbb1e..41e046b 100644
--- a/app/services/git_repository_service.rb
+++ b/app/services/git_repository_service.rb
@@ -82,26 +82,32 @@ class GitRepositoryService
end
def self.commit(path, sha)
- format = "%H%x00%h%x00%s%x00%B%x00%an%x00%ae%x00%ad%x00%T"
+ # %B contains embedded newlines, so we cannot rely on splitting by "\n" to
+ # find the end of the format output. Instead we append a sentinel that git
+ # will never emit naturally and split on that.
+ sentinel = "SIGIT-EOH"
+ format = "%H%x00%h%x00%s%x00%B%x00%an%x00%ae%x00%ad%x00%T%x00#{sentinel}"
out, _err, status = Open3.capture3(
"git", "--git-dir", path, "show",
- "--format=#{format}", "--stat", "--no-patch",
+ "--no-patch", "--format=#{format}",
sha
)
return nil unless status.success?
- lines = out.split("\n")
- parts = lines.first.to_s.split("\x00")
- return nil unless parts.length == 8
+
+ header_raw = out.split(sentinel, 2).first.to_s
+ parts = header_raw.split("\x00", 9) # 8 fields + possible trailing newline
+ return nil unless parts.length >= 8
+
diff_out, _e, _s = Open3.capture3("git", "--git-dir", path, "show", "--format=", sha)
{
- sha: parts[0],
- short_sha: parts[1],
- subject: parts[2],
- body: parts[3],
- author_name: parts[4],
- author_email: parts[5],
- authored_date: (Time.parse(parts[6]) rescue nil),
- diff: diff_out
+ sha: parts[0].strip,
+ short_sha: parts[1].strip,
+ subject: parts[2].strip,
+ body: parts[3],
+ author_name: parts[4].strip,
+ author_email: parts[5].strip,
+ authored_date: (Time.parse(parts[6].strip) rescue nil),
+ diff: diff_out
}
end
app/views/blobs/show.html.erb
+5
-5
index da0a3d1..55921e8 100644
--- a/app/views/blobs/show.html.erb
+++ b/app/views/blobs/show.html.erb
@@ -44,15 +44,15 @@
Binary file — <%= number_to_human_size(@file_size) %>
</div>
<% else %>
- <div class="code-container rounded-none border-0 relative">
+ <div class="code-container rounded-none border-0 relative overflow-x-auto">
<table class="w-full">
- <tbody>
- <% @raw_content.lines.each_with_index do |line, i| %>
+ <tbody class="highlight">
+ <% @highlighted_lines.each_with_index do |highlighted_line, i| %>
<tr class="hover:bg-brand-500/10">
- <td class="text-right text-gray-500 pr-4 pl-4 py-0 select-none w-10 text-xs border-r border-surface-600 align-top" id="L<%= i+1 %>">
+ <td class="text-right text-gray-500 pr-4 pl-4 py-0 select-none w-10 text-xs border-r border-surface-600 align-top font-mono" id="L<%= i+1 %>">
<a href="#L<%= i+1 %>" class="hover:text-brand-500"><%= i+1 %></a>
</td>
- <td class="pl-4 pr-4 py-0 font-mono text-xs leading-5 whitespace-pre-wrap break-all text-gray-200"><%= line %></td>
+ <td class="pl-4 pr-4 py-0 font-mono text-xs leading-5 whitespace-pre text-gray-200"><%= highlighted_line %></td>
</tr>
<% end %>
</tbody>
app/views/commits/show.html.erb
+87
-23
index a3a30dc..2e26922 100644
--- a/app/views/commits/show.html.erb
+++ b/app/views/commits/show.html.erb
@@ -2,43 +2,107 @@
<div class="border-b border-surface-600 bg-surface-800">
<div class="max-w-6xl mx-auto px-4 sm:px-6 pt-6 pb-4">
- <div class="flex items-center gap-2 text-sm mb-3">
+ <div class="flex items-center gap-2 text-sm mb-4">
<%= link_to "@#{@owner.username}", user_profile_path(@owner.username), class: "text-brand-500 hover:underline font-medium" %>
<span class="text-gray-500">/</span>
<%= link_to @repository.name, repository_path(@owner.username, @repository.name), class: "text-brand-500 hover:underline font-semibold" %>
+ <span class="text-gray-500">/</span>
+ <%= link_to "commits", repository_commits_path(@owner.username, @repository.name, @repository.default_branch), class: "text-gray-400 hover:text-gray-200 transition-colors" %>
+ <span class="text-gray-500">/</span>
+ <span class="font-mono text-gray-400"><%= @commit[:short_sha] %></span>
</div>
+
<h1 class="text-xl font-semibold text-gray-100 mb-1"><%= @commit[:subject] %></h1>
- <% if @commit[:body].present? && @commit[:body] != @commit[:subject] %>
- <p class="text-sm text-gray-400 whitespace-pre-line mb-3"><%= @commit[:body].delete_prefix(@commit[:subject]).strip %></p>
+
+ <% body = @commit[:body].to_s.delete_prefix(@commit[:subject]).strip %>
+ <% if body.present? %>
+ <p class="text-sm text-gray-400 whitespace-pre-line mt-2 mb-3"><%= body %></p>
<% end %>
- <div class="flex items-center gap-4 text-xs text-gray-400">
+
+ <div class="flex flex-wrap items-center gap-x-4 gap-y-1 text-xs text-gray-400 mt-3">
<span><strong class="text-gray-300"><%= @commit[:author_name] %></strong> committed</span>
<% if @commit[:authored_date] %>
<span><%= @commit[:authored_date].strftime("%b %-d, %Y at %H:%M UTC") %></span>
<% end %>
- <span class="font-mono badge-gray"><%= @commit[:sha] %></span>
+ <span class="font-mono badge-gray select-all"><%= @commit[:sha] %></span>
</div>
</div>
</div>
-<div class="max-w-6xl mx-auto px-4 sm:px-6 py-6">
- <% if @commit[:diff].present? %>
- <div class="card">
- <div class="px-4 py-2.5 border-b border-surface-600 bg-surface-600 text-xs font-medium text-gray-400">Diff</div>
- <div class="code-container rounded-none border-0 font-mono text-xs leading-5 overflow-x-auto">
- <% @commit[:diff].lines.each do |line| %>
- <% css = if line.start_with?("+") && !line.start_with?("+++")
- "diff-add block px-4"
- elsif line.start_with?("-") && !line.start_with?("---")
- "diff-remove block px-4"
- elsif line.start_with?("@@")
- "diff-header block px-4"
- else
- "block px-4 text-gray-400"
- end %>
- <span class="<%= css %>"><%= line %></span>
- <% end %>
- </div>
+<div class="max-w-6xl mx-auto px-4 sm:px-6 py-6 space-y-3">
+ <%
+ file_diffs = []
+ current = nil
+
+ @commit[:diff].to_s.each_line do |line|
+ if line.start_with?("diff --git ")
+ file_diffs << current if current
+ current = { header: line, lines: [] }
+ elsif current
+ current[:lines] << line
+ end
+ end
+ file_diffs << current if current
+ %>
+
+ <% if file_diffs.empty? %>
+ <p class="text-sm text-gray-400">No diff available for this commit.</p>
+ <% else %>
+ <%
+ total_additions = 0
+ total_deletions = 0
+ file_diffs.each do |fd|
+ total_additions += fd[:lines].count { |l| l.start_with?("+") && !l.start_with?("+++") }
+ total_deletions += fd[:lines].count { |l| l.start_with?("-") && !l.start_with?("---") }
+ end
+ %>
+
+ <div class="text-xs text-gray-400 mb-4">
+ <span class="text-gray-300 font-medium"><%= file_diffs.size %> <%= "file".pluralize(file_diffs.size) %> changed</span>
+ <% if total_additions > 0 %>
+ <span class="text-green-400 ml-2">+<%= total_additions %></span>
+ <% end %>
+ <% if total_deletions > 0 %>
+ <span class="text-red-400 ml-1">-<%= total_deletions %></span>
+ <% end %>
</div>
+
+ <% file_diffs.each do |fd| %>
+ <%
+ m = fd[:header].match(/diff --git a\/(.*) b\/(.*)/)
+ filename = m ? m[2].chomp : fd[:header].chomp
+ additions = fd[:lines].count { |l| l.start_with?("+") && !l.start_with?("+++") }
+ deletions = fd[:lines].count { |l| l.start_with?("-") && !l.start_with?("---") }
+ %>
+
+ <div class="card overflow-hidden">
+ <div class="flex items-center justify-between px-4 py-2.5 border-b border-surface-600 bg-surface-700">
+ <span class="font-mono text-xs text-gray-200 truncate"><%= filename %></span>
+ <div class="flex items-center gap-2 shrink-0 ml-4 font-mono text-xs">
+ <% if additions > 0 %>
+ <span class="text-green-400">+<%= additions %></span>
+ <% end %>
+ <% if deletions > 0 %>
+ <span class="text-red-400">-<%= deletions %></span>
+ <% end %>
+ </div>
+ </div>
+
+ <div class="font-mono text-xs leading-5 overflow-x-auto">
+ <% fd[:lines].each do |line| %>
+ <% css = if line.start_with?("+") && !line.start_with?("+++")
+ "diff-add block px-4"
+ elsif line.start_with?("-") && !line.start_with?("---")
+ "diff-remove block px-4"
+ elsif line.start_with?("@@")
+ "diff-header block px-4"
+ else
+ "block px-4 text-gray-500"
+ end %>
+ <span class="<%= css %>"><%= line.chomp %></span>
+ <% end %>
+ </div>
+ </div>
+ <% end %>
<% end %>
</div>
app/views/registrations/new.html.erb
+1
-1
index 71df018..041ea0c 100644
--- a/app/views/registrations/new.html.erb
+++ b/app/views/registrations/new.html.erb
@@ -5,7 +5,7 @@
<div class="mb-10 flex flex-col items-center gap-3">
<%= link_to root_path, class: "flex items-center gap-3 text-gray-100 hover:text-brand-500 transition-colors" do %>
<img src="/icon.png" alt="siGit" class="h-9 w-auto">
- <span class="text-xl font-semibold tracking-tight">siGit</span>
+ <span class="text-xl font-semibold tracking-tight">siGit Code & Deploy</span>
<% end %>
<p class="text-sm text-gray-500">Create your account</p>
</div>
app/views/sessions/new.html.erb
+1
-1
index 3c2a4f8..976b244 100644
--- a/app/views/sessions/new.html.erb
+++ b/app/views/sessions/new.html.erb
@@ -5,7 +5,7 @@
<div class="mb-10 flex flex-col items-center gap-3">
<%= link_to root_path, class: "flex items-center gap-3 text-gray-100 hover:text-brand-500 transition-colors" do %>
<img src="/icon.png" alt="siGit" class="h-9 w-auto">
- <span class="text-xl font-semibold tracking-tight">siGit</span>
+ <span class="text-xl font-semibold tracking-tight">siGit Code & Deploy</span>
<% end %>
<p class="text-sm text-gray-500">Sign in to continue</p>
</div>
app/views/users/show.html.erb
+15
-17
index c0069e7..1527d8a 100644
--- a/app/views/users/show.html.erb
+++ b/app/views/users/show.html.erb
@@ -65,26 +65,24 @@
<p class="text-xs text-gray-500 mb-2">2 hours ago</p>
<p class="text-sm mb-3">
<span class="text-gray-400">Pushed </span>
- <span class="font-semibold text-gray-200">3 commits</span>
+ <span class="font-semibold text-gray-200"><%= @feed_commits&.size || 0 %> commits</span>
<span class="text-gray-400"> to </span>
- <%= link_to "@#{@profile_user.username}/sigit-si", repository_path(@profile_user.username, "sigit-si"), class: "text-brand-500 hover:underline font-medium" %>
+ <% if @feed_repo %>
+ <%= link_to "@#{@profile_user.username}/#{@feed_repo.name}", repository_path(@profile_user.username, @feed_repo.name), class: "text-brand-500 hover:underline font-medium" %>
+ <% end %>
<span class="text-gray-400"> on </span>
- <code class="bg-surface-600 px-1.5 py-0.5 rounded text-xs text-gray-200">main</code>
+ <code class="bg-surface-600 px-1.5 py-0.5 rounded text-xs text-gray-200"><%= @feed_repo&.default_branch || "main" %></code>
</p>
- <ul class="space-y-1.5 border-t border-surface-600 pt-3">
- <li class="flex items-start gap-2 text-xs text-gray-400">
- <%= link_to "a3f9d12", repository_commit_path(@profile_user.username, "sigit-si", "a3f9d12"), class: "text-brand-400 shrink-0 font-mono hover:underline" %>
- <span>Fix avatar fallback when gravatar URL is empty</span>
- </li>
- <li class="flex items-start gap-2 text-xs text-gray-400">
- <%= link_to "b1e2c34", repository_commit_path(@profile_user.username, "sigit-si", "b1e2c34"), class: "text-brand-400 shrink-0 font-mono hover:underline" %>
- <span>Add dev panel to footer, remove Rails 8 credit</span>
- </li>
- <li class="flex items-start gap-2 text-xs text-gray-400">
- <%= link_to "8d04f71", repository_commit_path(@profile_user.username, "sigit-si", "8d04f71"), class: "text-brand-400 shrink-0 font-mono hover:underline" %>
- <span>Scope private repos to authenticated owner only</span>
- </li>
- </ul>
+ <% if @feed_commits.present? %>
+ <ul class="space-y-1.5 border-t border-surface-600 pt-3">
+ <% @feed_commits.each do |commit| %>
+ <li class="flex items-start gap-2 text-xs text-gray-400">
+ <%= link_to commit[:short_sha], repository_commit_path(@profile_user.username, @feed_repo.name, commit[:sha]), class: "text-brand-400 shrink-0 font-mono hover:underline" %>
+ <span><%= commit[:subject] %></span>
+ </li>
+ <% end %>
+ </ul>
+ <% end %>
</div>
</li>
config/routes.rb
+2
-2
index d0d43ed..c700995 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -34,8 +34,8 @@ Rails.application.routes.draw do
get "/:username/:repository/commits/:branch", to: "commits#index", as: :repository_commits,
constraints: { branch: /[^\/]+/, repository: /[^\/.][^\/]*/ }
get "/:username/:repository/commit/:sha", to: "commits#show", as: :repository_commit
- get "/:username/:repository/blob/:branch/*path", to: "blobs#show", as: :repository_blob
- get "/:username/:repository/raw/:branch/*path", to: "blobs#raw", as: :repository_blob_raw
+ get "/:username/:repository/blob/:branch/*path", to: "blobs#show", as: :repository_blob, format: false
+ get "/:username/:repository/raw/:branch/*path", to: "blobs#raw", as: :repository_blob_raw, format: false
get "/:username/:repository/tree/:branch/*path", to: "repositories#tree", as: :repository_tree
get "/:username/:repository/tree/:branch", to: "repositories#tree", as: :repository_branch
get "/:username/:repository", to: "repositories#show", as: :repository
lib/tasks/dev.rake
+17
-6
index e891cf8..5bfa74f 100644
--- a/lib/tasks/dev.rake
+++ b/lib/tasks/dev.rake
@@ -19,15 +19,26 @@ namespace :dev do
user = User.find_by(username: username)
abort " [dev:seed_repo] No user '#{username}' found. Sign in once first." unless user
- # ── Skip if already exists ────────────────────────────────────────────────
- if user.repositories.exists?(name: repo_name)
- puts " [dev:seed_repo] '#{username}/#{repo_name}' already exists — skipping."
+ # ── Point disk_path at the project's own .git directory ──────────────────
+ # This gives the web UI access to every real commit SHA without needing a
+ # separate bare repo or a push step.
+ git_path = Rails.root.join(".git").to_s
+
+ repo = user.repositories.find_or_initialize_by(name: repo_name)
+ if repo.persisted? && repo.disk_path == git_path
+ puts " [dev:seed_repo] '#{username}/#{repo_name}' already up to date — skipping."
next
end
- local_repos = Rails.root.join(".local-repos").to_s
- bare_path = File.join(local_repos, username, "#{repo_name}.git")
- puts " [dev:seed_repo] Creating bare repo at #{bare_path} …"
+ repo.assign_attributes(
+ description: description,
+ disk_path: git_path,
+ default_branch: branch,
+ is_private: false
+ )
+ repo.save!
+ puts " [dev:seed_repo] #{repo.previously_new_record? ? "Created" : "Updated"} '#{username}/#{repo_name}' → #{git_path}"
+ next
# ── 1. Create bare repo ───────────────────────────────────────────────────
FileUtils.mkdir_p(bare_path)