thunderbird-patch-review

Simple email patch review tool for Thunderbird

git clone git://mccd.space/thunderbird-patch-review

review.css (11865B)

      1 :root {
      2   --bg: #ffffff;
      3   --fg: #1f2328;
      4   --muted: #656d76;
      5   --border: #d1d9e0;
      6   --surface: #f6f8fa;
      7   --accent: #0969da;
      8   --add-bg: #dafbe1;
      9   --add-gutter: #aceebb;
     10   --del-bg: #ffebe9;
     11   --del-gutter: #ffcecb;
     12   --hunk-bg: #ddf4ff;
     13   --comment-bg: #fff8c5;
     14 }
     15 
     16 @media (prefers-color-scheme: dark) {
     17   :root {
     18     --bg: #0d1117;
     19     --fg: #e6edf3;
     20     --muted: #8d96a0;
     21     --border: #30363d;
     22     --surface: #161b22;
     23     --accent: #4493f8;
     24     --add-bg: #12261e;
     25     --add-gutter: #1b4721;
     26     --del-bg: #25171c;
     27     --del-gutter: #542426;
     28     --hunk-bg: #121d2f;
     29     --comment-bg: #272115;
     30   }
     31 }
     32 
     33 * { box-sizing: border-box; }
     34 
     35 /* The hidden attribute must always win, even over display: flex rules. */
     36 [hidden] { display: none !important; }
     37 
     38 body {
     39   margin: 0;
     40   background: var(--bg);
     41   color: var(--fg);
     42   font: 13px/1.5 -apple-system, "Segoe UI", Cantarell, Ubuntu, sans-serif;
     43 }
     44 
     45 #topbar {
     46   display: flex;
     47   align-items: center;
     48   justify-content: space-between;
     49   gap: 1rem;
     50   padding: 0.5rem 1rem;
     51   border-bottom: 1px solid var(--border);
     52   background: var(--surface);
     53   position: sticky;
     54   top: 0;
     55   z-index: 2;
     56 }
     57 
     58 #series-nav {
     59   display: flex;
     60   align-items: center;
     61   justify-content: flex-start;
     62   gap: 0.4rem;
     63   flex-wrap: wrap;
     64   margin: 0.6rem 0;
     65 }
     66 
     67 #series-title {
     68   font-weight: 600;
     69   overflow: hidden;
     70   text-overflow: ellipsis;
     71   white-space: nowrap;
     72 }
     73 
     74 #actions {
     75   display: flex;
     76   align-items: center;
     77   gap: 0.5rem;
     78   flex-shrink: 0;
     79 }
     80 
     81 
     82 #repo-path {
     83   max-width: 18rem;
     84   padding: 0.3rem 0.6rem;
     85   border: 1px solid var(--border);
     86   border-radius: 6px;
     87   background: var(--bg);
     88   color: var(--fg);
     89   font-family: ui-monospace, monospace;
     90   white-space: nowrap;
     91   overflow: hidden;
     92   text-overflow: ellipsis;
     93   user-select: none;
     94   cursor: pointer;
     95   text-align: left;
     96 }
     97 
     98 #repo-path .repo-branch { color: var(--muted); }
     99 #repo-path.error .repo-sep,
    100 #repo-path.error .repo-branch { color: inherit; }
    101 #repo-path:hover { background: var(--hunk-bg); }
    102 #repo-path.error { cursor: pointer; }
    103 #repo-path.error {
    104   border-color: #cf222e;
    105   color: #cf222e;
    106 }
    107 @media (prefers-color-scheme: dark) {
    108   #repo-path.error { border-color: #f85149; color: #f85149; }
    109 }
    110 
    111 /* A joined cluster: the repo chip and the Apply split read as one control.
    112    The seam is drawn by Apply's left border alone; the repo chip's right
    113    border goes transparent (this outranks the .error red by declaration
    114    order), so an error chip doesn't butt a red edge against Apply's. */
    115 .cluster {
    116   display: flex;
    117   align-items: stretch;
    118 }
    119 .cluster > #repo-path {
    120   border-top-right-radius: 0;
    121   border-bottom-right-radius: 0;
    122   border-right-color: transparent;
    123 }
    124 .cluster > .split {
    125   border-top-left-radius: 0;
    126   border-bottom-left-radius: 0;
    127 }
    128 .cluster > .split > button:first-child {
    129   border-top-left-radius: 0;
    130   border-bottom-left-radius: 0;
    131 }
    132 
    133 button {
    134   padding: 0.3rem 0.8rem;
    135   border: 1px solid var(--border);
    136   border-radius: 6px;
    137   background: var(--surface);
    138   color: var(--fg);
    139   cursor: pointer;
    140 }
    141 
    142 button:hover { background: var(--hunk-bg); }
    143 button:disabled { opacity: 0.5; cursor: not-allowed; }
    144 button:disabled:hover { background: inherit; opacity: 0.5; }
    145 button.primary:disabled { background: var(--accent); border-color: var(--accent); color: #fff; }
    146 button.primary:disabled:hover { opacity: 0.5; }
    147 
    148 .split .menu button.danger {
    149   color: #cf222e;
    150 }
    151 .split .menu button.danger:hover {
    152   background: #cf222e;
    153   color: #fff;
    154 }
    155 @media (prefers-color-scheme: dark) {
    156   .split .menu button.danger { color: #f85149; }
    157   .split .menu button.danger:hover { background: #f85149; color: #0d1117; }
    158 }
    159 button.primary { background: var(--accent); border-color: var(--accent); color: #fff; }
    160 button.primary:hover { color: #fff; opacity: 0.9; }
    161 
    162 /* Apply-button states from the pre-check (patchHost.check): the button
    163    turns green with a checkmark when the series is already in HEAD, red
    164    when it won't apply, yellow with a warning when the working tree is
    165    dirty (git am would refuse before reading the patches) — disabled in
    166    all three. State-checking is the transient "querying git" phase. The
    167    menu-toggle plants itself in the same split, so it shares the deafened
    168    look. */
    169 #btn-apply.state-checking { cursor: progress; opacity: 0.85; }
    170 
    171 #btn-apply.state-applied,
    172 #btn-apply.state-applied:hover {
    173   background: #dcfce7;
    174   border-color: #1a7f37;
    175   color: #1a7f37;
    176 }
    177 
    178 #btn-apply.state-conflict,
    179 #btn-apply.state-conflict:hover {
    180   background: #ffebe9;
    181   border-color: #cf222e;
    182   color: #cf222e;
    183 }
    184 
    185 #btn-apply.state-dirty,
    186 #btn-apply.state-dirty:hover {
    187   background: #fff8c5;
    188   border-color: #bf8700;
    189   color: #7d4e00;
    190 }
    191 
    192 @media (prefers-color-scheme: dark) {
    193   #btn-apply.state-applied,
    194   #btn-apply.state-applied:hover {
    195     background: #0d2818;
    196     border-color: #2ea043;
    197     color: #3fb950;
    198   }
    199   #btn-apply.state-conflict,
    200   #btn-apply.state-conflict:hover {
    201     background: #2d1216;
    202     border-color: #f85149;
    203     color: #ff7b72;
    204   }
    205   #btn-apply.state-dirty,
    206   #btn-apply.state-dirty:hover {
    207     background: #2a2306;
    208     border-color: #d29922;
    209     color: #e3b341;
    210   }
    211 }
    212 
    213 .split {
    214   position: relative;
    215   display: flex;
    216 }
    217 
    218 .split > button:first-child {
    219   border-top-right-radius: 0;
    220   border-bottom-right-radius: 0;
    221 }
    222 
    223 .split > .menu-toggle {
    224   border-top-left-radius: 0;
    225   border-bottom-left-radius: 0;
    226   border-left-color: rgba(255, 255, 255, 0.35);
    227   padding: 0.3rem 0.45rem;
    228 }
    229 
    230 .split .menu {
    231   position: absolute;
    232   top: calc(100% + 4px);
    233   right: 0;
    234   display: flex;
    235   flex-direction: column;
    236   min-width: 12rem;
    237   border: 1px solid var(--border);
    238   border-radius: 6px;
    239   background: var(--surface);
    240   box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
    241   z-index: 4;
    242   overflow: hidden;
    243 }
    244 
    245 .split .menu button {
    246   border: 0;
    247   border-radius: 0;
    248   background: none;
    249   text-align: left;
    250   padding: 0.45rem 0.8rem;
    251 }
    252 
    253 .split .menu button:hover {
    254   background: var(--hunk-bg);
    255   color: var(--fg);
    256 }
    257 
    258 .patch-box {
    259   margin: 0 0 1rem;
    260   border: 1px solid var(--border);
    261   border-radius: 6px;
    262   background: var(--surface);
    263   overflow: hidden;
    264 }
    265 
    266 #series-nav .pill {
    267   padding: 0.2rem 0.7rem;
    268   border: 1px solid var(--border);
    269   border-radius: 999px;
    270   cursor: pointer;
    271   color: var(--muted);
    272 }
    273 
    274 #series-nav .pill.active {
    275   border-color: var(--accent);
    276   color: var(--accent);
    277   font-weight: 600;
    278 }
    279 
    280 #series-nav .pill .badge {
    281   display: inline-block;
    282   min-width: 1.2em;
    283   margin-left: 0.35em;
    284   border-radius: 999px;
    285   background: var(--accent);
    286   color: #fff;
    287   font-size: 11px;
    288   text-align: center;
    289 }
    290 
    291 #content { padding: 0 1rem 4rem; max-width: 72rem; margin: 0 auto; }
    292 
    293 .commit-message {
    294   position: relative;
    295   margin: 0;
    296   padding: 0.8rem 1rem;
    297   border: 0;
    298   border-bottom: 1px solid var(--border);
    299   border-radius: 0;
    300   background: transparent;
    301   white-space: pre-wrap;
    302   font-family: ui-monospace, monospace;
    303 }
    304 
    305 .commit-message .btn-general {
    306   display: block;
    307   margin: 0.6rem 0 0;
    308 }
    309 
    310 .commit-message .patch-headers {
    311   margin: 0 0 0.4rem;
    312   font-family: ui-monospace, "Cascadia Mono", "Source Code Pro", monospace;
    313   color: var(--fg);
    314 }
    315 .commit-message .patch-headers dt {
    316   display: inline;
    317   color: var(--fg);
    318   margin-right: 0.3em;
    319 }
    320 .commit-message .patch-headers dd {
    321   display: inline;
    322   margin: 0;
    323   color: var(--fg);
    324 }
    325 .commit-message .patch-headers code {
    326   font-family: inherit;
    327   color: var(--fg);
    328 }
    329 .commit-message .patch-headers > dt + dd::after { content: "\A"; white-space: pre; }
    330 
    331 .file {
    332   margin-bottom: 1rem;
    333   border: 1px solid var(--border);
    334   border-radius: 6px;
    335   overflow: hidden;
    336 }
    337 
    338 .file > summary {
    339   padding: 0.4rem 0.8rem;
    340   background: var(--surface);
    341   font-family: ui-monospace, monospace;
    342   font-weight: 600;
    343   cursor: pointer;
    344 }
    345 
    346 .file .file-note { padding: 0.4rem 0.8rem; color: var(--muted); }
    347 
    348 .binary-note {
    349   padding: 0.4rem 0.8rem 0.6rem;
    350   color: var(--muted);
    351   border-left: 3px solid #d1242f;
    352 }
    353 .binary-note .file-note { padding: 0; }
    354 .binary-note button { margin-top: 0.4rem; }
    355 .binary-note .binary-raw {
    356   margin: 0.5rem 0 0;
    357   padding: 0.5rem 0.7rem;
    358   max-height: 16rem;
    359   overflow: auto;
    360   border: 1px solid var(--border);
    361   border-radius: 6px;
    362   background: var(--bg);
    363   font: 11px/1.4 ui-monospace, monospace;
    364   white-space: pre-wrap;
    365   color: var(--muted);
    366 }
    367 
    368 table.diff {
    369   width: 100%;
    370   border-collapse: collapse;
    371   font: 12px/1.45 ui-monospace, "Cascadia Mono", "Source Code Pro", monospace;
    372 }
    373 
    374 table.diff td { padding: 0 0.5em; vertical-align: top; }
    375 
    376 td.gutter {
    377   width: 3em;
    378   min-width: 3em;
    379   text-align: right;
    380   color: var(--muted);
    381   user-select: none;
    382   border-right: 1px solid var(--border);
    383 }
    384 
    385 td.code { white-space: pre-wrap; word-break: break-all; width: 100%; }
    386 
    387 tr.hunk-header td {
    388   background: var(--hunk-bg);
    389   color: var(--muted);
    390   padding: 0.2em 0.5em;
    391 }
    392 
    393 tr.line { cursor: pointer; }
    394 tr.line:hover td.code { box-shadow: inset 2px 0 0 var(--accent); }
    395 tr.line.add td { background: var(--add-bg); }
    396 tr.line.add td.gutter { background: var(--add-gutter); }
    397 tr.line.del td { background: var(--del-bg); }
    398 tr.line.del td.gutter { background: var(--del-gutter); }
    399 tr.line.commented td.code { box-shadow: inset 3px 0 0 var(--accent); }
    400 
    401 /* Intra-line highlight: the span that actually changed within a line. */
    402 tr.line.add td.code .hl { background: var(--add-gutter); border-radius: 2px; }
    403 tr.line.del td.code .hl { background: var(--del-gutter); border-radius: 2px; }
    404 
    405 tr.comment-row td { padding: 0; }
    406 
    407 .comment-box {
    408   margin: 0.3rem 2rem 0.5rem 3.5rem;
    409   border: 1px solid var(--border);
    410   border-radius: 6px;
    411   background: var(--comment-bg);
    412   font-family: -apple-system, "Segoe UI", sans-serif;
    413 }
    414 
    415 .comment-box .comment-text { padding: 0.5rem 0.8rem; white-space: pre-wrap; }
    416 
    417 .comment-box textarea {
    418   display: block;
    419   width: 100%;
    420   min-height: 4.5em;
    421   padding: 0.5rem 0.8rem;
    422   border: 0;
    423   border-bottom: 1px solid var(--border);
    424   background: transparent;
    425   color: var(--fg);
    426   font: inherit;
    427   resize: vertical;
    428 }
    429 
    430 .comment-box .comment-actions {
    431   display: flex;
    432   gap: 0.4rem;
    433   justify-content: flex-end;
    434   padding: 0.3rem 0.5rem;
    435   /* The editor inherits the yellow comment-box bg; the action row should read
    436      as a distinct footer, not blend into the yellow. */
    437   background: var(--surface);
    438   border-radius: 0 0 5px 5px;
    439 }
    440 
    441 #send-confirm {
    442   position: fixed;
    443   inset: 0;
    444   display: flex;
    445   align-items: center;
    446   justify-content: center;
    447   background: rgba(0, 0, 0, 0.4);
    448   z-index: 5;
    449 }
    450 
    451 #send-confirm .modal-card {
    452   width: min(32rem, 90vw);
    453   border: 1px solid var(--border);
    454   border-radius: 8px;
    455   background: var(--bg);
    456   box-shadow: 0 12px 32px rgba(0, 0, 0, 0.45);
    457   padding: 1.2rem 1.4rem;
    458 }
    459 
    460 #send-confirm h2 {
    461   margin: 0 0 0.4rem;
    462   font-size: 15px;
    463 }
    464 
    465 #send-confirm-summary {
    466   margin: 0 0 0.8rem;
    467   color: var(--muted);
    468   font-size: 12px;
    469 }
    470 
    471 #send-confirm #srht-status {
    472   display: flex;
    473   align-items: center;
    474   gap: 0.4rem;
    475   margin: 0 0 0.8rem;
    476   color: var(--muted);
    477 }
    478 
    479 #send-confirm #srht-status select {
    480   padding: 0.2rem 0.3rem;
    481   border: 1px solid var(--border);
    482   border-radius: 6px;
    483   background: var(--bg);
    484   color: var(--fg);
    485   font: inherit;
    486 }
    487 
    488 #send-confirm .modal-actions {
    489   display: flex;
    490   gap: 0.5rem;
    491   justify-content: flex-end;
    492 }
    493 
    494 #status {
    495   position: fixed;
    496   inset: auto 1rem 1rem auto;
    497   width: min(44rem, 90vw);
    498   max-height: 60vh;
    499   display: flex;
    500   flex-direction: column;
    501   border: 1px solid var(--border);
    502   border-radius: 6px;
    503   background: var(--surface);
    504   box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
    505   z-index: 3;
    506 }
    507 
    508 #status-text {
    509   margin: 0;
    510   padding: 0.8rem 1rem;
    511   overflow: auto;
    512   font: 12px/1.45 ui-monospace, monospace;
    513   white-space: pre-wrap;
    514 }
    515 
    516 #status-buttons {
    517   display: flex;
    518   gap: 0.5rem;
    519   justify-content: flex-end;
    520   padding: 0.5rem;
    521   border-top: 1px solid var(--border);
    522 }
    523 
    524 .error { color: #d1242f; }
    525 @media (prefers-color-scheme: dark) { .error { color: #ff7b72; } }