@grimoire-rs/indexer 0.4.4 → 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 +270 -0
- package/NOTICE +30 -0
- package/README.md +76 -331
- 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/init.d.ts.map +1 -1
- package/dist/cli/init.js +35 -4
- package/dist/cli/init.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/config.d.ts +107 -7
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +181 -36
- package/dist/config.js.map +1 -1
- package/dist/renderer/astro/components/CardLogo.d.ts +5 -0
- package/dist/renderer/astro/components/CardLogo.js +58 -0
- package/dist/renderer/astro/components/CardLogo.tsx +96 -0
- package/dist/renderer/astro/components/Catalog.d.ts +14 -1
- package/dist/renderer/astro/components/Catalog.js +573 -108
- package/dist/renderer/astro/components/Catalog.tsx +903 -349
- package/dist/renderer/astro/components/CodeBlock.astro +74 -0
- package/dist/renderer/astro/components/CommandBar.astro +66 -0
- package/dist/renderer/astro/components/CopyButton.d.ts +7 -0
- package/dist/renderer/astro/components/CopyButton.js +28 -0
- package/dist/renderer/astro/components/CopyButton.tsx +56 -0
- package/dist/renderer/astro/components/KindMark.d.ts +69 -0
- package/dist/renderer/astro/components/KindMark.js +66 -0
- package/dist/renderer/astro/components/KindMark.tsx +141 -0
- package/dist/renderer/astro/components/PackageCard.d.ts +18 -0
- package/dist/renderer/astro/components/PackageCard.js +50 -0
- package/dist/renderer/astro/components/PackageCard.tsx +273 -0
- package/dist/renderer/astro/components/PackageRow.d.ts +10 -0
- package/dist/renderer/astro/components/PackageRow.js +32 -0
- package/dist/renderer/astro/components/PackageRow.tsx +126 -0
- package/dist/renderer/astro/components/PickerMenu.astro +5 -14
- package/dist/renderer/astro/components/SiteFooter.astro +64 -0
- package/dist/renderer/astro/components/SiteHeader.astro +74 -0
- package/dist/renderer/astro/components/VersionMenu.astro +2 -2
- package/dist/renderer/astro/layouts/Base.astro +970 -227
- package/dist/renderer/astro/lib/base.d.ts +25 -0
- package/dist/renderer/astro/lib/base.js +23 -0
- package/dist/renderer/astro/lib/base.ts +27 -0
- package/dist/renderer/astro/lib/catalog.d.ts +24 -0
- package/dist/renderer/astro/lib/catalog.js +36 -0
- package/dist/renderer/astro/lib/catalog.ts +37 -0
- 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 +64 -0
- package/dist/renderer/astro/lib/commands.js +91 -0
- package/dist/renderer/astro/lib/commands.ts +133 -0
- package/dist/renderer/astro/lib/keywordRail.d.ts +44 -0
- package/dist/renderer/astro/lib/keywordRail.js +99 -0
- package/dist/renderer/astro/lib/keywordRail.ts +110 -0
- package/dist/renderer/astro/pages/index.astro +40 -87
- package/dist/renderer/astro/pages/p/[...slug].astro +349 -198
- package/dist/renderer/astro/styles/tokens.css +40 -5
- package/dist/renderer/index.d.ts +77 -0
- package/dist/renderer/index.d.ts.map +1 -1
- package/dist/renderer/index.js +565 -7
- package/dist/renderer/index.js.map +1 -1
- package/dist/renderer/types.d.ts +9 -0
- package/dist/renderer/types.d.ts.map +1 -1
- package/package.json +9 -4
- package/templates/README.md +6 -0
- package/templates/gitignore +4 -1
- package/templates/theme/README.md +38 -0
- package/templates/tsconfig.json +47 -0
|
@@ -3,11 +3,10 @@
|
|
|
3
3
|
// VS Code deep link points at it, and search engines index it — the route
|
|
4
4
|
// shape is a contract, not an implementation detail.
|
|
5
5
|
import {
|
|
6
|
+
ArrowBigUp,
|
|
6
7
|
BookOpen,
|
|
7
8
|
Bug,
|
|
8
9
|
ChevronRight,
|
|
9
|
-
FolderRoot,
|
|
10
|
-
Globe,
|
|
11
10
|
History,
|
|
12
11
|
House,
|
|
13
12
|
Image,
|
|
@@ -16,13 +15,15 @@ import {
|
|
|
16
15
|
MessageCircle,
|
|
17
16
|
Scale,
|
|
18
17
|
ShieldAlert,
|
|
18
|
+
TriangleAlert,
|
|
19
19
|
User,
|
|
20
20
|
} from "lucide-preact";
|
|
21
21
|
import Base from "../../layouts/Base.astro";
|
|
22
22
|
import VersionMenu from "../../components/VersionMenu.astro";
|
|
23
|
-
import
|
|
24
|
-
import
|
|
23
|
+
import CommandBar from "../../components/CommandBar.astro";
|
|
24
|
+
import CodeBlock from "../../components/CodeBlock.astro";
|
|
25
25
|
import { BrandMark } from "../../components/BrandMark.tsx";
|
|
26
|
+
import { DEPRECATED_MARK, KIND_MARKS, KindMark } from "../../components/KindMark.tsx";
|
|
26
27
|
import {
|
|
27
28
|
mdiBitbucket,
|
|
28
29
|
mdiGit,
|
|
@@ -31,20 +32,21 @@ 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
|
+
deprecationNote,
|
|
37
38
|
externalUrl,
|
|
38
39
|
lastUpdated,
|
|
39
40
|
resolveMemberRef,
|
|
40
41
|
timeAgo,
|
|
41
42
|
versionCascade,
|
|
42
43
|
vscodeUrl,
|
|
44
|
+
vscodeVoteUrl,
|
|
43
45
|
type CatalogPackage,
|
|
44
46
|
} from "../../lib/catalog";
|
|
47
|
+
import { addArtifactChoices } from "../../lib/commands";
|
|
45
48
|
import { data } from "../../lib/data";
|
|
46
49
|
import { getEntry, render } from "astro:content";
|
|
47
|
-
import { Code } from "astro:components";
|
|
48
50
|
|
|
49
51
|
export function getStaticPaths() {
|
|
50
52
|
return data.packages.map((pkg) => ({
|
|
@@ -122,12 +124,8 @@ const vscode = vscodeUrl(config.vscodeExtension, pkg.ref);
|
|
|
122
124
|
const addCommand = `grim add ${pkg.ref}`;
|
|
123
125
|
|
|
124
126
|
// The same two-way scope choice the package cards offer and the hero's
|
|
125
|
-
// registry bar draws,
|
|
126
|
-
|
|
127
|
-
const scopeChoices = [
|
|
128
|
-
{ name: "Global", command: `grim add --global ${pkg.ref}`, Icon: Globe },
|
|
129
|
-
{ name: "Project", command: addCommand, Icon: FolderRoot },
|
|
130
|
-
];
|
|
127
|
+
// registry bar draws, from the one helper all three read.
|
|
128
|
+
const scopeChoices = addArtifactChoices(pkg.ref);
|
|
131
129
|
|
|
132
130
|
// Every outbound string on this page was handed to the index by a registry
|
|
133
131
|
// it does not control, so each one goes through the scheme allowlist before
|
|
@@ -162,167 +160,246 @@ const supportChannels = [
|
|
|
162
160
|
// taking a row of its own and putting 40 hex characters in the rail.
|
|
163
161
|
const updated = lastUpdated(pkg);
|
|
164
162
|
const updatedTitle = pkg.revision ? `${updated} · revision ${pkg.revision}` : updated;
|
|
163
|
+
|
|
164
|
+
// The card's vote badge, on the page the card links to. Same two halves and
|
|
165
|
+
// the same rule: the count opens the forge thread, because no forge offers a
|
|
166
|
+
// URL that casts a vote, and the extension's `/vote?repo=` route is the only
|
|
167
|
+
// one that does — behind its own disclosure modal, never on this link alone.
|
|
168
|
+
const votes = pkg.rating ? `${pkg.rating.up} upvote${pkg.rating.up === 1 ? "" : "s"}` : "";
|
|
169
|
+
const thread = externalUrl(pkg.rating?.url);
|
|
170
|
+
const voteUrl = vscodeVoteUrl(config.vscodeExtension, pkg.ref);
|
|
171
|
+
|
|
172
|
+
// The kind as texture, the way a card wears it. A retired package spends it
|
|
173
|
+
// on the warning instead — the kind is written in the badge either way.
|
|
174
|
+
const headMark = pkg.deprecated ? DEPRECATED_MARK : KIND_MARKS[pkg.kind];
|
|
175
|
+
const headMarkColor = pkg.deprecated
|
|
176
|
+
? "var(--grim-color-deprecated)"
|
|
177
|
+
: `var(--grim-color-kind-${pkg.kind}, var(--grim-color-muted))`;
|
|
165
178
|
---
|
|
166
179
|
|
|
167
180
|
<Base title={`${title} — ${config.brand}`} description={description} image={pkg.logo}>
|
|
168
181
|
<div class="detail">
|
|
169
|
-
<header class="detail-head" data-slot="detail-header">
|
|
170
|
-
{
|
|
171
|
-
pkg.logo ? (
|
|
172
|
-
// The placeholder ships in the markup and the image fades in over
|
|
173
|
-
// it, so a slow or failing load never flashes a broken glyph. The
|
|
174
|
-
// slot starts `loading`; the handler in `Base.astro` moves it to
|
|
175
|
-
// `ready` or `broken`. Icon mirrors `LogoMark` in `Catalog.tsx`.
|
|
176
|
-
<span class="tile logo-slot" data-static data-state="loading">
|
|
177
|
-
{/* Both marks ship; CSS shows one per state. The card grid picks
|
|
178
|
-
between them in Preact instead, but this page has to settle
|
|
179
|
-
its placeholder before any script runs. */}
|
|
180
|
-
<Image class="logo-mark mark-loading" aria-hidden="true" />
|
|
181
|
-
<ImageOff class="logo-mark mark-broken" aria-hidden="true" />
|
|
182
|
-
<img src={withBase(pkg.logo)} alt="" />
|
|
183
|
-
</span>
|
|
184
|
-
) : (
|
|
185
|
-
<div class="tile letter" style={`background: var(--grim-color-kind-${pkg.kind}, var(--grim-color-muted))`}>
|
|
186
|
-
{pkg.name[0]?.toUpperCase()}
|
|
187
|
-
</div>
|
|
188
|
-
)
|
|
189
|
-
}
|
|
190
|
-
<div class="detail-head-text">
|
|
191
|
-
<h1>
|
|
192
|
-
{pkg.name} <span class={`badge kind-${pkg.kind}`}>{pkg.kind}</span>
|
|
193
|
-
{/* Skills only — every other kind reports null, and grim's own
|
|
194
|
-
surfaces show it beside the kind for the same reason: it
|
|
195
|
-
qualifies what the thing is, not what it says about itself. */}
|
|
196
|
-
{pkg.compatibility && <span class="badge compat">{pkg.compatibility}</span>}
|
|
197
|
-
</h1>
|
|
198
|
-
{description && <p class="summary">{description}</p>}
|
|
199
|
-
{byline && <p class="byline">by {byline}</p>}
|
|
200
|
-
</div>
|
|
201
|
-
</header>
|
|
202
|
-
|
|
203
|
-
{
|
|
204
|
-
pkg.deprecated && (
|
|
205
|
-
<p class="deprecated-banner" data-slot="deprecated-banner">
|
|
206
|
-
⚠️ deprecated{pkg.replacedBy ? ` — replaced by ${pkg.replacedBy}` : ""}
|
|
207
|
-
</p>
|
|
208
|
-
)
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
<div class="cmd-bar" data-os-switch data-os-noun={`add command for ${pkg.name}`}>
|
|
212
|
-
<PickerMenu choices={scopeChoices} title="Scope" label="Choose the scope" />
|
|
213
|
-
<CommandField
|
|
214
|
-
value={scopeChoices[0].command}
|
|
215
|
-
name={`${scopeChoices[0].name} add command for ${pkg.name}`}
|
|
216
|
-
label="Copy the add command"
|
|
217
|
-
/>
|
|
218
|
-
{
|
|
219
|
-
vscode && (
|
|
220
|
-
<a
|
|
221
|
-
class="seg brand-link"
|
|
222
|
-
href={vscode}
|
|
223
|
-
title="Open in VS Code"
|
|
224
|
-
aria-label={`Open ${pkg.name} in VS Code`}
|
|
225
|
-
>
|
|
226
|
-
<BrandMark path={mdiMicrosoftVisualStudioCode} />
|
|
227
|
-
</a>
|
|
228
|
-
)
|
|
229
|
-
}
|
|
230
|
-
</div>
|
|
231
|
-
|
|
232
182
|
<div class="detail-body" data-slot="detail-body">
|
|
233
|
-
<
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
class="
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
<div class="ribbon">
|
|
270
|
-
<label for="panel-readme" title={filled.readme ? undefined : "no readme available"}>
|
|
271
|
-
Readme
|
|
272
|
-
</label>
|
|
273
|
-
<label for="panel-contents" title={filled.contents ? undefined : "no contents available"}>
|
|
274
|
-
Contents
|
|
275
|
-
</label>
|
|
276
|
-
<label
|
|
277
|
-
for="panel-changelog"
|
|
278
|
-
title={filled.changelog ? undefined : "no changelog available"}
|
|
279
|
-
>
|
|
280
|
-
Changelog
|
|
281
|
-
</label>
|
|
282
|
-
</div>
|
|
283
|
-
{filled.readme && (
|
|
284
|
-
<div class="prose tab-panel" id="tab-readme">
|
|
285
|
-
<Readme />
|
|
183
|
+
<div class="detail-col">
|
|
184
|
+
<header class="detail-head" data-slot="detail-header">
|
|
185
|
+
{
|
|
186
|
+
pkg.logo ? (
|
|
187
|
+
// The placeholder ships in the markup and the image fades in over
|
|
188
|
+
// it, so a slow or failing load never flashes a broken glyph. The
|
|
189
|
+
// slot starts `loading`; the handler in `Base.astro` moves it to
|
|
190
|
+
// `ready` or `broken`. Icon mirrors `LogoMark` in `Catalog.tsx`.
|
|
191
|
+
<span class="tile logo-slot" data-static data-state="loading">
|
|
192
|
+
{/* Both marks ship; CSS shows one per state. The card grid picks
|
|
193
|
+
between them in Preact instead, but this page has to settle
|
|
194
|
+
its placeholder before any script runs. */}
|
|
195
|
+
<Image class="logo-mark mark-loading" aria-hidden="true" />
|
|
196
|
+
<ImageOff class="logo-mark mark-broken" aria-hidden="true" />
|
|
197
|
+
<img src={withBase(pkg.logo)} alt="" />
|
|
198
|
+
</span>
|
|
199
|
+
) : (
|
|
200
|
+
<div class="tile letter" style={`background: var(--grim-color-kind-${pkg.kind}, var(--grim-color-muted))`}>
|
|
201
|
+
{pkg.name[0]?.toUpperCase()}
|
|
202
|
+
</div>
|
|
203
|
+
)
|
|
204
|
+
}
|
|
205
|
+
<div class="detail-head-text">
|
|
206
|
+
<h1>
|
|
207
|
+
{pkg.name} <span class={`badge kind-${pkg.kind}`}>{pkg.kind}</span>
|
|
208
|
+
{/* Skills only — every other kind reports null, and grim's own
|
|
209
|
+
surfaces show it beside the kind for the same reason: it
|
|
210
|
+
qualifies what the thing is, not what it says about itself. */}
|
|
211
|
+
{pkg.compatibility && (
|
|
212
|
+
<span class="badge compat" title={`Built for ${pkg.compatibility}`}>
|
|
213
|
+
{pkg.compatibility}
|
|
214
|
+
</span>
|
|
215
|
+
)}
|
|
216
|
+
</h1>
|
|
217
|
+
{description && <p class="summary">{description}</p>}
|
|
218
|
+
{byline && <p class="byline">by {byline}</p>}
|
|
286
219
|
</div>
|
|
287
|
-
|
|
220
|
+
</header>
|
|
221
|
+
|
|
288
222
|
{
|
|
289
|
-
|
|
290
|
-
<
|
|
291
|
-
{
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
<a href={member.href}>{member.name}</a>
|
|
299
|
-
) : (
|
|
300
|
-
<span>{member.name}</span>
|
|
301
|
-
)}
|
|
302
|
-
<span class={`badge kind-${member.kind}`}>{member.kind}</span>
|
|
303
|
-
<code>{member.ref}</code>
|
|
304
|
-
</li>
|
|
305
|
-
))}
|
|
306
|
-
</ul>
|
|
307
|
-
) : (
|
|
308
|
-
<Code
|
|
309
|
-
code={JSON.stringify(contentsData, null, 2)}
|
|
310
|
-
lang="json"
|
|
311
|
-
themes={SHIKI_THEMES}
|
|
312
|
-
/>
|
|
313
|
-
)}
|
|
314
|
-
</div>
|
|
223
|
+
pkg.deprecated && (
|
|
224
|
+
<p class="deprecated-banner" data-slot="deprecated-banner">
|
|
225
|
+
{/* Drawn, not an emoji: ⚠️ renders as a different glyph on every
|
|
226
|
+
platform, takes the system's colour rather than the token's,
|
|
227
|
+
and sits off the text baseline. Every other icon on this site
|
|
228
|
+
comes from one stroked set; this one had no reason not to. */}
|
|
229
|
+
<TriangleAlert size={16} aria-hidden="true" />
|
|
230
|
+
{deprecationNote(pkg)}
|
|
231
|
+
</p>
|
|
315
232
|
)
|
|
316
233
|
}
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
234
|
+
|
|
235
|
+
<CommandBar
|
|
236
|
+
choices={scopeChoices}
|
|
237
|
+
noun={`add command for ${pkg.name}`}
|
|
238
|
+
copyLabel="Copy the add command"
|
|
239
|
+
pickerTitle="Scope"
|
|
240
|
+
pickerLabel="Choose the scope"
|
|
241
|
+
>
|
|
242
|
+
{
|
|
243
|
+
vscode && (
|
|
244
|
+
<a
|
|
245
|
+
slot="action"
|
|
246
|
+
class="seg brand-link"
|
|
247
|
+
href={vscode}
|
|
248
|
+
title="Open in VS Code"
|
|
249
|
+
aria-label={`Open ${pkg.name} in VS Code`}
|
|
250
|
+
>
|
|
251
|
+
<BrandMark path={mdiMicrosoftVisualStudioCode} />
|
|
252
|
+
</a>
|
|
253
|
+
)
|
|
254
|
+
}
|
|
255
|
+
</CommandBar>
|
|
256
|
+
|
|
257
|
+
<main class="detail-main">
|
|
258
|
+
{/* The kind as texture behind the panel, the way a card wears it:
|
|
259
|
+
oversized and hung off the panel's bottom-right corner, so
|
|
260
|
+
`overflow: hidden` crops it to the rounded edge. Decorative — the
|
|
261
|
+
kind is a badge beside the name — so it is aria-hidden unless it
|
|
262
|
+
is the warning a retired package wears instead. */}
|
|
263
|
+
{
|
|
264
|
+
headMark && (
|
|
265
|
+
<KindMark
|
|
266
|
+
glyph={headMark}
|
|
267
|
+
class="panel-watermark"
|
|
268
|
+
size={220}
|
|
269
|
+
role={pkg.deprecated ? "img" : undefined}
|
|
270
|
+
aria-label={pkg.deprecated ? "deprecated" : undefined}
|
|
271
|
+
aria-hidden={pkg.deprecated ? undefined : "true"}
|
|
272
|
+
style={`color: ${headMarkColor}`}
|
|
273
|
+
/>
|
|
274
|
+
)
|
|
275
|
+
}
|
|
276
|
+
{/* Radio tabs, not a scripted tablist: the ribbon works on a page
|
|
277
|
+
whose JS never ran, and a radio group already has the keyboard
|
|
278
|
+
behaviour tabs want (arrows move, only one selected). The inputs
|
|
279
|
+
are visually hidden rather than `hidden`, which would take them
|
|
280
|
+
out of the focus order along with the pixels. */}
|
|
281
|
+
{/* All three tabs are always offered — a ribbon that grows a tab per
|
|
282
|
+
package makes the panel a different shape on every page. An empty
|
|
283
|
+
one is disabled instead: greyed, not clickable through to a
|
|
284
|
+
sentence apologising for itself. */}
|
|
285
|
+
<input
|
|
286
|
+
type="radio"
|
|
287
|
+
name="panel"
|
|
288
|
+
id="panel-readme"
|
|
289
|
+
class="sr-only"
|
|
290
|
+
checked={openTab === "readme"}
|
|
291
|
+
disabled={!filled.readme}
|
|
292
|
+
/>
|
|
293
|
+
<input
|
|
294
|
+
type="radio"
|
|
295
|
+
name="panel"
|
|
296
|
+
id="panel-contents"
|
|
297
|
+
class="sr-only"
|
|
298
|
+
checked={openTab === "contents"}
|
|
299
|
+
disabled={!filled.contents}
|
|
300
|
+
/>
|
|
301
|
+
<input
|
|
302
|
+
type="radio"
|
|
303
|
+
name="panel"
|
|
304
|
+
id="panel-changelog"
|
|
305
|
+
class="sr-only"
|
|
306
|
+
checked={openTab === "changelog"}
|
|
307
|
+
disabled={!filled.changelog}
|
|
308
|
+
/>
|
|
309
|
+
{/* The greyed tab says it is not for clicking; the tooltip says why,
|
|
310
|
+
so the answer is one hover away instead of a panel of its own. */}
|
|
311
|
+
<div class="ribbon">
|
|
312
|
+
<label for="panel-readme" title={filled.readme ? undefined : "no readme available"}>
|
|
313
|
+
Readme
|
|
314
|
+
</label>
|
|
315
|
+
<label for="panel-contents" title={filled.contents ? undefined : "no contents available"}>
|
|
316
|
+
Contents
|
|
317
|
+
</label>
|
|
318
|
+
<label
|
|
319
|
+
for="panel-changelog"
|
|
320
|
+
title={filled.changelog ? undefined : "no changelog available"}
|
|
321
|
+
>
|
|
322
|
+
Changelog
|
|
323
|
+
</label>
|
|
324
|
+
{
|
|
325
|
+
pkg.rating && (
|
|
326
|
+
<span class="rating-group">
|
|
327
|
+
{thread ? (
|
|
328
|
+
<a
|
|
329
|
+
class="rating-count"
|
|
330
|
+
href={thread}
|
|
331
|
+
target="_blank"
|
|
332
|
+
rel="noopener noreferrer"
|
|
333
|
+
title={`${votes} — open the thread to vote`}
|
|
334
|
+
aria-label={`${votes}. Open the voting thread`}
|
|
335
|
+
>
|
|
336
|
+
<ArrowBigUp size={13} aria-hidden="true" />
|
|
337
|
+
{pkg.rating.up}
|
|
338
|
+
</a>
|
|
339
|
+
) : (
|
|
340
|
+
<span class="rating-count" title={votes}>
|
|
341
|
+
<ArrowBigUp size={13} aria-hidden="true" />
|
|
342
|
+
{pkg.rating.up}
|
|
343
|
+
</span>
|
|
344
|
+
)}
|
|
345
|
+
{voteUrl && (
|
|
346
|
+
<a
|
|
347
|
+
class="rating-vote"
|
|
348
|
+
href={voteUrl}
|
|
349
|
+
title="Upvote in VS Code"
|
|
350
|
+
aria-label={`Upvote ${pkg.name} in VS Code`}
|
|
351
|
+
>
|
|
352
|
+
<BrandMark path={mdiMicrosoftVisualStudioCode} size={12} />
|
|
353
|
+
</a>
|
|
354
|
+
)}
|
|
355
|
+
</span>
|
|
356
|
+
)
|
|
357
|
+
}
|
|
320
358
|
</div>
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
359
|
+
{filled.readme && (
|
|
360
|
+
<div class="prose tab-panel" id="tab-readme">
|
|
361
|
+
<Readme />
|
|
362
|
+
</div>
|
|
363
|
+
)}
|
|
364
|
+
{
|
|
365
|
+
filled.contents && (
|
|
366
|
+
<div class="prose tab-panel" id="tab-contents">
|
|
367
|
+
{Contents ? (
|
|
368
|
+
<Contents />
|
|
369
|
+
) : members.length > 0 ? (
|
|
370
|
+
<ul class="members">
|
|
371
|
+
{members.map((member) => (
|
|
372
|
+
<li>
|
|
373
|
+
{member.href ? (
|
|
374
|
+
<a href={member.href}>{member.name}</a>
|
|
375
|
+
) : (
|
|
376
|
+
<span>{member.name}</span>
|
|
377
|
+
)}
|
|
378
|
+
<span class={`badge kind-${member.kind}`}>{member.kind}</span>
|
|
379
|
+
<code>{member.ref}</code>
|
|
380
|
+
</li>
|
|
381
|
+
))}
|
|
382
|
+
</ul>
|
|
383
|
+
) : (
|
|
384
|
+
<CodeBlock
|
|
385
|
+
code={JSON.stringify(contentsData, null, 2)}
|
|
386
|
+
lang="json"
|
|
387
|
+
name="contents"
|
|
388
|
+
/>
|
|
389
|
+
)}
|
|
390
|
+
</div>
|
|
391
|
+
)
|
|
392
|
+
}
|
|
393
|
+
{filled.changelog && (
|
|
394
|
+
<div class="prose tab-panel" id="tab-changelog">
|
|
395
|
+
<Changelog />
|
|
396
|
+
</div>
|
|
397
|
+
)}
|
|
398
|
+
{/* Nothing anywhere: one statement in the middle of the panel, rather
|
|
399
|
+
than three tabs each denying it separately. */}
|
|
400
|
+
{openTab === null && <p class="panel-empty">No content available.</p>}
|
|
401
|
+
</main>
|
|
402
|
+
</div>
|
|
326
403
|
|
|
327
404
|
<aside class="detail-rail" data-slot="detail-rail">
|
|
328
405
|
{tags.length > 0 && (
|
|
@@ -349,6 +426,22 @@ const updatedTitle = pkg.revision ? `${updated} · revision ${pkg.revision}` : u
|
|
|
349
426
|
</section>
|
|
350
427
|
)}
|
|
351
428
|
|
|
429
|
+
{supportChannels.length > 0 && (
|
|
430
|
+
<section class="support">
|
|
431
|
+
<h2>Get help</h2>
|
|
432
|
+
<ul>
|
|
433
|
+
{supportChannels.map(({ label, href, Icon }) => (
|
|
434
|
+
<li>
|
|
435
|
+
<Icon size={15} aria-hidden="true" />
|
|
436
|
+
<a href={href} target="_blank" rel="noopener noreferrer">
|
|
437
|
+
{label}
|
|
438
|
+
</a>
|
|
439
|
+
</li>
|
|
440
|
+
))}
|
|
441
|
+
</ul>
|
|
442
|
+
</section>
|
|
443
|
+
)}
|
|
444
|
+
|
|
352
445
|
<section>
|
|
353
446
|
<h2>Details</h2>
|
|
354
447
|
<dl>
|
|
@@ -435,10 +528,13 @@ const updatedTitle = pkg.revision ? `${updated} · revision ${pkg.revision}` : u
|
|
|
435
528
|
<ul class="keywords">
|
|
436
529
|
{pkg.keywords.map((k) => (
|
|
437
530
|
<li>
|
|
438
|
-
{/* Back to the catalog with the
|
|
439
|
-
|
|
531
|
+
{/* Back to the catalog with the keyword FACET applied, not
|
|
532
|
+
a text search. `?q=` also matched every description with
|
|
533
|
+
the word in it, so a link labelled `cli` returned
|
|
534
|
+
packages nobody tagged `cli`. The catalog still reads
|
|
535
|
+
`?q=`, so links shared before this change still work. */}
|
|
440
536
|
<a
|
|
441
|
-
href={withBase(`/?
|
|
537
|
+
href={withBase(`/?kw=${encodeURIComponent(k)}`)}
|
|
442
538
|
title={`Find packages tagged ${k}`}
|
|
443
539
|
>
|
|
444
540
|
{k}
|
|
@@ -449,21 +545,6 @@ const updatedTitle = pkg.revision ? `${updated} · revision ${pkg.revision}` : u
|
|
|
449
545
|
)}
|
|
450
546
|
</section>
|
|
451
547
|
|
|
452
|
-
{supportChannels.length > 0 && (
|
|
453
|
-
<section class="support">
|
|
454
|
-
<h2>Get help</h2>
|
|
455
|
-
<ul>
|
|
456
|
-
{supportChannels.map(({ label, href, Icon }) => (
|
|
457
|
-
<li>
|
|
458
|
-
<Icon size={15} aria-hidden="true" />
|
|
459
|
-
<a href={href} target="_blank" rel="noopener noreferrer">
|
|
460
|
-
{label}
|
|
461
|
-
</a>
|
|
462
|
-
</li>
|
|
463
|
-
))}
|
|
464
|
-
</ul>
|
|
465
|
-
</section>
|
|
466
|
-
)}
|
|
467
548
|
</aside>
|
|
468
549
|
</div>
|
|
469
550
|
</div>
|
|
@@ -520,21 +601,50 @@ const updatedTitle = pkg.revision ? `${updated} · revision ${pkg.revision}` : u
|
|
|
520
601
|
flex-direction: column;
|
|
521
602
|
gap: var(--grim-space-7);
|
|
522
603
|
}
|
|
604
|
+
/* The left column: header, install bar and panel, all to the width of the
|
|
605
|
+
README rather than to the width of the page. The rail then runs the
|
|
606
|
+
full height beside them instead of starting a screenful down, which is
|
|
607
|
+
what left the top-right corner of the page empty. */
|
|
608
|
+
.detail-col {
|
|
609
|
+
display: flex;
|
|
610
|
+
flex-direction: column;
|
|
611
|
+
gap: var(--grim-space-7);
|
|
612
|
+
min-width: 0;
|
|
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. */
|
|
523
619
|
.detail-head {
|
|
524
620
|
display: flex;
|
|
525
|
-
align-items:
|
|
621
|
+
align-items: flex-start;
|
|
526
622
|
gap: var(--grim-space-6);
|
|
527
623
|
background: var(--grim-color-card);
|
|
528
624
|
border: var(--grim-border-width) solid var(--grim-color-border);
|
|
529
|
-
border-radius: var(--grim-radius-
|
|
625
|
+
border-radius: var(--grim-radius-surface);
|
|
530
626
|
padding: var(--grim-space-6) var(--grim-space-7);
|
|
531
627
|
}
|
|
628
|
+
.detail-head-text {
|
|
629
|
+
min-width: 0;
|
|
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. */
|
|
532
643
|
.tile {
|
|
533
|
-
flex
|
|
534
|
-
width:
|
|
535
|
-
height:
|
|
536
|
-
border-radius: var(--grim-radius-
|
|
537
|
-
object-fit: cover;
|
|
644
|
+
flex: 0 0 auto;
|
|
645
|
+
width: 4.5rem;
|
|
646
|
+
height: 4.5rem;
|
|
647
|
+
border-radius: var(--grim-radius-surface);
|
|
538
648
|
}
|
|
539
649
|
.tile.letter {
|
|
540
650
|
display: flex;
|
|
@@ -554,11 +664,14 @@ const updatedTitle = pkg.revision ? `${updated} · revision ${pkg.revision}` : u
|
|
|
554
664
|
font-family: ui-monospace, "SFMono-Regular", monospace;
|
|
555
665
|
}
|
|
556
666
|
.summary {
|
|
557
|
-
margin: var(--grim-space-
|
|
667
|
+
margin: var(--grim-space-4) 0 0;
|
|
558
668
|
color: var(--grim-color-muted);
|
|
559
669
|
}
|
|
670
|
+
/* Tight to the summary above it. Evenly spaced between the name and the
|
|
671
|
+
summary it read as a third heading; who publishes this is a footnote on
|
|
672
|
+
the sentence, so it sits with the sentence. */
|
|
560
673
|
.byline {
|
|
561
|
-
margin: var(--grim-space-
|
|
674
|
+
margin: var(--grim-space-1) 0 0;
|
|
562
675
|
color: var(--grim-color-muted);
|
|
563
676
|
font-size: var(--grim-text-sm);
|
|
564
677
|
}
|
|
@@ -590,16 +703,20 @@ const updatedTitle = pkg.revision ? `${updated} · revision ${pkg.revision}` : u
|
|
|
590
703
|
}
|
|
591
704
|
.deprecated-banner {
|
|
592
705
|
margin: 0;
|
|
706
|
+
/* The glyph sits on the sentence's line rather than above it. */
|
|
707
|
+
display: flex;
|
|
708
|
+
align-items: center;
|
|
709
|
+
gap: var(--grim-space-3);
|
|
593
710
|
background: var(--grim-color-banner-bg);
|
|
594
711
|
color: var(--grim-color-banner-fg);
|
|
595
712
|
border: var(--grim-border-width) solid var(--grim-color-banner-border);
|
|
596
|
-
border-radius: var(--grim-radius-
|
|
713
|
+
border-radius: var(--grim-radius-control);
|
|
597
714
|
padding: var(--grim-space-4) var(--grim-space-6);
|
|
598
715
|
font-weight: 600;
|
|
599
716
|
}
|
|
600
717
|
.detail-body {
|
|
601
718
|
display: grid;
|
|
602
|
-
grid-template-columns: 1fr 18rem;
|
|
719
|
+
grid-template-columns: minmax(0, 1fr) 18rem;
|
|
603
720
|
gap: var(--grim-space-8);
|
|
604
721
|
align-items: start;
|
|
605
722
|
}
|
|
@@ -612,8 +729,36 @@ const updatedTitle = pkg.revision ? `${updated} · revision ${pkg.revision}` : u
|
|
|
612
729
|
min-width: 0;
|
|
613
730
|
background: var(--grim-color-card);
|
|
614
731
|
border: var(--grim-border-width) solid var(--grim-color-border);
|
|
615
|
-
border-radius: var(--grim-radius-
|
|
732
|
+
border-radius: var(--grim-radius-surface);
|
|
616
733
|
padding: var(--grim-space-7) var(--grim-space-7);
|
|
734
|
+
/* Crops the oversized kind mark to the rounded edge and gives it a
|
|
735
|
+
stacking context to sit behind in — without `isolation` a
|
|
736
|
+
`z-index: -1` child escapes to the root and vanishes under the
|
|
737
|
+
panel's own background. */
|
|
738
|
+
position: relative;
|
|
739
|
+
overflow: hidden;
|
|
740
|
+
isolation: isolate;
|
|
741
|
+
}
|
|
742
|
+
/* Hung off the panel's bottom-right corner, cropped by the panel's own
|
|
743
|
+
rounded edge. Opacity on the element rather than in the colour: the
|
|
744
|
+
glyph's paths overlap, and a translucent fill composites every crossing
|
|
745
|
+
twice and comes out blotchy along its own joins. */
|
|
746
|
+
.detail-main :global(.panel-watermark) {
|
|
747
|
+
position: absolute;
|
|
748
|
+
z-index: -1;
|
|
749
|
+
opacity: 0.14;
|
|
750
|
+
bottom: 0;
|
|
751
|
+
right: 0;
|
|
752
|
+
/* Same 26% of its own box as the card's mark — see `.card-watermark`
|
|
753
|
+
in `Base.astro` for why this is a translate and not an inset. */
|
|
754
|
+
transform: translate(26%, 26%);
|
|
755
|
+
pointer-events: none;
|
|
756
|
+
}
|
|
757
|
+
/* The vote rides the ribbon's right end — the panel's own row of
|
|
758
|
+
controls, and the one line on this page that was already a toolbar. */
|
|
759
|
+
.ribbon .rating-group {
|
|
760
|
+
align-self: center;
|
|
761
|
+
margin-left: auto;
|
|
617
762
|
}
|
|
618
763
|
/* Markdown opens with its own heading, whose margin lands on top of the
|
|
619
764
|
panel padding and reads as a hole. Every panel sets its own top edge. */
|
|
@@ -692,7 +837,7 @@ const updatedTitle = pkg.revision ? `${updated} · revision ${pkg.revision}` : u
|
|
|
692
837
|
#panel-changelog:focus-visible ~ .ribbon label[for="panel-changelog"] {
|
|
693
838
|
outline: 2px solid var(--grim-color-accent);
|
|
694
839
|
outline-offset: 2px;
|
|
695
|
-
border-radius: var(--grim-radius-
|
|
840
|
+
border-radius: var(--grim-radius-code);
|
|
696
841
|
}
|
|
697
842
|
|
|
698
843
|
/* A bundle's member list: what it installs, one row each. A grid, not a
|
|
@@ -735,17 +880,23 @@ const updatedTitle = pkg.revision ? `${updated} · revision ${pkg.revision}` : u
|
|
|
735
880
|
grid-column: 1 / -1;
|
|
736
881
|
}
|
|
737
882
|
}
|
|
883
|
+
/* One panel, not three cards stacked. Beside a single tall README panel a
|
|
884
|
+
column of separately bordered boxes read as debris; the sections are
|
|
885
|
+
still separated, by a rule inside the panel rather than by a gap and a
|
|
886
|
+
second border each. */
|
|
738
887
|
.detail-rail {
|
|
739
888
|
display: flex;
|
|
740
889
|
flex-direction: column;
|
|
741
|
-
gap: var(--grim-space-7);
|
|
742
|
-
}
|
|
743
|
-
.detail-rail section {
|
|
744
890
|
background: var(--grim-color-card);
|
|
745
891
|
border: var(--grim-border-width) solid var(--grim-color-border);
|
|
746
|
-
border-radius: var(--grim-radius-
|
|
892
|
+
border-radius: var(--grim-radius-surface);
|
|
893
|
+
}
|
|
894
|
+
.detail-rail section {
|
|
747
895
|
padding: var(--grim-space-6);
|
|
748
896
|
}
|
|
897
|
+
.detail-rail section + section {
|
|
898
|
+
border-top: var(--grim-border-width) solid var(--grim-color-border);
|
|
899
|
+
}
|
|
749
900
|
/* Panel label. The main panel's ribbon labels match this by hand — they
|
|
750
901
|
are <label>s, not headings, so they cannot join the selector. */
|
|
751
902
|
.detail-rail h2 {
|