@magland/mochi 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +108 -0
- package/dist/ansi.js +174 -0
- package/dist/api/admin.js +416 -0
- package/dist/api/auth.js +166 -0
- package/dist/api/backup.js +598 -0
- package/dist/api/ci.js +336 -0
- package/dist/api/contents.js +339 -0
- package/dist/api/issues.js +165 -0
- package/dist/api/pulls.js +244 -0
- package/dist/api/releases.js +83 -0
- package/dist/api/repos.js +156 -0
- package/dist/api/write.js +518 -0
- package/dist/api.js +326 -0
- package/dist/assets.js +29 -0
- package/dist/atom.js +32 -0
- package/dist/atomic.js +171 -0
- package/dist/avatar.js +81 -0
- package/dist/browse.js +630 -0
- package/dist/build-info.json +4 -0
- package/dist/ci/actionref.js +86 -0
- package/dist/ci/api.js +829 -0
- package/dist/ci/artifacts.js +201 -0
- package/dist/ci/dispatch.js +30 -0
- package/dist/ci/engine.js +1321 -0
- package/dist/ci/expr.js +526 -0
- package/dist/ci/manual.js +199 -0
- package/dist/ci/present.js +82 -0
- package/dist/ci/protocol.js +6 -0
- package/dist/ci/runners.js +256 -0
- package/dist/ci/runs.js +208 -0
- package/dist/ci/trigger.js +28 -0
- package/dist/ci/views.js +441 -0
- package/dist/ci/wake.js +194 -0
- package/dist/ci/web.js +617 -0
- package/dist/ci/workflow.js +436 -0
- package/dist/cli/admin-cmd.js +324 -0
- package/dist/cli/api-cmd.js +128 -0
- package/dist/cli/backup-cmd.js +1500 -0
- package/dist/cli/exit.js +69 -0
- package/dist/cli/input.js +64 -0
- package/dist/cli/issue-cmd.js +243 -0
- package/dist/cli/output.js +93 -0
- package/dist/cli/parse.js +317 -0
- package/dist/cli/pr-cmd.js +289 -0
- package/dist/cli/release-cmd.js +171 -0
- package/dist/cli/repo-cmd.js +763 -0
- package/dist/cli/repo.js +101 -0
- package/dist/cli/run-cmd.js +438 -0
- package/dist/cli/target.js +54 -0
- package/dist/cli-api.js +84 -0
- package/dist/compare.js +111 -0
- package/dist/config.js +212 -0
- package/dist/credentials.js +235 -0
- package/dist/deploy-cli.js +859 -0
- package/dist/deploy-runner-cli.js +592 -0
- package/dist/diff.js +171 -0
- package/dist/discussion.js +253 -0
- package/dist/egress.js +559 -0
- package/dist/filecache.js +68 -0
- package/dist/find.js +162 -0
- package/dist/forms.js +737 -0
- package/dist/git.js +547 -0
- package/dist/githttp.js +428 -0
- package/dist/html.js +87 -0
- package/dist/icons.js +101 -0
- package/dist/import-cli.js +316 -0
- package/dist/index.js +752 -0
- package/dist/issues.js +308 -0
- package/dist/issueweb.js +447 -0
- package/dist/job-cli.js +197 -0
- package/dist/jobtoken.js +96 -0
- package/dist/languages.js +383 -0
- package/dist/layout.js +100 -0
- package/dist/lfs.js +438 -0
- package/dist/lfsstore.js +425 -0
- package/dist/limit.js +259 -0
- package/dist/logo.js +61 -0
- package/dist/markdown.js +382 -0
- package/dist/migrate.js +334 -0
- package/dist/multipart.js +90 -0
- package/dist/ops.js +869 -0
- package/dist/pagescript.js +465 -0
- package/dist/perms.js +370 -0
- package/dist/pointer.js +55 -0
- package/dist/profile.js +106 -0
- package/dist/pulls.js +320 -0
- package/dist/pullweb.js +461 -0
- package/dist/redirects.js +455 -0
- package/dist/releases.js +435 -0
- package/dist/render.js +233 -0
- package/dist/runner/actions.js +448 -0
- package/dist/runner/client.js +428 -0
- package/dist/runner/context.js +247 -0
- package/dist/runner/docker.js +197 -0
- package/dist/runner/externals.js +175 -0
- package/dist/runner/job.js +290 -0
- package/dist/runner/manual-run.js +272 -0
- package/dist/runner/overrides.js +554 -0
- package/dist/runner/steps.js +571 -0
- package/dist/runner/wake.js +84 -0
- package/dist/runner-cli.js +405 -0
- package/dist/scan.js +231 -0
- package/dist/server.js +424 -0
- package/dist/session.js +267 -0
- package/dist/site.js +259 -0
- package/dist/siteshost.js +94 -0
- package/dist/source.js +90 -0
- package/dist/style.js +1295 -0
- package/dist/themes.js +369 -0
- package/dist/vault.js +442 -0
- package/dist/version.js +88 -0
- package/dist/views.js +1007 -0
- package/dist/web.js +182 -0
- package/dist/webops.js +1402 -0
- package/package.json +71 -0
package/dist/style.js
ADDED
|
@@ -0,0 +1,1295 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// The structural stylesheet. Every color, radius, and font here comes from a
|
|
3
|
+
// custom property defined by the active theme (see themes.ts); nothing in
|
|
4
|
+
// this file names a color directly, so a new theme needs no changes here.
|
|
5
|
+
//
|
|
6
|
+
// The page is built from rules rather than from cards. A section is a 2px
|
|
7
|
+
// rule the width of the column, a caption under it, and then the content,
|
|
8
|
+
// and not a bordered panel with a filled strip across its top. The rule runs
|
|
9
|
+
// edge to edge while everything under it is inset by 12px, so the rule reads
|
|
10
|
+
// as the thing the section hangs from. A border on all four sides is reserved
|
|
11
|
+
// for what the reader can operate: buttons, inputs, menus, and list items that
|
|
12
|
+
// are themselves links. So a border means "this is a control" and a rule means
|
|
13
|
+
// "a section starts here". Fills are for code, which needs an edge of its own,
|
|
14
|
+
// and for the one row a menu or a form uses to separate itself from the page.
|
|
15
|
+
//
|
|
16
|
+
// When something must be flagged rather than merely divided, a merge state or
|
|
17
|
+
// a flash or an error, it takes a 3px rule down its left side in the colour of
|
|
18
|
+
// the news it carries. Nothing is a stadium: a pill that reports a state is a
|
|
19
|
+
// rectangle with the sheet's own corner radius, so the shape of a badge is the
|
|
20
|
+
// shape of a button and a reader learns one vocabulary rather than two.
|
|
21
|
+
//
|
|
22
|
+
// Two shapes come back from the mark in logo.ts. The square marks the active
|
|
23
|
+
// tab and stands in for a language's colour in the share list, and the
|
|
24
|
+
// monoline is why every rule on the page is one of two weights and no more.
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.CSS = void 0;
|
|
27
|
+
exports.CSS = `
|
|
28
|
+
/* The scale. Colour, radius, and the faces belong to the theme; spacing,
|
|
29
|
+
type, and measure belong to the sheet, so they live here as their own set
|
|
30
|
+
of properties and every rule below draws from them rather than from a fresh
|
|
31
|
+
round number. Six steps of space and six of type is enough for the whole
|
|
32
|
+
interface, and holding to them is what gives a page its rhythm. */
|
|
33
|
+
:root {
|
|
34
|
+
--s1: 4px;
|
|
35
|
+
--s2: 8px;
|
|
36
|
+
--s3: 12px;
|
|
37
|
+
--s4: 16px;
|
|
38
|
+
--s5: 24px;
|
|
39
|
+
--s6: 32px;
|
|
40
|
+
--s7: 48px;
|
|
41
|
+
--t-xs: 12px;
|
|
42
|
+
--t-sm: 13px;
|
|
43
|
+
--t-base: 15px;
|
|
44
|
+
--t-lg: 17px;
|
|
45
|
+
--t-xl: 20px;
|
|
46
|
+
--t-2xl: 26px;
|
|
47
|
+
/* The column the interface is read in. */
|
|
48
|
+
--page: 1120px;
|
|
49
|
+
/* The narrower column prose is read in. A line much past a hundred
|
|
50
|
+
characters loses the eye on its way back to the left margin, so a README
|
|
51
|
+
or an issue is capped even where the page has room to spare. Code, tables,
|
|
52
|
+
and images inside it still take the width they need. */
|
|
53
|
+
--measure: 820px;
|
|
54
|
+
/* The least height of anything that has to be hit. A 28px button is a fine
|
|
55
|
+
target for a pointer and a poor one for a thumb, so the number is raised
|
|
56
|
+
rather than the layout redrawn when the pointer is coarse. */
|
|
57
|
+
--touch: 30px;
|
|
58
|
+
}
|
|
59
|
+
@media (pointer: coarse) {
|
|
60
|
+
:root { --touch: 42px; }
|
|
61
|
+
}
|
|
62
|
+
* { box-sizing: border-box; }
|
|
63
|
+
/* The bar at the top stays put, so an anchored line or heading has to be
|
|
64
|
+
pushed clear of it when the page jumps to one. */
|
|
65
|
+
html { scroll-padding-top: 68px; }
|
|
66
|
+
body {
|
|
67
|
+
margin: 0;
|
|
68
|
+
font-family: var(--font-ui);
|
|
69
|
+
font-size: var(--t-base);
|
|
70
|
+
line-height: 1.55;
|
|
71
|
+
color: var(--fg);
|
|
72
|
+
background: var(--bg);
|
|
73
|
+
-webkit-text-size-adjust: 100%;
|
|
74
|
+
}
|
|
75
|
+
a { color: var(--accent); text-decoration: none; }
|
|
76
|
+
a:hover { text-decoration: underline; }
|
|
77
|
+
code, pre, .mono { font-family: var(--font-mono); }
|
|
78
|
+
/* One focus ring for everything a keyboard can reach. It is drawn only for
|
|
79
|
+
focus that came from the keyboard, so a clicked button does not light up,
|
|
80
|
+
and it sits outside the element's own border so a bordered control does not
|
|
81
|
+
have to give up its edge to show it. */
|
|
82
|
+
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
|
|
83
|
+
.container { max-width: var(--page); margin: 0 auto; padding: 0 var(--s4); }
|
|
84
|
+
/* The bar carries the way out of wherever the reader is, so it stays in view
|
|
85
|
+
rather than scrolling off the top of a long file or a long thread. */
|
|
86
|
+
.topbar {
|
|
87
|
+
position: sticky; top: 0; z-index: 30;
|
|
88
|
+
background: var(--surface); border-bottom: 1px solid var(--border);
|
|
89
|
+
}
|
|
90
|
+
.topbar .container { display: flex; align-items: center; gap: var(--s3); height: 52px; }
|
|
91
|
+
/* The brand is the logotype from logo.ts, which inherits the text colour. Its
|
|
92
|
+
250 x 75 box runs ascender to descender, the g's tail being the one thing
|
|
93
|
+
below the baseline, and the x-height band spans 20 to 60, so 20px here puts
|
|
94
|
+
the x-height at 10.7px and the word at 66.7px wide. */
|
|
95
|
+
.brand { display: flex; align-items: center; color: var(--fg); flex: none; }
|
|
96
|
+
.brand svg { display: block; height: 20px; width: auto; }
|
|
97
|
+
.brand:hover { text-decoration: none; }
|
|
98
|
+
/* The address in the bar gives way before anything else does: it shortens to
|
|
99
|
+
an ellipsis rather than pushing the account menu off the side of a phone. */
|
|
100
|
+
.topbar .crumbs {
|
|
101
|
+
color: var(--fg-subtle); font-size: var(--t-sm); flex: 0 1 auto; min-width: 0;
|
|
102
|
+
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
103
|
+
}
|
|
104
|
+
/* Quiet, because the page under it says the same thing in its own title. The
|
|
105
|
+
bar's copy is there for after the title has scrolled away, and a locator
|
|
106
|
+
that competed with the title would be read twice for no gain. */
|
|
107
|
+
.topbar .crumbs a { color: var(--fg-muted); }
|
|
108
|
+
.topbar .crumbs a:hover { color: var(--accent); }
|
|
109
|
+
.userbox { margin-left: auto; display: flex; align-items: center; gap: 12px; }
|
|
110
|
+
.user-menu > summary { display: flex; align-items: center; gap: 2px; }
|
|
111
|
+
.user-menu .dropdown-menu { width: 220px; }
|
|
112
|
+
.user-menu form { margin: 0; }
|
|
113
|
+
|
|
114
|
+
/* --- the jump box ---
|
|
115
|
+
|
|
116
|
+
The one control that goes from any page to any repository. In the bar it
|
|
117
|
+
reads as a search field rather than a button, because that is what pressing
|
|
118
|
+
it gives, and it carries its own keystroke so the shortcut is discovered by
|
|
119
|
+
people who never read a help page. On a phone the words go and the glyph
|
|
120
|
+
stays, since the bar has no room for a field that opens another field. */
|
|
121
|
+
.jump-open {
|
|
122
|
+
display: inline-flex; align-items: center; gap: 6px;
|
|
123
|
+
height: var(--touch); padding: 0 8px 0 10px;
|
|
124
|
+
background: var(--input-bg); border: 1px solid var(--border); border-radius: var(--radius);
|
|
125
|
+
color: var(--fg-subtle); font: inherit; font-size: var(--t-sm); cursor: pointer;
|
|
126
|
+
}
|
|
127
|
+
.jump-open:hover { border-color: var(--accent-soft); color: var(--fg-muted); }
|
|
128
|
+
.jump-open .glyph { color: var(--fg-subtle); flex: none; }
|
|
129
|
+
.jump-label { min-width: 96px; text-align: left; }
|
|
130
|
+
kbd {
|
|
131
|
+
font: inherit; font-family: var(--font-mono); font-size: 11px; line-height: 1;
|
|
132
|
+
padding: 3px 5px; border: 1px solid var(--border); border-bottom-width: 2px;
|
|
133
|
+
border-radius: 4px; background: var(--surface); color: var(--fg-subtle);
|
|
134
|
+
}
|
|
135
|
+
.jump::backdrop { background: var(--overlay); }
|
|
136
|
+
.jump {
|
|
137
|
+
width: min(560px, calc(100vw - 2 * var(--s4)));
|
|
138
|
+
padding: 0; border: 1px solid var(--border); border-radius: var(--radius);
|
|
139
|
+
background: var(--bg); color: var(--fg);
|
|
140
|
+
box-shadow: 0 16px 48px var(--shadow);
|
|
141
|
+
/* Near the top rather than centred: the list grows downwards, and a box that
|
|
142
|
+
re-centres itself as results arrive is a box whose first result moves out
|
|
143
|
+
from under the pointer. */
|
|
144
|
+
margin: 12vh auto auto;
|
|
145
|
+
}
|
|
146
|
+
.jump-field { display: flex; align-items: center; gap: var(--s2); padding: 0 var(--s4); border-bottom: 1px solid var(--border-soft); }
|
|
147
|
+
.jump-glyph { color: var(--fg-subtle); flex: none; }
|
|
148
|
+
/* The dialog's own edge is the field's edge, so the input draws none of its
|
|
149
|
+
own. Matched on the attribute as well as the class, or the general rule for
|
|
150
|
+
text inputs further down the sheet would put a box back around it. */
|
|
151
|
+
.jump-field input[type="text"] {
|
|
152
|
+
flex: 1; width: auto; max-width: none; border: 0; background: transparent; color: var(--fg);
|
|
153
|
+
font: inherit; font-size: var(--t-lg); padding: 14px 0;
|
|
154
|
+
}
|
|
155
|
+
.jump-field input:focus { outline: none; }
|
|
156
|
+
.jump-list { list-style: none; margin: 0; padding: var(--s2) 0; max-height: 52vh; overflow-y: auto; }
|
|
157
|
+
.jump-group {
|
|
158
|
+
padding: var(--s2) var(--s4) 4px;
|
|
159
|
+
font-size: var(--t-xs); text-transform: uppercase; letter-spacing: 0.06em;
|
|
160
|
+
color: var(--fg-subtle);
|
|
161
|
+
}
|
|
162
|
+
.jump-item {
|
|
163
|
+
display: flex; align-items: center; gap: var(--s2);
|
|
164
|
+
padding: 7px var(--s4); min-height: var(--touch);
|
|
165
|
+
color: var(--fg); font-size: var(--t-sm);
|
|
166
|
+
}
|
|
167
|
+
.jump-item:hover { text-decoration: none; }
|
|
168
|
+
/* The highlight follows the keyboard, and the pointer moves the keyboard, so
|
|
169
|
+
there is only ever one selected row however the reader is driving. */
|
|
170
|
+
.jump-item.on { background: var(--accent); color: var(--on-primary); }
|
|
171
|
+
.jump-note { margin-left: auto; color: var(--fg-subtle); font-size: var(--t-xs); }
|
|
172
|
+
.jump-item.on .jump-note { color: inherit; opacity: 0.75; }
|
|
173
|
+
.jump-empty { padding: var(--s4); color: var(--fg-subtle); font-size: var(--t-sm); }
|
|
174
|
+
.jump-foot {
|
|
175
|
+
display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
|
|
176
|
+
padding: var(--s2) var(--s4); border-top: 1px solid var(--border-soft);
|
|
177
|
+
color: var(--fg-subtle); font-size: var(--t-xs);
|
|
178
|
+
}
|
|
179
|
+
@media (max-width: 700px) {
|
|
180
|
+
.jump-label, .jump-key { display: none; }
|
|
181
|
+
.jump-open { padding: 0 8px; }
|
|
182
|
+
.jump { margin-top: 6vh; }
|
|
183
|
+
.jump-foot { display: none; }
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/* An identicon in its frame (see avatar.ts): a circle for a person, a rounded
|
|
187
|
+
square for a collection, so the two kinds of owner are told apart without
|
|
188
|
+
reading the name under them. */
|
|
189
|
+
.avatar { display: inline-flex; flex: none; border-radius: 50%; overflow: hidden; background: var(--surface); }
|
|
190
|
+
.avatar.square { border-radius: var(--radius); }
|
|
191
|
+
.avatar svg { display: block; width: 100%; height: 100%; }
|
|
192
|
+
.with-avatar { display: flex; align-items: center; gap: 8px; }
|
|
193
|
+
|
|
194
|
+
/* The file finder: one box, and the tree under it. */
|
|
195
|
+
.find-input {
|
|
196
|
+
width: 100%; padding: 8px 14px; font-size: 15px; font-family: inherit; margin-bottom: 12px;
|
|
197
|
+
border: 1px solid var(--border); border-radius: var(--radius); background: var(--input-bg); color: var(--fg);
|
|
198
|
+
}
|
|
199
|
+
.find-list { border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
|
|
200
|
+
.find-item {
|
|
201
|
+
display: flex; align-items: center; gap: 8px; padding: 6px 12px; color: var(--fg);
|
|
202
|
+
border-top: 1px solid var(--border-soft); font-family: var(--font-mono); font-size: 12px;
|
|
203
|
+
}
|
|
204
|
+
.find-item:first-child { border-top: none; }
|
|
205
|
+
.find-item:hover { background: var(--surface); text-decoration: none; }
|
|
206
|
+
.find-item .icon { margin-right: 0; }
|
|
207
|
+
|
|
208
|
+
.list-filter {
|
|
209
|
+
width: 320px; max-width: 100%; padding: 5px 12px; font-size: 14px; font-family: inherit;
|
|
210
|
+
border: 1px solid var(--border); border-radius: var(--radius); background: var(--input-bg); color: var(--fg);
|
|
211
|
+
}
|
|
212
|
+
.btn-link { border: none; background: none; padding: 0; font: inherit; color: var(--accent); cursor: pointer; }
|
|
213
|
+
.btn-link:hover { text-decoration: underline; }
|
|
214
|
+
main { padding: var(--s5) var(--s4) var(--s7); }
|
|
215
|
+
/* The foot of every page, carrying the build stamp and nothing else (see
|
|
216
|
+
buildStamp in views.ts). It is set small and quiet because it is looked up
|
|
217
|
+
rather than read: an operator checking which build is live wants it to be
|
|
218
|
+
there, and everybody else wants it out of the way. Ordinary inline flow, so
|
|
219
|
+
a narrow screen wraps the line instead of scrolling it. */
|
|
220
|
+
.pagefoot { border-top: 1px solid var(--border); }
|
|
221
|
+
.pagefoot .container { padding-top: var(--s3); padding-bottom: var(--s5); color: var(--fg-subtle); font-size: var(--t-xs); }
|
|
222
|
+
.pagefoot .mono { font-size: 11px; }
|
|
223
|
+
.foot-sep { color: var(--border); }
|
|
224
|
+
/* Titles are set larger than the body by a clear step rather than a nudge, so
|
|
225
|
+
the top of a page announces itself and the reader knows where they are
|
|
226
|
+
before reading a word of the content. */
|
|
227
|
+
h1 { font-family: var(--font-head); font-size: var(--t-2xl); line-height: 1.25; letter-spacing: -0.01em; margin: 0 0 var(--s4); }
|
|
228
|
+
h2 { font-family: var(--font-head); font-size: var(--t-xl); line-height: 1.3; }
|
|
229
|
+
h3 { font-family: var(--font-head); font-size: var(--t-lg); line-height: 1.35; }
|
|
230
|
+
.muted { color: var(--fg-muted); }
|
|
231
|
+
.small { font-size: var(--t-xs); }
|
|
232
|
+
.page-head { display: flex; align-items: center; justify-content: space-between; gap: var(--s3); margin-bottom: var(--s4); flex-wrap: wrap; }
|
|
233
|
+
.page-head h1, .page-head h2 { margin: 0; }
|
|
234
|
+
|
|
235
|
+
/* Icons are inline SVG (see icons.ts). text-bottom is what sits a 16px glyph
|
|
236
|
+
on the same baseline as the label beside it; flex:none keeps one from being
|
|
237
|
+
squeezed when it sits in a flex row. */
|
|
238
|
+
.glyph { display: inline-block; vertical-align: text-bottom; flex: none; overflow: visible; }
|
|
239
|
+
|
|
240
|
+
/* On a repository's pages there is no h1: the address is the title, so it is
|
|
241
|
+
set at the size a title is set at, and the repository's own name carries the
|
|
242
|
+
weight while the collection before it stays plain. */
|
|
243
|
+
.repo-title { display: flex; align-items: center; gap: var(--s2); font-size: var(--t-xl); margin-bottom: var(--s2); }
|
|
244
|
+
.repo-title .icon { color: var(--fg-muted); margin-right: 0; }
|
|
245
|
+
.repo-title b { font-weight: 600; }
|
|
246
|
+
/* The repository's sections. The active one is marked by the square from the
|
|
247
|
+
mark in logo.ts, set under the middle of the label and clear of the rule,
|
|
248
|
+
rather than by a bar drawn under the tab's whole width. The marker is
|
|
249
|
+
inside the tab's padding box because .tabs scrolls sideways on a narrow
|
|
250
|
+
screen, and anything hanging below it would be clipped. */
|
|
251
|
+
.tabs {
|
|
252
|
+
display: flex; gap: var(--s1); border-bottom: 1px solid var(--border);
|
|
253
|
+
margin-bottom: var(--s4); overflow-x: auto;
|
|
254
|
+
/* The row scrolls on a narrow screen. A scrollbar under it would read as a
|
|
255
|
+
second rule beside the one the tabs already hang from, so it is taken away
|
|
256
|
+
and the row fades out at its trailing edge instead. A tab cut off square at
|
|
257
|
+
the screen's edge reads as a page too wide for the phone; a tab fading out
|
|
258
|
+
reads as a row that continues. The mask is an alpha ramp and not a colour,
|
|
259
|
+
which is why it works over whatever the theme paints behind it. */
|
|
260
|
+
scrollbar-width: none; -webkit-overflow-scrolling: touch;
|
|
261
|
+
mask-image: linear-gradient(to right, black calc(100% - 28px), transparent 100%);
|
|
262
|
+
}
|
|
263
|
+
.tabs::-webkit-scrollbar { display: none; }
|
|
264
|
+
.tab {
|
|
265
|
+
position: relative; display: flex; align-items: center; gap: var(--s2); white-space: nowrap;
|
|
266
|
+
padding: var(--s2) var(--s3) 14px; color: var(--fg);
|
|
267
|
+
}
|
|
268
|
+
.tab .glyph { color: var(--fg-muted); }
|
|
269
|
+
.tab:hover { color: var(--accent); text-decoration: none; }
|
|
270
|
+
.tab:hover .glyph { color: var(--accent); }
|
|
271
|
+
.tab.active { font-weight: 600; }
|
|
272
|
+
.tab.active .glyph { color: var(--fg); }
|
|
273
|
+
.tab.active::after {
|
|
274
|
+
content: ''; position: absolute; left: 50%; bottom: 4px; margin-left: -3px;
|
|
275
|
+
width: 6px; height: 6px; background: var(--tab-marker);
|
|
276
|
+
}
|
|
277
|
+
/* A count beside a label is data, not decoration: it is set in the mono face
|
|
278
|
+
and left unenclosed. */
|
|
279
|
+
.counter { font-family: var(--font-mono); font-size: 12px; color: var(--fg-subtle); margin-left: 6px; }
|
|
280
|
+
/* A word set against a name: Default, and its kin. */
|
|
281
|
+
.badge { display: inline-block; border: 1px solid var(--accent); color: var(--accent); border-radius: var(--radius); padding: 0 6px; font-size: 12px; line-height: 18px; }
|
|
282
|
+
.ref-name { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
|
|
283
|
+
.ref-name .icon { color: var(--fg-muted); margin-right: 0; }
|
|
284
|
+
.ref-name div { flex-basis: 100%; }
|
|
285
|
+
.ref-form { padding: 16px; width: 300px; }
|
|
286
|
+
.ref-form .field:last-of-type { margin-bottom: 16px; }
|
|
287
|
+
|
|
288
|
+
.toolbar { display: flex; align-items: center; justify-content: space-between; gap: var(--s3); margin-bottom: var(--s3); flex-wrap: wrap; }
|
|
289
|
+
/* flex:1 is load-bearing: a wrapping flex container's max-content width is its
|
|
290
|
+
widest item rather than the sum, so without it .left collapses to the ref
|
|
291
|
+
selector and drops the breadcrumb onto a second line. */
|
|
292
|
+
.toolbar .left { display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap; flex: 1 1 auto; min-width: 0; }
|
|
293
|
+
/* A row of buttons wraps rather than running off the side of the page. Without
|
|
294
|
+
this the four controls on a tree page (Go to file, History, Fork, Code) are
|
|
295
|
+
wider than a phone, and the last of them is simply unreachable. */
|
|
296
|
+
.right-group { display: flex; align-items: center; gap: var(--s2) 6px; flex-wrap: wrap; min-width: 0; }
|
|
297
|
+
.crumb { font-size: var(--t-base); }
|
|
298
|
+
.crumb b { font-weight: 600; }
|
|
299
|
+
|
|
300
|
+
/* Menus: a <details> whose summary is a button and whose body is a popover.
|
|
301
|
+
The ref picker, the Code button, and the workflow dispatch form all use it. */
|
|
302
|
+
.dropdown { position: relative; }
|
|
303
|
+
.dropdown > summary { list-style: none; cursor: pointer; }
|
|
304
|
+
.dropdown > summary::-webkit-details-marker { display: none; }
|
|
305
|
+
.dropdown > summary .caret { opacity: 0.7; }
|
|
306
|
+
.dropdown-menu {
|
|
307
|
+
position: absolute; left: 0; z-index: 20; margin-top: 6px; width: 320px; max-width: 92vw;
|
|
308
|
+
background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius);
|
|
309
|
+
box-shadow: 0 8px 24px var(--shadow); text-align: left; overflow: hidden;
|
|
310
|
+
}
|
|
311
|
+
.dropdown-menu.dd-right { left: auto; right: 0; }
|
|
312
|
+
.dropdown-menu > .cmd-row, .dropdown-menu > p { margin: 10px 12px; }
|
|
313
|
+
.dd-section {
|
|
314
|
+
padding: 8px 12px; font-size: 12px; font-weight: 600; color: var(--fg-muted);
|
|
315
|
+
border-bottom: 1px solid var(--border-soft); background: var(--surface);
|
|
316
|
+
}
|
|
317
|
+
.dd-group + .dd-group .dd-section { border-top: 1px solid var(--border-soft); }
|
|
318
|
+
.dd-filter {
|
|
319
|
+
display: block; width: calc(100% - 24px); margin: 10px 12px; padding: 5px 10px; font-size: 13px;
|
|
320
|
+
border: 1px solid var(--border); border-radius: var(--radius); background: var(--input-bg); color: var(--fg);
|
|
321
|
+
}
|
|
322
|
+
.dd-scroll { max-height: 320px; overflow-y: auto; }
|
|
323
|
+
.dd-item {
|
|
324
|
+
display: flex; align-items: center; gap: 8px; padding: 7px 12px; color: var(--fg);
|
|
325
|
+
border-top: 1px solid var(--border-soft); font-size: 13px;
|
|
326
|
+
}
|
|
327
|
+
.dd-item:first-child { border-top: none; }
|
|
328
|
+
.dd-item:hover { background: var(--surface); text-decoration: none; }
|
|
329
|
+
button.dd-item { width: 100%; background: none; font: inherit; font-size: 13px; cursor: pointer; }
|
|
330
|
+
.dd-item.current { font-weight: 600; }
|
|
331
|
+
.dd-item .dd-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
332
|
+
.dd-check { width: 16px; flex: none; color: var(--accent); }
|
|
333
|
+
.dd-current { max-width: 240px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
334
|
+
|
|
335
|
+
/* Appearance menu. The check column keeps its width whether or not the glyph is
|
|
336
|
+
showing, so the labels do not shift as the reader moves between themes. */
|
|
337
|
+
.theme-menu > summary {
|
|
338
|
+
display: flex; align-items: center; justify-content: center;
|
|
339
|
+
width: var(--touch); height: var(--touch); border-radius: var(--radius); color: var(--fg-muted);
|
|
340
|
+
}
|
|
341
|
+
.theme-menu > summary:hover { background: var(--surface-hover); color: var(--fg); }
|
|
342
|
+
.theme-menu .dropdown-menu { width: 200px; }
|
|
343
|
+
.theme-item { text-align: left; }
|
|
344
|
+
.theme-check { width: 16px; flex: none; color: var(--accent); visibility: hidden; }
|
|
345
|
+
[aria-checked='true'] > .theme-check { visibility: visible; }
|
|
346
|
+
[aria-checked='true'].theme-item { font-weight: 600; }
|
|
347
|
+
.clone-menu .cmd-row input {
|
|
348
|
+
flex: 1; min-width: 0; padding: 5px 8px; font-size: 12px; border: 1px solid var(--border);
|
|
349
|
+
border-radius: var(--radius); background: var(--input-bg); color: var(--fg); font-family: var(--font-mono);
|
|
350
|
+
}
|
|
351
|
+
.btn {
|
|
352
|
+
display: inline-flex; align-items: center; justify-content: center; gap: 6px;
|
|
353
|
+
min-height: var(--touch); padding: 4px var(--s3); font-size: var(--t-sm);
|
|
354
|
+
border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface);
|
|
355
|
+
color: var(--fg); cursor: pointer; font-family: inherit; line-height: 1.5; white-space: nowrap;
|
|
356
|
+
}
|
|
357
|
+
.btn:hover { background: var(--surface-hover); text-decoration: none; }
|
|
358
|
+
.btn-primary { background: var(--primary); border-color: var(--primary); color: var(--on-primary); font-weight: 600; }
|
|
359
|
+
.btn-primary:hover { background: var(--primary-hover); }
|
|
360
|
+
.btn-danger { background: var(--danger); border-color: var(--danger); color: var(--on-danger); font-weight: 600; }
|
|
361
|
+
.btn-danger:hover { background: var(--danger-hover); }
|
|
362
|
+
.btn-danger-outline { color: var(--danger); }
|
|
363
|
+
.btn-danger-outline:hover { background: var(--danger); border-color: var(--danger); color: var(--on-danger); }
|
|
364
|
+
|
|
365
|
+
/* The repository root: the listing beside the About panel, stacking on a
|
|
366
|
+
narrow screen. Every other tree page is one column. */
|
|
367
|
+
.repo-layout { display: flex; align-items: flex-start; gap: 24px; }
|
|
368
|
+
.repo-main { flex: 1 1 auto; min-width: 0; }
|
|
369
|
+
.repo-side { flex: 0 0 296px; }
|
|
370
|
+
/* The panel beside the listing is divided the way the rest of the page is:
|
|
371
|
+
each block hangs from its own rule under an uppercase caption, so About,
|
|
372
|
+
Contributors, and Languages read as three sections of one column rather than
|
|
373
|
+
as three stacked cards. The first block's rule lines up with the listing's. */
|
|
374
|
+
.side-block { border-top: 2px solid var(--border); padding: var(--s3) 0 var(--s4); }
|
|
375
|
+
.side-block:last-child { padding-bottom: 0; }
|
|
376
|
+
.side-block h3 {
|
|
377
|
+
display: flex; align-items: center; gap: var(--s2); margin: 0 0 var(--s2);
|
|
378
|
+
font-family: var(--font-head); font-size: var(--t-sm); font-weight: 600;
|
|
379
|
+
letter-spacing: 0.04em; text-transform: uppercase;
|
|
380
|
+
}
|
|
381
|
+
.side-edit { margin-left: auto; color: var(--fg-muted); display: flex; }
|
|
382
|
+
.side-edit:hover { color: var(--accent); }
|
|
383
|
+
.side-desc { margin: 0 0 12px; }
|
|
384
|
+
.side-links { display: flex; flex-direction: column; gap: 8px; }
|
|
385
|
+
.side-links a { display: flex; align-items: center; gap: 8px; color: var(--fg); font-size: 13px; }
|
|
386
|
+
.side-links a:hover { color: var(--accent); text-decoration: none; }
|
|
387
|
+
.side-links .glyph { color: var(--fg-muted); }
|
|
388
|
+
/* When a two-column layout stacks, align-items has to be released along with
|
|
389
|
+
the direction: flex-start on a column means "as wide as your content", which
|
|
390
|
+
leaves the listing and the readme narrower than the panel that was beside
|
|
391
|
+
them a moment ago. Every stacking layout below does the same. */
|
|
392
|
+
@media (max-width: 1000px) {
|
|
393
|
+
.repo-layout { flex-direction: column; align-items: stretch; }
|
|
394
|
+
.repo-side { flex: 1 1 auto; width: 100%; }
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/* A listing is a rule, a caption row where there is one, and then the rows.
|
|
398
|
+
It has no frame: the page's column is its left and right edge. */
|
|
399
|
+
table.listing { width: 100%; border-collapse: collapse; border-top: 2px solid var(--border); }
|
|
400
|
+
table.listing th {
|
|
401
|
+
text-align: left; font-family: var(--font-head); font-size: 12px; font-weight: 600;
|
|
402
|
+
letter-spacing: 0.06em; text-transform: uppercase; color: var(--fg-muted);
|
|
403
|
+
padding: 8px 12px; border-bottom: 1px solid var(--border);
|
|
404
|
+
}
|
|
405
|
+
table.listing td { padding: 7px 12px; border-top: 1px solid var(--border-soft); }
|
|
406
|
+
table.listing tr:first-child td { border-top: none; }
|
|
407
|
+
table.listing tr:hover td { background: var(--surface); }
|
|
408
|
+
td.right, th.right { text-align: right; }
|
|
409
|
+
.icon { display: inline-block; width: 16px; text-align: center; margin-right: 6px; color: var(--accent-soft); }
|
|
410
|
+
.icon.file { color: var(--fg-subtle); }
|
|
411
|
+
|
|
412
|
+
/* The link to a repository's published site, in a collection listing. It sits
|
|
413
|
+
after the name as an icon alone, muted until the row is pointed at, so it
|
|
414
|
+
reads as an aside to the repository rather than a second name. */
|
|
415
|
+
.site-link { display: inline-flex; vertical-align: text-bottom; margin-left: 8px; color: var(--fg-subtle); }
|
|
416
|
+
.site-link:hover { color: var(--accent); }
|
|
417
|
+
|
|
418
|
+
/* Tree listings: name, then the message and age of the commit that last
|
|
419
|
+
touched the entry. The name column is sized to the content so long file
|
|
420
|
+
names are not truncated, and the message column takes the slack. */
|
|
421
|
+
table.listing.tree td.tree-name { width: 1%; white-space: nowrap; }
|
|
422
|
+
table.listing.tree td.tree-message { max-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
423
|
+
table.listing.tree td.tree-message a { color: var(--fg-muted); }
|
|
424
|
+
table.listing.tree td.tree-message a:hover { color: var(--accent); }
|
|
425
|
+
table.listing.tree td.tree-age { width: 1%; white-space: nowrap; }
|
|
426
|
+
@media (max-width: 700px) {
|
|
427
|
+
table.listing.tree td.tree-message { display: none; }
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/* The last commit to touch the tree sits on the listing's own rule and is
|
|
431
|
+
divided from the rows by a hairline, so the two read as one section. */
|
|
432
|
+
.latest-commit {
|
|
433
|
+
display: flex; justify-content: space-between; gap: 12px; align-items: center; flex-wrap: wrap;
|
|
434
|
+
border-top: 2px solid var(--border); border-bottom: 1px solid var(--border);
|
|
435
|
+
padding: 8px 12px;
|
|
436
|
+
}
|
|
437
|
+
.latest-commit + table.listing { border-top: none; }
|
|
438
|
+
.latest-commit .lc-main { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
439
|
+
.latest-commit .lc-main a { color: var(--fg-muted); }
|
|
440
|
+
.latest-commit .lc-main a:hover { color: var(--accent); }
|
|
441
|
+
.latest-commit .lc-meta { display: flex; align-items: center; gap: 8px; font-size: 12px; }
|
|
442
|
+
.lc-history { display: flex; align-items: center; gap: 6px; color: var(--fg); padding-left: 8px; border-left: 1px solid var(--border); }
|
|
443
|
+
.lc-history:hover { color: var(--accent); text-decoration: none; }
|
|
444
|
+
.lc-history .glyph { color: var(--fg-muted); }
|
|
445
|
+
|
|
446
|
+
/* A section of the page: the rule, the caption on it, and the content under
|
|
447
|
+
it, running the full width of the column rather than sitting inside a
|
|
448
|
+
frame. The caption is in the display face so that it reads as a title and
|
|
449
|
+
not as a row of the content. */
|
|
450
|
+
.box { border-top: 2px solid var(--border); margin-top: var(--s5); }
|
|
451
|
+
/* The caption on a section's rule carries the page's colour and weight rather
|
|
452
|
+
than the muted grey of secondary text: it is the answer to "what is this",
|
|
453
|
+
and a page whose every caption recedes reads as one undivided field. It is
|
|
454
|
+
not set in capitals, because some of these captions are a filename and one
|
|
455
|
+
of them is a whole sentence; the weight is what does the work. */
|
|
456
|
+
.box-header {
|
|
457
|
+
display: flex; align-items: center; gap: var(--s2); padding: 9px var(--s3) 8px;
|
|
458
|
+
border-bottom: 1px solid var(--border-soft);
|
|
459
|
+
font-family: var(--font-head); font-size: var(--t-sm); font-weight: 600;
|
|
460
|
+
letter-spacing: 0.01em; color: var(--fg);
|
|
461
|
+
}
|
|
462
|
+
.box-header .glyph { color: var(--fg-subtle); }
|
|
463
|
+
.box-body { padding: var(--s4) var(--s3) var(--s1); }
|
|
464
|
+
.box-header a { color: var(--fg); }
|
|
465
|
+
.box-header a:hover { color: var(--accent); text-decoration: none; }
|
|
466
|
+
|
|
467
|
+
.code-meta {
|
|
468
|
+
display: flex; justify-content: space-between; align-items: center; gap: 12px;
|
|
469
|
+
border-top: 2px solid var(--border); border-bottom: 1px solid var(--border);
|
|
470
|
+
padding: 7px 12px;
|
|
471
|
+
}
|
|
472
|
+
/* The file view is one element per line, so a line can be linked to and
|
|
473
|
+
highlighted when linked to (#L12). The row is as wide as its content but
|
|
474
|
+
never narrower than the viewport, which is what lets the highlight run the
|
|
475
|
+
full width; the number stays put while the code scrolls under it. */
|
|
476
|
+
.code-lines {
|
|
477
|
+
background: var(--code-bg); overflow-x: auto; padding: 8px 0;
|
|
478
|
+
font-family: var(--font-mono); font-size: 12px; line-height: 20px;
|
|
479
|
+
}
|
|
480
|
+
.cline { display: flex; width: max-content; min-width: 100%; }
|
|
481
|
+
.cline:target { background: var(--line-mark); }
|
|
482
|
+
.lnum {
|
|
483
|
+
position: sticky; left: 0; z-index: 1; flex: none; width: 56px; padding: 0 12px 0 8px;
|
|
484
|
+
text-align: right; color: var(--fg-subtle); background: var(--code-bg); user-select: none;
|
|
485
|
+
}
|
|
486
|
+
.lnum:hover { color: var(--fg-muted); text-decoration: none; }
|
|
487
|
+
.cline:target .lnum { background: var(--line-mark); color: var(--fg-muted); }
|
|
488
|
+
.ltext { white-space: pre; padding: 0 16px 0 4px; }
|
|
489
|
+
.blob-image { padding: 24px; text-align: center; background: var(--code-bg); }
|
|
490
|
+
.blob-image img { max-width: 100%; }
|
|
491
|
+
.blob-binary { padding: 32px; text-align: center; color: var(--fg-muted); }
|
|
492
|
+
.rendered { padding: 24px 12px 28px; background: var(--bg); }
|
|
493
|
+
|
|
494
|
+
/* Segmented Preview/Code switch on rendered files. */
|
|
495
|
+
.seg { display: inline-flex; border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
|
|
496
|
+
.seg a { display: inline-flex; align-items: center; gap: 6px; padding: 4px 12px; font-size: 13px; line-height: 1.5; color: var(--fg-muted); background: var(--surface); }
|
|
497
|
+
.seg a + a { border-left: 1px solid var(--border); }
|
|
498
|
+
.seg a:hover { background: var(--surface-hover); text-decoration: none; }
|
|
499
|
+
.seg a.current { background: var(--chip-bg); color: var(--fg); font-weight: 600; }
|
|
500
|
+
|
|
501
|
+
.commit-row { display: flex; justify-content: space-between; gap: 12px; align-items: center; padding: 8px 12px; }
|
|
502
|
+
/* A day's commits hang under the day, which is a caption on its own rule. */
|
|
503
|
+
.commit-day {
|
|
504
|
+
display: flex; align-items: center; gap: var(--s2); margin: var(--s5) 0 0; padding: 9px var(--s3) 8px;
|
|
505
|
+
border-top: 2px solid var(--border); border-bottom: 1px solid var(--border-soft);
|
|
506
|
+
font-family: var(--font-head); font-size: var(--t-sm); font-weight: 600;
|
|
507
|
+
letter-spacing: 0.01em; color: var(--fg);
|
|
508
|
+
}
|
|
509
|
+
.commit-day .glyph { color: var(--fg-subtle); }
|
|
510
|
+
.commit-group .commit-row { border-top: 1px solid var(--border-soft); }
|
|
511
|
+
.commit-group .commit-row:first-child { border-top: none; }
|
|
512
|
+
.commit-main { display: flex; align-items: flex-start; gap: 10px; min-width: 0; }
|
|
513
|
+
.commit-main > span { min-width: 0; }
|
|
514
|
+
.commit-row .title { font-weight: 600; color: var(--fg); }
|
|
515
|
+
.commit-row .title:hover { color: var(--accent); }
|
|
516
|
+
.sha {
|
|
517
|
+
font-family: var(--font-mono); font-size: 12px;
|
|
518
|
+
background: var(--inline-code-bg); border-radius: var(--radius); padding: 2px 6px; color: var(--accent);
|
|
519
|
+
}
|
|
520
|
+
.pagination { display: flex; gap: 8px; justify-content: center; margin-top: 16px; }
|
|
521
|
+
|
|
522
|
+
.commit-head { border-top: 2px solid var(--border); border-bottom: 1px solid var(--border-soft); margin-bottom: 16px; }
|
|
523
|
+
.commit-head .subject { font-family: var(--font-head); font-size: 17px; padding: 12px 12px 4px; }
|
|
524
|
+
.commit-head .body { padding: 0 12px; white-space: pre-wrap; color: var(--fg-muted); font-size: 13px; }
|
|
525
|
+
.commit-head .meta { display: flex; flex-wrap: wrap; gap: 16px; padding: 8px 12px 12px; color: var(--fg-muted); font-size: 12px; align-items: center; }
|
|
526
|
+
|
|
527
|
+
.diff-file { border-top: 2px solid var(--border); margin-bottom: 20px; }
|
|
528
|
+
.diff-file-header {
|
|
529
|
+
border-bottom: 1px solid var(--border-soft); padding: 8px 12px;
|
|
530
|
+
font-family: var(--font-mono); font-size: 12px;
|
|
531
|
+
}
|
|
532
|
+
.diff-body { overflow-x: auto; font-family: var(--font-mono); font-size: 12px; line-height: 20px; background: var(--code-bg); }
|
|
533
|
+
.dline { white-space: pre; padding: 0 10px; }
|
|
534
|
+
.dline.add { background: var(--diff-add); }
|
|
535
|
+
.dline.del { background: var(--diff-del); }
|
|
536
|
+
.dline.hunk { background: var(--diff-hunk); color: var(--fg-muted); }
|
|
537
|
+
.dline.meta { color: var(--fg-subtle); background: var(--diff-meta); }
|
|
538
|
+
|
|
539
|
+
/* Prose is capped at the reading measure even where the page has more room,
|
|
540
|
+
because a line of text is easier to follow than to find your way back along.
|
|
541
|
+
Code, tables, images, and display math are exempt by their own rules below:
|
|
542
|
+
they take the width they need and scroll if they cannot get it. */
|
|
543
|
+
.markdown-body { font-size: var(--t-base); max-width: var(--measure); }
|
|
544
|
+
.markdown-body > *:first-child { margin-top: 0; }
|
|
545
|
+
.markdown-body > *:last-child { margin-bottom: 0; }
|
|
546
|
+
.markdown-body h1, .markdown-body h2, .markdown-body h3 { font-family: var(--font-head); }
|
|
547
|
+
.markdown-body h1 { font-size: 1.7em; border-bottom: 1px solid var(--border-soft); padding-bottom: 0.3em; margin: 0.7em 0 0.5em; }
|
|
548
|
+
.markdown-body h2 { font-size: 1.4em; border-bottom: 1px solid var(--border-soft); padding-bottom: 0.3em; }
|
|
549
|
+
.markdown-body h3 { font-size: 1.15em; }
|
|
550
|
+
.markdown-body code { background: var(--inline-code-bg); padding: 0.2em 0.4em; border-radius: var(--radius); font-size: 85%; }
|
|
551
|
+
.markdown-body pre { margin: 0; background: var(--surface); padding: 16px; border-radius: var(--radius); overflow-x: auto; }
|
|
552
|
+
.markdown-body pre code { background: none; padding: 0; font-size: 12px; }
|
|
553
|
+
|
|
554
|
+
/* Fenced code: the copy button keeps out of the way until the pointer is
|
|
555
|
+
over the block it belongs to. */
|
|
556
|
+
.markdown-body .code-block { position: relative; margin: 1em 0; }
|
|
557
|
+
.markdown-body .code-block .copy-btn { position: absolute; top: 8px; right: 8px; opacity: 0; transition: opacity 0.1s; }
|
|
558
|
+
.markdown-body .code-block:hover .copy-btn, .markdown-body .code-block .copy-btn:focus { opacity: 1; }
|
|
559
|
+
|
|
560
|
+
.heading-anchor { margin-left: 8px; font-weight: 400; color: var(--fg-subtle); opacity: 0; }
|
|
561
|
+
.markdown-body h1:hover .heading-anchor, .markdown-body h2:hover .heading-anchor,
|
|
562
|
+
.markdown-body h3:hover .heading-anchor, .markdown-body h4:hover .heading-anchor,
|
|
563
|
+
.markdown-body h5:hover .heading-anchor, .markdown-body h6:hover .heading-anchor,
|
|
564
|
+
.heading-anchor:focus { opacity: 1; text-decoration: none; }
|
|
565
|
+
|
|
566
|
+
.markdown-body li.task-item { list-style: none; margin-left: -1.3em; }
|
|
567
|
+
.markdown-body li.task-item input[type="checkbox"] { margin-right: 6px; }
|
|
568
|
+
|
|
569
|
+
/* Alert callouts: > [!NOTE] and friends. The syntax is the one authors
|
|
570
|
+
already write; the drawing is this sheet's, a left rule in the colour of
|
|
571
|
+
the notice, which is what every other flagged thing here gets. */
|
|
572
|
+
.markdown-body blockquote.alert { border-left-color: var(--alert); color: var(--fg); }
|
|
573
|
+
.markdown-body .alert-title { font-weight: 600; color: var(--alert); margin: 0 0 4px; }
|
|
574
|
+
.markdown-body .alert-note { --alert: var(--accent); }
|
|
575
|
+
.markdown-body .alert-tip { --alert: var(--alert-tip); }
|
|
576
|
+
.markdown-body .alert-important { --alert: var(--alert-important); }
|
|
577
|
+
.markdown-body .alert-warning { --alert: var(--alert-warning); }
|
|
578
|
+
.markdown-body .alert-caution { --alert: var(--danger); }
|
|
579
|
+
|
|
580
|
+
/* Math. Display math scrolls sideways rather than widening the page. */
|
|
581
|
+
.markdown-body .math-block { overflow-x: auto; overflow-y: hidden; margin: 1em 0; }
|
|
582
|
+
.markdown-body .katex-display { margin: 0; padding: 2px 0; }
|
|
583
|
+
.markdown-body .math-error { color: var(--danger); }
|
|
584
|
+
|
|
585
|
+
.markdown-body .footnotes { font-size: 13px; color: var(--fg-muted); }
|
|
586
|
+
.markdown-body .footnotes-sep { margin-top: 32px; }
|
|
587
|
+
.markdown-body .footnote-backref { text-decoration: none; }
|
|
588
|
+
.markdown-body kbd {
|
|
589
|
+
font-family: var(--font-mono); font-size: 85%; padding: 2px 5px; border: 1px solid var(--border);
|
|
590
|
+
border-bottom-width: 2px; border-radius: var(--radius); background: var(--surface);
|
|
591
|
+
}
|
|
592
|
+
.markdown-body summary { cursor: pointer; font-weight: 600; }
|
|
593
|
+
.markdown-body blockquote { margin: 0; padding-left: 16px; border-left: 4px solid var(--border); color: var(--fg-muted); }
|
|
594
|
+
.markdown-body img { max-width: 100%; }
|
|
595
|
+
.markdown-body table { border-collapse: collapse; }
|
|
596
|
+
.markdown-body table th, .markdown-body table td { border: 1px solid var(--border); padding: 6px 12px; }
|
|
597
|
+
.markdown-body table th { background: var(--surface); }
|
|
598
|
+
.markdown-body table { display: block; overflow-x: auto; max-width: 100%; }
|
|
599
|
+
.markdown-body hr { border: none; border-top: 1px solid var(--border-soft); margin: 24px 0; }
|
|
600
|
+
.markdown-body li + li { margin-top: 0.25em; }
|
|
601
|
+
.markdown-body h1:target, .markdown-body h2:target, .markdown-body h3:target,
|
|
602
|
+
.markdown-body h4:target, .markdown-body h5:target, .markdown-body h6:target { scroll-margin-top: 16px; }
|
|
603
|
+
|
|
604
|
+
.cmd-row { display: flex; align-items: center; gap: 8px; margin: 4px 0; }
|
|
605
|
+
.cmd-row code {
|
|
606
|
+
flex: 1; overflow-x: auto; white-space: pre; padding: 5px 8px; background: var(--input-bg);
|
|
607
|
+
border: 1px solid var(--border); border-radius: var(--radius); font-size: 12px;
|
|
608
|
+
}
|
|
609
|
+
.copy-btn {
|
|
610
|
+
display: inline-flex; align-items: center; padding: 4px 10px; font-size: 12px; border: 1px solid var(--border);
|
|
611
|
+
border-radius: var(--radius); background: var(--surface); cursor: pointer; color: var(--fg);
|
|
612
|
+
white-space: nowrap; font-family: inherit; line-height: 1.5;
|
|
613
|
+
}
|
|
614
|
+
.copy-btn:hover { background: var(--surface-hover); }
|
|
615
|
+
/* Both faces of the button are in the page; copying swaps which one shows.
|
|
616
|
+
The faces are matched on their own class rather than through .copy-btn,
|
|
617
|
+
because the same pair also sits inside a plain .btn on the file view. */
|
|
618
|
+
.copy-idle, .copy-done { display: inline-flex; align-items: center; gap: 6px; }
|
|
619
|
+
.copy-done { display: none; color: var(--alert-tip); }
|
|
620
|
+
.copied .copy-idle { display: none; }
|
|
621
|
+
.copied .copy-done { display: inline-flex; }
|
|
622
|
+
/* The command blocks on the empty-repository page: several lines with one
|
|
623
|
+
button, where .cmd-row is one line with one button. */
|
|
624
|
+
.cmd-block { display: flex; align-items: flex-start; gap: 8px; margin: 4px 0 20px; }
|
|
625
|
+
.cmd-block pre {
|
|
626
|
+
flex: 1; margin: 0; overflow-x: auto; padding: 10px 12px; background: var(--input-bg);
|
|
627
|
+
border: 1px solid var(--border); border-radius: var(--radius); font-size: 12px; line-height: 20px;
|
|
628
|
+
}
|
|
629
|
+
.setup-head { font-family: var(--font-head); font-size: 15px; margin: 24px 0 8px; }
|
|
630
|
+
|
|
631
|
+
/* Nothing here yet. A frame on all four sides is this sheet's mark of
|
|
632
|
+
something the reader can operate, and an empty listing is the one thing on
|
|
633
|
+
the page they cannot, so it is drawn with space and a quieter colour instead
|
|
634
|
+
of with a box around it. */
|
|
635
|
+
.empty-state { border-top: 2px solid var(--border); padding: var(--s7) var(--s4); text-align: center; color: var(--fg-muted); }
|
|
636
|
+
.empty-state b { color: var(--fg); }
|
|
637
|
+
/* When the empty notice is what a filtered-away listing leaves behind, the
|
|
638
|
+
listing gives up its rule so the two do not show as a doubled line. */
|
|
639
|
+
table.listing:has(+ .empty-state:not([hidden])) { border-top: none; }
|
|
640
|
+
.error-page { text-align: center; padding: 64px 0; color: var(--fg-muted); }
|
|
641
|
+
.error-page .code { font-family: var(--font-head); font-size: 48px; font-weight: 700; color: var(--fg); }
|
|
642
|
+
|
|
643
|
+
/* Sign-in: the mark, a heading, and one narrow card, centred. */
|
|
644
|
+
.signin { max-width: 340px; margin: 40px auto; }
|
|
645
|
+
.signin-mark svg { display: block; width: 44px; height: 44px; margin: 0 auto 16px; color: var(--fg); }
|
|
646
|
+
.signin h1 { font-size: 20px; text-align: center; }
|
|
647
|
+
.signin .form-box { padding: 16px; }
|
|
648
|
+
.signin .btn { width: 100%; justify-content: center; }
|
|
649
|
+
.signin-note { margin: 16px 0 0; text-align: center; }
|
|
650
|
+
|
|
651
|
+
/* Two fields reading as one address, "collection / name". */
|
|
652
|
+
.name-row { display: flex; align-items: flex-end; gap: 8px; flex-wrap: wrap; }
|
|
653
|
+
.name-row .field { flex: 1 1 200px; margin-bottom: 0; }
|
|
654
|
+
.name-row .field input { width: 100%; }
|
|
655
|
+
.name-slash { font-size: 20px; color: var(--fg-muted); padding-bottom: 4px; }
|
|
656
|
+
hr.rule { border: none; border-top: 1px solid var(--border-soft); margin: 20px 0; }
|
|
657
|
+
|
|
658
|
+
/* A label that only a screen reader needs, where the placeholder or the
|
|
659
|
+
surrounding heading already says what the field is. */
|
|
660
|
+
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
|
|
661
|
+
|
|
662
|
+
.form-box { border: 1px solid var(--border); border-radius: var(--radius); padding: 20px 24px; max-width: 620px; }
|
|
663
|
+
.form-box.wide { max-width: 880px; }
|
|
664
|
+
.form-box h1, .form-box h2 { margin-top: 0; }
|
|
665
|
+
.field { margin-bottom: 14px; }
|
|
666
|
+
.field label { display: block; font-weight: 600; margin-bottom: 4px; }
|
|
667
|
+
.field label.checkbox { font-weight: 400; }
|
|
668
|
+
.field p { margin: 4px 0 0; }
|
|
669
|
+
input[type="text"], input[type="password"], select {
|
|
670
|
+
width: 100%; max-width: 420px; padding: 5px 12px; font-size: 14px; font-family: inherit;
|
|
671
|
+
border: 1px solid var(--border); border-radius: var(--radius); background: var(--input-bg); color: var(--fg);
|
|
672
|
+
}
|
|
673
|
+
.inline-form input[type="text"], .inline-form select { width: auto; }
|
|
674
|
+
input:focus, textarea:focus, select:focus { outline: 2px solid var(--focus); border-color: var(--accent); }
|
|
675
|
+
textarea.code-editor {
|
|
676
|
+
width: 100%; padding: 10px 12px; font-size: 12px; line-height: 20px; tab-size: 4;
|
|
677
|
+
font-family: var(--font-mono); border: 1px solid var(--border); border-radius: var(--radius);
|
|
678
|
+
background: var(--input-bg); color: var(--fg); resize: vertical;
|
|
679
|
+
}
|
|
680
|
+
.commit-box { border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); padding: 12px 16px; margin-top: 12px; max-width: 620px; }
|
|
681
|
+
.commit-box .field { margin-bottom: 10px; }
|
|
682
|
+
.commit-box-head { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
|
|
683
|
+
.commit-box input[type="text"], .commit-box textarea { max-width: 100%; }
|
|
684
|
+
textarea {
|
|
685
|
+
width: 100%; max-width: 100%; padding: 6px 12px; font-size: 14px; font-family: inherit; line-height: 1.5;
|
|
686
|
+
border: 1px solid var(--border); border-radius: var(--radius); background: var(--input-bg); color: var(--fg); resize: vertical;
|
|
687
|
+
}
|
|
688
|
+
.settings-box { max-width: 760px; }
|
|
689
|
+
.settings-box .box-body { padding: 16px 12px 8px; }
|
|
690
|
+
.actions { display: flex; gap: 8px; align-items: center; }
|
|
691
|
+
.file-head { font-weight: 400; }
|
|
692
|
+
.file-head .mono { font-weight: 600; }
|
|
693
|
+
.filename-row { display: flex; align-items: center; gap: 4px; }
|
|
694
|
+
.filename-row input { flex: 1; }
|
|
695
|
+
.inline-form { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 4px 0; }
|
|
696
|
+
.inline-form label { display: flex; align-items: center; gap: 6px; color: var(--fg-muted); font-size: 13px; }
|
|
697
|
+
.user-actions { padding: 12px; display: flex; flex-direction: column; gap: 10px; width: 380px; }
|
|
698
|
+
.user-actions .inline-form { margin: 0; }
|
|
699
|
+
.user-actions input[type="text"] { width: 100%; }
|
|
700
|
+
/* News, of either kind: the tint, and a rule down the side it is read from. */
|
|
701
|
+
.flash { background: var(--ok-bg); border-left: 3px solid var(--ok-border); padding: 8px 12px; margin-bottom: 16px; max-width: 620px; }
|
|
702
|
+
.form-error { background: var(--err-bg); border-left: 3px solid var(--err-border); padding: 8px 12px; margin-bottom: 16px; max-width: 620px; }
|
|
703
|
+
/* A section flagged by the colour of the news it carries, as the flashes and
|
|
704
|
+
the merge box are. Two grades, because they are not the same warning: amber
|
|
705
|
+
for what disrupts other people and can be put back, red for what is gone.
|
|
706
|
+
A page where both wore red would teach the reader to read past red. */
|
|
707
|
+
.danger-zone { border-left: 3px solid var(--danger); padding: 4px 0 4px var(--s4); margin-top: var(--s5); max-width: 620px; }
|
|
708
|
+
.danger-zone h3 { margin-top: 0; color: var(--danger); }
|
|
709
|
+
.danger-zone.caution { border-left-color: var(--alert-warning); }
|
|
710
|
+
.danger-zone.caution h3 { color: var(--alert-warning); }
|
|
711
|
+
|
|
712
|
+
/* Administration: the sections down the left, the page beside them. */
|
|
713
|
+
.admin-layout { display: flex; align-items: flex-start; gap: 32px; }
|
|
714
|
+
.admin-main { flex: 1 1 auto; min-width: 0; }
|
|
715
|
+
.admin-side { flex: 0 0 220px; }
|
|
716
|
+
/* At the top of the page rather than beside content, so the block's ruled top
|
|
717
|
+
would read as a stray line under the header; the heading carries it. */
|
|
718
|
+
.admin-side .side-block:first-child { border-top: 0; padding-top: var(--s2); }
|
|
719
|
+
.admin-side .side-links a { padding: 6px 8px; border-radius: var(--radius); }
|
|
720
|
+
.admin-side .side-links a:hover { background: var(--surface); }
|
|
721
|
+
.admin-side .side-links a.current { background: var(--surface); font-weight: 600; }
|
|
722
|
+
@media (max-width: 800px) {
|
|
723
|
+
.admin-layout { flex-direction: column; align-items: stretch; gap: var(--s4); }
|
|
724
|
+
.admin-side { flex: 1 1 auto; width: 100%; }
|
|
725
|
+
}
|
|
726
|
+
.with-avatar-row { display: flex; align-items: flex-start; gap: 8px; }
|
|
727
|
+
.with-avatar-row .icon { margin-right: 0; color: var(--fg-muted); }
|
|
728
|
+
|
|
729
|
+
/* One runner's facts: a label column narrow enough that the values line up,
|
|
730
|
+
collapsing to stacked rows where that column would crowd the value. */
|
|
731
|
+
.facts { display: grid; grid-template-columns: 160px 1fr; gap: 8px 16px; max-width: 880px; margin: 16px 0; }
|
|
732
|
+
.facts .fact { display: contents; }
|
|
733
|
+
.facts .k { color: var(--fg-muted); font-size: 13px; }
|
|
734
|
+
.facts .v { min-width: 0; overflow-wrap: anywhere; }
|
|
735
|
+
@media (max-width: 640px) {
|
|
736
|
+
.facts { grid-template-columns: 1fr; gap: 2px; }
|
|
737
|
+
.facts .fact + .fact .k { margin-top: 10px; }
|
|
738
|
+
}
|
|
739
|
+
.runner-status { display: inline-flex; align-items: center; gap: 6px; }
|
|
740
|
+
|
|
741
|
+
.card-list { display: flex; flex-direction: column; gap: 12px; margin-bottom: 20px; max-width: 760px; }
|
|
742
|
+
.card-list a.card { display: block; border: 1px solid var(--border); border-radius: var(--radius); padding: 12px 16px; color: var(--fg); }
|
|
743
|
+
.card-list a.card:hover { background: var(--surface); text-decoration: none; }
|
|
744
|
+
.card-list a.card b { display: block; }
|
|
745
|
+
|
|
746
|
+
/* The day's egress against its budget: the number first, then one bar in the
|
|
747
|
+
proportion used, coloured only as it gets close. Amber at four fifths rather
|
|
748
|
+
than at the line, since the reason to look at this page is to act before the
|
|
749
|
+
vault stops answering. */
|
|
750
|
+
.egress-total { display: flex; align-items: baseline; gap: 8px; margin-bottom: 6px; }
|
|
751
|
+
.egress-total b { font-size: 20px; }
|
|
752
|
+
.egress-meter { height: 6px; background: var(--chip-bg); border-radius: 3px; overflow: hidden; max-width: 620px; }
|
|
753
|
+
.egress-meter .fill { display: block; height: 100%; background: var(--fg-subtle); }
|
|
754
|
+
.egress-meter .fill.near { background: var(--alert-warning); }
|
|
755
|
+
.egress-meter .fill.over { background: var(--danger); }
|
|
756
|
+
|
|
757
|
+
.theme-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 12px; margin-bottom: 20px; }
|
|
758
|
+
.theme-card { border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
|
|
759
|
+
.theme-card.current { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
|
|
760
|
+
.theme-card label { display: block; cursor: pointer; }
|
|
761
|
+
.theme-swatch { padding: 12px 14px; border-bottom: 1px solid var(--border); }
|
|
762
|
+
.theme-swatch .bar { height: 8px; border-radius: 4px; margin-bottom: 8px; }
|
|
763
|
+
.theme-swatch .row { display: flex; gap: 6px; align-items: center; }
|
|
764
|
+
.theme-swatch .dot { width: 16px; height: 16px; border-radius: 50%; }
|
|
765
|
+
.theme-meta { padding: 10px 14px; }
|
|
766
|
+
.theme-meta .name { font-weight: 600; display: flex; align-items: center; gap: 8px; }
|
|
767
|
+
.theme-meta p { margin: 4px 0 0; }
|
|
768
|
+
|
|
769
|
+
/* --- Actions: runs, jobs, logs --- */
|
|
770
|
+
.chip { display: inline-block; background: var(--chip-bg); border-radius: var(--radius); padding: 1px 7px; font-size: 12px; color: var(--fg-muted); }
|
|
771
|
+
.run-status { display: inline-flex; align-items: center; justify-content: center; flex: none; }
|
|
772
|
+
.run-status.success { color: var(--alert-tip); }
|
|
773
|
+
.run-status.failure { color: var(--danger); }
|
|
774
|
+
/* A run in progress turns; a reader who has motion turned off gets the same
|
|
775
|
+
amber ring, still. */
|
|
776
|
+
.run-status.running { color: var(--alert-warning); }
|
|
777
|
+
.run-status.running .glyph { animation: spin 1.4s linear infinite; }
|
|
778
|
+
@keyframes spin { to { transform: rotate(360deg); } }
|
|
779
|
+
@media (prefers-reduced-motion: reduce) { .run-status.running .glyph { animation: none; } }
|
|
780
|
+
.run-status.queued { color: var(--fg-subtle); }
|
|
781
|
+
.run-status.cancelled, .run-status.skipped { color: var(--fg-subtle); }
|
|
782
|
+
.listing.runs td.run-cell { display: flex; align-items: flex-start; gap: 10px; }
|
|
783
|
+
.listing.runs td.run-cell > span { min-width: 0; }
|
|
784
|
+
/* The runs, with the repository's workflows listed down the side. */
|
|
785
|
+
.actions-layout { display: flex; align-items: flex-start; gap: 24px; }
|
|
786
|
+
.actions-main { flex: 1 1 auto; min-width: 0; }
|
|
787
|
+
.wf-side { flex: 0 0 240px; }
|
|
788
|
+
.wf-side .side-links a { padding: 5px 8px; border-radius: var(--radius); }
|
|
789
|
+
.wf-side .side-links a:hover { background: var(--surface); }
|
|
790
|
+
.wf-side .side-links a.current { background: var(--surface); font-weight: 600; }
|
|
791
|
+
.wf-side .side-links a span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
792
|
+
@media (max-width: 900px) {
|
|
793
|
+
.actions-layout { flex-direction: column; align-items: stretch; }
|
|
794
|
+
.wf-side { flex: 1 1 auto; width: 100%; }
|
|
795
|
+
}
|
|
796
|
+
.run-sub { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }
|
|
797
|
+
.chip .glyph { vertical-align: text-bottom; margin-right: 2px; }
|
|
798
|
+
.dispatch-body { padding: 16px; }
|
|
799
|
+
.run-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
|
|
800
|
+
.run-title { display: flex; align-items: center; gap: 10px; min-width: 0; }
|
|
801
|
+
.run-title h2 { margin: 0; }
|
|
802
|
+
.run-meta { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin: 6px 0 16px; }
|
|
803
|
+
.run-actor { display: inline-flex; align-items: center; gap: 4px; }
|
|
804
|
+
.run-body { display: flex; gap: 20px; align-items: flex-start; }
|
|
805
|
+
.job-list { flex: 0 0 240px; display: flex; flex-direction: column; gap: 2px; }
|
|
806
|
+
.job-item { display: flex; align-items: center; gap: 8px; padding: 8px 10px; border-radius: var(--radius); color: var(--fg); }
|
|
807
|
+
.job-item:hover { background: var(--surface); text-decoration: none; }
|
|
808
|
+
.job-item.current { background: var(--surface); font-weight: 600; }
|
|
809
|
+
.job-item > span:first-of-type { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
810
|
+
.job-detail { flex: 1; min-width: 0; }
|
|
811
|
+
.job-head { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; }
|
|
812
|
+
.job-head .raw-log-link { margin-left: auto; }
|
|
813
|
+
/* The steps of a job: a stack of foldables divided by hairlines, with the
|
|
814
|
+
first carrying the section's own rule. */
|
|
815
|
+
.step { border-top: 1px solid var(--border-soft); }
|
|
816
|
+
.step:first-of-type { border-top: 2px solid var(--border); }
|
|
817
|
+
.step > summary { display: flex; align-items: center; gap: 8px; padding: 8px 12px; cursor: pointer; }
|
|
818
|
+
.step > summary:hover { background: var(--surface); }
|
|
819
|
+
.step > summary .step-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
820
|
+
.joblog { background: var(--code-bg); color: var(--fg); font-family: var(--font-mono); font-size: 12px; line-height: 1.5; padding: 10px 12px; margin: 0; overflow-x: auto; white-space: pre-wrap; word-break: break-word; }
|
|
821
|
+
/* SGR colours from job output (src/ansi.ts). The sixteen ANSI tokens belong to
|
|
822
|
+
the theme like every other colour here, so a dark theme brightens them and a
|
|
823
|
+
light one darkens them. */
|
|
824
|
+
.joblog .a-b { font-weight: 600; }
|
|
825
|
+
.joblog .a-d { opacity: 0.65; }
|
|
826
|
+
.joblog .a-i { font-style: italic; }
|
|
827
|
+
.joblog .a-u { text-decoration: underline; }
|
|
828
|
+
.joblog .a-blk { color: var(--ansi-black); } .joblog .a-bblk { color: var(--ansi-bright-black); }
|
|
829
|
+
.joblog .a-red { color: var(--ansi-red); } .joblog .a-bred { color: var(--ansi-bright-red); }
|
|
830
|
+
.joblog .a-grn { color: var(--ansi-green); } .joblog .a-bgrn { color: var(--ansi-bright-green); }
|
|
831
|
+
.joblog .a-yel { color: var(--ansi-yellow); } .joblog .a-byel { color: var(--ansi-bright-yellow); }
|
|
832
|
+
.joblog .a-blu { color: var(--ansi-blue); } .joblog .a-bblu { color: var(--ansi-bright-blue); }
|
|
833
|
+
.joblog .a-mag { color: var(--ansi-magenta); } .joblog .a-bmag { color: var(--ansi-bright-magenta); }
|
|
834
|
+
.joblog .a-cyn { color: var(--ansi-cyan); } .joblog .a-bcyn { color: var(--ansi-bright-cyan); }
|
|
835
|
+
.joblog .a-wht { color: var(--ansi-white); } .joblog .a-bwht { color: var(--ansi-bright-white); }
|
|
836
|
+
.step .joblog { border-top: 1px solid var(--border); }
|
|
837
|
+
.joblog.live { border-top: 2px solid var(--border); max-height: 70vh; overflow-y: auto; }
|
|
838
|
+
@media (max-width: 700px) {
|
|
839
|
+
.run-body { flex-direction: column; align-items: stretch; }
|
|
840
|
+
.job-list { flex: 1 1 auto; width: 100%; }
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
.artifacts { margin-top: 20px; max-width: 620px; }
|
|
844
|
+
.artifacts .box-body { display: flex; flex-direction: column; gap: 6px; }
|
|
845
|
+
.artifacts a.artifact { display: flex; justify-content: space-between; align-items: center; gap: 12px; padding: 8px 10px; border: 1px solid var(--border); border-radius: var(--radius); color: var(--fg); }
|
|
846
|
+
.artifacts a.artifact:hover { background: var(--surface); text-decoration: none; }
|
|
847
|
+
.artifacts p { margin: 4px 0 0; }
|
|
848
|
+
|
|
849
|
+
/* --- history: a commit row carries actions on its right --- */
|
|
850
|
+
.commit-main .title { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
851
|
+
.commit-actions { display: flex; align-items: center; gap: 6px; flex: none; }
|
|
852
|
+
.commit-actions .btn { padding: 4px 8px; }
|
|
853
|
+
|
|
854
|
+
/* --- blame: the file view with a commit column down its left --- */
|
|
855
|
+
.blame {
|
|
856
|
+
background: var(--code-bg); overflow-x: auto;
|
|
857
|
+
font-family: var(--font-mono); font-size: 12px; line-height: 20px;
|
|
858
|
+
}
|
|
859
|
+
.blame-row { display: flex; width: max-content; min-width: 100%; }
|
|
860
|
+
.blame-row:target { background: var(--line-mark); }
|
|
861
|
+
/* A run of lines from one commit reads as a block, so only its first row
|
|
862
|
+
names the commit and only its first row carries the rule above it. */
|
|
863
|
+
.blame-row.blame-start { border-top: 1px solid var(--border-soft); }
|
|
864
|
+
.blame-row:first-child { border-top: none; }
|
|
865
|
+
.blame-commit {
|
|
866
|
+
position: sticky; left: 0; z-index: 1; flex: none; display: flex; align-items: center; gap: 6px;
|
|
867
|
+
width: 320px; padding: 0 10px; overflow: hidden; background: var(--surface);
|
|
868
|
+
border-right: 1px solid var(--border); font-family: var(--font-ui);
|
|
869
|
+
}
|
|
870
|
+
.blame-subject { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--fg); font-size: 12px; }
|
|
871
|
+
.blame-when { flex: none; white-space: nowrap; }
|
|
872
|
+
.blame-when time { color: inherit; }
|
|
873
|
+
.blame-prior { flex: none; display: flex; color: var(--fg-subtle); }
|
|
874
|
+
.blame-prior:hover { color: var(--accent); }
|
|
875
|
+
/* The commit column and the numbers both stay put while the code scrolls
|
|
876
|
+
under them, so the number sits at exactly the column's width. */
|
|
877
|
+
.blame .lnum { left: 320px; }
|
|
878
|
+
@media (max-width: 700px) {
|
|
879
|
+
.blame-commit { width: 180px; }
|
|
880
|
+
.blame .lnum { left: 180px; }
|
|
881
|
+
.blame-when { display: none; }
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
/* --- diffs: the numbers down each side, and the shape of the change. These
|
|
885
|
+
rules come after the base .dline and .diff-file-header ones and refine
|
|
886
|
+
them, rather than editing them in place. --- */
|
|
887
|
+
.diff-summary { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; }
|
|
888
|
+
.stat-add { color: var(--alert-tip); font-weight: 600; font-size: 12px; }
|
|
889
|
+
.stat-del { color: var(--danger); font-weight: 600; font-size: 12px; margin-left: 6px; }
|
|
890
|
+
/* One bar in the proportion of the change, drawn at the weight of a rule
|
|
891
|
+
rather than as a row of counters: the added share, then the removed. */
|
|
892
|
+
.statbar { display: inline-flex; width: 44px; height: 3px; margin-left: 10px; background: var(--chip-bg); }
|
|
893
|
+
.statbar span { display: block; height: 100%; }
|
|
894
|
+
.statbar .add { background: var(--alert-tip); }
|
|
895
|
+
.statbar .del { background: var(--danger); }
|
|
896
|
+
details.diff-file > summary.diff-file-header {
|
|
897
|
+
display: flex; align-items: center; gap: 8px; cursor: pointer; list-style: none; font-family: var(--font-ui);
|
|
898
|
+
}
|
|
899
|
+
details.diff-file > summary::-webkit-details-marker { display: none; }
|
|
900
|
+
.diff-file-header .fold { color: var(--fg-muted); transition: transform 0.1s; }
|
|
901
|
+
details.diff-file:not([open]) .fold { transform: rotate(-90deg); }
|
|
902
|
+
details.diff-file:not([open]) > summary.diff-file-header { border-bottom: none; }
|
|
903
|
+
.diff-path { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12px; }
|
|
904
|
+
.diff-file-stat { flex: none; display: flex; align-items: center; }
|
|
905
|
+
.diff-none { padding: 16px; }
|
|
906
|
+
.dline { display: flex; padding: 0; width: max-content; min-width: 100%; }
|
|
907
|
+
/* The numbers stay put while a long line scrolls under them; inheriting the
|
|
908
|
+
background keeps an added or removed line's colour behind its number. */
|
|
909
|
+
.dnum {
|
|
910
|
+
position: sticky; flex: none; width: 44px; padding: 0 6px; text-align: right;
|
|
911
|
+
color: var(--fg-subtle); user-select: none; background: inherit;
|
|
912
|
+
}
|
|
913
|
+
.dline .dnum:first-child { left: 0; }
|
|
914
|
+
.dline .dnum:nth-child(2) { left: 44px; box-shadow: 1px 0 0 var(--border-soft); }
|
|
915
|
+
.dtext { white-space: pre; padding: 0 10px; flex: 1 1 auto; }
|
|
916
|
+
@media (max-width: 700px) {
|
|
917
|
+
.dnum { width: 34px; }
|
|
918
|
+
.dline .dnum:nth-child(2) { left: 34px; }
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
/* --- Languages: the share bar in the About panel. Each segment's colour is
|
|
922
|
+
inline rather than here, because a language's colour belongs to the language
|
|
923
|
+
(it is Linguist's) and not to the theme; the table in languages.ts is where
|
|
924
|
+
they live. Only the shape is this file's business. --- */
|
|
925
|
+
.lang-bar { display: flex; height: 6px; overflow: hidden; background: var(--border-soft); margin-bottom: 12px; }
|
|
926
|
+
.lang-seg { display: block; height: 100%; }
|
|
927
|
+
.lang-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
|
|
928
|
+
.lang-list li { display: flex; align-items: center; gap: 8px; font-size: 13px; }
|
|
929
|
+
/* The square from the mark, at the size of a bullet. */
|
|
930
|
+
.lang-dot { flex: none; width: 9px; height: 9px; }
|
|
931
|
+
.lang-name { font-weight: 600; }
|
|
932
|
+
.lang-pct { margin-left: auto; }
|
|
933
|
+
|
|
934
|
+
/* --- search: the box in the repository header, and the results --- */
|
|
935
|
+
.repo-search { margin-left: auto; }
|
|
936
|
+
.search-form { position: relative; display: flex; align-items: center; }
|
|
937
|
+
.repo-search, .search-form { position: relative; }
|
|
938
|
+
.search-input {
|
|
939
|
+
width: 260px; max-width: 100%; padding: 5px 10px 5px 30px; font-size: 13px; font-family: inherit;
|
|
940
|
+
border: 1px solid var(--border); border-radius: var(--radius); background: var(--input-bg); color: var(--fg);
|
|
941
|
+
}
|
|
942
|
+
.search-form .search-input { width: 360px; }
|
|
943
|
+
.search-glyph { position: absolute; left: 8px; top: 50%; margin-top: -8px; color: var(--fg-muted); pointer-events: none; }
|
|
944
|
+
.search-file { margin-top: 16px; }
|
|
945
|
+
.search-file .box-header a { font-family: var(--font-mono); font-size: 12px; }
|
|
946
|
+
.search-hits { background: var(--code-bg); overflow-x: auto; }
|
|
947
|
+
.search-hit {
|
|
948
|
+
display: flex; gap: 12px; padding: 2px 12px; color: var(--fg);
|
|
949
|
+
font-family: var(--font-mono); font-size: 12px; line-height: 20px;
|
|
950
|
+
}
|
|
951
|
+
.search-hit:hover { background: var(--surface); text-decoration: none; }
|
|
952
|
+
.search-hit .lnum { flex: none; width: 44px; text-align: right; color: var(--fg-subtle); position: static; }
|
|
953
|
+
.search-hit .ltext { white-space: pre; padding: 0; }
|
|
954
|
+
.search-hit mark { background: var(--line-mark); color: inherit; font-weight: 600; }
|
|
955
|
+
.search-more { display: block; padding: 6px 12px; font-size: 12px; border-top: 1px solid var(--border-soft); }
|
|
956
|
+
@media (max-width: 860px) {
|
|
957
|
+
.repo-search { display: none; }
|
|
958
|
+
.search-form .search-input { width: 100%; }
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
/* --- comparing two revisions --- */
|
|
962
|
+
.cmp-form { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 16px; }
|
|
963
|
+
.cmp-picker { display: flex; align-items: center; gap: 6px; font-size: 13px; color: var(--fg-muted); }
|
|
964
|
+
.cmp-picker select { width: auto; max-width: 220px; padding: 4px 8px; font-size: 13px; }
|
|
965
|
+
.cmp-status { border-left: 3px solid var(--border); padding: 4px 0 4px 14px; margin-bottom: 16px; }
|
|
966
|
+
.cmp-commits { margin-bottom: 20px; }
|
|
967
|
+
|
|
968
|
+
/* --- contributors, and a filter the reader can take off --- */
|
|
969
|
+
.contributors { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
|
|
970
|
+
.contributor { display: inline-flex; }
|
|
971
|
+
.contributor:hover { text-decoration: none; opacity: 0.85; }
|
|
972
|
+
.filter-chip {
|
|
973
|
+
display: inline-flex; align-items: center; gap: 6px; padding: 2px 8px; font-size: 12px;
|
|
974
|
+
border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); color: var(--fg);
|
|
975
|
+
}
|
|
976
|
+
.filter-chip .glyph { color: var(--fg-muted); }
|
|
977
|
+
.filter-chip a { display: inline-flex; color: var(--fg-muted); }
|
|
978
|
+
.filter-chip a:hover { color: var(--danger); }
|
|
979
|
+
|
|
980
|
+
/* --- issues: the list, one issue and its thread --- */
|
|
981
|
+
.state-filter { display: flex; gap: 4px; flex-wrap: wrap; }
|
|
982
|
+
.state-tab { display: inline-flex; align-items: center; gap: 6px; padding: 5px 10px; border-radius: var(--radius); color: var(--fg-muted); font-size: 13px; }
|
|
983
|
+
.state-tab:hover { background: var(--surface); text-decoration: none; }
|
|
984
|
+
.state-tab.current { color: var(--fg); font-weight: 600; }
|
|
985
|
+
.state-tab .glyph { color: var(--fg-muted); }
|
|
986
|
+
/* The three states of a thread, in three colours the rest of the sheet
|
|
987
|
+
already uses for the same three meanings. Open takes the vault's accent,
|
|
988
|
+
which is what everything live and current is drawn in, so a vault's own
|
|
989
|
+
colour is what its open work is marked by. Merged takes the green a
|
|
990
|
+
passing run takes, because it is the same news: the thing landed. Closed
|
|
991
|
+
takes the muted grey, because a closed thread is over and should recede
|
|
992
|
+
rather than compete with the open ones beside it. */
|
|
993
|
+
.glyph.issue-open { color: var(--accent); }
|
|
994
|
+
.glyph.issue-closed { color: var(--fg-muted); }
|
|
995
|
+
table.listing.issues td.issue-cell { display: flex; align-items: flex-start; gap: 10px; }
|
|
996
|
+
table.listing.issues td.issue-cell > span { min-width: 0; }
|
|
997
|
+
.issue-link { color: var(--fg); font-weight: 600; }
|
|
998
|
+
.issue-link:hover { color: var(--accent); text-decoration: none; }
|
|
999
|
+
.chip.label { margin-left: 6px; }
|
|
1000
|
+
.issue-comments { display: inline-flex; align-items: center; gap: 4px; color: var(--fg-muted); }
|
|
1001
|
+
.issue-comments:hover { color: var(--accent); text-decoration: none; }
|
|
1002
|
+
|
|
1003
|
+
.issue-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
|
|
1004
|
+
.issue-title { font-size: 24px; margin: 0 0 8px; }
|
|
1005
|
+
.issue-sub {
|
|
1006
|
+
display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
|
|
1007
|
+
padding-bottom: 12px; border-bottom: 1px solid var(--border); margin-bottom: 20px;
|
|
1008
|
+
}
|
|
1009
|
+
.state-badge {
|
|
1010
|
+
display: inline-flex; align-items: center; gap: 6px; padding: 4px 10px; border-radius: var(--radius);
|
|
1011
|
+
font-size: 13px; font-weight: 600; color: var(--on-primary); background: var(--accent);
|
|
1012
|
+
}
|
|
1013
|
+
.state-badge.closed { background: var(--fg-muted); }
|
|
1014
|
+
/* A thread is a run of entries, each opening on its own rule with who wrote
|
|
1015
|
+
it and when, and the words under that. */
|
|
1016
|
+
.issue-thread { display: flex; flex-direction: column; gap: 20px; max-width: 880px; }
|
|
1017
|
+
.issue-comment { border-top: 2px solid var(--border); }
|
|
1018
|
+
.issue-comment-head {
|
|
1019
|
+
display: flex; align-items: center; gap: 8px;
|
|
1020
|
+
border-bottom: 1px solid var(--border-soft); padding: 8px 12px;
|
|
1021
|
+
}
|
|
1022
|
+
.issue-comment-body { padding: 16px 12px 4px; }
|
|
1023
|
+
.issue-event { display: flex; align-items: center; gap: 8px; color: var(--fg-muted); font-size: 13px; }
|
|
1024
|
+
.issue-reply { border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
|
|
1025
|
+
.issue-reply .issue-comment-head { border-bottom: 1px solid var(--border); padding: 8px 12px; }
|
|
1026
|
+
.issue-reply textarea { border: none; border-radius: 0; }
|
|
1027
|
+
.issue-reply .actions { padding: 10px 12px; justify-content: flex-end; border-top: 1px solid var(--border); }
|
|
1028
|
+
|
|
1029
|
+
/* --- releases: notes attached to a tag --- */
|
|
1030
|
+
.release { display: flex; gap: 24px; align-items: flex-start; padding: 20px 0; border-top: 1px solid var(--border-soft); }
|
|
1031
|
+
.release:first-of-type { border-top: none; }
|
|
1032
|
+
.release-side { flex: 0 0 200px; display: flex; flex-direction: column; align-items: flex-start; gap: 6px; }
|
|
1033
|
+
.release-tag { display: inline-flex; align-items: center; gap: 6px; color: var(--fg); font-size: 15px; }
|
|
1034
|
+
.release-tag .glyph { color: var(--fg-muted); }
|
|
1035
|
+
.release-main { flex: 1 1 auto; min-width: 0; }
|
|
1036
|
+
.release-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
|
|
1037
|
+
.release-head h2 { margin: 0; }
|
|
1038
|
+
.release-head h2 a { color: var(--fg); }
|
|
1039
|
+
.release-actions { margin-left: auto; }
|
|
1040
|
+
.chip-latest { background: var(--primary); color: var(--on-primary); font-weight: 600; }
|
|
1041
|
+
.chip-pre { background: transparent; border: 1px solid var(--alert-warning); color: var(--alert-warning); }
|
|
1042
|
+
.release-notes { margin: 12px 0 16px; }
|
|
1043
|
+
.release-downloads { display: flex; flex-direction: column; gap: 6px; max-width: 320px; }
|
|
1044
|
+
.release-download { display: flex; align-items: center; gap: 8px; padding: 6px 10px; border: 1px solid var(--border); border-radius: var(--radius); color: var(--fg); font-size: 13px; }
|
|
1045
|
+
.release-download:hover { background: var(--surface); text-decoration: none; }
|
|
1046
|
+
.release-download .glyph { color: var(--fg-muted); }
|
|
1047
|
+
@media (max-width: 860px) {
|
|
1048
|
+
.release { flex-direction: column; align-items: stretch; gap: var(--s2); }
|
|
1049
|
+
.release-side { flex: 1 1 auto; flex-direction: row; align-items: center; }
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
/* --- forks --- */
|
|
1053
|
+
.fork-note { display: flex; align-items: center; gap: 6px; margin: -4px 0 8px 24px; }
|
|
1054
|
+
.fork-note .glyph { color: var(--fg-subtle); }
|
|
1055
|
+
|
|
1056
|
+
/* --- the commit box's branch choice --- */
|
|
1057
|
+
.commit-target { border-top: 1px solid var(--border); margin-top: 10px; padding-top: 10px; }
|
|
1058
|
+
.commit-target .field { margin: 8px 0 4px; }
|
|
1059
|
+
.commit-target p { margin: 0; }
|
|
1060
|
+
|
|
1061
|
+
/* --- pull requests: the merge box, and the branch pair. The merge box is
|
|
1062
|
+
the page's one piece of news, so it is drawn the way the sheet draws news:
|
|
1063
|
+
a rule down the side it is read from, in the colour of what it says. --- */
|
|
1064
|
+
.cmp-status { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
|
|
1065
|
+
.state-badge.merged { background: var(--alert-tip); color: var(--on-primary); }
|
|
1066
|
+
.pull-merged { color: var(--alert-tip); }
|
|
1067
|
+
.merge-box {
|
|
1068
|
+
display: flex; align-items: flex-start; gap: 12px; margin: 20px 0;
|
|
1069
|
+
border-left: 3px solid var(--border); padding: 6px 0 6px 14px;
|
|
1070
|
+
}
|
|
1071
|
+
.merge-box > .glyph { margin-top: 2px; }
|
|
1072
|
+
.merge-box form { margin-left: auto; }
|
|
1073
|
+
.merge-do { display: flex; align-items: center; gap: 8px; }
|
|
1074
|
+
.merge-method select { width: auto; padding: 4px 8px; font-size: 13px; }
|
|
1075
|
+
.merge-box.clean { border-left-color: var(--alert-tip); }
|
|
1076
|
+
.merge-box.clean > .glyph { color: var(--alert-tip); }
|
|
1077
|
+
.merge-box.conflict { border-left-color: var(--danger); }
|
|
1078
|
+
.merge-box.conflict > .glyph { color: var(--danger); }
|
|
1079
|
+
.merge-box.merged { border-left-color: var(--alert-tip); }
|
|
1080
|
+
.merge-box.merged > .glyph { color: var(--alert-tip); }
|
|
1081
|
+
.merge-box.closed > .glyph, .merge-box.unknown > .glyph { color: var(--fg-muted); }
|
|
1082
|
+
.merge-conflicts { margin: 6px 0 0; padding-left: 18px; font-size: 12px; color: var(--fg-muted); }
|
|
1083
|
+
.pull-commits { display: flex; flex-direction: column; }
|
|
1084
|
+
.pull-commits .commit-row { border: none; border-top: 1px solid var(--border-soft); padding: 8px 0; }
|
|
1085
|
+
.pull-commits .commit-row:first-child { border-top: none; }
|
|
1086
|
+
.pull-files { margin: 24px 0 12px; font-size: 16px; }
|
|
1087
|
+
|
|
1088
|
+
/* --- narrowing a list of issues --- */
|
|
1089
|
+
.issue-filters { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin-bottom: 12px; }
|
|
1090
|
+
.issue-search { position: relative; }
|
|
1091
|
+
.issue-search .search-input { width: 320px; max-width: 100%; }
|
|
1092
|
+
/* A label chip is a link when it narrows the list, and keeps its own colour
|
|
1093
|
+
rather than the link colour. */
|
|
1094
|
+
a.chip.label:hover { text-decoration: none; filter: brightness(1.1); }
|
|
1095
|
+
.issue-filters .dropdown-menu { width: 260px; }
|
|
1096
|
+
.issue-filters .dd-item .muted { margin-left: auto; }
|
|
1097
|
+
@media (max-width: 700px) {
|
|
1098
|
+
.issue-search .search-input { width: 100%; }
|
|
1099
|
+
.issue-filters { flex-direction: column; align-items: stretch; }
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
/* --- the repository listing ---
|
|
1103
|
+
|
|
1104
|
+
The front page and a collection's page are the same list: every repository
|
|
1105
|
+
the reader can see, one card each. A name set against the far edge of a wide
|
|
1106
|
+
page with a thousand pixels of nothing between it and its date is a name the
|
|
1107
|
+
eye has to hunt for, so a card is capped at a readable width and the page's
|
|
1108
|
+
spare room is spent on a second column rather than on stretching the first.
|
|
1109
|
+
Three columns is where the cards get too narrow for a description, so two is
|
|
1110
|
+
as far as it goes. */
|
|
1111
|
+
.listing-controls {
|
|
1112
|
+
display: flex;
|
|
1113
|
+
align-items: center;
|
|
1114
|
+
gap: var(--s3);
|
|
1115
|
+
margin-bottom: var(--s4);
|
|
1116
|
+
flex-wrap: wrap;
|
|
1117
|
+
}
|
|
1118
|
+
.listing-controls .list-filter { flex: 1 1 240px; max-width: 360px; }
|
|
1119
|
+
.listing-controls .seg { margin-left: auto; }
|
|
1120
|
+
|
|
1121
|
+
.repo-grid {
|
|
1122
|
+
list-style: none;
|
|
1123
|
+
margin: 0;
|
|
1124
|
+
padding: 0;
|
|
1125
|
+
display: grid;
|
|
1126
|
+
grid-template-columns: 1fr;
|
|
1127
|
+
gap: 1px;
|
|
1128
|
+
background: var(--border-soft);
|
|
1129
|
+
border-block: 1px solid var(--border-soft);
|
|
1130
|
+
}
|
|
1131
|
+
@media (min-width: 900px) {
|
|
1132
|
+
.repo-grid { grid-template-columns: 1fr 1fr; }
|
|
1133
|
+
}
|
|
1134
|
+
.repo-card {
|
|
1135
|
+
background: var(--bg);
|
|
1136
|
+
padding: var(--s3) var(--s4) var(--s3) 0;
|
|
1137
|
+
min-width: 0;
|
|
1138
|
+
/* The card highlights on hover, so the whole card is the target that
|
|
1139
|
+
promises: the name's link is stretched over it, and the two marks that
|
|
1140
|
+
lead somewhere else are lifted back above it. */
|
|
1141
|
+
position: relative;
|
|
1142
|
+
}
|
|
1143
|
+
@media (min-width: 900px) {
|
|
1144
|
+
/* The gap between the two columns is a hairline of the grid's own
|
|
1145
|
+
background, so the right column gets the padding the gap would have been. */
|
|
1146
|
+
.repo-card:nth-child(even) { padding-left: var(--s4); }
|
|
1147
|
+
}
|
|
1148
|
+
.repo-card:hover { background: var(--surface); }
|
|
1149
|
+
.rc-top { display: flex; align-items: baseline; gap: var(--s2); }
|
|
1150
|
+
.rc-name {
|
|
1151
|
+
font-weight: 600;
|
|
1152
|
+
font-size: var(--t-base);
|
|
1153
|
+
min-width: 0;
|
|
1154
|
+
overflow-wrap: anywhere;
|
|
1155
|
+
}
|
|
1156
|
+
.rc-name::after { content: ''; position: absolute; inset: 0; }
|
|
1157
|
+
/* The collection stays whole, so a narrow column breaks the name at the slash
|
|
1158
|
+
rather than in the middle of the collection's own word. */
|
|
1159
|
+
.rc-collection { color: var(--fg-subtle); font-weight: 400; white-space: nowrap; }
|
|
1160
|
+
.rc-marks {
|
|
1161
|
+
display: inline-flex;
|
|
1162
|
+
align-items: center;
|
|
1163
|
+
gap: var(--s2);
|
|
1164
|
+
margin-left: auto;
|
|
1165
|
+
flex: none;
|
|
1166
|
+
position: relative;
|
|
1167
|
+
}
|
|
1168
|
+
.rc-desc {
|
|
1169
|
+
margin: 2px 0 0;
|
|
1170
|
+
color: var(--fg-muted);
|
|
1171
|
+
font-size: var(--t-sm);
|
|
1172
|
+
line-height: 1.45;
|
|
1173
|
+
/* Two lines, so a long description cannot make one card twice the height of
|
|
1174
|
+
its neighbour and break the rhythm of the column. */
|
|
1175
|
+
display: -webkit-box;
|
|
1176
|
+
-webkit-line-clamp: 2;
|
|
1177
|
+
-webkit-box-orient: vertical;
|
|
1178
|
+
overflow: hidden;
|
|
1179
|
+
}
|
|
1180
|
+
.rc-meta { margin-top: var(--s2); color: var(--fg-subtle); font-size: var(--t-xs); }
|
|
1181
|
+
.site-link, .ci-mark { display: inline-flex; color: var(--fg-subtle); }
|
|
1182
|
+
.site-link:hover, .ci-mark:hover { color: var(--accent); }
|
|
1183
|
+
.ci-success { color: var(--primary); }
|
|
1184
|
+
.ci-failure { color: var(--danger); }
|
|
1185
|
+
.ci-running { color: var(--alert-warning); }
|
|
1186
|
+
|
|
1187
|
+
/* Which collection a repository is in, when the vault has more than one. */
|
|
1188
|
+
.collection-chips { display: flex; flex-wrap: wrap; gap: var(--s2); margin-bottom: var(--s5); }
|
|
1189
|
+
.coll-chip {
|
|
1190
|
+
display: inline-flex;
|
|
1191
|
+
align-items: center;
|
|
1192
|
+
gap: 6px;
|
|
1193
|
+
padding: 4px 10px;
|
|
1194
|
+
border: 1px solid var(--border);
|
|
1195
|
+
border-radius: 999px;
|
|
1196
|
+
font-size: var(--t-sm);
|
|
1197
|
+
color: var(--fg);
|
|
1198
|
+
min-height: var(--touch);
|
|
1199
|
+
}
|
|
1200
|
+
.coll-chip:hover { background: var(--surface-hover); text-decoration: none; }
|
|
1201
|
+
.coll-count { color: var(--fg-subtle); font-variant-numeric: tabular-nums; }
|
|
1202
|
+
|
|
1203
|
+
.lede { margin: calc(-1 * var(--s3)) 0 var(--s4); color: var(--fg-muted); font-size: var(--t-sm); }
|
|
1204
|
+
.lede a.muted { color: var(--fg-muted); text-decoration: underline; }
|
|
1205
|
+
.vault-note { margin-top: var(--s5); }
|
|
1206
|
+
|
|
1207
|
+
/* /about: prose, so a measure rather than the full container. */
|
|
1208
|
+
.about-page { max-width: 720px; }
|
|
1209
|
+
.about-page h2 { margin-top: var(--s6); }
|
|
1210
|
+
.about-page .cmd-row { margin: var(--s3) 0; }
|
|
1211
|
+
|
|
1212
|
+
/* A collection's profile README, above its repository listing. A .box takes
|
|
1213
|
+
its space from above, and here the listing follows it, so the gap below is
|
|
1214
|
+
added rather than inherited. */
|
|
1215
|
+
.profile-box { margin-bottom: var(--s6); }
|
|
1216
|
+
.profile-hint { margin: 0 0 var(--s5); }
|
|
1217
|
+
|
|
1218
|
+
/* --- what a finger has to hit ---
|
|
1219
|
+
|
|
1220
|
+
Under a coarse pointer every control grows to --touch, which is the one
|
|
1221
|
+
number that changes. Nothing is re-laid-out and nothing moves: the rows and
|
|
1222
|
+
buttons that were comfortable for a mouse simply become large enough to be
|
|
1223
|
+
hit by a thumb. */
|
|
1224
|
+
@media (pointer: coarse) {
|
|
1225
|
+
.btn, .copy-btn, .seg a, .state-tab, .filter-chip, .list-filter { min-height: var(--touch); }
|
|
1226
|
+
.dd-item, .find-item, .job-item, .wf-side .side-links a, .admin-side .side-links a {
|
|
1227
|
+
min-height: var(--touch);
|
|
1228
|
+
}
|
|
1229
|
+
table.listing td { padding-top: 10px; padding-bottom: 10px; }
|
|
1230
|
+
.side-links a { min-height: 32px; }
|
|
1231
|
+
.tab { padding-top: var(--s3); padding-bottom: 18px; }
|
|
1232
|
+
.lnum { width: 44px; }
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
/* --- the narrow screen ---
|
|
1236
|
+
|
|
1237
|
+
The interface is one column from the start, so a phone needs the spacing
|
|
1238
|
+
loosened and two or three things told to stop insisting on a width, rather
|
|
1239
|
+
than a second layout of its own. */
|
|
1240
|
+
@media (max-width: 700px) {
|
|
1241
|
+
main { padding: var(--s4) var(--s3) var(--s6); }
|
|
1242
|
+
.container { padding: 0 var(--s3); }
|
|
1243
|
+
/* The bar is on every screen of every page, so it gives back what it can. */
|
|
1244
|
+
.topbar .container { height: 46px; gap: var(--s2); }
|
|
1245
|
+
h1 { font-size: 22px; }
|
|
1246
|
+
.repo-title { font-size: var(--t-lg); }
|
|
1247
|
+
/* Tighter tabs so more of the row is reachable without scrolling it. */
|
|
1248
|
+
.tab { padding-left: var(--s2); padding-right: var(--s2); gap: 6px; }
|
|
1249
|
+
/* A button in a wrapped row of them is easier to aim at when the row shares
|
|
1250
|
+
the width out evenly than when each is as wide as its own label. */
|
|
1251
|
+
.toolbar .right-group .btn, .toolbar .right-group .dropdown { flex: 1 1 auto; }
|
|
1252
|
+
.toolbar .right-group .dropdown > summary { width: 100%; }
|
|
1253
|
+
.form-box { padding: var(--s4); }
|
|
1254
|
+
/* A row of label-and-field pairs has to become a column here, or the pairs
|
|
1255
|
+
wrap where they like and a label ends up above somebody else's field. */
|
|
1256
|
+
.inline-form { flex-direction: column; align-items: stretch; }
|
|
1257
|
+
.inline-form label { margin-top: var(--s1); }
|
|
1258
|
+
.inline-form input[type="text"], .inline-form select { width: 100%; }
|
|
1259
|
+
.inline-form label.checkbox, .inline-form label:has(input) { margin-top: 0; }
|
|
1260
|
+
.empty-state { padding: var(--s6) var(--s3); }
|
|
1261
|
+
/* A row that reads "the file, and what you can do with it" becomes two rows
|
|
1262
|
+
rather than a squeeze, and the numbers down the left of a file give back
|
|
1263
|
+
the width they do not need. */
|
|
1264
|
+
.code-meta { flex-direction: column; align-items: stretch; gap: var(--s2); }
|
|
1265
|
+
.code-meta .right-group .btn, .code-meta .right-group .seg { flex: 1 1 auto; }
|
|
1266
|
+
.lnum { width: 40px; padding-left: var(--s1); }
|
|
1267
|
+
.blame .lnum { width: 40px; }
|
|
1268
|
+
.issue-title { font-size: var(--t-xl); }
|
|
1269
|
+
.release-side { flex-wrap: wrap; }
|
|
1270
|
+
/* The panel under the listing keeps its rules but loses the indent, since
|
|
1271
|
+
there is no listing beside it to line up with. */
|
|
1272
|
+
.repo-side .side-block:first-child { padding-top: var(--s3); }
|
|
1273
|
+
}
|
|
1274
|
+
@media (max-width: 480px) {
|
|
1275
|
+
/* A wrapped row of buttons on a phone lands wherever the widths happen to
|
|
1276
|
+
fall. Two even columns is a shape the reader can predict, and an odd last
|
|
1277
|
+
button takes the whole row rather than sitting half-width beside nothing. */
|
|
1278
|
+
.toolbar .right-group, .code-meta .right-group {
|
|
1279
|
+
display: grid; grid-template-columns: 1fr 1fr; gap: var(--s2); width: 100%;
|
|
1280
|
+
}
|
|
1281
|
+
.toolbar .right-group > *, .code-meta .right-group > * { width: 100%; }
|
|
1282
|
+
.toolbar .right-group > .dropdown > summary, .code-meta .right-group > .dropdown > summary { width: 100%; }
|
|
1283
|
+
.toolbar .right-group > *:last-child:nth-child(odd),
|
|
1284
|
+
.code-meta .right-group > *:last-child:nth-child(odd) { grid-column: 1 / -1; }
|
|
1285
|
+
/* At this width the date beside a name is worth less than the room it
|
|
1286
|
+
takes, so the roster's two columns become two lines. */
|
|
1287
|
+
table.listing.roster tr { display: block; }
|
|
1288
|
+
table.listing.roster td { display: block; border-top: none; padding: 2px var(--s3); }
|
|
1289
|
+
table.listing.roster tr:first-child td:first-child { padding-top: var(--s2); }
|
|
1290
|
+
table.listing.roster td:first-child { padding-top: var(--s2); border-top: 1px solid var(--border-soft); }
|
|
1291
|
+
table.listing.roster tr:first-child td:first-child { border-top: none; }
|
|
1292
|
+
table.listing.roster td.right { width: auto; text-align: left; padding-bottom: var(--s2); }
|
|
1293
|
+
}
|
|
1294
|
+
.side-block hr.rule { margin: var(--s3) 0; }
|
|
1295
|
+
`;
|