@terra-symposium/docs 0.1.75 → 0.1.76
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.
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { mkdir, readdir, readFile, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { dirname, join, relative, resolve, sep } from 'node:path';
|
|
4
|
+
import { parse } from 'yaml';
|
|
5
|
+
|
|
6
|
+
const ignoredDirectories = new Set([
|
|
7
|
+
'.astro',
|
|
8
|
+
'.cache',
|
|
9
|
+
'.git',
|
|
10
|
+
'.next',
|
|
11
|
+
'.nuxt',
|
|
12
|
+
'.output',
|
|
13
|
+
'.svelte-kit',
|
|
14
|
+
'.turbo',
|
|
15
|
+
'.vite',
|
|
16
|
+
'.vercel',
|
|
17
|
+
'.wrangler',
|
|
18
|
+
'build',
|
|
19
|
+
'coverage',
|
|
20
|
+
'dist',
|
|
21
|
+
'docs-dist',
|
|
22
|
+
'lib-dist',
|
|
23
|
+
'node_modules',
|
|
24
|
+
'site-dist',
|
|
25
|
+
'target',
|
|
26
|
+
'vendor',
|
|
27
|
+
]);
|
|
28
|
+
|
|
29
|
+
async function findSkillFiles(directory, files = []) {
|
|
30
|
+
const entries = await readdir(directory, { withFileTypes: true });
|
|
31
|
+
for (const entry of entries) {
|
|
32
|
+
if (entry.isDirectory() && ignoredDirectories.has(entry.name)) continue;
|
|
33
|
+
const path = join(directory, entry.name);
|
|
34
|
+
if (entry.isDirectory()) await findSkillFiles(path, files);
|
|
35
|
+
else if (entry.isFile() && entry.name === 'SKILL.md') files.push(path);
|
|
36
|
+
}
|
|
37
|
+
return files;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function splitFrontmatter(source) {
|
|
41
|
+
const match = /^---\s*\n([\s\S]*?)\n---\s*\n?/.exec(source);
|
|
42
|
+
if (!match) return { body: source.trim(), metadata: {}, metadataIssue: undefined };
|
|
43
|
+
try {
|
|
44
|
+
const metadata = parse(match[1]);
|
|
45
|
+
return {
|
|
46
|
+
body: source.slice(match[0].length).trim(),
|
|
47
|
+
metadata: metadata && typeof metadata === 'object' && !Array.isArray(metadata) ? metadata : {},
|
|
48
|
+
metadataIssue: undefined,
|
|
49
|
+
};
|
|
50
|
+
} catch (error) {
|
|
51
|
+
return {
|
|
52
|
+
body: source.slice(match[0].length).trim(),
|
|
53
|
+
metadata: {},
|
|
54
|
+
metadataIssue: error instanceof Error ? error.message : String(error),
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function skillLocation(root, path) {
|
|
60
|
+
const segments = relative(root, path).split(sep);
|
|
61
|
+
const skillRootIndex = segments.findIndex(
|
|
62
|
+
(segment, index) =>
|
|
63
|
+
(segment === '.agents' || segment === '.codex') && segments[index + 1] === 'skills',
|
|
64
|
+
);
|
|
65
|
+
if (skillRootIndex >= 0) {
|
|
66
|
+
const scope = segments.slice(0, skillRootIndex).join('/') || 'repository';
|
|
67
|
+
const directory = segments.slice(skillRootIndex + 2, -1).join('/') || 'root';
|
|
68
|
+
return { directory, scope };
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
directory: segments.slice(0, -1).join('/') || 'root',
|
|
72
|
+
scope: 'repository',
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function fallbackDescription(body) {
|
|
77
|
+
return (
|
|
78
|
+
body
|
|
79
|
+
.replace(/^\s*#.+\n/, '')
|
|
80
|
+
.split(/\n\s*\n/)
|
|
81
|
+
.map((paragraph) => paragraph.replace(/\s+/g, ' ').trim())
|
|
82
|
+
.find((paragraph) => paragraph && !paragraph.startsWith('#')) ?? ''
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function humanize(value) {
|
|
87
|
+
return value.replace(/[-_]+/g, ' ').replace(/\b\p{L}/gu, (letter) => letter.toUpperCase());
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export async function generateSkillsIndex(repositoryRoot, outputPath) {
|
|
91
|
+
const root = resolve(repositoryRoot);
|
|
92
|
+
const files = await findSkillFiles(root);
|
|
93
|
+
const skills = await Promise.all(
|
|
94
|
+
files.map(async (path) => {
|
|
95
|
+
const source = await readFile(path, 'utf8');
|
|
96
|
+
const { body, metadata, metadataIssue } = splitFrontmatter(source);
|
|
97
|
+
const location = skillLocation(root, path);
|
|
98
|
+
const directoryName = location.directory.split('/').at(-1) ?? 'skill';
|
|
99
|
+
const name = typeof metadata.name === 'string' ? metadata.name.trim() : directoryName;
|
|
100
|
+
const title = /^#\s+(.+?)\s*#*\s*$/m.exec(body)?.[1]?.trim() ?? humanize(name);
|
|
101
|
+
const description =
|
|
102
|
+
typeof metadata.description === 'string'
|
|
103
|
+
? metadata.description.trim()
|
|
104
|
+
: fallbackDescription(body);
|
|
105
|
+
|
|
106
|
+
return {
|
|
107
|
+
body,
|
|
108
|
+
description,
|
|
109
|
+
directory: location.directory,
|
|
110
|
+
metadata,
|
|
111
|
+
...(metadataIssue ? { metadataIssue } : {}),
|
|
112
|
+
name,
|
|
113
|
+
scope: location.scope,
|
|
114
|
+
sourcePath: relative(root, path).split(sep).join('/'),
|
|
115
|
+
title,
|
|
116
|
+
};
|
|
117
|
+
}),
|
|
118
|
+
);
|
|
119
|
+
skills.sort((first, second) =>
|
|
120
|
+
`${first.scope}/${first.directory}/${first.name}`.localeCompare(
|
|
121
|
+
`${second.scope}/${second.directory}/${second.name}`,
|
|
122
|
+
),
|
|
123
|
+
);
|
|
124
|
+
await mkdir(dirname(outputPath), { recursive: true });
|
|
125
|
+
await writeFile(outputPath, `${JSON.stringify(skills, null, 2)}\n`);
|
|
126
|
+
return skills;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (process.argv[1] && resolve(process.argv[1]) === resolve(new URL(import.meta.url).pathname)) {
|
|
130
|
+
const root = resolve(process.argv[2] ?? process.cwd());
|
|
131
|
+
const outputPath = resolve(process.argv[3] ?? join(root, '.terra-docs/cache/skills.generated.json'));
|
|
132
|
+
const skills = await generateSkillsIndex(root, outputPath);
|
|
133
|
+
console.log(`Indexed ${skills.length} skills from ${root}`);
|
|
134
|
+
}
|
package/bin/terra-docs.mjs
CHANGED
|
@@ -6,6 +6,7 @@ import { dirname, extname, join, relative, resolve } from 'node:path';
|
|
|
6
6
|
import { fileURLToPath } from 'node:url';
|
|
7
7
|
import { createRequire } from 'node:module';
|
|
8
8
|
import { parse } from 'yaml';
|
|
9
|
+
import { generateSkillsIndex } from './generate-skills.mjs';
|
|
9
10
|
|
|
10
11
|
const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
|
11
12
|
const require = createRequire(import.meta.url);
|
|
@@ -45,6 +46,7 @@ const publicRoot = resolve(root, '.terra-docs/public');
|
|
|
45
46
|
const publicAssetsRoot = resolve(publicRoot, '_terra-docs/assets');
|
|
46
47
|
const outputDirectory = options.get('--out') ?? 'dist/docs';
|
|
47
48
|
const searchIndexPath = resolve(publicRoot, '_terra-docs/search-index.json');
|
|
49
|
+
const skillsIndexPath = resolve(root, '.terra-docs/cache/skills.generated.json');
|
|
48
50
|
const excludedDirectories = new Set(['node_modules', '.astro', 'dist']);
|
|
49
51
|
const manifest = await readJson(resolve(root, 'package.json')).catch(() => ({}));
|
|
50
52
|
const siteTitle = options.get('--title') ?? titleFromName(manifest.name) ?? 'Documentation';
|
|
@@ -183,7 +185,10 @@ const env = {
|
|
|
183
185
|
PUBLIC_TERRA_DOCS_DESCRIPTION: siteDescription,
|
|
184
186
|
};
|
|
185
187
|
|
|
186
|
-
if (mode !== 'preview')
|
|
188
|
+
if (mode !== 'preview') {
|
|
189
|
+
await generateSkillsIndex(root, skillsIndexPath);
|
|
190
|
+
await buildSearchIndex();
|
|
191
|
+
}
|
|
187
192
|
|
|
188
193
|
function runAstro(command) {
|
|
189
194
|
const args = [astroCli, command, '--root', root, '--config', relative(root, configFile)];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@terra-symposium/docs",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.76",
|
|
4
4
|
"description": "Terra Docs workspace and Markdown site generator.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -35,7 +35,11 @@
|
|
|
35
35
|
"import": "./lib-dist/components/LocalDocsWorkspace.js"
|
|
36
36
|
},
|
|
37
37
|
"./site-layout.astro": "./src/components/SiteLayout.astro",
|
|
38
|
-
"./not-found.astro": "./src/components/NotFound.astro"
|
|
38
|
+
"./not-found.astro": "./src/components/NotFound.astro",
|
|
39
|
+
"./skills": {
|
|
40
|
+
"types": "./site/src/components/SkillsExplorer.tsx",
|
|
41
|
+
"import": "./site/src/components/SkillsExplorer.tsx"
|
|
42
|
+
}
|
|
39
43
|
},
|
|
40
44
|
"bin": {
|
|
41
45
|
"terra-docs": "bin/terra-docs.mjs"
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { useMemo, useState } from 'react';
|
|
2
|
+
import '../styles/skills-explorer.css';
|
|
3
|
+
|
|
4
|
+
export type SkillRecord = {
|
|
5
|
+
body: string;
|
|
6
|
+
description: string;
|
|
7
|
+
directory: string;
|
|
8
|
+
metadata: Record<string, unknown>;
|
|
9
|
+
metadataIssue?: string;
|
|
10
|
+
name: string;
|
|
11
|
+
scope: string;
|
|
12
|
+
sourcePath: string;
|
|
13
|
+
title: string;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
function metadataValue(value: unknown) {
|
|
17
|
+
if (typeof value === 'string') return value;
|
|
18
|
+
if (typeof value === 'number' || typeof value === 'boolean') return String(value);
|
|
19
|
+
return JSON.stringify(value);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function plural(count: number, singular: string) {
|
|
23
|
+
return `${count} ${singular}${count === 1 ? '' : 's'}`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function SkillsExplorer({ skills }: { skills: SkillRecord[] }) {
|
|
27
|
+
const [query, setQuery] = useState('');
|
|
28
|
+
const [activePath, setActivePath] = useState(skills[0]?.sourcePath ?? '');
|
|
29
|
+
const filteredSkills = useMemo(() => {
|
|
30
|
+
const normalizedQuery = query.trim().toLocaleLowerCase();
|
|
31
|
+
if (!normalizedQuery) return skills;
|
|
32
|
+
return skills.filter((skill) =>
|
|
33
|
+
[
|
|
34
|
+
skill.name,
|
|
35
|
+
skill.title,
|
|
36
|
+
skill.description,
|
|
37
|
+
skill.directory,
|
|
38
|
+
skill.scope,
|
|
39
|
+
skill.sourcePath,
|
|
40
|
+
JSON.stringify(skill.metadata),
|
|
41
|
+
skill.body,
|
|
42
|
+
]
|
|
43
|
+
.join('\n')
|
|
44
|
+
.toLocaleLowerCase()
|
|
45
|
+
.includes(normalizedQuery),
|
|
46
|
+
);
|
|
47
|
+
}, [query, skills]);
|
|
48
|
+
const selectedSkill =
|
|
49
|
+
filteredSkills.find((skill) => skill.sourcePath === activePath) ?? filteredSkills[0] ?? null;
|
|
50
|
+
const groups = useMemo(() => {
|
|
51
|
+
const byScope = new Map<string, SkillRecord[]>();
|
|
52
|
+
for (const skill of filteredSkills) {
|
|
53
|
+
const entries = byScope.get(skill.scope) ?? [];
|
|
54
|
+
entries.push(skill);
|
|
55
|
+
byScope.set(skill.scope, entries);
|
|
56
|
+
}
|
|
57
|
+
return [...byScope.entries()];
|
|
58
|
+
}, [filteredSkills]);
|
|
59
|
+
const metadataEntries = selectedSkill
|
|
60
|
+
? Object.entries(selectedSkill.metadata).filter(([key]) => !['name', 'description'].includes(key))
|
|
61
|
+
: [];
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<section aria-label="Repository skills" className="docs-skills">
|
|
65
|
+
<header className="docs-skills__header">
|
|
66
|
+
<div>
|
|
67
|
+
<p className="docs-skills__eyebrow">Repository inventory</p>
|
|
68
|
+
<h2>Skills</h2>
|
|
69
|
+
<p>Browse the workflows and guidance files included in this repository.</p>
|
|
70
|
+
</div>
|
|
71
|
+
<div aria-live="polite" className="docs-skills__summary">
|
|
72
|
+
<strong>{skills.length}</strong>
|
|
73
|
+
<span>{plural(skills.length, 'skill')} indexed</span>
|
|
74
|
+
</div>
|
|
75
|
+
</header>
|
|
76
|
+
|
|
77
|
+
{skills.length ? (
|
|
78
|
+
<div className="docs-skills__layout">
|
|
79
|
+
<nav aria-label="Available skills" className="docs-skills__index">
|
|
80
|
+
<label className="docs-skills__filter">
|
|
81
|
+
<span>Filter skills</span>
|
|
82
|
+
<input
|
|
83
|
+
onChange={(event) => setQuery(event.currentTarget.value)}
|
|
84
|
+
placeholder="Name, topic, or text"
|
|
85
|
+
type="search"
|
|
86
|
+
value={query}
|
|
87
|
+
/>
|
|
88
|
+
</label>
|
|
89
|
+
<p aria-live="polite" className="docs-skills__count">
|
|
90
|
+
Showing {filteredSkills.length} of {skills.length}
|
|
91
|
+
</p>
|
|
92
|
+
{groups.length ? (
|
|
93
|
+
groups.map(([scope, entries]) => (
|
|
94
|
+
<section aria-label={scope} className="docs-skills__group" key={scope}>
|
|
95
|
+
<h3>{scope}</h3>
|
|
96
|
+
<ul>
|
|
97
|
+
{entries.map((skill) => (
|
|
98
|
+
<li key={skill.sourcePath}>
|
|
99
|
+
<button
|
|
100
|
+
aria-current={selectedSkill?.sourcePath === skill.sourcePath ? 'true' : undefined}
|
|
101
|
+
className="docs-skills__item"
|
|
102
|
+
onClick={() => setActivePath(skill.sourcePath)}
|
|
103
|
+
type="button"
|
|
104
|
+
>
|
|
105
|
+
<span className="docs-skills__item-title">{skill.title}</span>
|
|
106
|
+
<span className="docs-skills__item-description">{skill.description || skill.name}</span>
|
|
107
|
+
<span className="docs-skills__item-path">{skill.directory}</span>
|
|
108
|
+
</button>
|
|
109
|
+
</li>
|
|
110
|
+
))}
|
|
111
|
+
</ul>
|
|
112
|
+
</section>
|
|
113
|
+
))
|
|
114
|
+
) : (
|
|
115
|
+
<p className="docs-skills__empty">No skills match “{query}”.</p>
|
|
116
|
+
)}
|
|
117
|
+
</nav>
|
|
118
|
+
|
|
119
|
+
{selectedSkill ? (
|
|
120
|
+
<article aria-live="polite" className="docs-skills__detail">
|
|
121
|
+
<header>
|
|
122
|
+
<p className="docs-skills__eyebrow">{selectedSkill.scope}</p>
|
|
123
|
+
<h3>{selectedSkill.title}</h3>
|
|
124
|
+
<p className="docs-skills__description">{selectedSkill.description}</p>
|
|
125
|
+
<p className="docs-skills__source-path">{selectedSkill.sourcePath}</p>
|
|
126
|
+
</header>
|
|
127
|
+
{metadataEntries.length ? (
|
|
128
|
+
<dl className="docs-skills__metadata">
|
|
129
|
+
{metadataEntries.map(([key, value]) => (
|
|
130
|
+
<div key={key}>
|
|
131
|
+
<dt>{key.replace(/[-_]+/g, ' ')}</dt>
|
|
132
|
+
<dd>{metadataValue(value)}</dd>
|
|
133
|
+
</div>
|
|
134
|
+
))}
|
|
135
|
+
</dl>
|
|
136
|
+
) : null}
|
|
137
|
+
{selectedSkill.metadataIssue ? (
|
|
138
|
+
<p className="docs-skills__metadata-issue">Frontmatter could not be parsed: {selectedSkill.metadataIssue}</p>
|
|
139
|
+
) : null}
|
|
140
|
+
<details className="docs-skills__source" open>
|
|
141
|
+
<summary>Full skill instructions</summary>
|
|
142
|
+
<pre><code>{selectedSkill.body}</code></pre>
|
|
143
|
+
</details>
|
|
144
|
+
</article>
|
|
145
|
+
) : null}
|
|
146
|
+
</div>
|
|
147
|
+
) : (
|
|
148
|
+
<p className="docs-skills__empty">
|
|
149
|
+
No SKILL.md files were found in this repository. Add skills under a .agents/skills or
|
|
150
|
+
.codex/skills folder, then rebuild the Docs site.
|
|
151
|
+
</p>
|
|
152
|
+
)}
|
|
153
|
+
</section>
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export default SkillsExplorer;
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
.docs-skills {
|
|
2
|
+
margin-block: 2rem;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
.docs-skills__header {
|
|
6
|
+
display: flex;
|
|
7
|
+
align-items: flex-end;
|
|
8
|
+
justify-content: space-between;
|
|
9
|
+
gap: 1.5rem;
|
|
10
|
+
margin-bottom: 1rem;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.docs-skills__header h2,
|
|
14
|
+
.docs-skills__detail h3 {
|
|
15
|
+
margin: 0;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.docs-skills__header p:not(.docs-skills__eyebrow) {
|
|
19
|
+
margin: 0.4rem 0 0;
|
|
20
|
+
color: color-mix(in srgb, currentColor 72%, transparent);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.docs-skills__eyebrow {
|
|
24
|
+
margin: 0 0 0.35rem;
|
|
25
|
+
color: color-mix(in srgb, currentColor 64%, transparent);
|
|
26
|
+
font-size: 0.75rem;
|
|
27
|
+
font-weight: 700;
|
|
28
|
+
letter-spacing: 0.07em;
|
|
29
|
+
text-transform: uppercase;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.docs-skills__summary {
|
|
33
|
+
display: grid;
|
|
34
|
+
justify-items: end;
|
|
35
|
+
color: color-mix(in srgb, currentColor 70%, transparent);
|
|
36
|
+
font-size: 0.85rem;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.docs-skills__summary strong {
|
|
40
|
+
color: var(--docs-accent, currentColor);
|
|
41
|
+
font-size: 1.6rem;
|
|
42
|
+
line-height: 1;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.docs-skills__layout {
|
|
46
|
+
display: grid;
|
|
47
|
+
grid-template-columns: minmax(16rem, 0.8fr) minmax(0, 1.6fr);
|
|
48
|
+
align-items: start;
|
|
49
|
+
gap: 1rem;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.docs-skills__index,
|
|
53
|
+
.docs-skills__detail {
|
|
54
|
+
min-width: 0;
|
|
55
|
+
overflow: hidden;
|
|
56
|
+
border: 1px solid color-mix(in srgb, currentColor 18%, transparent);
|
|
57
|
+
border-radius: 0.75rem;
|
|
58
|
+
background: color-mix(in srgb, currentColor 3%, transparent);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.docs-skills__index {
|
|
62
|
+
max-height: 78vh;
|
|
63
|
+
overflow: auto;
|
|
64
|
+
padding: 0.75rem;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.docs-skills__filter {
|
|
68
|
+
display: grid;
|
|
69
|
+
gap: 0.35rem;
|
|
70
|
+
font-size: 0.8rem;
|
|
71
|
+
font-weight: 600;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.docs-skills__filter input {
|
|
75
|
+
box-sizing: border-box;
|
|
76
|
+
width: 100%;
|
|
77
|
+
min-height: 2.5rem;
|
|
78
|
+
border: 1px solid color-mix(in srgb, currentColor 24%, transparent);
|
|
79
|
+
border-radius: 0.45rem;
|
|
80
|
+
background: var(--background, transparent);
|
|
81
|
+
color: inherit;
|
|
82
|
+
font: inherit;
|
|
83
|
+
font-weight: 400;
|
|
84
|
+
padding: 0.45rem 0.6rem;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.docs-skills__filter input:focus-visible,
|
|
88
|
+
.docs-skills__item:focus-visible {
|
|
89
|
+
outline: 2px solid var(--docs-accent, currentColor);
|
|
90
|
+
outline-offset: 2px;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.docs-skills__count {
|
|
94
|
+
margin: 0.65rem 0 1rem;
|
|
95
|
+
color: color-mix(in srgb, currentColor 65%, transparent);
|
|
96
|
+
font-size: 0.8rem;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.docs-skills__group + .docs-skills__group {
|
|
100
|
+
margin-top: 1rem;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.docs-skills__group h3 {
|
|
104
|
+
margin: 0 0 0.4rem;
|
|
105
|
+
color: color-mix(in srgb, currentColor 68%, transparent);
|
|
106
|
+
font-size: 0.75rem;
|
|
107
|
+
font-weight: 700;
|
|
108
|
+
overflow-wrap: anywhere;
|
|
109
|
+
text-transform: uppercase;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.docs-skills__group ul {
|
|
113
|
+
display: grid;
|
|
114
|
+
gap: 0.35rem;
|
|
115
|
+
margin: 0;
|
|
116
|
+
padding: 0;
|
|
117
|
+
list-style: none;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.docs-skills__item {
|
|
121
|
+
display: grid;
|
|
122
|
+
width: 100%;
|
|
123
|
+
min-width: 0;
|
|
124
|
+
gap: 0.2rem;
|
|
125
|
+
border: 0;
|
|
126
|
+
border-radius: 0.5rem;
|
|
127
|
+
background: transparent;
|
|
128
|
+
color: inherit;
|
|
129
|
+
cursor: pointer;
|
|
130
|
+
font: inherit;
|
|
131
|
+
padding: 0.55rem;
|
|
132
|
+
text-align: left;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.docs-skills__item:hover,
|
|
136
|
+
.docs-skills__item[aria-current='true'] {
|
|
137
|
+
background: color-mix(in srgb, var(--docs-accent, currentColor) 12%, transparent);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.docs-skills__item[aria-current='true'] .docs-skills__item-title {
|
|
141
|
+
color: var(--docs-accent, currentColor);
|
|
142
|
+
font-weight: 700;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.docs-skills__item-title {
|
|
146
|
+
font-weight: 600;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.docs-skills__item-description,
|
|
150
|
+
.docs-skills__item-path {
|
|
151
|
+
color: color-mix(in srgb, currentColor 68%, transparent);
|
|
152
|
+
font-size: 0.78rem;
|
|
153
|
+
line-height: 1.4;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.docs-skills__item-path,
|
|
157
|
+
.docs-skills__source-path {
|
|
158
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
|
159
|
+
overflow-wrap: anywhere;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.docs-skills__detail > header {
|
|
163
|
+
padding: 1.25rem 1.25rem 0.75rem;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.docs-skills__description {
|
|
167
|
+
margin: 0.6rem 0;
|
|
168
|
+
color: color-mix(in srgb, currentColor 74%, transparent);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
.docs-skills__source-path {
|
|
172
|
+
margin: 0;
|
|
173
|
+
color: color-mix(in srgb, currentColor 60%, transparent);
|
|
174
|
+
font-size: 0.75rem;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.docs-skills__metadata {
|
|
178
|
+
display: grid;
|
|
179
|
+
grid-template-columns: repeat(auto-fit, minmax(min(100%, 11rem), 1fr));
|
|
180
|
+
gap: 0.6rem;
|
|
181
|
+
margin: 0;
|
|
182
|
+
padding: 0.75rem 1.25rem 1.25rem;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.docs-skills__metadata > div {
|
|
186
|
+
min-width: 0;
|
|
187
|
+
border: 1px solid color-mix(in srgb, currentColor 15%, transparent);
|
|
188
|
+
border-radius: 0.5rem;
|
|
189
|
+
padding: 0.6rem 0.7rem;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
.docs-skills__metadata dt {
|
|
193
|
+
color: color-mix(in srgb, currentColor 62%, transparent);
|
|
194
|
+
font-size: 0.72rem;
|
|
195
|
+
font-weight: 700;
|
|
196
|
+
text-transform: capitalize;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.docs-skills__metadata dd {
|
|
200
|
+
margin: 0.2rem 0 0;
|
|
201
|
+
font-size: 0.82rem;
|
|
202
|
+
overflow-wrap: anywhere;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.docs-skills__metadata-issue {
|
|
206
|
+
margin: 0 1.25rem 1rem;
|
|
207
|
+
color: #a12b2b;
|
|
208
|
+
font-size: 0.85rem;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
.docs-skills__source {
|
|
212
|
+
border-top: 1px solid color-mix(in srgb, currentColor 16%, transparent);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
.docs-skills__source summary {
|
|
216
|
+
cursor: pointer;
|
|
217
|
+
font-weight: 600;
|
|
218
|
+
padding: 0.8rem 1.25rem;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
.docs-skills__source summary:focus-visible {
|
|
222
|
+
outline: 2px solid var(--docs-accent, currentColor);
|
|
223
|
+
outline-offset: -2px;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.docs-skills__source pre {
|
|
227
|
+
max-height: 60vh;
|
|
228
|
+
overflow: auto;
|
|
229
|
+
margin: 0;
|
|
230
|
+
border-top: 1px solid color-mix(in srgb, currentColor 12%, transparent);
|
|
231
|
+
background: color-mix(in srgb, currentColor 4%, transparent);
|
|
232
|
+
padding: 1rem 1.25rem;
|
|
233
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
|
234
|
+
font-size: 0.8rem;
|
|
235
|
+
line-height: 1.6;
|
|
236
|
+
white-space: pre-wrap;
|
|
237
|
+
overflow-wrap: anywhere;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
.docs-skills__empty {
|
|
241
|
+
border: 1px solid color-mix(in srgb, currentColor 18%, transparent);
|
|
242
|
+
border-radius: 0.75rem;
|
|
243
|
+
padding: 1rem;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
@media (max-width: 48rem) {
|
|
247
|
+
.docs-skills__header {
|
|
248
|
+
align-items: flex-start;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
.docs-skills__layout {
|
|
252
|
+
grid-template-columns: minmax(0, 1fr);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
.docs-skills__index {
|
|
256
|
+
max-height: 45vh;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
@media (max-width: 30rem) {
|
|
261
|
+
.docs-skills__header {
|
|
262
|
+
flex-direction: column;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
.docs-skills__summary {
|
|
266
|
+
justify-items: start;
|
|
267
|
+
}
|
|
268
|
+
}
|