co-maintainer 0.5.0 → 0.5.1-beta.2
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 +21 -21
- package/README.md +5 -3
- package/dist/package.json +1 -1
- package/dist/src/ai/anthropic.d.ts +55 -0
- package/dist/src/ai/anthropic.js +262 -0
- package/dist/src/ai/batch.d.ts +2 -2
- package/dist/src/ai/batch.js +1 -1
- package/dist/src/ai/estimate.d.ts +4 -0
- package/dist/src/ai/estimate.js +17 -2
- package/dist/src/ai/mermaid_loop.js +7 -4
- package/dist/src/ai/openai.d.ts +45 -0
- package/dist/src/ai/openai.js +206 -0
- package/dist/src/ai/opencode.d.ts +10 -0
- package/dist/src/ai/opencode.js +76 -0
- package/dist/src/ai/openrouter.d.ts +23 -7
- package/dist/src/ai/openrouter.js +39 -23
- package/dist/src/ai/pricing.js +1 -1
- package/dist/src/ai/provider.d.ts +9 -0
- package/dist/src/ai/provider.js +42 -3
- package/dist/src/ai/verify.d.ts +9 -1
- package/dist/src/ai/verify.js +81 -3
- package/dist/src/cli/args.d.ts +1 -2
- package/dist/src/cli/args.js +38 -26
- package/dist/src/cli/commands/config.d.ts +1 -1
- package/dist/src/cli/commands/config.js +10 -4
- package/dist/src/cli/commands/probe.js +3 -11
- package/dist/src/cli/commands/registry.d.ts +1 -9
- package/dist/src/cli/commands/registry.js +75 -10
- package/dist/src/cli/commands/review.js +18 -13
- package/dist/src/cli/commands/rollback.d.ts +1 -0
- package/dist/src/cli/commands/rollback.js +42 -0
- package/dist/src/cli/commands/serve.js +2 -3
- package/dist/src/cli/commands/set.js +71 -7
- package/dist/src/cli/commands/view.js +10 -9
- package/dist/src/cli/error.d.ts +4 -4
- package/dist/src/cli/error.js +4 -4
- package/dist/src/cli/main.d.ts +4 -4
- package/dist/src/cli/main.js +13 -8
- package/dist/src/cli/prompt.js +1 -1
- package/dist/src/cli/review_args.d.ts +9 -3
- package/dist/src/cli/review_args.js +28 -5
- package/dist/src/cli/review_github.d.ts +31 -0
- package/dist/src/cli/review_github.js +128 -0
- package/dist/src/cli/review_result.d.ts +27 -16
- package/dist/src/cli/review_result.js +7 -7
- package/dist/src/config.d.ts +28 -6
- package/dist/src/config.js +15 -1
- package/dist/src/git/default_branch.d.ts +1 -1
- package/dist/src/git/default_branch.js +1 -1
- package/dist/src/github/app.js +2 -2
- package/dist/src/github/app_manifest.d.ts +1 -7
- package/dist/src/github/app_manifest.js +2 -7
- package/dist/src/github/client.d.ts +4 -1
- package/dist/src/github/client.js +14 -2
- package/dist/src/github/gh.js +10 -7
- package/dist/src/github/pat.js +4 -4
- package/dist/src/github/permissions.d.ts +36 -0
- package/dist/src/github/permissions.js +334 -0
- package/dist/src/knowledge/facts.js +1 -1
- package/dist/src/knowledge/guide.d.ts +3 -3
- package/dist/src/knowledge/guide.js +5 -5
- package/dist/src/knowledge/probe.js +1 -2
- package/dist/src/knowledge/sections.d.ts +3 -3
- package/dist/src/knowledge/sections.js +3 -3
- package/dist/src/knowledge/skill.d.ts +1 -1
- package/dist/src/knowledge/skill.js +4 -4
- package/dist/src/knowledge/synthesis.d.ts +1 -1
- package/dist/src/knowledge/synthesis.js +5 -5
- package/dist/src/knowledge/types.d.ts +1 -1
- package/dist/src/local/codegraph_exclude.d.ts +1 -1
- package/dist/src/local/codegraph_exclude.js +1 -1
- package/dist/src/local/codegraph_prepare.js +1 -1
- package/dist/src/local/git_ops.d.ts +1 -1
- package/dist/src/local/git_ops.js +1 -1
- package/dist/src/local/git_parse.d.ts +2 -2
- package/dist/src/local/git_parse.js +2 -2
- package/dist/src/local/git_revision.d.ts +1 -1
- package/dist/src/local/git_revision.js +1 -1
- package/dist/src/local/git_untracked.d.ts +1 -1
- package/dist/src/local/git_untracked.js +1 -1
- package/dist/src/local/review_local.js +17 -14
- package/dist/src/local/review_lock.d.ts +1 -1
- package/dist/src/local/review_lock.js +1 -1
- package/dist/src/pr/checkout.js +2 -2
- package/dist/src/pr/findings.js +1 -1
- package/dist/src/pr/findings_json.d.ts +2 -2
- package/dist/src/pr/findings_json.js +1 -1
- package/dist/src/pr/github_revision.js +1 -1
- package/dist/src/pr/review_copy.d.ts +2 -2
- package/dist/src/pr/review_copy.js +2 -2
- package/dist/src/pr/reviewer.d.ts +6 -0
- package/dist/src/pr/reviewer.js +49 -39
- package/dist/src/remote/client.d.ts +4 -0
- package/dist/src/remote/client.js +84 -23
- package/dist/src/remote/fixtures.js +1 -1
- package/dist/src/remote/http.d.ts +10 -1
- package/dist/src/remote/http.js +15 -3
- package/dist/src/remote/schema.d.ts +3 -3
- package/dist/src/remote/schema.js +3 -3
- package/dist/src/remote/server/body.d.ts +1 -1
- package/dist/src/remote/server/body.js +1 -1
- package/dist/src/remote/server/byok_keys.d.ts +15 -0
- package/dist/src/remote/server/byok_keys.js +40 -0
- package/dist/src/remote/server/guides.js +2 -2
- package/dist/src/remote/server/routes.js +8 -1
- package/dist/src/remote/validate.js +15 -1
- package/dist/src/review/blocking.d.ts +2 -2
- package/dist/src/review/blocking.js +2 -2
- package/dist/src/review/carry_over.d.ts +2 -2
- package/dist/src/review/carry_over.js +2 -2
- package/dist/src/review/guides.js +1 -1
- package/dist/src/server/api/remote_tokens.js +2 -0
- package/dist/src/server/api/repos.js +51 -0
- package/dist/src/server/api/settings.js +10 -0
- package/dist/src/server/errors.d.ts +1 -1
- package/dist/src/server/http.js +1 -1
- package/dist/src/server/pages/activity.d.ts +2 -0
- package/dist/src/server/pages/activity.js +18 -4
- package/dist/src/server/pages/add_repo.js +61 -1
- package/dist/src/server/pages/analytics.js +18 -8
- package/dist/src/server/pages/cost.d.ts +21 -0
- package/dist/src/server/pages/cost.js +72 -0
- package/dist/src/server/pages/pr.js +5 -4
- package/dist/src/server/pages/repo.js +3 -2
- package/dist/src/server/pages/repo_prs.js +3 -2
- package/dist/src/server/pages/repo_remote.js +3 -2
- package/dist/src/server/pages/repo_settings.js +326 -12
- package/dist/src/server/pages/router.js +3 -2
- package/dist/src/server/pages/settings.js +50 -6
- package/dist/src/server/pages/styles.d.ts +1 -1
- package/dist/src/server/pages/styles.js +1 -1
- package/dist/src/services/credentials.js +8 -11
- package/dist/src/services/dashboard.d.ts +22 -9
- package/dist/src/services/dashboard.js +16 -2
- package/dist/src/services/probe.js +1 -1
- package/dist/src/services/remote_review.js +142 -35
- package/dist/src/services/remote_review_abort.d.ts +5 -1
- package/dist/src/services/remote_review_abort.js +9 -1
- package/dist/src/services/review.d.ts +1 -1
- package/dist/src/services/review.js +19 -5
- package/dist/src/services/review_policy.d.ts +116 -0
- package/dist/src/services/review_policy.js +489 -0
- package/dist/src/services/setup.d.ts +4 -3
- package/dist/src/services/setup.js +18 -16
- package/dist/src/services/setup_checklist.js +2 -2
- package/dist/src/services/webhook.d.ts +23 -0
- package/dist/src/services/webhook.js +141 -34
- package/dist/src/store/app_db.d.ts +5 -0
- package/dist/src/store/app_db.js +69 -13
- package/dist/src/store/backup.d.ts +31 -0
- package/dist/src/store/backup.js +158 -0
- package/dist/src/store/migrations.d.ts +1 -1
- package/dist/src/store/migrations.js +21 -1
- package/dist/src/store/remote_review_inputs.d.ts +3 -0
- package/dist/src/store/remote_review_inputs.js +3 -3
- package/dist/src/store/remote_tokens.d.ts +1 -1
- package/dist/src/store/remote_tokens.js +1 -1
- package/dist/src/store/repos.d.ts +2 -2
- package/dist/src/store/repos.js +1 -1
- package/dist/src/store/reviews.d.ts +21 -13
- package/dist/src/store/reviews.js +60 -15
- package/dist/src/store/rows.d.ts +10 -0
- package/dist/src/tools/codegraph.d.ts +4 -4
- package/dist/src/tools/codegraph.js +5 -5
- package/dist/src/tools/codegraph_exec.d.ts +1 -1
- package/dist/src/tools/codegraph_exec.js +1 -1
- package/dist/src/types.d.ts +12 -6
- package/dist/src/util/cost.d.ts +52 -0
- package/dist/src/util/cost.js +70 -0
- package/dist/src/util/log.d.ts +1 -1
- package/dist/src/util/log.js +2 -2
- package/dist/src/util/redact.js +2 -0
- package/dist/src/util/run_summary.d.ts +7 -2
- package/dist/src/util/run_summary.js +28 -2
- package/dist/src/util/time.d.ts +1 -1
- package/dist/src/util/time.js +1 -1
- package/package.json +1 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { html, layout, skSlot, text } from "./layout.js";
|
|
2
2
|
import { resolveAppPrivateKey } from "../../config.js";
|
|
3
3
|
import { VERSION } from "../../version.js";
|
|
4
|
+
import { TEMPLATE_INFO } from "../../services/review_policy.js";
|
|
4
5
|
import { defaultAppName, manifestBlockedReason, } from "../../github/app_manifest.js";
|
|
5
6
|
const SOURCE_URL = "https://github.com/GroophyLifefor/co-maintainer";
|
|
6
7
|
export function renderSettings(username, config, webhookUrl, baseUrl) {
|
|
@@ -45,7 +46,10 @@ export function renderSettings(username, config, webhookUrl, baseUrl) {
|
|
|
45
46
|
<select id="ai">
|
|
46
47
|
<option value="none"${!config.ai || config.ai === "none" ? " selected" : ""}>None</option>
|
|
47
48
|
<option value="openrouter"${config.ai === "openrouter" ? " selected" : ""}>OpenRouter</option>
|
|
48
|
-
<option value="
|
|
49
|
+
<option value="openai"${config.ai === "openai" ? " selected" : ""}>OpenAI</option>
|
|
50
|
+
<option value="anthropic"${config.ai === "anthropic" ? " selected" : ""}>Anthropic</option>
|
|
51
|
+
<option value="opencode-zen"${config.ai === "opencode-zen" ? " selected" : ""}>OpenCode Zen</option>
|
|
52
|
+
<option value="opencode-go"${config.ai === "opencode-go" ? " selected" : ""}>OpenCode Go</option>
|
|
49
53
|
</select></div>
|
|
50
54
|
<div class="field"><label>API key</label>
|
|
51
55
|
<input id="token" type="password" placeholder="Leave blank to keep the current key">
|
|
@@ -72,7 +76,8 @@ export function renderSettings(username, config, webhookUrl, baseUrl) {
|
|
|
72
76
|
<option value="pat"${config.auth === "pat" ? " selected" : ""}>Personal access token</option>
|
|
73
77
|
</select></div>
|
|
74
78
|
<div class="field"><label>Personal access token</label>
|
|
75
|
-
<input id="pat" type="password" placeholder="Leave blank to keep the current token"
|
|
79
|
+
<input id="pat" type="password" placeholder="Leave blank to keep the current token">
|
|
80
|
+
<div class="hint">Read-only is enough. <a href="https://co-maintainer.com/docs/authentication.html" target="_blank" rel="noopener">Which permissions to grant</a></div></div>
|
|
76
81
|
</div>
|
|
77
82
|
<div class="ft"><button class="primary" id="save-gh">Save</button></div>
|
|
78
83
|
</div>
|
|
@@ -81,7 +86,7 @@ export function renderSettings(username, config, webhookUrl, baseUrl) {
|
|
|
81
86
|
<div class="hd"><h2>GitHub App</h2>
|
|
82
87
|
<span class="st ${appOk ? "ok" : "warn"}" style="margin-left:auto">${appOk ? "Configured" : "Not set"}</span></div>
|
|
83
88
|
<div class="bd">
|
|
84
|
-
<p class="muted" style="margin:0 0 16px">Needed to post reviews on pull requests
|
|
89
|
+
<p class="muted" style="margin:0 0 16px">Needed to post reviews on pull requests. <a href="https://co-maintainer.com/docs/github-app.html#permissions" target="_blank" rel="noopener">Permissions it needs</a></p>
|
|
85
90
|
<div style="margin:0 0 16px">
|
|
86
91
|
<div class="two" style="max-width:none;margin:0 0 12px">
|
|
87
92
|
<div class="field"><label>App name</label>
|
|
@@ -124,6 +129,21 @@ Leave blank to keep the current key"></textarea></div>
|
|
|
124
129
|
<div class="field"><label>Max lines per pull request</label>
|
|
125
130
|
<input id="def-lines" value="${text(config.defaults?.maxPullRequestChangeLines ?? "")}"></div>
|
|
126
131
|
</div>
|
|
132
|
+
<div class="field wide" style="margin-top:18px">
|
|
133
|
+
<label for="def-policy">Who gets a review</label>
|
|
134
|
+
<select id="def-policy">
|
|
135
|
+
<option value=""${config.reviewPolicy === undefined ? " selected" : ""}>Everyone except drafts and bots (the simple switches)</option>
|
|
136
|
+
${TEMPLATE_INFO.map((item) => `<option value="${text(item.name)}"${config.reviewPolicy === item.name ? " selected" : ""}>${text(item.label)}</option>`).join("")}
|
|
137
|
+
${typeof config.reviewPolicy === "object"
|
|
138
|
+
? `<option value="__custom" selected>Custom rules from a file</option>`
|
|
139
|
+
: ""}
|
|
140
|
+
</select>
|
|
141
|
+
<div class="hint" id="def-policy-desc">${text(typeof config.reviewPolicy === "string"
|
|
142
|
+
? (TEMPLATE_INFO.find((item) => item.name === config.reviewPolicy)
|
|
143
|
+
?.description ?? "")
|
|
144
|
+
: "")}</div>
|
|
145
|
+
<div class="hint">New repositories start with this. Each repository can change it in its own settings. Custom rules are set with <span class="mono">co-maintainer set --review-policy-file=policy.json</span>.</div>
|
|
146
|
+
</div>
|
|
127
147
|
</div>
|
|
128
148
|
<div class="ft"><button class="primary" id="save-def">Save</button></div>
|
|
129
149
|
</div>
|
|
@@ -156,9 +176,17 @@ Leave blank to keep the current key"></textarea></div>
|
|
|
156
176
|
<div class="field"><label>New token name</label>
|
|
157
177
|
<input id="remote-token-name" placeholder="e.g. laptop"></div>
|
|
158
178
|
</div>
|
|
179
|
+
<div class="field wide" style="margin-top:16px"><label>Client key policy (BYOK)</label>
|
|
180
|
+
<select id="remote-byok-policy">
|
|
181
|
+
<option value="off"${(config.remoteByokPolicy ?? "off") === "off" ? " selected" : ""}>off: reject a client key</option>
|
|
182
|
+
<option value="allow"${config.remoteByokPolicy === "allow" ? " selected" : ""}>allow: accept a client key, fall back to the server key</option>
|
|
183
|
+
<option value="require"${config.remoteByokPolicy === "require" ? " selected" : ""}>require: a client key is mandatory</option>
|
|
184
|
+
</select>
|
|
185
|
+
<div class="hint">Whether <code>review --remote</code> clients may send their own AI key. The <code>CM_REMOTE_BYOK_POLICY</code> env var overrides this.</div></div>
|
|
159
186
|
</div>
|
|
160
187
|
<div class="ft">
|
|
161
188
|
<button class="primary" id="create-remote-token">Create token</button>
|
|
189
|
+
<button class="btn" id="save-remote-byok">Save policy</button>
|
|
162
190
|
</div>
|
|
163
191
|
</div>
|
|
164
192
|
<div class="card" id="access" data-async>
|
|
@@ -318,6 +346,12 @@ document.getElementById("save-server").addEventListener("click", function() {
|
|
|
318
346
|
remoteToolOutputMaxChars: toolChars.value
|
|
319
347
|
});
|
|
320
348
|
});
|
|
349
|
+
document.getElementById("save-remote-byok").addEventListener("click", function() {
|
|
350
|
+
var card = this.closest("[data-async]");
|
|
351
|
+
save(this, {
|
|
352
|
+
remoteByokPolicy: document.getElementById("remote-byok-policy").value
|
|
353
|
+
});
|
|
354
|
+
});
|
|
321
355
|
function escHtml(s) {
|
|
322
356
|
return String(s).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
323
357
|
}
|
|
@@ -332,7 +366,7 @@ async function loadRemoteTokens() {
|
|
|
332
366
|
el.innerHTML = "<table><thead><tr><th>Name</th><th>Status</th><th class=\\"num\\">Reviews (30d)</th><th class=\\"num\\">Cost (30d)</th><th></th></tr></thead><tbody>" +
|
|
333
367
|
rows.map(function(r) {
|
|
334
368
|
return "<tr><td>" + escHtml(r.name) + "</td><td>" + (r.active ? "Active" : "Inactive") + "</td>" +
|
|
335
|
-
"<td class=\\"num\\">" + r.reviews30d + "</td><td class=\\"num\\">" + r.
|
|
369
|
+
"<td class=\\"num\\">" + r.reviews30d + "</td><td class=\\"num\\">" + escHtml(r.cost30dLabel) + "</td>" +
|
|
336
370
|
"<td><button type=\\"button\\" class=\\"btn sm\\" data-token-id=\\"" + escHtml(r.id) + "\\" data-active=\\"" + r.active + "\\">" +
|
|
337
371
|
(r.active ? "Deactivate" : "Activate") + "</button> " +
|
|
338
372
|
"<button type=\\"button\\" class=\\"btn sm\\" data-delete-token=\\"" + escHtml(r.id) + "\\">Delete</button></td></tr>";
|
|
@@ -412,6 +446,13 @@ document.getElementById("create-remote-token").addEventListener("click", functio
|
|
|
412
446
|
await loadRemoteTokens();
|
|
413
447
|
});
|
|
414
448
|
});
|
|
449
|
+
var POLICY_INFO = ${JSON.stringify(TEMPLATE_INFO)};
|
|
450
|
+
document.getElementById("def-policy").addEventListener("change", function() {
|
|
451
|
+
var value = this.value;
|
|
452
|
+
var text = "";
|
|
453
|
+
POLICY_INFO.forEach(function(item) { if (item.name === value) text = item.description; });
|
|
454
|
+
document.getElementById("def-policy-desc").textContent = text;
|
|
455
|
+
});
|
|
415
456
|
document.getElementById("save-def").addEventListener("click", function() {
|
|
416
457
|
var card = this.closest("[data-async]");
|
|
417
458
|
var positiveInt = function(id, label) {
|
|
@@ -438,13 +479,16 @@ document.getElementById("save-def").addEventListener("click", function() {
|
|
|
438
479
|
fail(card, lines.message, function () {});
|
|
439
480
|
return;
|
|
440
481
|
}
|
|
441
|
-
|
|
482
|
+
var body = {
|
|
442
483
|
defaults: {
|
|
443
484
|
maxPrMonths: months.value,
|
|
444
485
|
maxCommits: commits.value,
|
|
445
486
|
maxPullRequestChangeLines: lines.value
|
|
446
487
|
}
|
|
447
|
-
}
|
|
488
|
+
};
|
|
489
|
+
var policy = document.getElementById("def-policy").value;
|
|
490
|
+
if (policy !== "__custom") body.reviewPolicy = policy === "" ? null : policy;
|
|
491
|
+
save(this, body);
|
|
448
492
|
});
|
|
449
493
|
</script>`,
|
|
450
494
|
}));
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** Generated from src/server/pages/styles.css by scripts/embed_assets.mjs — do not edit. */
|
|
2
|
-
export declare const styles = ":root {\n --bg: #f6f6f7;\n --card: #fff;\n --border: #e2e2e4;\n --border2: #ededef;\n --text: #1f2124;\n --muted: #6a6f75;\n --dim: #8b9096;\n --accent: #80c580;\n --accent-d: #6cae6c;\n --link: #0b5cd5;\n --green: #1a9d5a;\n --amber: #c98a00;\n --red: #cf3c3c;\n --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n}\n* {\n box-sizing: border-box;\n}\nbody {\n margin: 0;\n background: var(--bg);\n color: var(--text);\n font:\n 14px/1.5 -apple-system,\n BlinkMacSystemFont,\n \"Segoe UI\",\n Roboto,\n Helvetica,\n Arial,\n sans-serif;\n}\na {\n color: var(--link);\n text-decoration: none;\n}\na:hover {\n text-decoration: underline;\n}\ncode,\n.mono {\n font-family: var(--mono);\n font-size: 0.9em;\n}\n\n/* top bar */\n.top {\n background: #fff;\n border-bottom: 1px solid var(--border);\n}\n.top .in {\n max-width: 1100px;\n margin: 0 auto;\n padding: 0 24px;\n height: 56px;\n display: flex;\n align-items: center;\n gap: 12px;\n}\n.logo {\n display: flex;\n align-items: center;\n gap: 9px;\n font-weight: 600;\n font-size: 15px;\n color: var(--text);\n}\n.logo:hover {\n text-decoration: none;\n}\n.logo img {\n height: 32px;\n width: 32px;\n display: block;\n border-radius: 5px;\n image-rendering: pixelated;\n object-fit: cover;\n}\n.brand {\n width: 180px;\n height: auto;\n display: block;\n margin: 0 0 14px;\n border-radius: 8px;\n image-rendering: pixelated;\n}\n.top .right {\n margin-left: auto;\n display: flex;\n align-items: center;\n gap: 18px;\n font-size: 13px;\n color: var(--muted);\n}\n\n/* layout */\n.wrap {\n max-width: 1100px;\n margin: 0 auto;\n padding: 28px 24px 72px;\n}\n.wrap.side {\n display: grid;\n grid-template-columns: 196px 1fr;\n gap: 32px;\n}\naside nav {\n display: flex;\n flex-direction: column;\n gap: 1px;\n position: sticky;\n top: 24px;\n}\naside nav a {\n padding: 7px 12px;\n border-radius: 4px;\n color: var(--text);\n font-size: 14px;\n}\naside nav a:hover {\n background: #ededef;\n text-decoration: none;\n}\naside nav a.on {\n background: #fff;\n box-shadow: inset 2px 0 0 var(--accent);\n border: 1px solid var(--border);\n font-weight: 500;\n}\naside .lbl {\n font-size: 11px;\n text-transform: uppercase;\n letter-spacing: 0.06em;\n color: var(--dim);\n padding: 0 12px;\n margin: 0 0 8px;\n}\n\nh1 {\n font-size: 22px;\n font-weight: 600;\n margin: 0;\n}\nh2 {\n font-size: 15px;\n font-weight: 600;\n margin: 0;\n}\np.lead {\n color: var(--muted);\n margin: 6px 0 0;\n font-size: 14px;\n}\n.pagehead {\n display: flex;\n align-items: center;\n gap: 16px;\n margin-bottom: 22px;\n}\n.pagehead .actions {\n margin-left: auto;\n display: flex;\n gap: 8px;\n}\n\n.card {\n background: var(--card);\n border: 1px solid var(--border);\n border-radius: 6px;\n margin-bottom: 18px;\n}\n.card .hd {\n padding: 14px 18px;\n border-bottom: 1px solid var(--border);\n display: flex;\n align-items: center;\n gap: 10px;\n}\n.card .bd {\n padding: 18px;\n}\n.card .bd.flush {\n padding: 0;\n}\n.card .ft {\n padding: 12px 18px;\n border-top: 1px solid var(--border);\n background: #fbfbfc;\n display: flex;\n align-items: center;\n gap: 10px;\n font-size: 13px;\n color: var(--muted);\n border-radius: 0 0 6px 6px;\n}\n\n/* table */\ntable {\n width: 100%;\n border-collapse: collapse;\n}\nth {\n text-align: left;\n font-size: 12px;\n font-weight: 600;\n color: var(--muted);\n padding: 10px 18px;\n border-bottom: 1px solid var(--border);\n background: #fbfbfc;\n}\ntd {\n padding: 14px 18px;\n border-bottom: 1px solid var(--border2);\n font-size: 14px;\n}\ntr:last-child td {\n border-bottom: none;\n}\ntbody tr:hover {\n background: #fafafb;\n}\n.num {\n text-align: right;\n font-variant-numeric: tabular-nums;\n}\n\n/* status text, not badges */\n.st {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n font-size: 13px;\n}\n.st::before {\n content: \"\";\n width: 7px;\n height: 7px;\n border-radius: 50%;\n background: var(--dim);\n}\n.st.ok::before {\n background: var(--green);\n}\n.st.warn::before {\n background: var(--amber);\n}\n.st.err::before {\n background: var(--red);\n}\n.st.run::before {\n background: var(--link);\n}\n\n/* buttons */\nbutton,\n.btn {\n font: inherit;\n font-size: 14px;\n padding: 6px 14px;\n border-radius: 4px;\n border: 1px solid var(--border);\n background: #fff;\n color: var(--text);\n cursor: pointer;\n display: inline-flex;\n align-items: center;\n gap: 7px;\n}\nbutton:hover,\n.btn:hover {\n background: #f4f4f5;\n text-decoration: none;\n}\n.btn.primary,\nbutton.primary {\n background: var(--accent);\n border-color: var(--accent);\n color: #fff;\n font-weight: 500;\n}\n.btn.primary:hover,\nbutton.primary:hover {\n background: var(--accent-d);\n border-color: var(--accent-d);\n}\n.btn.sm {\n padding: 4px 10px;\n font-size: 13px;\n}\n.btn.link {\n border: none;\n background: none;\n color: var(--link);\n padding: 0;\n}\n.btn.link:hover {\n background: none;\n text-decoration: underline;\n}\nbutton[disabled] {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n/* toggle */\n.tg {\n position: relative;\n width: 38px;\n height: 22px;\n border-radius: 22px;\n background: #cfd2d6;\n border: none;\n padding: 0;\n cursor: pointer;\n transition: background 0.12s ease;\n}\n.tg::after {\n content: \"\";\n position: absolute;\n top: 2px;\n left: 2px;\n width: 18px;\n height: 18px;\n border-radius: 50%;\n background: #fff;\n transition: left 0.12s ease;\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);\n}\n.tg.on {\n background: var(--accent);\n}\n.tg.on::after {\n left: 18px;\n}\n.tg:hover {\n filter: brightness(0.97);\n}\n\n/* forms */\nlabel {\n display: block;\n font-size: 13px;\n font-weight: 500;\n margin-bottom: 6px;\n}\ninput,\nselect,\ntextarea {\n width: 100%;\n background: #fff;\n border: 1px solid #cfd2d6;\n border-radius: 4px;\n color: var(--text);\n padding: 8px 10px;\n font: inherit;\n font-size: 14px;\n}\ninput:focus,\nselect:focus,\ntextarea:focus {\n outline: none;\n border-color: var(--accent);\n box-shadow: 0 0 0 2px rgba(128, 197, 128, 0.35);\n}\ntextarea {\n resize: vertical;\n line-height: 1.35;\n}\n.pem {\n min-height: 150px;\n}\n.field {\n margin-bottom: 18px;\n max-width: 420px;\n}\n.field.wide {\n max-width: none;\n}\n.hint {\n font-size: 13px;\n color: var(--muted);\n margin-top: 6px;\n font-weight: 400;\n}\n.two {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 0 24px;\n}\n\n/* bits */\n.muted {\n color: var(--muted);\n}\n.dim {\n color: var(--dim);\n}\n.big {\n font-size: 26px;\n font-weight: 600;\n letter-spacing: -0.01em;\n}\n.kfig {\n display: flex;\n gap: 44px;\n}\n.kfig .k {\n font-size: 13px;\n color: var(--muted);\n margin-bottom: 2px;\n}\n.notice {\n background: #f3faf3;\n border: 1px solid #cfe6cf;\n border-radius: 6px;\n padding: 14px 18px;\n display: flex;\n align-items: center;\n gap: 14px;\n margin-bottom: 18px;\n}\n.notice.info {\n background: #f2f7ff;\n border-color: #cfe0fb;\n}\n.notice.bad {\n background: #fdf3f3;\n border-color: #f3d0d0;\n}\n.notice .txt {\n flex: 1;\n}\n.notice b {\n font-weight: 600;\n}\n.notice .dismiss-worth {\n margin-left: auto;\n border: 0;\n background: transparent;\n color: var(--muted);\n padding: 0 4px;\n font-size: 20px;\n line-height: 1;\n cursor: pointer;\n}\n.notice .dismiss-worth:hover {\n background: transparent;\n color: var(--text);\n}\n.markdown > :first-child {\n margin-top: 0;\n}\n.markdown > :last-child {\n margin-bottom: 0;\n}\n.markdown p,\n.markdown h1,\n.markdown h2,\n.markdown h3,\n.markdown ul,\n.markdown ol,\n.markdown pre {\n margin: 0 0 12px;\n}\n.markdown h1,\n.markdown h2,\n.markdown h3 {\n font-size: 15px;\n}\n.markdown ul,\n.markdown ol {\n padding-left: 22px;\n}\n.markdown pre {\n overflow-x: auto;\n padding: 10px 12px;\n border-radius: 4px;\n background: #f1f2f3;\n font-family: var(--mono);\n font-size: 13px;\n white-space: pre;\n}\n.markdown code {\n padding: 1px 4px;\n border-radius: 3px;\n background: #f1f2f3;\n}\n.markdown pre code {\n padding: 0;\n background: transparent;\n}\n.log {\n background: #1f2124;\n color: #e6e6e6;\n border-radius: 6px;\n padding: 14px 16px;\n font-family: var(--mono);\n font-size: 12.5px;\n line-height: 1.7;\n max-height: 320px;\n overflow: auto;\n white-space: pre-wrap;\n}\n.log .t {\n color: #8b9096;\n}\n.crumbs {\n font-size: 13px;\n color: var(--muted);\n margin-bottom: 14px;\n}\n.pager {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 12px 18px;\n border-top: 1px solid var(--border);\n font-size: 13px;\n color: var(--muted);\n}\n.pager .sp {\n margin-left: auto;\n}\n.empty {\n padding: 56px 24px;\n text-align: center;\n color: var(--muted);\n}\n.empty h2 {\n margin-bottom: 6px;\n}\n.steps {\n list-style: none;\n padding: 0;\n margin: 0;\n}\n.steps li {\n display: flex;\n gap: 14px;\n padding: 16px 0;\n border-bottom: 1px solid var(--border2);\n}\n.steps li:last-child {\n border-bottom: none;\n}\n.steps .n {\n width: 24px;\n height: 24px;\n border-radius: 50%;\n background: #ededef;\n color: var(--muted);\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 12px;\n font-weight: 600;\n flex: none;\n}\n.steps li.done .n {\n background: var(--green);\n color: #fff;\n}\n.steps li.now .n {\n background: var(--accent);\n color: #fff;\n}\n.steps .body {\n flex: 1;\n}\n.steps .body b {\n display: block;\n font-weight: 600;\n margin-bottom: 2px;\n}\n\nbutton,\n.btn,\n.tg,\n.tg::after,\ntbody tr {\n transition:\n background 0.12s ease,\n border-color 0.12s ease,\n filter 0.12s ease,\n left 0.12s ease,\n color 0.12s ease;\n}\n:focus-visible {\n outline: 2px solid var(--accent);\n outline-offset: 2px;\n}\n\n#toasts {\n position: fixed;\n right: 20px;\n bottom: 20px;\n display: flex;\n flex-direction: column;\n gap: 8px;\n z-index: 20;\n max-width: 360px;\n}\n.toast {\n background: #1f2124;\n color: #fff;\n border-radius: 6px;\n padding: 10px 14px;\n font-size: 13px;\n box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);\n}\n.fail {\n margin: 12px 18px;\n padding: 12px 14px;\n background: #fdf3f3;\n border: 1px solid #f3d0d0;\n border-radius: 6px;\n display: flex;\n align-items: center;\n gap: 12px;\n font-size: 13px;\n}\n.fail p {\n margin: 0;\n flex: 1;\n}\n\n.secret {\n margin: 0 0 16px;\n padding: 12px 14px;\n background: #fffbea;\n border: 1px solid #ecd9a0;\n border-radius: 6px;\n font-size: 13px;\n}\n.secret b {\n display: block;\n margin-bottom: 8px;\n}\n.secret-row {\n display: flex;\n align-items: center;\n gap: 10px;\n flex-wrap: wrap;\n}\n.secret-row code {\n flex: 1;\n min-width: 220px;\n word-break: break-all;\n}\n\n.sk-slot {\n display: none;\n padding: 18px;\n}\n.sk,\n.sk-row {\n height: 12px;\n background: #ededef;\n border-radius: 4px;\n margin-bottom: 10px;\n}\n.sk-row {\n height: 36px;\n}\n[data-async].busy > :not(.sk-slot):not([data-fail]) {\n visibility: hidden;\n}\n[data-async].busy > .sk-slot {\n display: block !important;\n visibility: visible;\n}\n\n@media (prefers-reduced-motion: reduce) {\n *,\n *::before,\n *::after {\n animation: none !important;\n transition: none !important;\n }\n}\n.bars {\n display: flex;\n align-items: flex-end;\n gap: 2px;\n height: 56px;\n}\n.bars i {\n flex: 1 1 0;\n min-width: 2px;\n align-self: stretch;\n background-image: linear-gradient(\n to top,\n var(--bar, var(--accent)) var(--h),\n transparent var(--h)\n );\n}\n.bars i:hover {\n --bar: var(--accent-d);\n background-color: var(--border2);\n}\n.trend {\n font-size: 12px;\n margin-top: 3px;\n}\n.trend.up {\n color: var(--green);\n}\n.trend.down {\n color: var(--red);\n}\n.kfig.wrap4 {\n flex-wrap: wrap;\n gap: 28px 44px;\n}\n";
|
|
2
|
+
export declare const styles = ":root {\n --bg: #f6f6f7;\n --card: #fff;\n --border: #e2e2e4;\n --border2: #ededef;\n --text: #1f2124;\n --muted: #6a6f75;\n --dim: #8b9096;\n --accent: #80c580;\n --accent-d: #6cae6c;\n --link: #0b5cd5;\n --green: #1a9d5a;\n --amber: #c98a00;\n --red: #cf3c3c;\n --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n}\n* {\n box-sizing: border-box;\n}\nbody {\n margin: 0;\n background: var(--bg);\n color: var(--text);\n font:\n 14px/1.5 -apple-system,\n BlinkMacSystemFont,\n \"Segoe UI\",\n Roboto,\n Helvetica,\n Arial,\n sans-serif;\n}\na {\n color: var(--link);\n text-decoration: none;\n}\na:hover {\n text-decoration: underline;\n}\ncode,\n.mono {\n font-family: var(--mono);\n font-size: 0.9em;\n}\n\n/* top bar */\n.top {\n background: #fff;\n border-bottom: 1px solid var(--border);\n}\n.top .in {\n max-width: 1100px;\n margin: 0 auto;\n padding: 0 24px;\n height: 56px;\n display: flex;\n align-items: center;\n gap: 12px;\n}\n.logo {\n display: flex;\n align-items: center;\n gap: 9px;\n font-weight: 600;\n font-size: 15px;\n color: var(--text);\n}\n.logo:hover {\n text-decoration: none;\n}\n.logo img {\n height: 32px;\n width: 32px;\n display: block;\n border-radius: 5px;\n image-rendering: pixelated;\n object-fit: cover;\n}\n.brand {\n width: 180px;\n height: auto;\n display: block;\n margin: 0 0 14px;\n border-radius: 8px;\n image-rendering: pixelated;\n}\n.top .right {\n margin-left: auto;\n display: flex;\n align-items: center;\n gap: 18px;\n font-size: 13px;\n color: var(--muted);\n}\n\n/* layout */\n.wrap {\n max-width: 1100px;\n margin: 0 auto;\n padding: 28px 24px 72px;\n}\n.wrap.side {\n display: grid;\n grid-template-columns: 196px 1fr;\n gap: 32px;\n}\naside nav {\n display: flex;\n flex-direction: column;\n gap: 1px;\n position: sticky;\n top: 24px;\n}\naside nav a {\n padding: 7px 12px;\n border-radius: 4px;\n color: var(--text);\n font-size: 14px;\n}\naside nav a:hover {\n background: #ededef;\n text-decoration: none;\n}\naside nav a.on {\n background: #fff;\n box-shadow: inset 2px 0 0 var(--accent);\n border: 1px solid var(--border);\n font-weight: 500;\n}\naside .lbl {\n font-size: 11px;\n text-transform: uppercase;\n letter-spacing: 0.06em;\n color: var(--dim);\n padding: 0 12px;\n margin: 0 0 8px;\n}\n\nh1 {\n font-size: 22px;\n font-weight: 600;\n margin: 0;\n}\nh2 {\n font-size: 15px;\n font-weight: 600;\n margin: 0;\n}\np.lead {\n color: var(--muted);\n margin: 6px 0 0;\n font-size: 14px;\n}\n.pagehead {\n display: flex;\n align-items: center;\n gap: 16px;\n margin-bottom: 22px;\n}\n.pagehead .actions {\n margin-left: auto;\n display: flex;\n gap: 8px;\n}\n\n.card {\n background: var(--card);\n border: 1px solid var(--border);\n border-radius: 6px;\n margin-bottom: 18px;\n}\n.card .hd {\n padding: 14px 18px;\n border-bottom: 1px solid var(--border);\n display: flex;\n align-items: center;\n gap: 10px;\n}\n.card .bd {\n padding: 18px;\n}\n.card .bd.flush {\n padding: 0;\n}\n.card .ft {\n padding: 12px 18px;\n border-top: 1px solid var(--border);\n background: #fbfbfc;\n display: flex;\n align-items: center;\n gap: 10px;\n font-size: 13px;\n color: var(--muted);\n border-radius: 0 0 6px 6px;\n}\n\n/* table */\ntable {\n width: 100%;\n border-collapse: collapse;\n}\nth {\n text-align: left;\n font-size: 12px;\n font-weight: 600;\n color: var(--muted);\n padding: 10px 18px;\n border-bottom: 1px solid var(--border);\n background: #fbfbfc;\n}\ntd {\n padding: 14px 18px;\n border-bottom: 1px solid var(--border2);\n font-size: 14px;\n}\ntr:last-child td {\n border-bottom: none;\n}\ntbody tr:hover {\n background: #fafafb;\n}\n.num {\n text-align: right;\n font-variant-numeric: tabular-nums;\n}\n\n/* status text, not badges */\n.st {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n font-size: 13px;\n}\n.st::before {\n content: \"\";\n width: 7px;\n height: 7px;\n border-radius: 50%;\n background: var(--dim);\n}\n.st.ok::before {\n background: var(--green);\n}\n.st.warn::before {\n background: var(--amber);\n}\n.st.err::before {\n background: var(--red);\n}\n.st.run::before {\n background: var(--link);\n}\n\n/* buttons */\nbutton,\n.btn {\n font: inherit;\n font-size: 14px;\n padding: 6px 14px;\n border-radius: 4px;\n border: 1px solid var(--border);\n background: #fff;\n color: var(--text);\n cursor: pointer;\n display: inline-flex;\n align-items: center;\n gap: 7px;\n}\nbutton:hover,\n.btn:hover {\n background: #f4f4f5;\n text-decoration: none;\n}\n.btn.primary,\nbutton.primary {\n background: var(--accent);\n border-color: var(--accent);\n color: #fff;\n font-weight: 500;\n}\n.btn.primary:hover,\nbutton.primary:hover {\n background: var(--accent-d);\n border-color: var(--accent-d);\n}\n.btn.sm {\n padding: 4px 10px;\n font-size: 13px;\n}\n.btn.link {\n border: none;\n background: none;\n color: var(--link);\n padding: 0;\n}\n.btn.link:hover {\n background: none;\n text-decoration: underline;\n}\nbutton[disabled] {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n/* toggle */\n.tg {\n position: relative;\n width: 38px;\n height: 22px;\n border-radius: 22px;\n background: #cfd2d6;\n border: none;\n padding: 0;\n cursor: pointer;\n transition: background 0.12s ease;\n}\n.tg::after {\n content: \"\";\n position: absolute;\n top: 2px;\n left: 2px;\n width: 18px;\n height: 18px;\n border-radius: 50%;\n background: #fff;\n transition: left 0.12s ease;\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);\n}\n.tg.on {\n background: var(--accent);\n}\n.tg.on::after {\n left: 18px;\n}\n.tg:hover {\n filter: brightness(0.97);\n}\n\n/* forms */\nlabel {\n display: block;\n font-size: 13px;\n font-weight: 500;\n margin-bottom: 6px;\n}\ninput,\nselect,\ntextarea {\n width: 100%;\n background: #fff;\n border: 1px solid #cfd2d6;\n border-radius: 4px;\n color: var(--text);\n padding: 8px 10px;\n font: inherit;\n font-size: 14px;\n}\ninput[type=\"checkbox\"],\ninput[type=\"radio\"] {\n width: auto;\n padding: 0;\n margin: 0 6px 0 0;\n vertical-align: middle;\n}\ninput:focus,\nselect:focus,\ntextarea:focus {\n outline: none;\n border-color: var(--accent);\n box-shadow: 0 0 0 2px rgba(128, 197, 128, 0.35);\n}\ntextarea {\n resize: vertical;\n line-height: 1.35;\n}\n.pem {\n min-height: 150px;\n}\n.field {\n margin-bottom: 18px;\n max-width: 420px;\n}\n.field.wide {\n max-width: none;\n}\n.hint {\n font-size: 13px;\n color: var(--muted);\n margin-top: 6px;\n font-weight: 400;\n}\n.two {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 0 24px;\n}\n\n/* bits */\n.muted {\n color: var(--muted);\n}\n.dim {\n color: var(--dim);\n}\n.big {\n font-size: 26px;\n font-weight: 600;\n letter-spacing: -0.01em;\n}\n.kfig {\n display: flex;\n gap: 44px;\n}\n.kfig .k {\n font-size: 13px;\n color: var(--muted);\n margin-bottom: 2px;\n}\n.notice {\n background: #f3faf3;\n border: 1px solid #cfe6cf;\n border-radius: 6px;\n padding: 14px 18px;\n display: flex;\n align-items: center;\n gap: 14px;\n margin-bottom: 18px;\n}\n.notice.info {\n background: #f2f7ff;\n border-color: #cfe0fb;\n}\n.notice.bad {\n background: #fdf3f3;\n border-color: #f3d0d0;\n}\n.notice .txt {\n flex: 1;\n}\n.notice b {\n font-weight: 600;\n}\n.notice .dismiss-worth {\n margin-left: auto;\n border: 0;\n background: transparent;\n color: var(--muted);\n padding: 0 4px;\n font-size: 20px;\n line-height: 1;\n cursor: pointer;\n}\n.notice .dismiss-worth:hover {\n background: transparent;\n color: var(--text);\n}\n.markdown > :first-child {\n margin-top: 0;\n}\n.markdown > :last-child {\n margin-bottom: 0;\n}\n.markdown p,\n.markdown h1,\n.markdown h2,\n.markdown h3,\n.markdown ul,\n.markdown ol,\n.markdown pre {\n margin: 0 0 12px;\n}\n.markdown h1,\n.markdown h2,\n.markdown h3 {\n font-size: 15px;\n}\n.markdown ul,\n.markdown ol {\n padding-left: 22px;\n}\n.markdown pre {\n overflow-x: auto;\n padding: 10px 12px;\n border-radius: 4px;\n background: #f1f2f3;\n font-family: var(--mono);\n font-size: 13px;\n white-space: pre;\n}\n.markdown code {\n padding: 1px 4px;\n border-radius: 3px;\n background: #f1f2f3;\n}\n.markdown pre code {\n padding: 0;\n background: transparent;\n}\n.log {\n background: #1f2124;\n color: #e6e6e6;\n border-radius: 6px;\n padding: 14px 16px;\n font-family: var(--mono);\n font-size: 12.5px;\n line-height: 1.7;\n max-height: 320px;\n overflow: auto;\n white-space: pre-wrap;\n}\n.log .t {\n color: #8b9096;\n}\n.crumbs {\n font-size: 13px;\n color: var(--muted);\n margin-bottom: 14px;\n}\n.pager {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 12px 18px;\n border-top: 1px solid var(--border);\n font-size: 13px;\n color: var(--muted);\n}\n.pager .sp {\n margin-left: auto;\n}\n.empty {\n padding: 56px 24px;\n text-align: center;\n color: var(--muted);\n}\n.empty h2 {\n margin-bottom: 6px;\n}\n.steps {\n list-style: none;\n padding: 0;\n margin: 0;\n}\n.steps li {\n display: flex;\n gap: 14px;\n padding: 16px 0;\n border-bottom: 1px solid var(--border2);\n}\n.steps li:last-child {\n border-bottom: none;\n}\n.steps .n {\n width: 24px;\n height: 24px;\n border-radius: 50%;\n background: #ededef;\n color: var(--muted);\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 12px;\n font-weight: 600;\n flex: none;\n}\n.steps li.done .n {\n background: var(--green);\n color: #fff;\n}\n.steps li.now .n {\n background: var(--accent);\n color: #fff;\n}\n.steps .body {\n flex: 1;\n}\n.steps .body b {\n display: block;\n font-weight: 600;\n margin-bottom: 2px;\n}\n\nbutton,\n.btn,\n.tg,\n.tg::after,\ntbody tr {\n transition:\n background 0.12s ease,\n border-color 0.12s ease,\n filter 0.12s ease,\n left 0.12s ease,\n color 0.12s ease;\n}\n:focus-visible {\n outline: 2px solid var(--accent);\n outline-offset: 2px;\n}\n\n#toasts {\n position: fixed;\n right: 20px;\n bottom: 20px;\n display: flex;\n flex-direction: column;\n gap: 8px;\n z-index: 20;\n max-width: 360px;\n}\n.toast {\n background: #1f2124;\n color: #fff;\n border-radius: 6px;\n padding: 10px 14px;\n font-size: 13px;\n box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);\n}\n.fail {\n margin: 12px 18px;\n padding: 12px 14px;\n background: #fdf3f3;\n border: 1px solid #f3d0d0;\n border-radius: 6px;\n display: flex;\n align-items: center;\n gap: 12px;\n font-size: 13px;\n}\n.fail p {\n margin: 0;\n flex: 1;\n}\n\n.secret {\n margin: 0 0 16px;\n padding: 12px 14px;\n background: #fffbea;\n border: 1px solid #ecd9a0;\n border-radius: 6px;\n font-size: 13px;\n}\n.secret b {\n display: block;\n margin-bottom: 8px;\n}\n.secret-row {\n display: flex;\n align-items: center;\n gap: 10px;\n flex-wrap: wrap;\n}\n.secret-row code {\n flex: 1;\n min-width: 220px;\n word-break: break-all;\n}\n\n.sk-slot {\n display: none;\n padding: 18px;\n}\n.sk,\n.sk-row {\n height: 12px;\n background: #ededef;\n border-radius: 4px;\n margin-bottom: 10px;\n}\n.sk-row {\n height: 36px;\n}\n[data-async].busy > :not(.sk-slot):not([data-fail]) {\n visibility: hidden;\n}\n[data-async].busy > .sk-slot {\n display: block !important;\n visibility: visible;\n}\n\n@media (prefers-reduced-motion: reduce) {\n *,\n *::before,\n *::after {\n animation: none !important;\n transition: none !important;\n }\n}\n.bars {\n display: flex;\n align-items: flex-end;\n gap: 2px;\n height: 56px;\n}\n.bars i {\n flex: 1 1 0;\n min-width: 2px;\n align-self: stretch;\n background-image: linear-gradient(\n to top,\n var(--bar, var(--accent)) var(--h),\n transparent var(--h)\n );\n}\n.bars i:hover {\n --bar: var(--accent-d);\n background-color: var(--border2);\n}\n.trend {\n font-size: 12px;\n margin-top: 3px;\n}\n.cost-unknown {\n border-bottom: 1px dotted var(--muted);\n cursor: help;\n}\n.trend.up {\n color: var(--green);\n}\n.trend.down {\n color: var(--red);\n}\n.kfig.wrap4 {\n flex-wrap: wrap;\n gap: 28px 44px;\n}\n\n/* Review policy editor: one rule, and the tick boxes inside it, kept compact. */\n.rule {\n border: 1px solid var(--border);\n border-radius: 6px;\n padding: 12px 14px;\n margin-bottom: 10px;\n background: var(--card);\n}\n.ticks {\n display: flex;\n flex-wrap: wrap;\n gap: 2px 18px;\n border: 0;\n padding: 0;\n margin: 8px 0;\n}\n.ticks legend {\n padding: 0;\n margin-bottom: 4px;\n font-size: 13px;\n color: var(--muted);\n}\n.ticks label {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n margin: 2px 0;\n}\n";
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** Generated from src/server/pages/styles.css by scripts/embed_assets.mjs — do not edit. */
|
|
2
|
-
export const styles = ":root {\n --bg: #f6f6f7;\n --card: #fff;\n --border: #e2e2e4;\n --border2: #ededef;\n --text: #1f2124;\n --muted: #6a6f75;\n --dim: #8b9096;\n --accent: #80c580;\n --accent-d: #6cae6c;\n --link: #0b5cd5;\n --green: #1a9d5a;\n --amber: #c98a00;\n --red: #cf3c3c;\n --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n}\n* {\n box-sizing: border-box;\n}\nbody {\n margin: 0;\n background: var(--bg);\n color: var(--text);\n font:\n 14px/1.5 -apple-system,\n BlinkMacSystemFont,\n \"Segoe UI\",\n Roboto,\n Helvetica,\n Arial,\n sans-serif;\n}\na {\n color: var(--link);\n text-decoration: none;\n}\na:hover {\n text-decoration: underline;\n}\ncode,\n.mono {\n font-family: var(--mono);\n font-size: 0.9em;\n}\n\n/* top bar */\n.top {\n background: #fff;\n border-bottom: 1px solid var(--border);\n}\n.top .in {\n max-width: 1100px;\n margin: 0 auto;\n padding: 0 24px;\n height: 56px;\n display: flex;\n align-items: center;\n gap: 12px;\n}\n.logo {\n display: flex;\n align-items: center;\n gap: 9px;\n font-weight: 600;\n font-size: 15px;\n color: var(--text);\n}\n.logo:hover {\n text-decoration: none;\n}\n.logo img {\n height: 32px;\n width: 32px;\n display: block;\n border-radius: 5px;\n image-rendering: pixelated;\n object-fit: cover;\n}\n.brand {\n width: 180px;\n height: auto;\n display: block;\n margin: 0 0 14px;\n border-radius: 8px;\n image-rendering: pixelated;\n}\n.top .right {\n margin-left: auto;\n display: flex;\n align-items: center;\n gap: 18px;\n font-size: 13px;\n color: var(--muted);\n}\n\n/* layout */\n.wrap {\n max-width: 1100px;\n margin: 0 auto;\n padding: 28px 24px 72px;\n}\n.wrap.side {\n display: grid;\n grid-template-columns: 196px 1fr;\n gap: 32px;\n}\naside nav {\n display: flex;\n flex-direction: column;\n gap: 1px;\n position: sticky;\n top: 24px;\n}\naside nav a {\n padding: 7px 12px;\n border-radius: 4px;\n color: var(--text);\n font-size: 14px;\n}\naside nav a:hover {\n background: #ededef;\n text-decoration: none;\n}\naside nav a.on {\n background: #fff;\n box-shadow: inset 2px 0 0 var(--accent);\n border: 1px solid var(--border);\n font-weight: 500;\n}\naside .lbl {\n font-size: 11px;\n text-transform: uppercase;\n letter-spacing: 0.06em;\n color: var(--dim);\n padding: 0 12px;\n margin: 0 0 8px;\n}\n\nh1 {\n font-size: 22px;\n font-weight: 600;\n margin: 0;\n}\nh2 {\n font-size: 15px;\n font-weight: 600;\n margin: 0;\n}\np.lead {\n color: var(--muted);\n margin: 6px 0 0;\n font-size: 14px;\n}\n.pagehead {\n display: flex;\n align-items: center;\n gap: 16px;\n margin-bottom: 22px;\n}\n.pagehead .actions {\n margin-left: auto;\n display: flex;\n gap: 8px;\n}\n\n.card {\n background: var(--card);\n border: 1px solid var(--border);\n border-radius: 6px;\n margin-bottom: 18px;\n}\n.card .hd {\n padding: 14px 18px;\n border-bottom: 1px solid var(--border);\n display: flex;\n align-items: center;\n gap: 10px;\n}\n.card .bd {\n padding: 18px;\n}\n.card .bd.flush {\n padding: 0;\n}\n.card .ft {\n padding: 12px 18px;\n border-top: 1px solid var(--border);\n background: #fbfbfc;\n display: flex;\n align-items: center;\n gap: 10px;\n font-size: 13px;\n color: var(--muted);\n border-radius: 0 0 6px 6px;\n}\n\n/* table */\ntable {\n width: 100%;\n border-collapse: collapse;\n}\nth {\n text-align: left;\n font-size: 12px;\n font-weight: 600;\n color: var(--muted);\n padding: 10px 18px;\n border-bottom: 1px solid var(--border);\n background: #fbfbfc;\n}\ntd {\n padding: 14px 18px;\n border-bottom: 1px solid var(--border2);\n font-size: 14px;\n}\ntr:last-child td {\n border-bottom: none;\n}\ntbody tr:hover {\n background: #fafafb;\n}\n.num {\n text-align: right;\n font-variant-numeric: tabular-nums;\n}\n\n/* status text, not badges */\n.st {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n font-size: 13px;\n}\n.st::before {\n content: \"\";\n width: 7px;\n height: 7px;\n border-radius: 50%;\n background: var(--dim);\n}\n.st.ok::before {\n background: var(--green);\n}\n.st.warn::before {\n background: var(--amber);\n}\n.st.err::before {\n background: var(--red);\n}\n.st.run::before {\n background: var(--link);\n}\n\n/* buttons */\nbutton,\n.btn {\n font: inherit;\n font-size: 14px;\n padding: 6px 14px;\n border-radius: 4px;\n border: 1px solid var(--border);\n background: #fff;\n color: var(--text);\n cursor: pointer;\n display: inline-flex;\n align-items: center;\n gap: 7px;\n}\nbutton:hover,\n.btn:hover {\n background: #f4f4f5;\n text-decoration: none;\n}\n.btn.primary,\nbutton.primary {\n background: var(--accent);\n border-color: var(--accent);\n color: #fff;\n font-weight: 500;\n}\n.btn.primary:hover,\nbutton.primary:hover {\n background: var(--accent-d);\n border-color: var(--accent-d);\n}\n.btn.sm {\n padding: 4px 10px;\n font-size: 13px;\n}\n.btn.link {\n border: none;\n background: none;\n color: var(--link);\n padding: 0;\n}\n.btn.link:hover {\n background: none;\n text-decoration: underline;\n}\nbutton[disabled] {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n/* toggle */\n.tg {\n position: relative;\n width: 38px;\n height: 22px;\n border-radius: 22px;\n background: #cfd2d6;\n border: none;\n padding: 0;\n cursor: pointer;\n transition: background 0.12s ease;\n}\n.tg::after {\n content: \"\";\n position: absolute;\n top: 2px;\n left: 2px;\n width: 18px;\n height: 18px;\n border-radius: 50%;\n background: #fff;\n transition: left 0.12s ease;\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);\n}\n.tg.on {\n background: var(--accent);\n}\n.tg.on::after {\n left: 18px;\n}\n.tg:hover {\n filter: brightness(0.97);\n}\n\n/* forms */\nlabel {\n display: block;\n font-size: 13px;\n font-weight: 500;\n margin-bottom: 6px;\n}\ninput,\nselect,\ntextarea {\n width: 100%;\n background: #fff;\n border: 1px solid #cfd2d6;\n border-radius: 4px;\n color: var(--text);\n padding: 8px 10px;\n font: inherit;\n font-size: 14px;\n}\ninput:focus,\nselect:focus,\ntextarea:focus {\n outline: none;\n border-color: var(--accent);\n box-shadow: 0 0 0 2px rgba(128, 197, 128, 0.35);\n}\ntextarea {\n resize: vertical;\n line-height: 1.35;\n}\n.pem {\n min-height: 150px;\n}\n.field {\n margin-bottom: 18px;\n max-width: 420px;\n}\n.field.wide {\n max-width: none;\n}\n.hint {\n font-size: 13px;\n color: var(--muted);\n margin-top: 6px;\n font-weight: 400;\n}\n.two {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 0 24px;\n}\n\n/* bits */\n.muted {\n color: var(--muted);\n}\n.dim {\n color: var(--dim);\n}\n.big {\n font-size: 26px;\n font-weight: 600;\n letter-spacing: -0.01em;\n}\n.kfig {\n display: flex;\n gap: 44px;\n}\n.kfig .k {\n font-size: 13px;\n color: var(--muted);\n margin-bottom: 2px;\n}\n.notice {\n background: #f3faf3;\n border: 1px solid #cfe6cf;\n border-radius: 6px;\n padding: 14px 18px;\n display: flex;\n align-items: center;\n gap: 14px;\n margin-bottom: 18px;\n}\n.notice.info {\n background: #f2f7ff;\n border-color: #cfe0fb;\n}\n.notice.bad {\n background: #fdf3f3;\n border-color: #f3d0d0;\n}\n.notice .txt {\n flex: 1;\n}\n.notice b {\n font-weight: 600;\n}\n.notice .dismiss-worth {\n margin-left: auto;\n border: 0;\n background: transparent;\n color: var(--muted);\n padding: 0 4px;\n font-size: 20px;\n line-height: 1;\n cursor: pointer;\n}\n.notice .dismiss-worth:hover {\n background: transparent;\n color: var(--text);\n}\n.markdown > :first-child {\n margin-top: 0;\n}\n.markdown > :last-child {\n margin-bottom: 0;\n}\n.markdown p,\n.markdown h1,\n.markdown h2,\n.markdown h3,\n.markdown ul,\n.markdown ol,\n.markdown pre {\n margin: 0 0 12px;\n}\n.markdown h1,\n.markdown h2,\n.markdown h3 {\n font-size: 15px;\n}\n.markdown ul,\n.markdown ol {\n padding-left: 22px;\n}\n.markdown pre {\n overflow-x: auto;\n padding: 10px 12px;\n border-radius: 4px;\n background: #f1f2f3;\n font-family: var(--mono);\n font-size: 13px;\n white-space: pre;\n}\n.markdown code {\n padding: 1px 4px;\n border-radius: 3px;\n background: #f1f2f3;\n}\n.markdown pre code {\n padding: 0;\n background: transparent;\n}\n.log {\n background: #1f2124;\n color: #e6e6e6;\n border-radius: 6px;\n padding: 14px 16px;\n font-family: var(--mono);\n font-size: 12.5px;\n line-height: 1.7;\n max-height: 320px;\n overflow: auto;\n white-space: pre-wrap;\n}\n.log .t {\n color: #8b9096;\n}\n.crumbs {\n font-size: 13px;\n color: var(--muted);\n margin-bottom: 14px;\n}\n.pager {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 12px 18px;\n border-top: 1px solid var(--border);\n font-size: 13px;\n color: var(--muted);\n}\n.pager .sp {\n margin-left: auto;\n}\n.empty {\n padding: 56px 24px;\n text-align: center;\n color: var(--muted);\n}\n.empty h2 {\n margin-bottom: 6px;\n}\n.steps {\n list-style: none;\n padding: 0;\n margin: 0;\n}\n.steps li {\n display: flex;\n gap: 14px;\n padding: 16px 0;\n border-bottom: 1px solid var(--border2);\n}\n.steps li:last-child {\n border-bottom: none;\n}\n.steps .n {\n width: 24px;\n height: 24px;\n border-radius: 50%;\n background: #ededef;\n color: var(--muted);\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 12px;\n font-weight: 600;\n flex: none;\n}\n.steps li.done .n {\n background: var(--green);\n color: #fff;\n}\n.steps li.now .n {\n background: var(--accent);\n color: #fff;\n}\n.steps .body {\n flex: 1;\n}\n.steps .body b {\n display: block;\n font-weight: 600;\n margin-bottom: 2px;\n}\n\nbutton,\n.btn,\n.tg,\n.tg::after,\ntbody tr {\n transition:\n background 0.12s ease,\n border-color 0.12s ease,\n filter 0.12s ease,\n left 0.12s ease,\n color 0.12s ease;\n}\n:focus-visible {\n outline: 2px solid var(--accent);\n outline-offset: 2px;\n}\n\n#toasts {\n position: fixed;\n right: 20px;\n bottom: 20px;\n display: flex;\n flex-direction: column;\n gap: 8px;\n z-index: 20;\n max-width: 360px;\n}\n.toast {\n background: #1f2124;\n color: #fff;\n border-radius: 6px;\n padding: 10px 14px;\n font-size: 13px;\n box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);\n}\n.fail {\n margin: 12px 18px;\n padding: 12px 14px;\n background: #fdf3f3;\n border: 1px solid #f3d0d0;\n border-radius: 6px;\n display: flex;\n align-items: center;\n gap: 12px;\n font-size: 13px;\n}\n.fail p {\n margin: 0;\n flex: 1;\n}\n\n.secret {\n margin: 0 0 16px;\n padding: 12px 14px;\n background: #fffbea;\n border: 1px solid #ecd9a0;\n border-radius: 6px;\n font-size: 13px;\n}\n.secret b {\n display: block;\n margin-bottom: 8px;\n}\n.secret-row {\n display: flex;\n align-items: center;\n gap: 10px;\n flex-wrap: wrap;\n}\n.secret-row code {\n flex: 1;\n min-width: 220px;\n word-break: break-all;\n}\n\n.sk-slot {\n display: none;\n padding: 18px;\n}\n.sk,\n.sk-row {\n height: 12px;\n background: #ededef;\n border-radius: 4px;\n margin-bottom: 10px;\n}\n.sk-row {\n height: 36px;\n}\n[data-async].busy > :not(.sk-slot):not([data-fail]) {\n visibility: hidden;\n}\n[data-async].busy > .sk-slot {\n display: block !important;\n visibility: visible;\n}\n\n@media (prefers-reduced-motion: reduce) {\n *,\n *::before,\n *::after {\n animation: none !important;\n transition: none !important;\n }\n}\n.bars {\n display: flex;\n align-items: flex-end;\n gap: 2px;\n height: 56px;\n}\n.bars i {\n flex: 1 1 0;\n min-width: 2px;\n align-self: stretch;\n background-image: linear-gradient(\n to top,\n var(--bar, var(--accent)) var(--h),\n transparent var(--h)\n );\n}\n.bars i:hover {\n --bar: var(--accent-d);\n background-color: var(--border2);\n}\n.trend {\n font-size: 12px;\n margin-top: 3px;\n}\n.trend.up {\n color: var(--green);\n}\n.trend.down {\n color: var(--red);\n}\n.kfig.wrap4 {\n flex-wrap: wrap;\n gap: 28px 44px;\n}\n";
|
|
2
|
+
export const styles = ":root {\n --bg: #f6f6f7;\n --card: #fff;\n --border: #e2e2e4;\n --border2: #ededef;\n --text: #1f2124;\n --muted: #6a6f75;\n --dim: #8b9096;\n --accent: #80c580;\n --accent-d: #6cae6c;\n --link: #0b5cd5;\n --green: #1a9d5a;\n --amber: #c98a00;\n --red: #cf3c3c;\n --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n}\n* {\n box-sizing: border-box;\n}\nbody {\n margin: 0;\n background: var(--bg);\n color: var(--text);\n font:\n 14px/1.5 -apple-system,\n BlinkMacSystemFont,\n \"Segoe UI\",\n Roboto,\n Helvetica,\n Arial,\n sans-serif;\n}\na {\n color: var(--link);\n text-decoration: none;\n}\na:hover {\n text-decoration: underline;\n}\ncode,\n.mono {\n font-family: var(--mono);\n font-size: 0.9em;\n}\n\n/* top bar */\n.top {\n background: #fff;\n border-bottom: 1px solid var(--border);\n}\n.top .in {\n max-width: 1100px;\n margin: 0 auto;\n padding: 0 24px;\n height: 56px;\n display: flex;\n align-items: center;\n gap: 12px;\n}\n.logo {\n display: flex;\n align-items: center;\n gap: 9px;\n font-weight: 600;\n font-size: 15px;\n color: var(--text);\n}\n.logo:hover {\n text-decoration: none;\n}\n.logo img {\n height: 32px;\n width: 32px;\n display: block;\n border-radius: 5px;\n image-rendering: pixelated;\n object-fit: cover;\n}\n.brand {\n width: 180px;\n height: auto;\n display: block;\n margin: 0 0 14px;\n border-radius: 8px;\n image-rendering: pixelated;\n}\n.top .right {\n margin-left: auto;\n display: flex;\n align-items: center;\n gap: 18px;\n font-size: 13px;\n color: var(--muted);\n}\n\n/* layout */\n.wrap {\n max-width: 1100px;\n margin: 0 auto;\n padding: 28px 24px 72px;\n}\n.wrap.side {\n display: grid;\n grid-template-columns: 196px 1fr;\n gap: 32px;\n}\naside nav {\n display: flex;\n flex-direction: column;\n gap: 1px;\n position: sticky;\n top: 24px;\n}\naside nav a {\n padding: 7px 12px;\n border-radius: 4px;\n color: var(--text);\n font-size: 14px;\n}\naside nav a:hover {\n background: #ededef;\n text-decoration: none;\n}\naside nav a.on {\n background: #fff;\n box-shadow: inset 2px 0 0 var(--accent);\n border: 1px solid var(--border);\n font-weight: 500;\n}\naside .lbl {\n font-size: 11px;\n text-transform: uppercase;\n letter-spacing: 0.06em;\n color: var(--dim);\n padding: 0 12px;\n margin: 0 0 8px;\n}\n\nh1 {\n font-size: 22px;\n font-weight: 600;\n margin: 0;\n}\nh2 {\n font-size: 15px;\n font-weight: 600;\n margin: 0;\n}\np.lead {\n color: var(--muted);\n margin: 6px 0 0;\n font-size: 14px;\n}\n.pagehead {\n display: flex;\n align-items: center;\n gap: 16px;\n margin-bottom: 22px;\n}\n.pagehead .actions {\n margin-left: auto;\n display: flex;\n gap: 8px;\n}\n\n.card {\n background: var(--card);\n border: 1px solid var(--border);\n border-radius: 6px;\n margin-bottom: 18px;\n}\n.card .hd {\n padding: 14px 18px;\n border-bottom: 1px solid var(--border);\n display: flex;\n align-items: center;\n gap: 10px;\n}\n.card .bd {\n padding: 18px;\n}\n.card .bd.flush {\n padding: 0;\n}\n.card .ft {\n padding: 12px 18px;\n border-top: 1px solid var(--border);\n background: #fbfbfc;\n display: flex;\n align-items: center;\n gap: 10px;\n font-size: 13px;\n color: var(--muted);\n border-radius: 0 0 6px 6px;\n}\n\n/* table */\ntable {\n width: 100%;\n border-collapse: collapse;\n}\nth {\n text-align: left;\n font-size: 12px;\n font-weight: 600;\n color: var(--muted);\n padding: 10px 18px;\n border-bottom: 1px solid var(--border);\n background: #fbfbfc;\n}\ntd {\n padding: 14px 18px;\n border-bottom: 1px solid var(--border2);\n font-size: 14px;\n}\ntr:last-child td {\n border-bottom: none;\n}\ntbody tr:hover {\n background: #fafafb;\n}\n.num {\n text-align: right;\n font-variant-numeric: tabular-nums;\n}\n\n/* status text, not badges */\n.st {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n font-size: 13px;\n}\n.st::before {\n content: \"\";\n width: 7px;\n height: 7px;\n border-radius: 50%;\n background: var(--dim);\n}\n.st.ok::before {\n background: var(--green);\n}\n.st.warn::before {\n background: var(--amber);\n}\n.st.err::before {\n background: var(--red);\n}\n.st.run::before {\n background: var(--link);\n}\n\n/* buttons */\nbutton,\n.btn {\n font: inherit;\n font-size: 14px;\n padding: 6px 14px;\n border-radius: 4px;\n border: 1px solid var(--border);\n background: #fff;\n color: var(--text);\n cursor: pointer;\n display: inline-flex;\n align-items: center;\n gap: 7px;\n}\nbutton:hover,\n.btn:hover {\n background: #f4f4f5;\n text-decoration: none;\n}\n.btn.primary,\nbutton.primary {\n background: var(--accent);\n border-color: var(--accent);\n color: #fff;\n font-weight: 500;\n}\n.btn.primary:hover,\nbutton.primary:hover {\n background: var(--accent-d);\n border-color: var(--accent-d);\n}\n.btn.sm {\n padding: 4px 10px;\n font-size: 13px;\n}\n.btn.link {\n border: none;\n background: none;\n color: var(--link);\n padding: 0;\n}\n.btn.link:hover {\n background: none;\n text-decoration: underline;\n}\nbutton[disabled] {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n/* toggle */\n.tg {\n position: relative;\n width: 38px;\n height: 22px;\n border-radius: 22px;\n background: #cfd2d6;\n border: none;\n padding: 0;\n cursor: pointer;\n transition: background 0.12s ease;\n}\n.tg::after {\n content: \"\";\n position: absolute;\n top: 2px;\n left: 2px;\n width: 18px;\n height: 18px;\n border-radius: 50%;\n background: #fff;\n transition: left 0.12s ease;\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);\n}\n.tg.on {\n background: var(--accent);\n}\n.tg.on::after {\n left: 18px;\n}\n.tg:hover {\n filter: brightness(0.97);\n}\n\n/* forms */\nlabel {\n display: block;\n font-size: 13px;\n font-weight: 500;\n margin-bottom: 6px;\n}\ninput,\nselect,\ntextarea {\n width: 100%;\n background: #fff;\n border: 1px solid #cfd2d6;\n border-radius: 4px;\n color: var(--text);\n padding: 8px 10px;\n font: inherit;\n font-size: 14px;\n}\ninput[type=\"checkbox\"],\ninput[type=\"radio\"] {\n width: auto;\n padding: 0;\n margin: 0 6px 0 0;\n vertical-align: middle;\n}\ninput:focus,\nselect:focus,\ntextarea:focus {\n outline: none;\n border-color: var(--accent);\n box-shadow: 0 0 0 2px rgba(128, 197, 128, 0.35);\n}\ntextarea {\n resize: vertical;\n line-height: 1.35;\n}\n.pem {\n min-height: 150px;\n}\n.field {\n margin-bottom: 18px;\n max-width: 420px;\n}\n.field.wide {\n max-width: none;\n}\n.hint {\n font-size: 13px;\n color: var(--muted);\n margin-top: 6px;\n font-weight: 400;\n}\n.two {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 0 24px;\n}\n\n/* bits */\n.muted {\n color: var(--muted);\n}\n.dim {\n color: var(--dim);\n}\n.big {\n font-size: 26px;\n font-weight: 600;\n letter-spacing: -0.01em;\n}\n.kfig {\n display: flex;\n gap: 44px;\n}\n.kfig .k {\n font-size: 13px;\n color: var(--muted);\n margin-bottom: 2px;\n}\n.notice {\n background: #f3faf3;\n border: 1px solid #cfe6cf;\n border-radius: 6px;\n padding: 14px 18px;\n display: flex;\n align-items: center;\n gap: 14px;\n margin-bottom: 18px;\n}\n.notice.info {\n background: #f2f7ff;\n border-color: #cfe0fb;\n}\n.notice.bad {\n background: #fdf3f3;\n border-color: #f3d0d0;\n}\n.notice .txt {\n flex: 1;\n}\n.notice b {\n font-weight: 600;\n}\n.notice .dismiss-worth {\n margin-left: auto;\n border: 0;\n background: transparent;\n color: var(--muted);\n padding: 0 4px;\n font-size: 20px;\n line-height: 1;\n cursor: pointer;\n}\n.notice .dismiss-worth:hover {\n background: transparent;\n color: var(--text);\n}\n.markdown > :first-child {\n margin-top: 0;\n}\n.markdown > :last-child {\n margin-bottom: 0;\n}\n.markdown p,\n.markdown h1,\n.markdown h2,\n.markdown h3,\n.markdown ul,\n.markdown ol,\n.markdown pre {\n margin: 0 0 12px;\n}\n.markdown h1,\n.markdown h2,\n.markdown h3 {\n font-size: 15px;\n}\n.markdown ul,\n.markdown ol {\n padding-left: 22px;\n}\n.markdown pre {\n overflow-x: auto;\n padding: 10px 12px;\n border-radius: 4px;\n background: #f1f2f3;\n font-family: var(--mono);\n font-size: 13px;\n white-space: pre;\n}\n.markdown code {\n padding: 1px 4px;\n border-radius: 3px;\n background: #f1f2f3;\n}\n.markdown pre code {\n padding: 0;\n background: transparent;\n}\n.log {\n background: #1f2124;\n color: #e6e6e6;\n border-radius: 6px;\n padding: 14px 16px;\n font-family: var(--mono);\n font-size: 12.5px;\n line-height: 1.7;\n max-height: 320px;\n overflow: auto;\n white-space: pre-wrap;\n}\n.log .t {\n color: #8b9096;\n}\n.crumbs {\n font-size: 13px;\n color: var(--muted);\n margin-bottom: 14px;\n}\n.pager {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 12px 18px;\n border-top: 1px solid var(--border);\n font-size: 13px;\n color: var(--muted);\n}\n.pager .sp {\n margin-left: auto;\n}\n.empty {\n padding: 56px 24px;\n text-align: center;\n color: var(--muted);\n}\n.empty h2 {\n margin-bottom: 6px;\n}\n.steps {\n list-style: none;\n padding: 0;\n margin: 0;\n}\n.steps li {\n display: flex;\n gap: 14px;\n padding: 16px 0;\n border-bottom: 1px solid var(--border2);\n}\n.steps li:last-child {\n border-bottom: none;\n}\n.steps .n {\n width: 24px;\n height: 24px;\n border-radius: 50%;\n background: #ededef;\n color: var(--muted);\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 12px;\n font-weight: 600;\n flex: none;\n}\n.steps li.done .n {\n background: var(--green);\n color: #fff;\n}\n.steps li.now .n {\n background: var(--accent);\n color: #fff;\n}\n.steps .body {\n flex: 1;\n}\n.steps .body b {\n display: block;\n font-weight: 600;\n margin-bottom: 2px;\n}\n\nbutton,\n.btn,\n.tg,\n.tg::after,\ntbody tr {\n transition:\n background 0.12s ease,\n border-color 0.12s ease,\n filter 0.12s ease,\n left 0.12s ease,\n color 0.12s ease;\n}\n:focus-visible {\n outline: 2px solid var(--accent);\n outline-offset: 2px;\n}\n\n#toasts {\n position: fixed;\n right: 20px;\n bottom: 20px;\n display: flex;\n flex-direction: column;\n gap: 8px;\n z-index: 20;\n max-width: 360px;\n}\n.toast {\n background: #1f2124;\n color: #fff;\n border-radius: 6px;\n padding: 10px 14px;\n font-size: 13px;\n box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);\n}\n.fail {\n margin: 12px 18px;\n padding: 12px 14px;\n background: #fdf3f3;\n border: 1px solid #f3d0d0;\n border-radius: 6px;\n display: flex;\n align-items: center;\n gap: 12px;\n font-size: 13px;\n}\n.fail p {\n margin: 0;\n flex: 1;\n}\n\n.secret {\n margin: 0 0 16px;\n padding: 12px 14px;\n background: #fffbea;\n border: 1px solid #ecd9a0;\n border-radius: 6px;\n font-size: 13px;\n}\n.secret b {\n display: block;\n margin-bottom: 8px;\n}\n.secret-row {\n display: flex;\n align-items: center;\n gap: 10px;\n flex-wrap: wrap;\n}\n.secret-row code {\n flex: 1;\n min-width: 220px;\n word-break: break-all;\n}\n\n.sk-slot {\n display: none;\n padding: 18px;\n}\n.sk,\n.sk-row {\n height: 12px;\n background: #ededef;\n border-radius: 4px;\n margin-bottom: 10px;\n}\n.sk-row {\n height: 36px;\n}\n[data-async].busy > :not(.sk-slot):not([data-fail]) {\n visibility: hidden;\n}\n[data-async].busy > .sk-slot {\n display: block !important;\n visibility: visible;\n}\n\n@media (prefers-reduced-motion: reduce) {\n *,\n *::before,\n *::after {\n animation: none !important;\n transition: none !important;\n }\n}\n.bars {\n display: flex;\n align-items: flex-end;\n gap: 2px;\n height: 56px;\n}\n.bars i {\n flex: 1 1 0;\n min-width: 2px;\n align-self: stretch;\n background-image: linear-gradient(\n to top,\n var(--bar, var(--accent)) var(--h),\n transparent var(--h)\n );\n}\n.bars i:hover {\n --bar: var(--accent-d);\n background-color: var(--border2);\n}\n.trend {\n font-size: 12px;\n margin-top: 3px;\n}\n.cost-unknown {\n border-bottom: 1px dotted var(--muted);\n cursor: help;\n}\n.trend.up {\n color: var(--green);\n}\n.trend.down {\n color: var(--red);\n}\n.kfig.wrap4 {\n flex-wrap: wrap;\n gap: 28px 44px;\n}\n\n/* Review policy editor: one rule, and the tick boxes inside it, kept compact. */\n.rule {\n border: 1px solid var(--border);\n border-radius: 6px;\n padding: 12px 14px;\n margin-bottom: 10px;\n background: var(--card);\n}\n.ticks {\n display: flex;\n flex-wrap: wrap;\n gap: 2px 18px;\n border: 0;\n padding: 0;\n margin: 8px 0;\n}\n.ticks legend {\n padding: 0;\n margin-bottom: 4px;\n font-size: 13px;\n color: var(--muted);\n}\n.ticks label {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n margin: 2px 0;\n}\n";
|
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
/** Probe GitHub credentials before they are saved. A token that cannot
|
|
2
|
-
* authenticate, or an App missing the permissions
|
|
2
|
+
* authenticate, or an App missing the permissions it needs,
|
|
3
3
|
* is rejected instead of stored and failing later on a review. */
|
|
4
4
|
import { githubFetch, GitHubHttpError } from "../github/client.js";
|
|
5
5
|
import { AppJwtClient } from "../github/app.js";
|
|
6
6
|
import { GhClient } from "../github/gh.js";
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
pull_requests: "write",
|
|
12
|
-
checks: "write",
|
|
13
|
-
};
|
|
7
|
+
import { APP_PERMISSIONS } from "../github/permissions.js";
|
|
8
|
+
const APP_NEED = APP_PERMISSIONS;
|
|
9
|
+
const AUTH_DOCS = "https://co-maintainer.com/docs/authentication.html";
|
|
10
|
+
const APP_DOCS = "https://co-maintainer.com/docs/github-app.html#permissions";
|
|
14
11
|
export function missingAppPermissions(permissions) {
|
|
15
12
|
const got = permissions ?? {};
|
|
16
13
|
const missing = [];
|
|
@@ -45,7 +42,7 @@ export async function testAppAccess(opts) {
|
|
|
45
42
|
const account = installation.account?.login ?? `installation ${installation.id}`;
|
|
46
43
|
return {
|
|
47
44
|
ok: false,
|
|
48
|
-
message: `The App on ${account} is missing ${missing.join(" and ")}
|
|
45
|
+
message: `The App on ${account} is missing ${missing.join(" and ")}. Permissions it needs: ${APP_DOCS}`,
|
|
49
46
|
};
|
|
50
47
|
}
|
|
51
48
|
return { ok: true, installations: installations.length };
|
|
@@ -74,7 +71,7 @@ async function testPat(token) {
|
|
|
74
71
|
if (!canRead) {
|
|
75
72
|
return {
|
|
76
73
|
ok: false,
|
|
77
|
-
message:
|
|
74
|
+
message: `This token cannot read repositories. Grant the repo scope. ${AUTH_DOCS}`,
|
|
78
75
|
};
|
|
79
76
|
}
|
|
80
77
|
return { ok: true, login: user.login };
|
|
@@ -83,7 +80,7 @@ async function testPat(token) {
|
|
|
83
80
|
if (repos.status === 403 || repos.status === 401) {
|
|
84
81
|
return {
|
|
85
82
|
ok: false,
|
|
86
|
-
message:
|
|
83
|
+
message: `This token cannot list repositories. Grant it access to the repositories with Contents, Pull requests, Issues and Metadata read access. ${AUTH_DOCS}`,
|
|
87
84
|
};
|
|
88
85
|
}
|
|
89
86
|
if (!repos.ok) {
|
|
@@ -15,25 +15,35 @@ export declare function statsForRange(range: string): {
|
|
|
15
15
|
findings: number | undefined;
|
|
16
16
|
cost: number | undefined;
|
|
17
17
|
};
|
|
18
|
-
byRepo: (import("../store/reviews.ts").
|
|
18
|
+
byRepo: (import("../store/reviews.ts").CostTotals & {
|
|
19
|
+
reviews: number;
|
|
20
|
+
pullRequests: number;
|
|
21
|
+
findings: number;
|
|
22
|
+
tokensIn: number;
|
|
23
|
+
tokensOut: number;
|
|
24
|
+
failed: number;
|
|
25
|
+
avgDurationMs: number;
|
|
26
|
+
} & {
|
|
19
27
|
repo: string;
|
|
20
28
|
})[];
|
|
21
|
-
byDay:
|
|
22
|
-
|
|
29
|
+
byDay: import("../store/reviews.ts").DayStats[];
|
|
30
|
+
byModel: (import("../store/reviews.ts").CostTotals & {
|
|
23
31
|
reviews: number;
|
|
32
|
+
pullRequests: number;
|
|
24
33
|
findings: number;
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
34
|
+
tokensIn: number;
|
|
35
|
+
tokensOut: number;
|
|
36
|
+
failed: number;
|
|
37
|
+
avgDurationMs: number;
|
|
38
|
+
} & {
|
|
28
39
|
model: string;
|
|
29
40
|
})[];
|
|
30
|
-
byRemoteToken: {
|
|
41
|
+
byRemoteToken: (import("../store/reviews.ts").CostTotals & {
|
|
31
42
|
tokenId: string;
|
|
32
43
|
tokenName: string;
|
|
33
44
|
reviews: number;
|
|
34
|
-
cost: number;
|
|
35
45
|
findings: number;
|
|
36
|
-
}[];
|
|
46
|
+
})[];
|
|
37
47
|
bySeverity: import("../store/findings.ts").SeverityStats[];
|
|
38
48
|
};
|
|
39
49
|
export type RepoListItem = {
|
|
@@ -101,6 +111,9 @@ export declare function prDetail(fullName: string, prNumber: number): {
|
|
|
101
111
|
tokens_in: number;
|
|
102
112
|
tokens_out: number;
|
|
103
113
|
cost: number | null;
|
|
114
|
+
cost_status: "known" | "unknown" | null;
|
|
115
|
+
cost_note: string | null;
|
|
116
|
+
billed_to: "server" | "byok" | null;
|
|
104
117
|
duration_ms: number | null;
|
|
105
118
|
posted_review_id: string | null;
|
|
106
119
|
status: string;
|
|
@@ -37,7 +37,17 @@ function fillDays(days, rows) {
|
|
|
37
37
|
const out = [];
|
|
38
38
|
for (let back = days - 1; back >= 0; back--) {
|
|
39
39
|
const day = daysAgoIso(back).slice(0, 10);
|
|
40
|
-
out.push(byDay.get(day) ?? {
|
|
40
|
+
out.push(byDay.get(day) ?? {
|
|
41
|
+
day,
|
|
42
|
+
reviews: 0,
|
|
43
|
+
findings: 0,
|
|
44
|
+
cost: 0,
|
|
45
|
+
knownCount: 0,
|
|
46
|
+
unknownCount: 0,
|
|
47
|
+
byokUsd: 0,
|
|
48
|
+
byokKnownCount: 0,
|
|
49
|
+
byokUnknownCount: 0,
|
|
50
|
+
});
|
|
41
51
|
}
|
|
42
52
|
return out;
|
|
43
53
|
}
|
|
@@ -54,7 +64,11 @@ export function statsForRange(range) {
|
|
|
54
64
|
change: {
|
|
55
65
|
pullRequests: percentChange(previous.pullRequests, totals.pullRequests),
|
|
56
66
|
findings: percentChange(previous.findings, totals.findings),
|
|
57
|
-
|
|
67
|
+
// A trend line over a partly unknown total would compare an amount to
|
|
68
|
+
// one that is not the same thing, so it is left out rather than misled.
|
|
69
|
+
cost: totals.unknownCount > 0 || previous.unknownCount > 0
|
|
70
|
+
? undefined
|
|
71
|
+
: percentChange(previous.cost, totals.cost),
|
|
58
72
|
},
|
|
59
73
|
byRepo: reviewStatsByRepo(since),
|
|
60
74
|
byDay: fillDays(days, reviewStatsByDay(since)),
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** The reading half of `co-maintainer probe`, shared by the CLI and the
|
|
2
|
-
* dashboard's Add repository preview
|
|
2
|
+
* dashboard's Add repository preview.
|
|
3
3
|
*
|
|
4
4
|
* It only reads: no guide is written, nothing is cached here. The CLI adds
|
|
5
5
|
* the estimate, the printing and `--run`; `/api/repos/preview` adds the
|