Signed by hanna SSH key fingerprint: SHA256:4g9cWhkAAw8gwqhJUcVbSnGEvdGwklW+1Aa/fMUu59k
assets/base.css +25 −12 assets/fabrica.js +6 −16 crates/web/src/releases.rs +20 −9 data/fabrica/releases/01kyf3cgb99w0wjas5a5jbx2pt +0 −0 assets/base.css +25 −12 Expand 2218 hidden lines 2219 2219 . back-link { 2220 2220 font-size : 0.9em ; 2221 2221 } 2222 + 2223 + . crumbs { 2224 + display : flex ; 2225 + align-items : center ; 2226 + gap : 0.4rem ; 2227 + flex-wrap : wrap ; 2228 + margin-bottom : 1rem ; 2229 + font-size : 0.95em ; 2230 + } 2231 + . crumbs a { 2232 + color : var ( --fb-accent ) ; 2233 + } 2234 + . crumb-current { 2235 + color : var ( --fb-fg-muted ) ; 2236 + } 2222 2237 . release { 2223 2238 margin-bottom : 1.25rem ; 2224 2239 } Expand 104 hidden lines 2329 2344 opacity : 0 ; 2330 2345 pointer-events : none ; 2331 2346 } 2332 - 2333 - . asset-pending { 2334 - display : flex ; 2335 - align-items : center ; 2336 - gap : 0.6rem ; 2337 - margin-bottom : 0.75rem ; 2338 - } 2339 - . asset-pending-name { 2340 - flex : 1 ; 2341 - min-width : 0 ; 2342 - font-family : var ( --fb-font-mono ) ; 2343 - } 2344 2347 . asset-upload . btn { 2345 2348 margin-top : 0.75rem ; 2346 2349 } 2350 + 2351 + 2352 + 2353 + . card . asset-list . asset-row form button [ type = "submit" ] { 2354 + margin-top : 0 ; 2355 + } 2356 + . asset-row . icon-btn { 2357 + width : 1.75rem ; 2358 + height : 1.75rem ; 2359 + } 2347 2360 . release-form-actions { 2348 2361 display : flex ; 2349 2362 align-items : center ;
assets/fabrica.js +6 −16 Expand 169 hidden lines 170 170 } 171 171 } ) ; 172 172 173 - 173 + 174 174 document . querySelectorAll ( ".asset-upload" ) . forEach ( function ( form ) { 175 175 var input = form . querySelector ( "input[type=file]" ) ; 176 176 var zone = form . querySelector ( ".dropzone" ) ; 177 - var pending = form . querySelector ( ".asset-pending" ) ; 178 - if ( ! input || ! zone || ! pending ) return ; 179 - function show ( ) { 180 - var f = input . files[ 0] ; 181 - pending . hidden = ! f ; 182 - if ( ! f ) return ; 183 - pending . querySelector ( "input[type=text]" ) . value = f . name; 184 - pending . querySelector ( ".asset-size" ) . textContent = 185 - "(" + ( f . size < 1048576 ? ( f . size / 1024) . toFixed ( 1) + " KB" : ( f . size / 1048576) . toFixed ( 2) + " MB" ) + ")" ; 186 - } 187 - input . addEventListener ( "change" , show ) ; 188 - pending . querySelector ( ".asset-remove" ) . addEventListener ( "click" , function ( ) { 189 - input . value = "" ; pending . hidden = true ; 190 - } ) ; 177 + if ( ! input || ! zone ) return ; 178 + var submit = form . querySelector ( "button[type=submit]" ) ; 179 + if ( submit ) submit . hidden = true ; 180 + input . addEventListener ( "change" , function ( ) { if ( input . files. length) form . submit ( ) ; } ) ; 191 181 [ "dragover" , "dragenter" , "dragleave" , "drop" ] . forEach ( function ( ev ) { 192 182 zone . addEventListener ( ev , function ( e ) { 193 183 e . preventDefault ( ) ; 194 184 zone . classList. toggle ( "drag" , ev === "dragover" || ev === "dragenter" ) ; 195 - if ( ev === "drop" && e . dataTransfer. files. length) { input . files = e . dataTransfer. files; show ( ) ; } 185 + if ( ev === "drop" && e . dataTransfer. files. length) { input . files = e . dataTransfer. files; form . submit ( ) ; } 196 186 } ) ; 197 187 } ) ; 198 188 } ) ;
crates/web/src/releases.rs +20 −9 Expand 146 hidden lines 147 147 . await ; 148 148 let body = html ! { 149 149 ( repo_header ( state, & ctx, Tab :: Releases , & ctx. repo. default_branch, & branches) ) 150 - p { a class="back-link" href=( format ! ( "{base}/-/releases" ) ) { "← Releases" } } 150 + nav class="crumbs" aria-label="Breadcrumb" { 151 + a href=( format ! ( "{base}/-/releases" ) ) { "Releases" } 152 + span class="slug-sep" { "/" } 153 + span class="crumb-current" { ( if release. name. is_empty ( ) { & release. tag } else { & release. name } ) } 154 + } 151 155 ( card) 152 156 } ; 153 157 let title = format ! ( Expand 124 hidden lines 278 282 . user_by_id ( & repo. owner_id) 279 283 . await ? 280 284 . map_or ( repo. owner_id. clone ( ) , |u| u. username) ; 285 + let releases = format ! ( "/{owner}/{}/-/releases" , repo. path) ; 281 286 let back = format ! ( "/{owner}/{}/-/releases/{}" , repo. path, release. tag) ; 287 + let crumb = if release. name. is_empty ( ) { 288 + & release. tag 289 + } else { 290 + & release. name 291 + } ; 282 292 let assets = state. store. list_release_assets ( & release. id) . await ?; 283 293 let ( jar, chrome) = build_chrome ( & state, jar, Some ( user) , uri. path ( ) . to_string ( ) ) ; 284 294 let body = html ! { 285 - p { a class="back-link" href=( back) { "← Back to release" } } 295 + nav class="crumbs" aria-label="Breadcrumb" { 296 + a href=( releases) { "Releases" } 297 + span class="slug-sep" { "/" } 298 + a href=( back) { ( crumb) } 299 + span class="slug-sep" { "/" } 300 + span class="crumb-current" { "Edit" } 301 + } 286 302 ( release_form ( & format ! ( "/release/{}/edit" , release. id) , & chrome. csrf, Some ( & release) , & [ ] , & [ ] ) ) 287 303 ( assets_editor ( & release. id, & chrome. csrf, & assets) ) 288 304 } ; Expand 77 hidden lines 366 382 } 367 383 form method="post" action=( format ! ( "/release/{release_id}/asset" ) ) enctype="multipart/form-data" class="asset-upload" { 368 384 input type ="hidden" name="_csrf" value=( csrf) ; 369 - 370 - 371 - div class="asset-pending" hidden { 372 - input type ="text" name="name" class="asset-pending-name" aria-label="Asset name" ; 373 - span class="asset-size muted" { } 374 - button type ="button" class="asset-remove icon-btn" aria-label="Remove file" title="Remove file" { ( icon ( Icon :: X ) ) } 375 - } 376 385 label class="dropzone" { 377 386 input type ="file" name="asset" class="dropzone-input" required; 378 387 ( icon ( Icon :: Download ) ) 379 388 span { "Attach a file by dropping it here or selecting it." } 380 389 } 390 + 391 + 381 392 button class="btn inline-btn" type ="submit" { "Upload asset" } 382 393 } 383 394 }
data/fabrica/releases/01kyf3cgb99w0wjas5a5jbx2pt +0 −0 Binary file not shown.