.forge/prs/3c5c330e-760e-4db1-bc2c-ef25644b575b/patch.diff 5.8 KiB 136 lines Raw
1 From e8d1601ad7161d3ad131b9572943ddff7b96c8dc Mon Sep 17 00:00:00 20012 From: gl0w <gl0w@world>3 Date: Thu, 10 Sep 2026 09:33:29 +01004 Subject: [PATCH] m6/code-bits: CI badge colors + details gap fix5 6 ---7  src/build.rs     | 63 +++++++++++++++++++++++++++++++++++++++++++++---8  static/style.css | 15 +++++++++++-9  2 files changed, 74 insertions(+), 4 deletions(-)10 11 diff --git a/src/build.rs b/src/build.rs12 index 5d176ba..ffd8e86 10064413 --- a/src/build.rs14 +++ b/src/build.rs15 @@ -669,7 +669,7 @@ pub fn clone_block(16      };17      let mut out = format!(18          "<details class=\"clonebox clone-block\" aria-label=\"Clone and download\">\n\19 -         <summary>Clone &amp; download <span class=\"muted\">{} @ {}</span></summary>\n",20 +         <summary>Clone &amp; download <span class=\"muted\">{} @ {}</span></summary>\n<div class=\"clone-body\">\n",21          esc(branch),22          esc(short_sha)23      );24 @@ -696,7 +696,7 @@ pub fn clone_block(25                  esc(url)26              ));27          }28 -        out.push_str("</details>\n");29 +        out.push_str("</div>\n</details>\n");30          return out;31      }32      if let Some(url) = clone_url {33 @@ -727,7 +727,7 @@ pub fn clone_block(34               served from this server: ask the host for its address.</p>\n",35          );36      }37 -    out.push_str("</details>\n");38 +    out.push_str("</div>\n</details>\n");39      out40  }41  42 @@ -1996,6 +1996,63 @@ mod tests {43          assert!(bundle_pos < tar_pos, "bundle before tarball");44      }45  46 +    #[test]47 +    fn code_head_ci_badge_green_red() {48 +        // f0rge #43: code repo-head CI badge is green/red via49 +        // `.badge.pass` / `.badge.fail` (running/cancelled keep tones).50 +        for verdict in ["pass", "fail"] {51 +            let repo = fixture_repo();52 +            let run_dir = repo.join(".forge").join("ci/runs/2026-09-10T00-00-00Z");53 +            std::fs::create_dir_all(&run_dir).unwrap();54 +            std::fs::write(55 +                run_dir.join("summary.md"),56 +                format!(57 +                    "---\nrun: 2026-09-10T00-00-00Z\nverdict: {verdict}\ncreated: 2026-09-10T00:00:00Z\n---\nlog\n"58 +                ),59 +            )60 +            .unwrap();61 +            git(&repo, &["add", "-A"]);62 +            git(&repo, &["commit", "-m", &format!("ci {verdict}")]);63 +            let out = mktemp(&format!("out-ci-{verdict}"));64 +            run_build(&repo, &out, &opts()).expect("build");65 +            let index = std::fs::read_to_string(out.join("index.html")).expect("index");66 +            let head_start = index.find("<div class=\"repo-head\">").expect("repo-head");67 +            let head_end = index[head_start..].find("</div>").expect("repo-head end") + head_start;68 +            let head = &index[head_start..head_end];69 +            assert!(70 +                head.contains(&format!("badge {verdict}")),71 +                "code head carries badge {verdict}"72 +            );73 +        }74 +    }75 +76 +    #[test]77 +    fn clone_summary_followed_by_body_no_gap() {78 +        // f0rge #44: summary immediately followed by content wrapper —79 +        // no empty <p>, no margin-collapse gap when collapsed.80 +        let (_repo, out) = build_fixture();81 +        let index = std::fs::read_to_string(out.join("index.html")).expect("index");82 +        assert!(83 +            index.contains("</summary>\n<div class=\"clone-body\">"),84 +            "summary immediately followed by clone-body wrapper"85 +        );86 +        let s = index.find("</summary>").expect("summary end");87 +        let after = &index[s..s + 200];88 +        assert!(89 +            !after.contains("<p></p>"),90 +            "no empty paragraph after summary"91 +        );92 +        assert!(93 +            !after.contains("<p> </p>"),94 +            "no blank paragraph after summary"95 +        );96 +        // Wrapper closes before the details closes (structure intact).97 +        assert!(98 +            index.contains("</div>\n</details>"),99 +            "clone-body closes inside details"100 +        );101 +    }102 +103      #[test]104      fn is_binary_sniffs_first_8kib() {105          // f0rge #37: NUL in the first 8KiB means binary for math.106 diff --git a/static/style.css b/static/style.css107 index fa1b70d..386ad62 100644108 --- a/static/style.css109 +++ b/static/style.css110 @@ -189,9 +189,22 @@ a.ln:hover,a.ln:focus{text-decoration:underline}111  .lang-legend li{display:inline-flex;align-items:center;gap:.35rem}112  .lang-dot{display:inline-block;width:.75rem;height:.75rem;flex:none}113  details.clonebox{margin:1rem 0;padding:.25rem 1rem 1rem;background:var(--bg-alt);border:1px solid var(--border)}114 -details.clonebox>summary{cursor:pointer;min-height:40px;font-weight:700;color:var(--cyan)}115 +details.clonebox>summary{cursor:pointer;min-height:40px;font-weight:700;color:var(--cyan);margin:0}116  details.clonebox>summary:focus-visible{outline:2px solid var(--blue);outline-offset:2px}117 +/* Clone gap (m6/code-bits, f0rge #44): no blank line under summary when118 +   collapsed — collapsed padding stays tight; open gap lives on the119 +   wrapper's first child so margins never collapse through summary. */120 +details.clonebox:not([open]){padding-bottom:.25rem}121 +.clonebox .clone-body{margin:0}122 +.clonebox .clone-body>:first-child{margin-top:.75rem}123  .clone-hint,.clonebox summary .muted{font-weight:400}124 +/* CI verdict badges (m6/code-bits, f0rge #43): GitHub-style green/red on125 +   the code repo-head; running/cancelled keep muted tones. */126 +.badge{display:inline-block;padding:.1em .5em;border:1px solid var(--border);font-weight:700;font-size:.85rem}127 +.badge.pass{color:var(--green);border-color:var(--green)}128 +.badge.fail{color:var(--red);border-color:var(--red)}129 +.badge.running{color:var(--yellow);border-color:var(--yellow)}130 +.badge.cancelled{color:var(--muted);border-color:var(--muted)}131  /* Stacked cards on narrow (m6/no-scroll, f0rge #39): Gitea-style reflow.132     Desktop (>=768px) keeps .table-scroll horizontal scroll untouched above.133     Narrow: no scroll (overflow visible), thead clipped for SR only, each134 -- 135 2.55.0136