@grimoire-rs/indexer 0.5.0 → 0.5.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/CHANGELOG.md +81 -1
- package/dist/cli/dev.d.ts +14 -0
- package/dist/cli/dev.d.ts.map +1 -1
- package/dist/cli/dev.js +22 -1
- package/dist/cli/dev.js.map +1 -1
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/main.js +5 -0
- package/dist/cli/main.js.map +1 -1
- package/dist/renderer/astro/components/Catalog.js +109 -3
- package/dist/renderer/astro/components/Catalog.tsx +156 -9
- package/dist/renderer/astro/components/CodeBlock.astro +74 -0
- package/dist/renderer/astro/layouts/Base.astro +143 -54
- package/dist/renderer/astro/lib/code.d.ts +2 -2
- package/dist/renderer/astro/lib/code.js +2 -2
- package/dist/renderer/astro/lib/code.ts +2 -2
- package/dist/renderer/astro/lib/commands.d.ts +9 -3
- package/dist/renderer/astro/lib/commands.js +10 -5
- package/dist/renderer/astro/lib/commands.ts +22 -6
- package/dist/renderer/astro/pages/p/[...slug].astro +23 -17
- package/dist/renderer/index.d.ts +19 -0
- package/dist/renderer/index.d.ts.map +1 -1
- package/dist/renderer/index.js +18 -2
- package/dist/renderer/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
---
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
// Copyright 2026 The Grimoire Authors
|
|
4
|
+
|
|
5
|
+
// A highlighted code block wearing the site's own copy affordance, and
|
|
6
|
+
// optionally a link out to VS Code.
|
|
7
|
+
//
|
|
8
|
+
// It exists for the same reason `CommandBar.astro` does: an index's own page
|
|
9
|
+
// under `theme/pages/` should be able to draw what the rest of the site draws
|
|
10
|
+
// rather than a `<pre>` that looks almost like it. A setup guide showing a
|
|
11
|
+
// config snippet, a publish guide showing a workflow — both want this, and
|
|
12
|
+
// neither wants to restate the Shiki theme pair or the copy markup.
|
|
13
|
+
//
|
|
14
|
+
// ---
|
|
15
|
+
// import CodeBlock from "@grim/components/CodeBlock.astro";
|
|
16
|
+
// import { vscodeUrl } from "@grim/lib/catalog";
|
|
17
|
+
// import { data } from "@grim/lib/data";
|
|
18
|
+
// ---
|
|
19
|
+
// <CodeBlock code="grim add acme/code-review" name="add command"
|
|
20
|
+
// vscodeHref={vscodeUrl(data.config.vscodeExtension, "acme/code-review")}
|
|
21
|
+
// vscodeLabel="Open in VS Code" />
|
|
22
|
+
//
|
|
23
|
+
// The copy button itself is NOT rendered here. `Base.astro` injects one into
|
|
24
|
+
// every code block on the page — it has to, because a block inside rendered
|
|
25
|
+
// markdown has no markup to hang one on — and it fills the `.code-actions`
|
|
26
|
+
// slot below when it finds it. One button, one clipboard path, one toast, and
|
|
27
|
+
// no second copy of the markup to drift.
|
|
28
|
+
import { Code } from "astro:components";
|
|
29
|
+
import { mdiMicrosoftVisualStudioCode } from "@mdi/js";
|
|
30
|
+
import { BrandMark } from "./BrandMark.tsx";
|
|
31
|
+
import { SHIKI_THEMES } from "../lib/code";
|
|
32
|
+
|
|
33
|
+
interface Props {
|
|
34
|
+
/** The snippet, verbatim. What the copy button copies. */
|
|
35
|
+
code: string;
|
|
36
|
+
/** Shiki language id. `sh` covers the commands this site mostly shows. */
|
|
37
|
+
lang?: string;
|
|
38
|
+
/** What the copy toast calls this, e.g. `"add command"`. */
|
|
39
|
+
name?: string;
|
|
40
|
+
/**
|
|
41
|
+
* Any URL for the trailing button — a `vscode://` deep link from
|
|
42
|
+
* `@grim/lib/catalog`'s builders, or one written by hand. Null or omitted
|
|
43
|
+
* draws no button, which is what an index with `vscodeExtension: null` gets
|
|
44
|
+
* from those builders.
|
|
45
|
+
*/
|
|
46
|
+
vscodeHref?: string | null;
|
|
47
|
+
/** Accessible name for that button. */
|
|
48
|
+
vscodeLabel?: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const {
|
|
52
|
+
code,
|
|
53
|
+
lang = "sh",
|
|
54
|
+
name,
|
|
55
|
+
vscodeHref = null,
|
|
56
|
+
vscodeLabel = "Open in VS Code",
|
|
57
|
+
} = Astro.props;
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
<div class="code-block" data-slot="code-block" data-copy-name={name}>
|
|
61
|
+
<Code code={code} lang={lang} themes={SHIKI_THEMES} />
|
|
62
|
+
{/* Present even when empty: it is where `Base.astro` puts the copy button,
|
|
63
|
+
and a block that renders one before the script runs would flash a second
|
|
64
|
+
one after it. */}
|
|
65
|
+
<span class="code-actions">
|
|
66
|
+
{
|
|
67
|
+
vscodeHref && (
|
|
68
|
+
<a class="code-vscode" href={vscodeHref} title={vscodeLabel} aria-label={vscodeLabel}>
|
|
69
|
+
<BrandMark path={mdiMicrosoftVisualStudioCode} size={15} />
|
|
70
|
+
</a>
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
</span>
|
|
74
|
+
</div>
|
|
@@ -279,10 +279,25 @@ const { title, description, image = config.logo ?? config.favicon } = Astro.prop
|
|
|
279
279
|
'<path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/></svg>';
|
|
280
280
|
|
|
281
281
|
for (const pre of document.querySelectorAll("pre.astro-code")) {
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
282
|
+
// `CodeBlock.astro` already ships the wrapper and the action slot, so
|
|
283
|
+
// adopt them where they are there. Wrapping a second time would nest
|
|
284
|
+
// two positioning contexts and orphan whatever the component put in
|
|
285
|
+
// the slot.
|
|
286
|
+
const parent = pre.parentElement;
|
|
287
|
+
let wrapper = parent?.classList.contains("code-block") ? parent : null;
|
|
288
|
+
if (!wrapper) {
|
|
289
|
+
wrapper = document.createElement("div");
|
|
290
|
+
wrapper.className = "code-block";
|
|
291
|
+
pre.replaceWith(wrapper);
|
|
292
|
+
wrapper.append(pre);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
let actions = wrapper.querySelector(".code-actions");
|
|
296
|
+
if (!actions) {
|
|
297
|
+
actions = document.createElement("span");
|
|
298
|
+
actions.className = "code-actions";
|
|
299
|
+
wrapper.append(actions);
|
|
300
|
+
}
|
|
286
301
|
|
|
287
302
|
const btn = document.createElement("button");
|
|
288
303
|
btn.type = "button";
|
|
@@ -292,9 +307,10 @@ const { title, description, image = config.logo ?? config.favicon } = Astro.prop
|
|
|
292
307
|
// `textContent`, not `innerText`: the block is not rendered yet at
|
|
293
308
|
// this point, and `innerText` would collapse its whitespace.
|
|
294
309
|
btn.dataset.copy = pre.textContent ?? "";
|
|
295
|
-
|
|
310
|
+
// A component-rendered block names itself; a markdown one cannot.
|
|
311
|
+
btn.dataset.copyName = wrapper.dataset.copyName || "code block";
|
|
296
312
|
btn.innerHTML = CLIPBOARD_ICON + '<span class="sr-only" role="status"></span>';
|
|
297
|
-
|
|
313
|
+
actions.append(btn);
|
|
298
314
|
}
|
|
299
315
|
|
|
300
316
|
for (const btn of document.querySelectorAll("[data-copy]")) {
|
|
@@ -917,6 +933,20 @@ const { title, description, image = config.logo ?? config.favicon } = Astro.prop
|
|
|
917
933
|
.chip.deprecated-toggle {
|
|
918
934
|
flex-shrink: 0;
|
|
919
935
|
}
|
|
936
|
+
/* Lifts every keyword at once, and is only in the markup while there
|
|
937
|
+
is one to lift — so it never competes with the rail for room in the
|
|
938
|
+
common case. Muted until hovered: it undoes work, and it should not
|
|
939
|
+
read as another facet to pick. */
|
|
940
|
+
.chip.kw-clear {
|
|
941
|
+
flex-shrink: 0;
|
|
942
|
+
gap: var(--grim-space-2);
|
|
943
|
+
color: var(--grim-color-muted);
|
|
944
|
+
}
|
|
945
|
+
.chip.kw-clear:hover,
|
|
946
|
+
.chip.kw-clear:focus-visible {
|
|
947
|
+
border-color: var(--grim-color-accent);
|
|
948
|
+
color: var(--grim-color-fg);
|
|
949
|
+
}
|
|
920
950
|
.kw-menu {
|
|
921
951
|
flex-shrink: 0;
|
|
922
952
|
}
|
|
@@ -1047,51 +1077,61 @@ const { title, description, image = config.logo ?? config.favicon } = Astro.prop
|
|
|
1047
1077
|
transition: border-color var(--grim-duration-base) ease;
|
|
1048
1078
|
}
|
|
1049
1079
|
}
|
|
1050
|
-
/* The overflow menu. A
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1080
|
+
/* The overflow menu. A popover, and it has to be one: `.filter-row`
|
|
1081
|
+
above is a scroll container, and an absolutely-positioned panel
|
|
1082
|
+
inside a scroll container is cropped to the row AND stretches the
|
|
1083
|
+
row's scroll extent — which is where a menu that opened invisibly,
|
|
1084
|
+
with a stray horizontal and vertical scrollbar to show for it, came
|
|
1085
|
+
from. The top layer is outside every ancestor's `overflow`, and it
|
|
1086
|
+
brings light dismiss and Escape, which the `<details>` this replaced
|
|
1087
|
+
never had. Its seat is `placeKwMenu` in `Catalog.tsx`. */
|
|
1054
1088
|
.kw-menu {
|
|
1055
|
-
position: relative;
|
|
1056
1089
|
display: flex;
|
|
1057
1090
|
}
|
|
1058
1091
|
/* The trigger wears `.chip`, so its box comes from there and cannot
|
|
1059
1092
|
drift a pixel off the chips it sits beside. Only what a chip does
|
|
1060
1093
|
not cover is stated here. */
|
|
1061
|
-
.kw-menu >
|
|
1094
|
+
.kw-menu > button {
|
|
1062
1095
|
display: inline-flex;
|
|
1063
1096
|
align-items: center;
|
|
1064
1097
|
color: var(--grim-color-muted);
|
|
1065
|
-
/* Both spellings: the default triangle is a marker in Firefox and
|
|
1066
|
-
a `::-webkit-details-marker` pseudo-element in older WebKit. */
|
|
1067
|
-
list-style: none;
|
|
1068
|
-
}
|
|
1069
|
-
.kw-menu > summary::-webkit-details-marker {
|
|
1070
|
-
display: none;
|
|
1071
1098
|
}
|
|
1072
|
-
.kw-menu >
|
|
1073
|
-
.kw-menu >
|
|
1074
|
-
.kw-menu
|
|
1099
|
+
.kw-menu > button:hover,
|
|
1100
|
+
.kw-menu > button:focus-visible,
|
|
1101
|
+
.kw-menu > button[aria-expanded="true"] {
|
|
1075
1102
|
border-color: var(--grim-color-accent);
|
|
1076
1103
|
color: var(--grim-color-fg);
|
|
1077
1104
|
}
|
|
1105
|
+
/* `inset` and `margin` undo the UA's centring of a popover, so the
|
|
1106
|
+
`left`/`top` the script writes are the whole position. `color` is
|
|
1107
|
+
restated because the UA sets `CanvasText` on `[popover]` directly,
|
|
1108
|
+
which beats what the panel would otherwise inherit. `display` is
|
|
1109
|
+
deliberately absent: the UA's `display: none` for a closed popover
|
|
1110
|
+
has to keep winning, so the flex box is declared on `:popover-open`
|
|
1111
|
+
alone — an unconditional `display` here would show the menu shut. */
|
|
1078
1112
|
.kw-menu-panel {
|
|
1079
|
-
position:
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
z-index: 5;
|
|
1083
|
-
display: flex;
|
|
1084
|
-
flex-direction: column;
|
|
1085
|
-
gap: var(--grim-space-3);
|
|
1113
|
+
position: fixed;
|
|
1114
|
+
inset: auto;
|
|
1115
|
+
margin: 0;
|
|
1086
1116
|
/* The panel's own measurement: wide enough for the longest keyword
|
|
1087
1117
|
a publisher is likely to write plus its count, and capped so a
|
|
1088
1118
|
long vocabulary scrolls instead of running off the viewport. */
|
|
1089
1119
|
width: 15rem;
|
|
1090
1120
|
padding: var(--grim-space-3);
|
|
1091
1121
|
background: var(--grim-color-card);
|
|
1122
|
+
color: var(--grim-color-fg);
|
|
1092
1123
|
border: var(--grim-border-width) solid var(--grim-color-border);
|
|
1093
1124
|
border-radius: var(--grim-radius-control);
|
|
1094
1125
|
box-shadow: var(--grim-shadow-raised);
|
|
1126
|
+
/* The list inside is the thing that scrolls. Without this the UA's
|
|
1127
|
+
own `overflow: auto` on `[popover]` adds a second scrollbar once
|
|
1128
|
+
the script caps the panel's height. */
|
|
1129
|
+
overflow: hidden;
|
|
1130
|
+
}
|
|
1131
|
+
.kw-menu-panel:popover-open {
|
|
1132
|
+
display: flex;
|
|
1133
|
+
flex-direction: column;
|
|
1134
|
+
gap: var(--grim-space-3);
|
|
1095
1135
|
}
|
|
1096
1136
|
.kw-menu-search {
|
|
1097
1137
|
width: 100%;
|
|
@@ -1115,6 +1155,11 @@ const { title, description, image = config.logo ?? config.favicon } = Astro.prop
|
|
|
1115
1155
|
as a list rather than a page. */
|
|
1116
1156
|
max-height: 15rem;
|
|
1117
1157
|
overflow-y: auto;
|
|
1158
|
+
/* And shorter still where the panel's own cap is tighter — a
|
|
1159
|
+
flex item's automatic minimum size is its content, so without
|
|
1160
|
+
`min-height` the list refuses to shrink and overflows the panel
|
|
1161
|
+
the script just sized to the space below the trigger. */
|
|
1162
|
+
min-height: 0;
|
|
1118
1163
|
}
|
|
1119
1164
|
.kw-menu-item {
|
|
1120
1165
|
display: flex;
|
|
@@ -1208,6 +1253,27 @@ const { title, description, image = config.logo ?? config.favicon } = Astro.prop
|
|
|
1208
1253
|
grid-template-columns: repeat(auto-fill, minmax(19rem, 1fr));
|
|
1209
1254
|
gap: var(--grim-space-6);
|
|
1210
1255
|
}
|
|
1256
|
+
/* Off-screen cards skip layout and paint until they are scrolled to.
|
|
1257
|
+
A card is the expensive item on this page — a masked keyword strip,
|
|
1258
|
+
a clipped watermark in its own stacking context, a logo — and the
|
|
1259
|
+
grid and the table are separate component trees, so switching view
|
|
1260
|
+
repaints every one of them at once.
|
|
1261
|
+
|
|
1262
|
+
`auto` remembers each card's last real height, so the scrollbar
|
|
1263
|
+
stops jumping after the first pass; the `18rem` is only the guess
|
|
1264
|
+
before that. Cards stay focusable and findable — the engine renders
|
|
1265
|
+
one on focus or find-in-page.
|
|
1266
|
+
|
|
1267
|
+
ponytail: this bounds the PAINT, not the JS. Measured in jsdom (no
|
|
1268
|
+
layout at all) the Preact commit for one view switch is ~30ms at 100
|
|
1269
|
+
packages, ~90-170ms at 500 and ~180-350ms at 1000, because the two
|
|
1270
|
+
views share no element types and every card unmounts as every row
|
|
1271
|
+
mounts. Past a few hundred packages the answer is windowing the
|
|
1272
|
+
list, not another CSS property. */
|
|
1273
|
+
.grid > li {
|
|
1274
|
+
content-visibility: auto;
|
|
1275
|
+
contain-intrinsic-size: auto 18rem;
|
|
1276
|
+
}
|
|
1211
1277
|
.card {
|
|
1212
1278
|
display: flex;
|
|
1213
1279
|
flex-direction: column;
|
|
@@ -1351,10 +1417,6 @@ const { title, description, image = config.logo ?? config.favicon } = Astro.prop
|
|
|
1351
1417
|
height: 36px;
|
|
1352
1418
|
border-radius: var(--grim-radius-inset);
|
|
1353
1419
|
}
|
|
1354
|
-
img.card-logo {
|
|
1355
|
-
object-fit: contain;
|
|
1356
|
-
background: var(--grim-color-chip-bg);
|
|
1357
|
-
}
|
|
1358
1420
|
.card-logo-fallback {
|
|
1359
1421
|
display: flex;
|
|
1360
1422
|
align-items: center;
|
|
@@ -1375,17 +1437,26 @@ const { title, description, image = config.logo ?? config.favicon } = Astro.prop
|
|
|
1375
1437
|
|
|
1376
1438
|
loading placeholder, no slash — the resting state
|
|
1377
1439
|
ready image faded in, placeholder faded out
|
|
1378
|
-
broken placeholder with its slash
|
|
1440
|
+
broken placeholder with its slash
|
|
1379
1441
|
|
|
1380
1442
|
Kept distinct from `.card-logo-fallback`: that is a package with
|
|
1381
1443
|
no logo at all, which is normal, not a fault. */
|
|
1444
|
+
/* No background. A logo is already a designed mark on its own ground,
|
|
1445
|
+
and a tinted square behind it framed it twice — worst on the many
|
|
1446
|
+
logos that are themselves a rounded square, which then sat inside a
|
|
1447
|
+
slightly larger rounded square. What is left is a box with a size,
|
|
1448
|
+
so the three states still swap without shifting anything. Nothing is
|
|
1449
|
+
drawn in any of them: the placeholder and the broken glyph carry
|
|
1450
|
+
their own colour, and the slash is what separates the two — a
|
|
1451
|
+
dashed frame around the broken one read as a fault in the layout
|
|
1452
|
+
rather than in the image, which is not what it means. The state is
|
|
1453
|
+
still announced, through `role="img"` and a label on the slot. */
|
|
1382
1454
|
.logo-slot {
|
|
1383
1455
|
position: relative;
|
|
1384
1456
|
display: flex;
|
|
1385
1457
|
align-items: center;
|
|
1386
1458
|
justify-content: center;
|
|
1387
1459
|
overflow: hidden;
|
|
1388
|
-
background: var(--grim-color-chip-bg);
|
|
1389
1460
|
color: var(--grim-color-muted);
|
|
1390
1461
|
}
|
|
1391
1462
|
.logo-slot .logo-mark,
|
|
@@ -1418,9 +1489,6 @@ const { title, description, image = config.logo ?? config.favicon } = Astro.prop
|
|
|
1418
1489
|
.logo-slot:not([data-state="broken"]) .mark-broken {
|
|
1419
1490
|
display: none;
|
|
1420
1491
|
}
|
|
1421
|
-
.logo-slot[data-state="broken"] {
|
|
1422
|
-
border: var(--grim-border-width) dashed var(--grim-color-border);
|
|
1423
|
-
}
|
|
1424
1492
|
@media (prefers-reduced-motion: reduce) {
|
|
1425
1493
|
.logo-slot .logo-mark,
|
|
1426
1494
|
.logo-slot img {
|
|
@@ -1524,10 +1592,7 @@ const { title, description, image = config.logo ?? config.favicon } = Astro.prop
|
|
|
1524
1592
|
name beside it starts on one line down the whole list. The colour
|
|
1525
1593
|
is set inline, from the kind's own token or the deprecation one. */
|
|
1526
1594
|
.t-kind {
|
|
1527
|
-
|
|
1528
|
-
}
|
|
1529
|
-
.t-kind svg {
|
|
1530
|
-
vertical-align: middle;
|
|
1595
|
+
justify-content: center;
|
|
1531
1596
|
}
|
|
1532
1597
|
/* The card's tile at a row's height. Geometry, not a scale step, and
|
|
1533
1598
|
measured against the text line beside it rather than against the
|
|
@@ -1561,23 +1626,33 @@ const { title, description, image = config.logo ?? config.favicon } = Astro.prop
|
|
|
1561
1626
|
wide cell — they are one reading, and the gap that separated them
|
|
1562
1627
|
was the cell's width showing through. */
|
|
1563
1628
|
.t-rating {
|
|
1564
|
-
|
|
1629
|
+
justify-content: flex-end;
|
|
1565
1630
|
color: var(--grim-color-muted);
|
|
1566
1631
|
}
|
|
1567
1632
|
.t-rating svg {
|
|
1568
|
-
vertical-align: middle;
|
|
1569
1633
|
margin-left: var(--grim-space-2);
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1634
|
+
}
|
|
1635
|
+
/* The two cells that carry a glyph centre it as a box, not on the
|
|
1636
|
+
text line. `vertical-align: middle` aligns to half the x-height,
|
|
1637
|
+
which sits about 2px under the line's true middle at this step —
|
|
1638
|
+
the eye reads that as the mark and the arrow sagging below the
|
|
1639
|
+
words beside them. Neither cell owes anything to the row's
|
|
1640
|
+
no-flex rule above: the kind is one mark and the rating is a
|
|
1641
|
+
fixed-width count plus its arrow, so there is no text here for
|
|
1642
|
+
`text-overflow` to elide. */
|
|
1643
|
+
.t-kind,
|
|
1644
|
+
.t-rating {
|
|
1645
|
+
display: flex;
|
|
1646
|
+
align-items: center;
|
|
1574
1647
|
}
|
|
1575
1648
|
/* The count carries the column's alignment, and the arrow after it
|
|
1576
1649
|
inherits it: a fixed box, right-aligned, in tabular figures. Without
|
|
1577
1650
|
the fixed box a three-digit count would push its own arrow right of
|
|
1578
1651
|
the one above it. Geometry — it holds four digits at this step. */
|
|
1579
1652
|
.t-votes {
|
|
1580
|
-
|
|
1653
|
+
/* Never shrunk to its digits: the box is what holds the arrow
|
|
1654
|
+
after it in one place down the column. */
|
|
1655
|
+
flex: none;
|
|
1581
1656
|
width: 2rem;
|
|
1582
1657
|
text-align: right;
|
|
1583
1658
|
font-variant-numeric: tabular-nums;
|
|
@@ -1912,12 +1987,21 @@ const { title, description, image = config.logo ?? config.favicon } = Astro.prop
|
|
|
1912
1987
|
.astro-code span:not([style*="background"]) {
|
|
1913
1988
|
background-color: transparent !important;
|
|
1914
1989
|
}
|
|
1915
|
-
/*
|
|
1916
|
-
|
|
1917
|
-
|
|
1990
|
+
/* The corner the block's buttons sit in. It is a row rather than one
|
|
1991
|
+
positioned button because `CodeBlock.astro` can add a second one —
|
|
1992
|
+
a VS Code deep link — and the copy button `Base.astro` injects has
|
|
1993
|
+
to land beside it without either knowing the other's width. */
|
|
1994
|
+
.code-actions {
|
|
1918
1995
|
position: absolute;
|
|
1919
1996
|
top: 0.45rem;
|
|
1920
1997
|
right: 0.45rem;
|
|
1998
|
+
display: flex;
|
|
1999
|
+
gap: var(--grim-space-2);
|
|
2000
|
+
}
|
|
2001
|
+
/* Reveals on hover or keyboard focus, so they are not a permanent
|
|
2002
|
+
smudge over the first line of every block. */
|
|
2003
|
+
.code-copy,
|
|
2004
|
+
.code-vscode {
|
|
1921
2005
|
display: inline-flex;
|
|
1922
2006
|
align-items: center;
|
|
1923
2007
|
justify-content: center;
|
|
@@ -1928,6 +2012,7 @@ const { title, description, image = config.logo ?? config.favicon } = Astro.prop
|
|
|
1928
2012
|
border-radius: var(--grim-radius-inset);
|
|
1929
2013
|
background: var(--grim-color-card);
|
|
1930
2014
|
color: var(--grim-color-muted);
|
|
2015
|
+
text-decoration: none;
|
|
1931
2016
|
cursor: pointer;
|
|
1932
2017
|
opacity: 0;
|
|
1933
2018
|
transition:
|
|
@@ -1936,10 +2021,13 @@ const { title, description, image = config.logo ?? config.favicon } = Astro.prop
|
|
|
1936
2021
|
border-color var(--grim-duration-fast) ease;
|
|
1937
2022
|
}
|
|
1938
2023
|
.code-block:hover .code-copy,
|
|
1939
|
-
.code-
|
|
2024
|
+
.code-block:hover .code-vscode,
|
|
2025
|
+
.code-copy:focus-visible,
|
|
2026
|
+
.code-vscode:focus-visible {
|
|
1940
2027
|
opacity: 1;
|
|
1941
2028
|
}
|
|
1942
|
-
.code-copy:hover
|
|
2029
|
+
.code-copy:hover,
|
|
2030
|
+
.code-vscode:hover {
|
|
1943
2031
|
color: var(--grim-color-accent);
|
|
1944
2032
|
border-color: var(--grim-color-accent);
|
|
1945
2033
|
}
|
|
@@ -1949,7 +2037,8 @@ const { title, description, image = config.logo ?? config.favicon } = Astro.prop
|
|
|
1949
2037
|
opacity: 1;
|
|
1950
2038
|
}
|
|
1951
2039
|
@media (prefers-reduced-motion: reduce) {
|
|
1952
|
-
.code-copy
|
|
2040
|
+
.code-copy,
|
|
2041
|
+
.code-vscode {
|
|
1953
2042
|
transition: none;
|
|
1954
2043
|
}
|
|
1955
2044
|
}
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* Shiki is what Astro already ships — the same highlighter VS Code renders
|
|
5
5
|
* with — so nothing here adds a library; this only stops the two consumers
|
|
6
6
|
* drifting onto different themes. The markdown pipeline reads it through
|
|
7
|
-
* `astro.config`'s `shikiConfig`, and
|
|
8
|
-
*
|
|
7
|
+
* `astro.config`'s `shikiConfig`, and `CodeBlock.astro` — what every
|
|
8
|
+
* hand-written snippet on the site goes through — passes it to `<Code>`.
|
|
9
9
|
*
|
|
10
10
|
* A *pair*, not one theme: with a single theme a block stays dark on a light
|
|
11
11
|
* page. Given two, Shiki writes the light colour as an inline style and the
|
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
* Shiki is what Astro already ships — the same highlighter VS Code renders
|
|
7
7
|
* with — so nothing here adds a library; this only stops the two consumers
|
|
8
8
|
* drifting onto different themes. The markdown pipeline reads it through
|
|
9
|
-
* `astro.config`'s `shikiConfig`, and
|
|
10
|
-
*
|
|
9
|
+
* `astro.config`'s `shikiConfig`, and `CodeBlock.astro` — what every
|
|
10
|
+
* hand-written snippet on the site goes through — passes it to `<Code>`.
|
|
11
11
|
*
|
|
12
12
|
* A *pair*, not one theme: with a single theme a block stays dark on a light
|
|
13
13
|
* page. Given two, Shiki writes the light colour as an inline style and the
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
* Shiki is what Astro already ships — the same highlighter VS Code renders
|
|
8
8
|
* with — so nothing here adds a library; this only stops the two consumers
|
|
9
9
|
* drifting onto different themes. The markdown pipeline reads it through
|
|
10
|
-
* `astro.config`'s `shikiConfig`, and
|
|
11
|
-
*
|
|
10
|
+
* `astro.config`'s `shikiConfig`, and `CodeBlock.astro` — what every
|
|
11
|
+
* hand-written snippet on the site goes through — passes it to `<Code>`.
|
|
12
12
|
*
|
|
13
13
|
* A *pair*, not one theme: with a single theme a block stays dark on a light
|
|
14
14
|
* page. Given two, Shiki writes the light colour as an inline style and the
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { ResolvedSiteConfig } from "../../../config.js";
|
|
1
|
+
import type { RegistryHint, ResolvedSiteConfig } from "../../../config.js";
|
|
2
|
+
export type { RegistryHint };
|
|
2
3
|
/**
|
|
3
4
|
* One option in a command bar: what it is called, what it copies, and the
|
|
4
5
|
* glyph that stands for it.
|
|
@@ -35,8 +36,13 @@ export declare function installChoices(config: ResolvedSiteConfig): Choice[];
|
|
|
35
36
|
* the `helm repo add` ergonomic. `null` when `registry` is unconfigured,
|
|
36
37
|
* which means the index has no public URL to hand out; the block is omitted
|
|
37
38
|
* rather than guessed.
|
|
39
|
+
*
|
|
40
|
+
* `registry` defaults to the site's own, and is a parameter so a page can draw
|
|
41
|
+
* the same bar for a *different* index: a corporate setup guide that hands out
|
|
42
|
+
* its own index in the hero and the public one further down needs two bars
|
|
43
|
+
* differing in nothing but this argument.
|
|
38
44
|
*/
|
|
39
|
-
export declare function registryAddCommand(config: ResolvedSiteConfig): string | null;
|
|
45
|
+
export declare function registryAddCommand(config: ResolvedSiteConfig, registry?: RegistryHint | null): string | null;
|
|
40
46
|
/**
|
|
41
47
|
* Scope choices for the registry-add bar. Empty when there is no command to
|
|
42
48
|
* scope — the bar can still render for the sake of its VS Code segment.
|
|
@@ -49,7 +55,7 @@ export declare function registryAddCommand(config: ResolvedSiteConfig): string |
|
|
|
49
55
|
* flag, and appended after a long `--index <url>` it fell off the end of the
|
|
50
56
|
* line, so switching scope looked like it changed nothing at all.
|
|
51
57
|
*/
|
|
52
|
-
export declare function registryScopeChoices(config: ResolvedSiteConfig): Choice[];
|
|
58
|
+
export declare function registryScopeChoices(config: ResolvedSiteConfig, registry?: RegistryHint | null): Choice[];
|
|
53
59
|
/**
|
|
54
60
|
* Scope choices for adding one package — the same two-way choice, the same
|
|
55
61
|
* two glyphs, the same order as the registry bar and the package cards.
|
|
@@ -47,10 +47,15 @@ export function installChoices(config) {
|
|
|
47
47
|
* the `helm repo add` ergonomic. `null` when `registry` is unconfigured,
|
|
48
48
|
* which means the index has no public URL to hand out; the block is omitted
|
|
49
49
|
* rather than guessed.
|
|
50
|
+
*
|
|
51
|
+
* `registry` defaults to the site's own, and is a parameter so a page can draw
|
|
52
|
+
* the same bar for a *different* index: a corporate setup guide that hands out
|
|
53
|
+
* its own index in the hero and the public one further down needs two bars
|
|
54
|
+
* differing in nothing but this argument.
|
|
50
55
|
*/
|
|
51
|
-
export function registryAddCommand(config) {
|
|
52
|
-
return
|
|
53
|
-
? `grim config registry add ${
|
|
56
|
+
export function registryAddCommand(config, registry = config.registry) {
|
|
57
|
+
return registry
|
|
58
|
+
? `grim config registry add ${registry.alias} --index ${registry.index}`
|
|
54
59
|
: null;
|
|
55
60
|
}
|
|
56
61
|
/**
|
|
@@ -65,8 +70,8 @@ export function registryAddCommand(config) {
|
|
|
65
70
|
* flag, and appended after a long `--index <url>` it fell off the end of the
|
|
66
71
|
* line, so switching scope looked like it changed nothing at all.
|
|
67
72
|
*/
|
|
68
|
-
export function registryScopeChoices(config) {
|
|
69
|
-
const add = registryAddCommand(config);
|
|
73
|
+
export function registryScopeChoices(config, registry = config.registry) {
|
|
74
|
+
const add = registryAddCommand(config, registry);
|
|
70
75
|
if (!add)
|
|
71
76
|
return [];
|
|
72
77
|
return [
|
|
@@ -20,7 +20,12 @@ import {
|
|
|
20
20
|
mdiMicrosoftWindows,
|
|
21
21
|
mdiPenguin,
|
|
22
22
|
} from "@mdi/js";
|
|
23
|
-
import type { ResolvedSiteConfig } from "../../../config.js";
|
|
23
|
+
import type { RegistryHint, ResolvedSiteConfig } from "../../../config.js";
|
|
24
|
+
|
|
25
|
+
// Re-exported so an index's own page can name the shape it passes to the two
|
|
26
|
+
// functions below without reaching past `@grim/lib/*` into the package's
|
|
27
|
+
// internals, which the overlay does not publish.
|
|
28
|
+
export type { RegistryHint };
|
|
24
29
|
|
|
25
30
|
/**
|
|
26
31
|
* One option in a command bar: what it is called, what it copies, and the
|
|
@@ -77,10 +82,18 @@ export function installChoices(config: ResolvedSiteConfig): Choice[] {
|
|
|
77
82
|
* the `helm repo add` ergonomic. `null` when `registry` is unconfigured,
|
|
78
83
|
* which means the index has no public URL to hand out; the block is omitted
|
|
79
84
|
* rather than guessed.
|
|
85
|
+
*
|
|
86
|
+
* `registry` defaults to the site's own, and is a parameter so a page can draw
|
|
87
|
+
* the same bar for a *different* index: a corporate setup guide that hands out
|
|
88
|
+
* its own index in the hero and the public one further down needs two bars
|
|
89
|
+
* differing in nothing but this argument.
|
|
80
90
|
*/
|
|
81
|
-
export function registryAddCommand(
|
|
82
|
-
|
|
83
|
-
|
|
91
|
+
export function registryAddCommand(
|
|
92
|
+
config: ResolvedSiteConfig,
|
|
93
|
+
registry: RegistryHint | null = config.registry,
|
|
94
|
+
): string | null {
|
|
95
|
+
return registry
|
|
96
|
+
? `grim config registry add ${registry.alias} --index ${registry.index}`
|
|
84
97
|
: null;
|
|
85
98
|
}
|
|
86
99
|
|
|
@@ -96,8 +109,11 @@ export function registryAddCommand(config: ResolvedSiteConfig): string | null {
|
|
|
96
109
|
* flag, and appended after a long `--index <url>` it fell off the end of the
|
|
97
110
|
* line, so switching scope looked like it changed nothing at all.
|
|
98
111
|
*/
|
|
99
|
-
export function registryScopeChoices(
|
|
100
|
-
|
|
112
|
+
export function registryScopeChoices(
|
|
113
|
+
config: ResolvedSiteConfig,
|
|
114
|
+
registry: RegistryHint | null = config.registry,
|
|
115
|
+
): Choice[] {
|
|
116
|
+
const add = registryAddCommand(config, registry);
|
|
101
117
|
if (!add) return [];
|
|
102
118
|
return [
|
|
103
119
|
{ name: "Global", command: `grim --global ${add.slice("grim ".length)}`, Icon: Globe },
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
import Base from "../../layouts/Base.astro";
|
|
22
22
|
import VersionMenu from "../../components/VersionMenu.astro";
|
|
23
23
|
import CommandBar from "../../components/CommandBar.astro";
|
|
24
|
+
import CodeBlock from "../../components/CodeBlock.astro";
|
|
24
25
|
import { BrandMark } from "../../components/BrandMark.tsx";
|
|
25
26
|
import { DEPRECATED_MARK, KIND_MARKS, KindMark } from "../../components/KindMark.tsx";
|
|
26
27
|
import {
|
|
@@ -31,7 +32,6 @@ import {
|
|
|
31
32
|
mdiMicrosoftVisualStudioCode,
|
|
32
33
|
} from "@mdi/js";
|
|
33
34
|
import { withBase } from "../../lib/base";
|
|
34
|
-
import { SHIKI_THEMES } from "../../lib/code";
|
|
35
35
|
import {
|
|
36
36
|
compareVersions,
|
|
37
37
|
deprecationNote,
|
|
@@ -47,7 +47,6 @@ import {
|
|
|
47
47
|
import { addArtifactChoices } from "../../lib/commands";
|
|
48
48
|
import { data } from "../../lib/data";
|
|
49
49
|
import { getEntry, render } from "astro:content";
|
|
50
|
-
import { Code } from "astro:components";
|
|
51
50
|
|
|
52
51
|
export function getStaticPaths() {
|
|
53
52
|
return data.packages.map((pkg) => ({
|
|
@@ -382,10 +381,10 @@ const headMarkColor = pkg.deprecated
|
|
|
382
381
|
))}
|
|
383
382
|
</ul>
|
|
384
383
|
) : (
|
|
385
|
-
<
|
|
384
|
+
<CodeBlock
|
|
386
385
|
code={JSON.stringify(contentsData, null, 2)}
|
|
387
386
|
lang="json"
|
|
388
|
-
|
|
387
|
+
name="contents"
|
|
389
388
|
/>
|
|
390
389
|
)}
|
|
391
390
|
</div>
|
|
@@ -612,11 +611,14 @@ const headMarkColor = pkg.deprecated
|
|
|
612
611
|
gap: var(--grim-space-7);
|
|
613
612
|
min-width: 0;
|
|
614
613
|
}
|
|
614
|
+
/* Top-aligned, not centred. Centring pinned the tile to the middle of the
|
|
615
|
+
text block beside it, so every extra line of description pushed the logo
|
|
616
|
+
further down the header — the same package's mark sat at a different
|
|
617
|
+
height on every page. Aligned to the start it keeps its place against the
|
|
618
|
+
name and the byline whatever the description runs to. */
|
|
615
619
|
.detail-head {
|
|
616
620
|
display: flex;
|
|
617
|
-
|
|
618
|
-
beside it rather than sitting centred against a third of it. */
|
|
619
|
-
align-items: stretch;
|
|
621
|
+
align-items: flex-start;
|
|
620
622
|
gap: var(--grim-space-6);
|
|
621
623
|
background: var(--grim-color-card);
|
|
622
624
|
border: var(--grim-border-width) solid var(--grim-color-border);
|
|
@@ -626,19 +628,23 @@ const headMarkColor = pkg.deprecated
|
|
|
626
628
|
.detail-head-text {
|
|
627
629
|
min-width: 0;
|
|
628
630
|
}
|
|
631
|
+
/* One fixed square, the same on every package — a size, not a frame: the
|
|
632
|
+
logo slot draws no background, so what this reserves is the space the
|
|
633
|
+
mark letterboxes into. Sizing it off the text block beside it did not
|
|
634
|
+
work: `aspect-ratio` derives the width from the height the box has
|
|
635
|
+
*before* `align-self: stretch` grows it, so the tile came out 56px wide
|
|
636
|
+
against a 93px-tall header — a portrait chip that hugged the logo instead
|
|
637
|
+
of framing it. A definite square has no such ordering problem, and every
|
|
638
|
+
package's mark then lands at one size whatever its aspect ratio.
|
|
639
|
+
|
|
640
|
+
`.tile.letter` below is the exception that keeps a background, and it is
|
|
641
|
+
not one: there the coloured ground IS the mark, and the initial sitting on
|
|
642
|
+
it would be invisible without it. */
|
|
629
643
|
.tile {
|
|
630
644
|
flex: 0 0 auto;
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
height: auto;
|
|
634
|
-
/* Square, sized by the text block it stands beside. Bounded at both
|
|
635
|
-
ends: a one-line header must not shrink it to a favicon, and a long
|
|
636
|
-
summary must not grow it into the page. */
|
|
637
|
-
aspect-ratio: 1;
|
|
638
|
-
min-height: 3.5rem;
|
|
639
|
-
max-height: 7rem;
|
|
645
|
+
width: 4.5rem;
|
|
646
|
+
height: 4.5rem;
|
|
640
647
|
border-radius: var(--grim-radius-surface);
|
|
641
|
-
object-fit: cover;
|
|
642
648
|
}
|
|
643
649
|
.tile.letter {
|
|
644
650
|
display: flex;
|