Generated monogram fallback + optional dark-mode icon per application
Some checks failed
Some checks failed
When an application has no icon attached, render a deterministic monogram SVG instead of the generic picture-frame placeholder. Initials are picked from capital letters in the name (ShelfLife -> SL); fall back to the first two letters when fewer than two capitals exist (Audiobookshelf -> AU). Background colour is hashed from the name for stable per-app identity across visits. Adds an optional second icon attachment, icon_dark, alongside the main icon. When present, render a <picture> with a prefers-color-scheme: dark source so the browser swaps automatically; when absent, the main icon is used in both modes. The SVG sanitization, content-type fix, and size/format validation now run over both attachments uniformly. Bumps to 0.14.0. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -115,6 +115,23 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<%= form.label :icon_dark, "Dark mode icon (optional)", class: "block text-sm font-medium text-gray-700 dark:text-gray-300" %>
|
||||
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">Used in place of the main icon when the user's theme is dark. If omitted, the main icon is used in both modes.</p>
|
||||
<% if application.icon_dark.attached? && application.persisted? && application.icon_dark.blob&.persisted? && application.icon_dark.blob.key.present? %>
|
||||
<div class="mt-2 mb-3 flex items-center gap-4">
|
||||
<%= image_tag application.icon_dark, class: "h-16 w-16 rounded-lg object-cover border border-gray-200 dark:border-gray-700 bg-gray-900", alt: "Current dark-mode icon" %>
|
||||
<div class="text-sm text-gray-600 dark:text-gray-400">
|
||||
<p class="font-medium">Current dark-mode icon</p>
|
||||
<p class="text-xs"><%= number_to_human_size(application.icon_dark.blob.byte_size) %></p>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
<%= form.file_field :icon_dark,
|
||||
accept: "image/png,image/jpg,image/jpeg,image/gif,image/svg+xml",
|
||||
class: "mt-2 block w-full text-sm text-gray-700 dark:text-gray-300 file:mr-3 file:py-2 file:px-3 file:rounded-md file:border-0 file:text-sm file:font-medium file:bg-blue-50 file:text-blue-700 dark:file:bg-blue-900/30 dark:file:text-blue-300 hover:file:bg-blue-100 dark:hover:file:bg-blue-900/50" %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
@@ -30,13 +30,9 @@
|
||||
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-gray-900 dark:text-gray-100 sm:pl-0">
|
||||
<div class="flex items-center gap-3">
|
||||
<% if application.icon.attached? %>
|
||||
<%= image_tag application.icon, class: "h-10 w-10 rounded-lg object-cover border border-gray-200 dark:border-gray-700 flex-shrink-0", alt: "#{application.name} icon" %>
|
||||
<%= app_icon_picture application, class: "h-10 w-10 rounded-lg object-cover border border-gray-200 dark:border-gray-700 flex-shrink-0" %>
|
||||
<% else %>
|
||||
<div class="h-10 w-10 rounded-lg bg-gray-100 dark:bg-gray-700 border border-gray-200 dark:border-gray-600 flex items-center justify-center flex-shrink-0">
|
||||
<svg class="h-6 w-6 text-gray-400 dark:text-gray-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
||||
</svg>
|
||||
</div>
|
||||
<%= render "shared/app_monogram", name: application.name, class: "h-10 w-10 rounded-lg flex-shrink-0" %>
|
||||
<% end %>
|
||||
<%= link_to application.name, admin_application_path(application), class: "text-blue-600 hover:text-blue-900" %>
|
||||
</div>
|
||||
|
||||
@@ -49,13 +49,9 @@
|
||||
<div class="sm:flex sm:items-start sm:justify-between">
|
||||
<div class="flex items-start gap-4">
|
||||
<% if @application.icon.attached? %>
|
||||
<%= image_tag @application.icon, class: "h-16 w-16 rounded-lg object-cover border border-gray-200 dark:border-gray-700 shrink-0", alt: "#{@application.name} icon" %>
|
||||
<%= app_icon_picture @application, class: "h-16 w-16 rounded-lg object-cover border border-gray-200 dark:border-gray-700 shrink-0" %>
|
||||
<% else %>
|
||||
<div class="h-16 w-16 rounded-lg bg-gray-100 dark:bg-gray-700 border border-gray-200 dark:border-gray-600 flex items-center justify-center shrink-0">
|
||||
<svg class="h-8 w-8 text-gray-400 dark:text-gray-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
||||
</svg>
|
||||
</div>
|
||||
<%= render "shared/app_monogram", name: @application.name, class: "h-16 w-16 rounded-lg shrink-0" %>
|
||||
<% end %>
|
||||
<div>
|
||||
<h1 class="text-2xl font-semibold text-gray-900 dark:text-gray-100"><%= @application.name %></h1>
|
||||
|
||||
Reference in New Issue
Block a user