| 1 | <% content_for :title, @repository.full_name %> |
| 2 | <%= render "shared/repository_social", repository: @repository %> |
| 3 | <% content_for :structured_data, repository_structured_data(@repository) %> |
| 4 | <% card = @card || @repository.model_card %> |
| 5 | |
| 6 | <div data-controller="tabs" data-tabs-active-value="<%= params[:tab] || 'card' %>"> |
| 7 | |
| 8 | <%# ── Model header band ── %> |
| 9 | <div class="border-b border-surface-600 bg-surface-800"> |
| 10 | <div class="max-w-6xl mx-auto px-4 sm:px-6 pt-6 pb-0"> |
| 11 | |
| 12 | <div class="flex items-start justify-between gap-4 mb-4"> |
| 13 | <div class="min-w-0"> |
| 14 | <div class="flex items-center gap-2 text-sm flex-wrap"> |
| 15 | <%# cube / weights glyph %> |
| 16 | <svg class="w-5 h-5 text-brand-500 shrink-0" viewBox="0 0 24 24" fill="none" |
| 17 | stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"> |
| 18 | <path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16Z"/> |
| 19 | <path d="m3.27 6.96 8.73 5.05 8.73-5.05M12 22.08V12"/> |
| 20 | </svg> |
| 21 | <%= link_to "@#{@owner.username}", user_profile_path(@owner.username), |
| 22 | class: "text-brand-500 hover:underline font-medium" %> |
| 23 | <span class="text-gray-500">/</span> |
| 24 | <%= link_to @repository.name, repository_path(@owner.username, @repository.name), |
| 25 | class: "text-gray-100 hover:underline font-semibold text-base" %> |
| 26 | <% if @repository.is_private %> |
| 27 | <span class="badge-gray ml-1">Private</span> |
| 28 | <% end %> |
| 29 | </div> |
| 30 | |
| 31 | <% if @repository.description.present? %> |
| 32 | <p class="text-sm text-gray-400 mt-2 max-w-2xl"><%= @repository.description %></p> |
| 33 | <% end %> |
| 34 | |
| 35 | <%# ── Metadata pills ── %> |
| 36 | <div class="flex items-center gap-2 flex-wrap mt-3 text-xs"> |
| 37 | <% if (label = pipeline_label(card.pipeline_tag)) %> |
| 38 | <%= link_to label, models_path(pipeline_tag: card.pipeline_tag), |
| 39 | class: "badge-blue hover:bg-brand-500/20" %> |
| 40 | <% end %> |
| 41 | <% if card.library_name.present? %> |
| 42 | <%= link_to models_path(library: card.library_name), |
| 43 | class: "badge-gray hover:bg-surface-500 flex items-center gap-1" do %> |
| 44 | <svg class="w-3 h-3" viewBox="0 0 16 16" fill="currentColor"><path d="M3 2.75A.75.75 0 0 1 3.75 2h8.5a.75.75 0 0 1 .75.75v10.5a.75.75 0 0 1-.75.75h-8.5a.75.75 0 0 1-.75-.75Zm1.5.75v9h7v-9Z"/></svg> |
| 45 | <%= card.library_name %> |
| 46 | <% end %> |
| 47 | <% end %> |
| 48 | <% if card.license.present? %> |
| 49 | <span class="badge-gray">License: <%= card.license %></span> |
| 50 | <% end %> |
| 51 | <% card.tags.first(6).each do |tag| %> |
| 52 | <span class="badge-gray text-gray-400"><%= tag %></span> |
| 53 | <% end %> |
| 54 | </div> |
| 55 | </div> |
| 56 | |
| 57 | <%# ── Like / download stats ── %> |
| 58 | <div class="flex items-center gap-2 shrink-0"> |
| 59 | <%= button_to repository_star_path(@owner.username, @repository.name), |
| 60 | method: :post, |
| 61 | class: "btn-secondary text-xs py-1.5 px-3 #{'text-amber-300 border-amber-500/40' if @repository.starred_by?(current_user)}" do %> |
| 62 | <svg class="w-3.5 h-3.5" viewBox="0 0 16 16" |
| 63 | fill="<%= @repository.starred_by?(current_user) ? 'currentColor' : 'none' %>" |
| 64 | stroke="currentColor" stroke-width="1.5"> |
| 65 | <path d="M8 .25a.75.75 0 0 1 .673.418l1.882 3.815 4.21.612a.75.75 0 0 1 .416 1.279l-3.046 2.97.719 4.192a.751.751 0 0 1-1.088.791L8 12.347l-3.766 1.98a.75.75 0 0 1-1.088-.79l.72-4.194L.818 6.374a.75.75 0 0 1 .416-1.28l4.21-.611L7.327.668A.75.75 0 0 1 8 .25Z"/> |
| 66 | </svg> |
| 67 | <span>Like</span> |
| 68 | <span class="badge-gray ml-0.5"><%= number_with_delimiter(@repository.stars_count) %></span> |
| 69 | <% end %> |
| 70 | </div> |
| 71 | </div> |
| 72 | |
| 73 | <%# ── Tabs ── %> |
| 74 | <div class="flex items-center gap-0 -mb-px"> |
| 75 | <button data-tabs-target="tab" data-tabs-key="card" data-action="click->tabs#switch" |
| 76 | class="tab-item active flex items-center gap-1.5"> |
| 77 | <svg class="w-4 h-4" viewBox="0 0 16 16" fill="currentColor"> |
| 78 | <path d="M0 1.75A.75.75 0 0 1 .75 1h4.253c1.227 0 2.317.59 3 1.501A3.743 3.743 0 0 1 11.006 1h4.245a.75.75 0 0 1 .75.75v10.5a.75.75 0 0 1-.75.75h-4.507a2.25 2.25 0 0 0-1.591.659l-.622.621a.75.75 0 0 1-1.06 0l-.622-.621A2.25 2.25 0 0 0 5.258 13H.75a.75.75 0 0 1-.75-.75Z"/> |
| 79 | </svg> |
| 80 | Model card |
| 81 | </button> |
| 82 | <button data-tabs-target="tab" data-tabs-key="files" data-action="click->tabs#switch" |
| 83 | class="tab-item flex items-center gap-1.5"> |
| 84 | <svg class="w-4 h-4" viewBox="0 0 16 16" fill="currentColor"> |
| 85 | <path d="M2 1.75C2 .784 2.784 0 3.75 0h6.586c.464 0 .909.184 1.237.513l2.914 2.914c.329.328.513.773.513 1.237v9.586A1.75 1.75 0 0 1 13.25 16h-9.5A1.75 1.75 0 0 1 2 14.25Z"/> |
| 86 | </svg> |
| 87 | Files and versions |
| 88 | <% if @files&.any? %><span class="badge-gray text-xs"><%= @files.size %></span><% end %> |
| 89 | </button> |
| 90 | </div> |
| 91 | </div> |
| 92 | </div> |
| 93 | |
| 94 | <%# ── Body ── %> |
| 95 | <div class="max-w-6xl mx-auto px-4 sm:px-6 py-6"> |
| 96 | |
| 97 | <% if @empty %> |
| 98 | <div class="card p-8 text-center max-w-2xl mx-auto"> |
| 99 | <h2 class="text-lg font-semibold text-gray-100 mb-2">This model has no files yet</h2> |
| 100 | <p class="text-sm text-gray-400 mb-6">Push your model card and weights to get started.</p> |
| 101 | <div class="code-container text-left p-4 max-w-lg mx-auto text-xs font-mono space-y-0.5"> |
| 102 | <p class="text-gray-500 mb-1"># Push weights with Git LFS</p> |
| 103 | <p class="text-gray-300">git clone git@sigitsi.com:<%= @owner.username %>/<%= @repository.name %></p> |
| 104 | <p class="text-gray-300">cd <%= @repository.name %></p> |
| 105 | <p class="text-gray-300">git lfs install && git lfs track "*.safetensors" "*.gguf"</p> |
| 106 | <p class="text-gray-300">git add . && git commit -m "Add weights"</p> |
| 107 | <p class="text-gray-300">git push origin <%= @repository.default_branch %></p> |
| 108 | </div> |
| 109 | </div> |
| 110 | <% else %> |
| 111 | <div class="grid grid-cols-1 lg:grid-cols-3 gap-6"> |
| 112 | |
| 113 | <%# ── Main column ── %> |
| 114 | <div class="lg:col-span-2 min-w-0"> |
| 115 | <%# Model card panel %> |
| 116 | <div data-tabs-target="panel" data-tabs-key="card"> |
| 117 | <% if @card_html.present? %> |
| 118 | <div class="card"> |
| 119 | <div class="px-6 py-6 prose-readme"><%= @card_html %></div> |
| 120 | </div> |
| 121 | <% else %> |
| 122 | <div class="card px-6 py-12 text-center text-sm text-gray-500"> |
| 123 | No model card yet. Add a <code>README.md</code> to describe this model. |
| 124 | </div> |
| 125 | <% end %> |
| 126 | </div> |
| 127 | |
| 128 | <%# Files and versions panel %> |
| 129 | <div data-tabs-target="panel" data-tabs-key="files" class="hidden"> |
| 130 | <div class="card rounded-b-none border-b-0 px-4 py-2.5 flex items-center justify-between gap-4 bg-surface-600"> |
| 131 | <span class="text-xs text-gray-400 flex items-center gap-2"> |
| 132 | <span class="btn-secondary py-1 px-2.5 text-xs font-mono cursor-default"><%= @branch %></span> |
| 133 | <%= pluralize(@files.size, "file") %> |
| 134 | </span> |
| 135 | <span class="text-xs text-gray-500"> |
| 136 | Total: <span class="text-gray-300"><%= number_to_human_size(@total_size) %></span> |
| 137 | </span> |
| 138 | </div> |
| 139 | <div class="card rounded-t-none"> |
| 140 | <% @files.each do |entry| %> |
| 141 | <div class="file-row"> |
| 142 | <% if entry[:type] == "tree" %> |
| 143 | <svg class="w-4 h-4 text-brand-400 shrink-0" viewBox="0 0 16 16" fill="currentColor"> |
| 144 | <path d="M1.75 1A1.75 1.75 0 0 0 0 2.75v10.5C0 14.216.784 15 1.75 15h12.5A1.75 1.75 0 0 0 16 13.25v-8.5A1.75 1.75 0 0 0 14.25 3H7.5a.25.25 0 0 1-.2-.1l-.9-1.2C6.07 1.26 5.55 1 5 1Z"/> |
| 145 | </svg> |
| 146 | <%= link_to entry[:name], |
| 147 | repository_tree_path(@owner.username, @repository.name, @branch, entry[:path]), |
| 148 | class: "text-brand-500 hover:underline font-medium flex-1" %> |
| 149 | <% else %> |
| 150 | <svg class="w-4 h-4 text-gray-500 shrink-0" viewBox="0 0 16 16" fill="currentColor"> |
| 151 | <path d="M2 1.75C2 .784 2.784 0 3.75 0h6.586c.464 0 .909.184 1.237.513l2.914 2.914c.329.328.513.773.513 1.237v9.586A1.75 1.75 0 0 1 13.25 16h-9.5A1.75 1.75 0 0 1 2 14.25Zm1.75-.25a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25h9.5a.25.25 0 0 0 .25-.25V6h-2.75A1.75 1.75 0 0 1 9 4.25V1.5Z"/> |
| 152 | </svg> |
| 153 | <%= link_to entry[:name], |
| 154 | repository_blob_path(@owner.username, @repository.name, @branch, entry[:path]), |
| 155 | class: "text-gray-200 hover:text-brand-500 hover:underline flex-1 truncate" %> |
| 156 | <div class="flex items-center gap-2 shrink-0"> |
| 157 | <% if (fmt = weight_format(entry[:name])) %> |
| 158 | <span class="badge-blue text-[10px]"><%= fmt %></span> |
| 159 | <% end %> |
| 160 | <% if entry[:lfs] %> |
| 161 | <span class="badge-gray text-[10px] text-gray-400" title="Stored with Git LFS">LFS</span> |
| 162 | <% end %> |
| 163 | <% if entry[:size] %> |
| 164 | <span class="text-xs text-gray-500 font-mono w-20 text-right"><%= number_to_human_size(entry[:size]) %></span> |
| 165 | <% end %> |
| 166 | <%= link_to repository_blob_raw_path(@owner.username, @repository.name, @branch, entry[:path]), |
| 167 | class: "text-gray-500 hover:text-brand-500", title: "Download" do %> |
| 168 | <svg class="w-4 h-4" viewBox="0 0 16 16" fill="currentColor"><path d="M7.47 10.78a.75.75 0 0 0 1.06 0l3.75-3.75a.75.75 0 0 0-1.06-1.06L8.75 8.44V1.75a.75.75 0 0 0-1.5 0v6.69L4.78 5.97a.75.75 0 0 0-1.06 1.06ZM3.75 13a.75.75 0 0 0 0 1.5h8.5a.75.75 0 0 0 0-1.5Z"/></svg> |
| 169 | <% end %> |
| 170 | </div> |
| 171 | <% end %> |
| 172 | </div> |
| 173 | <% end %> |
| 174 | </div> |
| 175 | </div> |
| 176 | </div> |
| 177 | |
| 178 | <%# ── Sidebar ── %> |
| 179 | <aside class="lg:col-span-1 space-y-4"> |
| 180 | <%= render "repositories/model_use", repository: @repository, owner: @owner, card: card %> |
| 181 | |
| 182 | <div class="card p-4 space-y-3 text-sm"> |
| 183 | <h3 class="text-xs uppercase tracking-wider text-gray-500 font-semibold">Model details</h3> |
| 184 | <% if card.base_model.present? %> |
| 185 | <div class="flex items-center justify-between gap-2"> |
| 186 | <span class="text-gray-500">Base model</span> |
| 187 | <span class="text-gray-300 truncate font-mono text-xs"><%= card.base_model %></span> |
| 188 | </div> |
| 189 | <% end %> |
| 190 | <% if card.library_name.present? %> |
| 191 | <div class="flex items-center justify-between"><span class="text-gray-500">Library</span><span class="text-gray-300"><%= card.library_name %></span></div> |
| 192 | <% end %> |
| 193 | <% if card.license.present? %> |
| 194 | <div class="flex items-center justify-between"><span class="text-gray-500">License</span><span class="text-gray-300"><%= card.license %></span></div> |
| 195 | <% end %> |
| 196 | <% if card.language.any? %> |
| 197 | <div class="flex items-center justify-between"><span class="text-gray-500">Languages</span><span class="text-gray-300"><%= card.language.first(4).join(", ") %></span></div> |
| 198 | <% end %> |
| 199 | <div class="flex items-center justify-between"><span class="text-gray-500">Downloads</span><span class="text-gray-300"><%= number_with_delimiter(@repository.downloads_count) %></span></div> |
| 200 | <div class="flex items-center justify-between"><span class="text-gray-500">Likes</span><span class="text-gray-300"><%= number_with_delimiter(@repository.stars_count) %></span></div> |
| 201 | <div class="flex items-center justify-between"><span class="text-gray-500">Updated</span><span class="text-gray-300"><%= time_ago_in_words(@repository.updated_at) %> ago</span></div> |
| 202 | </div> |
| 203 | |
| 204 | <% if card.tags.any? %> |
| 205 | <div class="card p-4"> |
| 206 | <h3 class="text-xs uppercase tracking-wider text-gray-500 font-semibold mb-3">Tags</h3> |
| 207 | <div class="flex flex-wrap gap-1.5"> |
| 208 | <% card.tags.each do |tag| %> |
| 209 | <span class="badge-gray text-[11px] text-gray-400"><%= tag %></span> |
| 210 | <% end %> |
| 211 | </div> |
| 212 | </div> |
| 213 | <% end %> |
| 214 | </aside> |
| 215 | |
| 216 | </div> |
| 217 | <% end %> |
| 218 | </div> |
| 219 | </div> |