@grimoire-rs/indexer 0.5.0 → 0.5.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.
@@ -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
- const wrapper = document.createElement("div");
283
- wrapper.className = "code-block";
284
- pre.replaceWith(wrapper);
285
- wrapper.append(pre);
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
- btn.dataset.copyName = "code block";
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
- wrapper.append(btn);
313
+ actions.append(btn);
298
314
  }
299
315
 
300
316
  for (const btn of document.querySelectorAll("[data-copy]")) {
@@ -833,10 +849,6 @@ const { title, description, image = config.logo ?? config.favicon } = Astro.prop
833
849
  font-size: var(--grim-text-md);
834
850
  min-width: 0;
835
851
  }
836
- .controls input[type="search"]:focus-visible {
837
- outline: 2px solid var(--grim-color-accent);
838
- outline-offset: 1px;
839
- }
840
852
  /* The `/` shortcut, shown as the key it is. It is a hint for a field
841
853
  that is not yet in use, so it withdraws the moment the field is
842
854
  focused or holds a query — at which point it would only be
@@ -917,6 +929,20 @@ const { title, description, image = config.logo ?? config.favicon } = Astro.prop
917
929
  .chip.deprecated-toggle {
918
930
  flex-shrink: 0;
919
931
  }
932
+ /* Lifts every keyword at once, and is only in the markup while there
933
+ is one to lift — so it never competes with the rail for room in the
934
+ common case. Muted until hovered: it undoes work, and it should not
935
+ read as another facet to pick. */
936
+ .chip.kw-clear {
937
+ flex-shrink: 0;
938
+ gap: var(--grim-space-2);
939
+ color: var(--grim-color-muted);
940
+ }
941
+ .chip.kw-clear:hover,
942
+ .chip.kw-clear:focus-visible {
943
+ border-color: var(--grim-color-accent);
944
+ color: var(--grim-color-fg);
945
+ }
920
946
  .kw-menu {
921
947
  flex-shrink: 0;
922
948
  }
@@ -996,11 +1022,6 @@ const { title, description, image = config.logo ?? config.favicon } = Astro.prop
996
1022
  .sort-field:focus-visible {
997
1023
  z-index: 1;
998
1024
  }
999
- .sort-dir:focus-visible,
1000
- .sort-field:focus-visible {
1001
- outline: 2px solid var(--grim-color-accent);
1002
- outline-offset: 1px;
1003
- }
1004
1025
  /* One box for every chip in the filter row — the kinds, the keywords,
1005
1026
  the deprecated toggle and the overflow menu's trigger alike. The
1006
1027
  line-height is stated rather than inherited because a `<summary>`
@@ -1032,66 +1053,68 @@ const { title, description, image = config.logo ?? config.favicon } = Astro.prop
1032
1053
  background: var(--grim-color-accent-soft);
1033
1054
  }
1034
1055
  /* The keyword rail reorders on every click — it is rescored against
1035
- what is on screen — and the chips slide rather than jump. The FLIP
1036
- effect in `Catalog.tsx` sets an inline `translate` back to where
1037
- each chip was and drops it on the next frame; this transition is
1038
- what plays that back. Without a declared duration the invert would
1039
- simply stick. */
1040
- .chip.kw {
1041
- transition:
1042
- border-color var(--grim-duration-base) ease,
1043
- translate var(--grim-duration-slow) ease-out;
1044
- }
1045
- @media (prefers-reduced-motion: reduce) {
1046
- .chip.kw {
1047
- transition: border-color var(--grim-duration-base) ease;
1048
- }
1049
- }
1050
- /* The overflow menu. A `<details>` for the same reasons the platform
1051
- picker is one — it opens, closes and takes Escape without a line of
1052
- script — and the panel is the `.os-menu` popover's measurements so
1053
- the two read as the same object in two places. */
1056
+ what is on screen — and the chips slide rather than jump. There is
1057
+ deliberately no `translate` transition here: the FLIP effect in
1058
+ `Catalog.tsx` runs each slide as a Web Animation instead, reading
1059
+ `--grim-duration-slow` for its length and honouring
1060
+ `prefers-reduced-motion` itself. A transition needs an inline style
1061
+ to drive it, and an inline style is what a superseded pass used to
1062
+ leave behind on a chip stopped off its seat. */
1063
+ /* The overflow menu. A popover, and it has to be one: `.filter-row`
1064
+ above is a scroll container, and an absolutely-positioned panel
1065
+ inside a scroll container is cropped to the row AND stretches the
1066
+ row's scroll extent — which is where a menu that opened invisibly,
1067
+ with a stray horizontal and vertical scrollbar to show for it, came
1068
+ from. The top layer is outside every ancestor's `overflow`, and it
1069
+ brings light dismiss and Escape, which the `<details>` this replaced
1070
+ never had. Its seat is `placeKwMenu` in `Catalog.tsx`. */
1054
1071
  .kw-menu {
1055
- position: relative;
1056
1072
  display: flex;
1057
1073
  }
1058
1074
  /* The trigger wears `.chip`, so its box comes from there and cannot
1059
1075
  drift a pixel off the chips it sits beside. Only what a chip does
1060
1076
  not cover is stated here. */
1061
- .kw-menu > summary {
1077
+ .kw-menu > button {
1062
1078
  display: inline-flex;
1063
1079
  align-items: center;
1064
1080
  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
1081
  }
1069
- .kw-menu > summary::-webkit-details-marker {
1070
- display: none;
1071
- }
1072
- .kw-menu > summary:hover,
1073
- .kw-menu > summary:focus-visible,
1074
- .kw-menu[open] > summary {
1082
+ .kw-menu > button:hover,
1083
+ .kw-menu > button:focus-visible,
1084
+ .kw-menu > button[aria-expanded="true"] {
1075
1085
  border-color: var(--grim-color-accent);
1076
1086
  color: var(--grim-color-fg);
1077
1087
  }
1088
+ /* `inset` and `margin` undo the UA's centring of a popover, so the
1089
+ `left`/`top` the script writes are the whole position. `color` is
1090
+ restated because the UA sets `CanvasText` on `[popover]` directly,
1091
+ which beats what the panel would otherwise inherit. `display` is
1092
+ deliberately absent: the UA's `display: none` for a closed popover
1093
+ has to keep winning, so the flex box is declared on `:popover-open`
1094
+ alone — an unconditional `display` here would show the menu shut. */
1078
1095
  .kw-menu-panel {
1079
- position: absolute;
1080
- top: calc(100% + var(--grim-space-2));
1081
- left: 0;
1082
- z-index: 5;
1083
- display: flex;
1084
- flex-direction: column;
1085
- gap: var(--grim-space-3);
1096
+ position: fixed;
1097
+ inset: auto;
1098
+ margin: 0;
1086
1099
  /* The panel's own measurement: wide enough for the longest keyword
1087
1100
  a publisher is likely to write plus its count, and capped so a
1088
1101
  long vocabulary scrolls instead of running off the viewport. */
1089
1102
  width: 15rem;
1090
1103
  padding: var(--grim-space-3);
1091
1104
  background: var(--grim-color-card);
1105
+ color: var(--grim-color-fg);
1092
1106
  border: var(--grim-border-width) solid var(--grim-color-border);
1093
1107
  border-radius: var(--grim-radius-control);
1094
1108
  box-shadow: var(--grim-shadow-raised);
1109
+ /* The list inside is the thing that scrolls. Without this the UA's
1110
+ own `overflow: auto` on `[popover]` adds a second scrollbar once
1111
+ the script caps the panel's height. */
1112
+ overflow: hidden;
1113
+ }
1114
+ .kw-menu-panel:popover-open {
1115
+ display: flex;
1116
+ flex-direction: column;
1117
+ gap: var(--grim-space-3);
1095
1118
  }
1096
1119
  .kw-menu-search {
1097
1120
  width: 100%;
@@ -1103,10 +1126,6 @@ const { title, description, image = config.logo ?? config.favicon } = Astro.prop
1103
1126
  font: inherit;
1104
1127
  font-size: var(--grim-text-sm);
1105
1128
  }
1106
- .kw-menu-search:focus-visible {
1107
- outline: 2px solid var(--grim-color-accent);
1108
- outline-offset: 1px;
1109
- }
1110
1129
  .kw-menu-list {
1111
1130
  display: flex;
1112
1131
  flex-direction: column;
@@ -1115,6 +1134,11 @@ const { title, description, image = config.logo ?? config.favicon } = Astro.prop
1115
1134
  as a list rather than a page. */
1116
1135
  max-height: 15rem;
1117
1136
  overflow-y: auto;
1137
+ /* And shorter still where the panel's own cap is tighter — a
1138
+ flex item's automatic minimum size is its content, so without
1139
+ `min-height` the list refuses to shrink and overflows the panel
1140
+ the script just sized to the space below the trigger. */
1141
+ min-height: 0;
1118
1142
  }
1119
1143
  .kw-menu-item {
1120
1144
  display: flex;
@@ -1195,9 +1219,26 @@ const { title, description, image = config.logo ?? config.favicon } = Astro.prop
1195
1219
  .view-pick.active {
1196
1220
  z-index: 1;
1197
1221
  }
1222
+
1223
+ /* One focus ring for every bordered control in the toolbar, and it is
1224
+ the card's: the border takes the accent and the outline sits
1225
+ directly on it, so the two merge into one thicker edge.
1226
+
1227
+ An offset ring is what put two borders on the sort combo. Hover
1228
+ already tints the border, so a ring standing 1px clear of it drew a
1229
+ second line in the same colour — one state reading as two. Every
1230
+ control here tints its border on hover or while active, so the
1231
+ offset had to go from all of them together rather than from the one
1232
+ that was reported. */
1233
+ .chip:focus-visible,
1234
+ .controls input[type="search"]:focus-visible,
1235
+ .kw-menu-search:focus-visible,
1236
+ .sort-dir:focus-visible,
1237
+ .sort-field:focus-visible,
1198
1238
  .view-pick:focus-visible {
1199
1239
  outline: 2px solid var(--grim-color-accent);
1200
- outline-offset: 1px;
1240
+ outline-offset: 0;
1241
+ border-color: var(--grim-color-accent);
1201
1242
  }
1202
1243
 
1203
1244
  .grid {
@@ -1208,6 +1249,27 @@ const { title, description, image = config.logo ?? config.favicon } = Astro.prop
1208
1249
  grid-template-columns: repeat(auto-fill, minmax(19rem, 1fr));
1209
1250
  gap: var(--grim-space-6);
1210
1251
  }
1252
+ /* Off-screen cards skip layout and paint until they are scrolled to.
1253
+ A card is the expensive item on this page — a masked keyword strip,
1254
+ a clipped watermark in its own stacking context, a logo — and the
1255
+ grid and the table are separate component trees, so switching view
1256
+ repaints every one of them at once.
1257
+
1258
+ `auto` remembers each card's last real height, so the scrollbar
1259
+ stops jumping after the first pass; the `18rem` is only the guess
1260
+ before that. Cards stay focusable and findable — the engine renders
1261
+ one on focus or find-in-page.
1262
+
1263
+ ponytail: this bounds the PAINT, not the JS. Measured in jsdom (no
1264
+ layout at all) the Preact commit for one view switch is ~30ms at 100
1265
+ packages, ~90-170ms at 500 and ~180-350ms at 1000, because the two
1266
+ views share no element types and every card unmounts as every row
1267
+ mounts. Past a few hundred packages the answer is windowing the
1268
+ list, not another CSS property. */
1269
+ .grid > li {
1270
+ content-visibility: auto;
1271
+ contain-intrinsic-size: auto 18rem;
1272
+ }
1211
1273
  .card {
1212
1274
  display: flex;
1213
1275
  flex-direction: column;
@@ -1351,10 +1413,6 @@ const { title, description, image = config.logo ?? config.favicon } = Astro.prop
1351
1413
  height: 36px;
1352
1414
  border-radius: var(--grim-radius-inset);
1353
1415
  }
1354
- img.card-logo {
1355
- object-fit: contain;
1356
- background: var(--grim-color-chip-bg);
1357
- }
1358
1416
  .card-logo-fallback {
1359
1417
  display: flex;
1360
1418
  align-items: center;
@@ -1375,17 +1433,26 @@ const { title, description, image = config.logo ?? config.favicon } = Astro.prop
1375
1433
 
1376
1434
  loading placeholder, no slash — the resting state
1377
1435
  ready image faded in, placeholder faded out
1378
- broken placeholder with its slash, dashed edge
1436
+ broken placeholder with its slash
1379
1437
 
1380
1438
  Kept distinct from `.card-logo-fallback`: that is a package with
1381
1439
  no logo at all, which is normal, not a fault. */
1440
+ /* No background. A logo is already a designed mark on its own ground,
1441
+ and a tinted square behind it framed it twice — worst on the many
1442
+ logos that are themselves a rounded square, which then sat inside a
1443
+ slightly larger rounded square. What is left is a box with a size,
1444
+ so the three states still swap without shifting anything. Nothing is
1445
+ drawn in any of them: the placeholder and the broken glyph carry
1446
+ their own colour, and the slash is what separates the two — a
1447
+ dashed frame around the broken one read as a fault in the layout
1448
+ rather than in the image, which is not what it means. The state is
1449
+ still announced, through `role="img"` and a label on the slot. */
1382
1450
  .logo-slot {
1383
1451
  position: relative;
1384
1452
  display: flex;
1385
1453
  align-items: center;
1386
1454
  justify-content: center;
1387
1455
  overflow: hidden;
1388
- background: var(--grim-color-chip-bg);
1389
1456
  color: var(--grim-color-muted);
1390
1457
  }
1391
1458
  .logo-slot .logo-mark,
@@ -1418,9 +1485,6 @@ const { title, description, image = config.logo ?? config.favicon } = Astro.prop
1418
1485
  .logo-slot:not([data-state="broken"]) .mark-broken {
1419
1486
  display: none;
1420
1487
  }
1421
- .logo-slot[data-state="broken"] {
1422
- border: var(--grim-border-width) dashed var(--grim-color-border);
1423
- }
1424
1488
  @media (prefers-reduced-motion: reduce) {
1425
1489
  .logo-slot .logo-mark,
1426
1490
  .logo-slot img {
@@ -1524,10 +1588,7 @@ const { title, description, image = config.logo ?? config.favicon } = Astro.prop
1524
1588
  name beside it starts on one line down the whole list. The colour
1525
1589
  is set inline, from the kind's own token or the deprecation one. */
1526
1590
  .t-kind {
1527
- text-align: center;
1528
- }
1529
- .t-kind svg {
1530
- vertical-align: middle;
1591
+ justify-content: center;
1531
1592
  }
1532
1593
  /* The card's tile at a row's height. Geometry, not a scale step, and
1533
1594
  measured against the text line beside it rather than against the
@@ -1561,23 +1622,33 @@ const { title, description, image = config.logo ?? config.favicon } = Astro.prop
1561
1622
  wide cell — they are one reading, and the gap that separated them
1562
1623
  was the cell's width showing through. */
1563
1624
  .t-rating {
1564
- text-align: right;
1625
+ justify-content: flex-end;
1565
1626
  color: var(--grim-color-muted);
1566
1627
  }
1567
1628
  .t-rating svg {
1568
- vertical-align: middle;
1569
1629
  margin-left: var(--grim-space-2);
1570
- /* Optical, not rhythm: the glyph's ink sits high in its own box, so
1571
- the middle of the box reads above the digits beside it. A pixel
1572
- down puts the two on the same visual line. */
1573
- translate: 0 1px;
1630
+ }
1631
+ /* The two cells that carry a glyph centre it as a box, not on the
1632
+ text line. `vertical-align: middle` aligns to half the x-height,
1633
+ which sits about 2px under the line's true middle at this step —
1634
+ the eye reads that as the mark and the arrow sagging below the
1635
+ words beside them. Neither cell owes anything to the row's
1636
+ no-flex rule above: the kind is one mark and the rating is a
1637
+ fixed-width count plus its arrow, so there is no text here for
1638
+ `text-overflow` to elide. */
1639
+ .t-kind,
1640
+ .t-rating {
1641
+ display: flex;
1642
+ align-items: center;
1574
1643
  }
1575
1644
  /* The count carries the column's alignment, and the arrow after it
1576
1645
  inherits it: a fixed box, right-aligned, in tabular figures. Without
1577
1646
  the fixed box a three-digit count would push its own arrow right of
1578
1647
  the one above it. Geometry — it holds four digits at this step. */
1579
1648
  .t-votes {
1580
- display: inline-block;
1649
+ /* Never shrunk to its digits: the box is what holds the arrow
1650
+ after it in one place down the column. */
1651
+ flex: none;
1581
1652
  width: 2rem;
1582
1653
  text-align: right;
1583
1654
  font-variant-numeric: tabular-nums;
@@ -1670,6 +1741,34 @@ const { title, description, image = config.logo ?? config.favicon } = Astro.prop
1670
1741
  font-size: var(--grim-text-xs);
1671
1742
  line-height: 1.3;
1672
1743
  font-family: ui-monospace, "SFMono-Regular", monospace;
1744
+ /* One line, whatever the address is. A namespace breaks at its own
1745
+ slashes and dots, so left to itself a long one wrapped to a second
1746
+ line and made the card's head taller than its neighbours'. */
1747
+ display: flex;
1748
+ min-width: 0;
1749
+ white-space: nowrap;
1750
+ }
1751
+ /* Trimmed from the FRONT: `github.com/` is on every address in an
1752
+ index and the repository at the tail is what tells two apart, so the
1753
+ head is what to spend.
1754
+
1755
+ `direction: rtl` is the mechanism — `text-overflow` ellipsises at
1756
+ the line's inline END, which in an RTL box is its left edge, and
1757
+ `text-align: left` keeps a short address sitting under the name
1758
+ rather than flung to the right. The address itself still reads
1759
+ left-to-right: it is one run of Latin characters, so the bidi
1760
+ algorithm lays it out LTR inside the RTL box. That holds because a
1761
+ namespace neither starts nor ends with punctuation — one that did
1762
+ would have that character reordered to the far end.
1763
+
1764
+ The two-value `text-overflow: ellipsis ""` would say this directly
1765
+ and is Firefox-only. */
1766
+ .namespace .address {
1767
+ min-width: 0;
1768
+ overflow: hidden;
1769
+ text-overflow: ellipsis;
1770
+ direction: rtl;
1771
+ text-align: left;
1673
1772
  }
1674
1773
  /* The rating is one control with two halves — the count, which opens
1675
1774
  the forge thread, and the vote, which casts one — joined the way
@@ -1912,12 +2011,21 @@ const { title, description, image = config.logo ?? config.favicon } = Astro.prop
1912
2011
  .astro-code span:not([style*="background"]) {
1913
2012
  background-color: transparent !important;
1914
2013
  }
1915
- /* Reveals on hover or keyboard focus, so it is not a permanent
1916
- smudge over the first line of every block. */
1917
- .code-copy {
2014
+ /* The corner the block's buttons sit in. It is a row rather than one
2015
+ positioned button because `CodeBlock.astro` can add a second one —
2016
+ a VS Code deep link — and the copy button `Base.astro` injects has
2017
+ to land beside it without either knowing the other's width. */
2018
+ .code-actions {
1918
2019
  position: absolute;
1919
2020
  top: 0.45rem;
1920
2021
  right: 0.45rem;
2022
+ display: flex;
2023
+ gap: var(--grim-space-2);
2024
+ }
2025
+ /* Reveals on hover or keyboard focus, so they are not a permanent
2026
+ smudge over the first line of every block. */
2027
+ .code-copy,
2028
+ .code-vscode {
1921
2029
  display: inline-flex;
1922
2030
  align-items: center;
1923
2031
  justify-content: center;
@@ -1928,6 +2036,7 @@ const { title, description, image = config.logo ?? config.favicon } = Astro.prop
1928
2036
  border-radius: var(--grim-radius-inset);
1929
2037
  background: var(--grim-color-card);
1930
2038
  color: var(--grim-color-muted);
2039
+ text-decoration: none;
1931
2040
  cursor: pointer;
1932
2041
  opacity: 0;
1933
2042
  transition:
@@ -1936,10 +2045,13 @@ const { title, description, image = config.logo ?? config.favicon } = Astro.prop
1936
2045
  border-color var(--grim-duration-fast) ease;
1937
2046
  }
1938
2047
  .code-block:hover .code-copy,
1939
- .code-copy:focus-visible {
2048
+ .code-block:hover .code-vscode,
2049
+ .code-copy:focus-visible,
2050
+ .code-vscode:focus-visible {
1940
2051
  opacity: 1;
1941
2052
  }
1942
- .code-copy:hover {
2053
+ .code-copy:hover,
2054
+ .code-vscode:hover {
1943
2055
  color: var(--grim-color-accent);
1944
2056
  border-color: var(--grim-color-accent);
1945
2057
  }
@@ -1949,7 +2061,8 @@ const { title, description, image = config.logo ?? config.favicon } = Astro.prop
1949
2061
  opacity: 1;
1950
2062
  }
1951
2063
  @media (prefers-reduced-motion: reduce) {
1952
- .code-copy {
2064
+ .code-copy,
2065
+ .code-vscode {
1953
2066
  transition: none;
1954
2067
  }
1955
2068
  }
@@ -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 the `<Code>` component on the package
8
- * page takes it as a prop.
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 the `<Code>` component on the package
10
- * page takes it as a prop.
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 the `<Code>` component on the package
11
- * page takes it as a prop.
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 config.registry
53
- ? `grim config registry add ${config.registry.alias} --index ${config.registry.index}`
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(config: ResolvedSiteConfig): string | null {
82
- return config.registry
83
- ? `grim config registry add ${config.registry.alias} --index ${config.registry.index}`
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(config: ResolvedSiteConfig): Choice[] {
100
- const add = registryAddCommand(config);
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 },