@hgdc/docs 0.0.0-stage → 0.1.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/bin/hgdc-docs.mjs +48 -0
- package/docs.schema.json +294 -0
- package/package.json +51 -4
- package/src/build/auto-import.ts +74 -0
- package/src/build/markdown.ts +114 -0
- package/src/build/outputs.ts +91 -0
- package/src/build/public-files.ts +35 -0
- package/src/components/Accordion.astro +12 -0
- package/src/components/AccordionGroup.astro +4 -0
- package/src/components/Card.astro +15 -0
- package/src/components/CardGroup.astro +8 -0
- package/src/components/Check.astro +10 -0
- package/src/components/CodeGroup.astro +4 -0
- package/src/components/Columns.astro +8 -0
- package/src/components/Danger.astro +10 -0
- package/src/components/Frame.astro +11 -0
- package/src/components/Info.astro +10 -0
- package/src/components/Note.astro +10 -0
- package/src/components/Step.astro +11 -0
- package/src/components/Steps.astro +4 -0
- package/src/components/Tab.astro +9 -0
- package/src/components/Tabs.astro +9 -0
- package/src/components/Tip.astro +10 -0
- package/src/components/Warning.astro +10 -0
- package/src/core/answer-markdown.ts +109 -0
- package/src/core/assistant-api.ts +65 -0
- package/src/core/chunk.ts +134 -0
- package/src/core/config.ts +153 -0
- package/src/core/corpus.ts +42 -0
- package/src/core/json-schema.ts +12 -0
- package/src/core/llms.ts +95 -0
- package/src/core/navigation.ts +88 -0
- package/src/core/theme.ts +111 -0
- package/src/integration.ts +229 -0
- package/src/locals.d.ts +6 -0
- package/src/overrides/Assistant.astro +72 -0
- package/src/overrides/Head.astro +12 -0
- package/src/overrides/PageFrame.astro +15 -0
- package/src/overrides/PageTitle.astro +94 -0
- package/src/overrides/Sidebar.astro +60 -0
- package/src/overrides/route-data.ts +33 -0
- package/src/styles/theme.css +773 -0
- package/src/virtual.d.ts +6 -0
- package/src/widget/client.ts +252 -0
- package/template/.astro/collections/docs.schema.json +651 -0
- package/template/.astro/content-assets.mjs +1 -0
- package/template/.astro/content-modules.mjs +18 -0
- package/template/.astro/content.d.ts +179 -0
- package/template/.astro/types.d.ts +2 -0
- package/template/.build/.well-known/llms.txt +38 -0
- package/template/.build/404.html +42 -0
- package/template/.build/_astro/Assistant.astro_astro_type_script_index_0_lang.DWgmlr85.js +4 -0
- package/template/.build/_astro/MobileTableOfContents.astro_astro_type_script_index_0_lang.C3fM4Kra.js +1 -0
- package/template/.build/_astro/Search.astro_astro_type_script_index_0_lang.g9hoxVJg.js +1 -0
- package/template/.build/_astro/TableOfContents.astro_astro_type_script_index_0_lang.BpJaGz7G.js +1 -0
- package/template/.build/_astro/common.8GHCkW7i.css +3 -0
- package/template/.build/_astro/ec.0vx5m.js +3 -0
- package/template/.build/_astro/ec.8nqtp.css +1 -0
- package/template/.build/_astro/geist-cyrillic-ext-wght-normal.DjL33-gN.woff2 +0 -0
- package/template/.build/_astro/geist-cyrillic-wght-normal.BEAKL7Jp.woff2 +0 -0
- package/template/.build/_astro/geist-latin-ext-wght-normal.DC-KSUi6.woff2 +0 -0
- package/template/.build/_astro/geist-latin-wght-normal.BgDaEnEv.woff2 +0 -0
- package/template/.build/_astro/geist-vietnamese-wght-normal.6IgcOCM7.woff2 +0 -0
- package/template/.build/_astro/inter-cyrillic-ext-wght-normal.BOeWTOD4.woff2 +0 -0
- package/template/.build/_astro/inter-cyrillic-wght-normal.DqGufNeO.woff2 +0 -0
- package/template/.build/_astro/inter-greek-ext-wght-normal.DlzME5K_.woff2 +0 -0
- package/template/.build/_astro/inter-greek-wght-normal.CkhJZR-_.woff2 +0 -0
- package/template/.build/_astro/inter-latin-ext-wght-normal.DO1Apj_S.woff2 +0 -0
- package/template/.build/_astro/inter-latin-wght-normal.Dx4kXJAl.woff2 +0 -0
- package/template/.build/_astro/inter-vietnamese-wght-normal.CBcvBZtf.woff2 +0 -0
- package/template/.build/_astro/logo-clair.BqNh6JNi.svg +1 -0
- package/template/.build/_astro/logo-sombre.BkgsFXxh.svg +1 -0
- package/template/.build/_astro/page.cNngjUgL.js +1 -0
- package/template/.build/_astro/print.ehPL0gv-.css +1 -0
- package/template/.build/_astro/starlight-toc.D4UDIxQL.js +1 -0
- package/template/.build/_astro/ui-core.Clh5jUUO.js +2 -0
- package/template/.build/_hgdc/corpus.json +1 -0
- package/template/.build/demarrer/premiere-doc/index.html +84 -0
- package/template/.build/demarrer/premiere-doc.md +65 -0
- package/template/.build/explications/donnees/index.html +114 -0
- package/template/.build/explications/donnees.md +28 -0
- package/template/.build/explications/fonctionnement/index.html +92 -0
- package/template/.build/explications/fonctionnement.md +32 -0
- package/template/.build/guides/agents/index.html +113 -0
- package/template/.build/guides/agents.md +40 -0
- package/template/.build/guides/assistant/index.html +117 -0
- package/template/.build/guides/assistant.md +42 -0
- package/template/.build/guides/composants/index.html +97 -0
- package/template/.build/guides/composants.md +103 -0
- package/template/.build/guides/ecrire/index.html +115 -0
- package/template/.build/guides/ecrire.md +63 -0
- package/template/.build/guides/navigation/index.html +89 -0
- package/template/.build/guides/navigation.md +63 -0
- package/template/.build/guides/publier/index.html +86 -0
- package/template/.build/guides/publier.md +65 -0
- package/template/.build/guides/theme/index.html +86 -0
- package/template/.build/guides/theme.md +33 -0
- package/template/.build/images/favicon.svg +1 -0
- package/template/.build/images/logo-clair.svg +1 -0
- package/template/.build/images/logo-sombre.svg +1 -0
- package/template/.build/index.html +87 -0
- package/template/.build/index.md +35 -0
- package/template/.build/llms-full.txt +830 -0
- package/template/.build/llms.txt +38 -0
- package/template/.build/pagefind/fragment/fr-ch_148313b.pf_fragment +0 -0
- package/template/.build/pagefind/fragment/fr-ch_424fd16.pf_fragment +0 -0
- package/template/.build/pagefind/fragment/fr-ch_4f8575c.pf_fragment +0 -0
- package/template/.build/pagefind/fragment/fr-ch_5a4d77d.pf_fragment +0 -0
- package/template/.build/pagefind/fragment/fr-ch_6c36edc.pf_fragment +0 -0
- package/template/.build/pagefind/fragment/fr-ch_73d7887.pf_fragment +0 -0
- package/template/.build/pagefind/fragment/fr-ch_8234ed3.pf_fragment +0 -0
- package/template/.build/pagefind/fragment/fr-ch_8f8a19e.pf_fragment +0 -0
- package/template/.build/pagefind/fragment/fr-ch_9f1b981.pf_fragment +0 -0
- package/template/.build/pagefind/fragment/fr-ch_a19039d.pf_fragment +0 -0
- package/template/.build/pagefind/fragment/fr-ch_d72cc1e.pf_fragment +0 -0
- package/template/.build/pagefind/fragment/fr-ch_d732215.pf_fragment +0 -0
- package/template/.build/pagefind/fragment/fr-ch_e2c46e3.pf_fragment +0 -0
- package/template/.build/pagefind/fragment/fr-ch_e78d58b.pf_fragment +0 -0
- package/template/.build/pagefind/fragment/fr-ch_e92f972.pf_fragment +0 -0
- package/template/.build/pagefind/index/fr-ch_f94c95d.pf_index +0 -0
- package/template/.build/pagefind/pagefind-component-ui.css +1509 -0
- package/template/.build/pagefind/pagefind-component-ui.js +56 -0
- package/template/.build/pagefind/pagefind-entry.json +1 -0
- package/template/.build/pagefind/pagefind-highlight.js +1070 -0
- package/template/.build/pagefind/pagefind-modular-ui.css +214 -0
- package/template/.build/pagefind/pagefind-modular-ui.js +8 -0
- package/template/.build/pagefind/pagefind-ui.css +1 -0
- package/template/.build/pagefind/pagefind-ui.js +2 -0
- package/template/.build/pagefind/pagefind-worker.js +6 -0
- package/template/.build/pagefind/pagefind.fr-ch_fb78ab42961f4.pf_meta +0 -0
- package/template/.build/pagefind/pagefind.js +6 -0
- package/template/.build/pagefind/wasm.fr-ch.pagefind +0 -0
- package/template/.build/pagefind/wasm.unknown.pagefind +0 -0
- package/template/.build/reference/api-assistant/index.html +161 -0
- package/template/.build/reference/api-assistant.md +60 -0
- package/template/.build/reference/cli/index.html +153 -0
- package/template/.build/reference/cli.md +31 -0
- package/template/.build/reference/docs-json/index.html +192 -0
- package/template/.build/reference/docs-json.md +43 -0
- package/template/.build/reference/mcp/index.html +151 -0
- package/template/.build/reference/mcp.md +49 -0
- package/template/astro.config.ts +6 -0
- package/template/node_modules/.vite/deps/_metadata.json +74 -0
- package/template/node_modules/.vite/deps/astro-B3JHJyiv.js +434 -0
- package/template/node_modules/.vite/deps/astro-B3JHJyiv.js.map +1 -0
- package/template/node_modules/.vite/deps/astro_n_aria-query.js +6390 -0
- package/template/node_modules/.vite/deps/astro_n_aria-query.js.map +1 -0
- package/template/node_modules/.vite/deps/astro_n_axobject-query.js +2705 -0
- package/template/node_modules/.vite/deps/astro_n_axobject-query.js.map +1 -0
- package/template/node_modules/.vite/deps/astro_n_html-escaper.js +65 -0
- package/template/node_modules/.vite/deps/astro_n_html-escaper.js.map +1 -0
- package/template/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js +455 -0
- package/template/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js.map +1 -0
- package/template/node_modules/.vite/deps/audit-Do8QH17W.js +1543 -0
- package/template/node_modules/.vite/deps/audit-Do8QH17W.js.map +1 -0
- package/template/node_modules/.vite/deps/highlight-BLtvqtzO.js +48 -0
- package/template/node_modules/.vite/deps/highlight-BLtvqtzO.js.map +1 -0
- package/template/node_modules/.vite/deps/icons-C92i61-2.js +42 -0
- package/template/node_modules/.vite/deps/icons-C92i61-2.js.map +1 -0
- package/template/node_modules/.vite/deps/package.json +3 -0
- package/template/node_modules/.vite/deps/rolldown-runtime-C3I60J0A.js +4 -0
- package/template/node_modules/.vite/deps/settings-BYmTKrLi.js +189 -0
- package/template/node_modules/.vite/deps/settings-BYmTKrLi.js.map +1 -0
- package/template/node_modules/.vite/deps/toolbar-CEKhukuK.js +461 -0
- package/template/node_modules/.vite/deps/toolbar-CEKhukuK.js.map +1 -0
- package/template/node_modules/.vite/deps/ui-library-BO4--ANp.js +1015 -0
- package/template/node_modules/.vite/deps/ui-library-BO4--ANp.js.map +1 -0
- package/template/node_modules/.vite/deps/window-vo3A6TMn.js +32 -0
- package/template/node_modules/.vite/deps/window-vo3A6TMn.js.map +1 -0
- package/template/node_modules/.vite/deps/xray-Lf9x6g8P.js +112 -0
- package/template/node_modules/.vite/deps/xray-Lf9x6g8P.js.map +1 -0
- package/template/src/content.config.ts +21 -0
- package/README.md +0 -3
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import { cpSync, existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync } from "node:fs"
|
|
2
|
+
import { dirname, join, relative, resolve } from "node:path"
|
|
3
|
+
import { fileURLToPath } from "node:url"
|
|
4
|
+
|
|
5
|
+
import { unified } from "@astrojs/markdown-remark"
|
|
6
|
+
import starlight from "@astrojs/starlight"
|
|
7
|
+
import type { AstroIntegration, AstroUserConfig } from "astro"
|
|
8
|
+
|
|
9
|
+
import { remarkAutoImport } from "./build/auto-import.ts"
|
|
10
|
+
import { publicFiles } from "./build/public-files.ts"
|
|
11
|
+
import { writeMachineReadable } from "./build/outputs.ts"
|
|
12
|
+
import { parseDocsConfig, withHostProject, type DocsConfig } from "./core/config.ts"
|
|
13
|
+
import { missingPages, normalizeNavigation, pageToSlug, type NavItem, type NavTree } from "./core/navigation.ts"
|
|
14
|
+
import { themeCss, themeFor } from "./core/theme.ts"
|
|
15
|
+
|
|
16
|
+
/** Où lire la doc du client et où écrire le site : posé par la CLI `hgdc-docs`. */
|
|
17
|
+
export type SiteOptions = {
|
|
18
|
+
contentDir: string
|
|
19
|
+
outDir: string
|
|
20
|
+
/** Adresse publique du site, pour les liens absolus de `llms.txt` et le sitemap. */
|
|
21
|
+
site: string | null
|
|
22
|
+
/** API de l'assistant (console HGDC Docs). */
|
|
23
|
+
apiEndpoint: string
|
|
24
|
+
/** Projet de la console annoncé par HGDC Host (`HGDC_DOCS_PROJECT`), si docs.json n'en a pas. */
|
|
25
|
+
project: string | null
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const DEFAULT_API = "https://docs.hgdc.ch"
|
|
29
|
+
const here = dirname(fileURLToPath(import.meta.url))
|
|
30
|
+
const file = (path: string) => resolve(here, path)
|
|
31
|
+
|
|
32
|
+
export function readSiteOptions(env: NodeJS.ProcessEnv = process.env): SiteOptions {
|
|
33
|
+
const contentDir = resolve(env.HGDC_DOCS_DIR ?? process.cwd())
|
|
34
|
+
const vercelUrl = env.VERCEL_PROJECT_PRODUCTION_URL ? `https://${env.VERCEL_PROJECT_PRODUCTION_URL}` : null
|
|
35
|
+
return {
|
|
36
|
+
contentDir,
|
|
37
|
+
outDir: resolve(env.HGDC_DOCS_OUT ?? join(contentDir, "dist")),
|
|
38
|
+
site: env.HGDC_DOCS_SITE ?? vercelUrl,
|
|
39
|
+
apiEndpoint: env.HGDC_DOCS_API ?? DEFAULT_API,
|
|
40
|
+
project: env.HGDC_DOCS_PROJECT ?? null,
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export class DocsConfigError extends Error {}
|
|
45
|
+
|
|
46
|
+
export function loadDocsConfig(contentDir: string, hostProject: string | null = null): DocsConfig {
|
|
47
|
+
const path = join(contentDir, "docs.json")
|
|
48
|
+
if (!existsSync(path)) throw new DocsConfigError(`Aucun docs.json dans ${contentDir}. Crée-le à la racine de la doc.`)
|
|
49
|
+
const parsed = parseDocsConfig(readFileSync(path, "utf8"))
|
|
50
|
+
const result = parsed.ok ? withHostProject(parsed.config, hostProject ?? undefined) : parsed
|
|
51
|
+
if (!result.ok) throw new DocsConfigError(`docs.json invalide :\n- ${result.errors.join("\n- ")}`)
|
|
52
|
+
return result.config
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Ce que les composants du modèle lisent par `virtual:hgdc-docs/config`. */
|
|
56
|
+
export type VirtualConfig = {
|
|
57
|
+
config: DocsConfig
|
|
58
|
+
tree: NavTree
|
|
59
|
+
apiEndpoint: string
|
|
60
|
+
site: string | null
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const IGNORED_DIRS = new Set(["node_modules", "dist", ".hgdc-docs", ".git", ".github", ".vercel"])
|
|
64
|
+
|
|
65
|
+
function collectPublic(contentDir: string, declared: ReadonlyArray<string>): string {
|
|
66
|
+
const target = join(contentDir, ".hgdc-docs", "public")
|
|
67
|
+
rmSync(target, { recursive: true, force: true })
|
|
68
|
+
mkdirSync(target, { recursive: true })
|
|
69
|
+
for (const file of publicFiles(contentDir, declared)) cpSync(file.from, join(target, file.to))
|
|
70
|
+
return target
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Pages du dépôt (`guides/publier`) et leur `sidebarTitle` éventuel (champ Mintlify). */
|
|
74
|
+
function scanPages(contentDir: string): Map<string, string | null> {
|
|
75
|
+
const pages = new Map<string, string | null>()
|
|
76
|
+
const walk = (dir: string) => {
|
|
77
|
+
for (const name of readdirSync(dir)) {
|
|
78
|
+
if (name.startsWith(".") || IGNORED_DIRS.has(name)) continue
|
|
79
|
+
const path = join(dir, name)
|
|
80
|
+
if (statSync(path).isDirectory()) walk(path)
|
|
81
|
+
else if (/\.mdx?$/.test(name) && !name.startsWith("_")) {
|
|
82
|
+
const page = relative(contentDir, path)
|
|
83
|
+
.replaceAll("\\", "/")
|
|
84
|
+
.replace(/\.mdx?$/, "")
|
|
85
|
+
const frontmatter = /^---\r?\n([\s\S]*?)\r?\n---/.exec(readFileSync(path, "utf8"))?.[1] ?? ""
|
|
86
|
+
const label = /^sidebarTitle:\s*["']?(.+?)["']?\s*$/m.exec(frontmatter)?.[1] ?? null
|
|
87
|
+
pages.set(pageToSlug(page), label)
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
walk(contentDir)
|
|
92
|
+
return pages
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
type SidebarItem = { slug: string; label?: string } | { label: string; collapsed: boolean; items: Array<SidebarItem> }
|
|
96
|
+
|
|
97
|
+
function sidebarItems(items: Array<NavItem>, labels: ReadonlyMap<string, string | null>): Array<SidebarItem> {
|
|
98
|
+
return items.map((item) => {
|
|
99
|
+
if (item.type === "group")
|
|
100
|
+
return { label: item.label, collapsed: !item.expanded, items: sidebarItems(item.items, labels) }
|
|
101
|
+
const slug = pageToSlug(item.page)
|
|
102
|
+
const label = labels.get(slug)
|
|
103
|
+
return label ? { slug, label } : { slug }
|
|
104
|
+
})
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Une entrée de premier niveau par couple (version, onglet), dans l'ordre de l'arbre ;
|
|
109
|
+
* `route-data.ts` ne garde ensuite que celle de la page affichée.
|
|
110
|
+
*/
|
|
111
|
+
export function starlightSidebar(
|
|
112
|
+
tree: NavTree,
|
|
113
|
+
labels: ReadonlyMap<string, string | null> = new Map(),
|
|
114
|
+
): Array<SidebarItem> {
|
|
115
|
+
const sections = tree.versions.flatMap((version) => version.tabs)
|
|
116
|
+
if (sections.length === 1) return sidebarItems(sections[0]!.items, labels)
|
|
117
|
+
return tree.versions.flatMap((version) =>
|
|
118
|
+
version.tabs.map((tab) => ({
|
|
119
|
+
label: [version.label, tab.label].filter(Boolean).join(" · ") || "Documentation",
|
|
120
|
+
collapsed: false,
|
|
121
|
+
items: sidebarItems(tab.items, labels),
|
|
122
|
+
})),
|
|
123
|
+
)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const SOCIAL_ICONS = new Set([
|
|
127
|
+
"github",
|
|
128
|
+
"gitlab",
|
|
129
|
+
"linkedin",
|
|
130
|
+
"x.com",
|
|
131
|
+
"mastodon",
|
|
132
|
+
"discord",
|
|
133
|
+
"youtube",
|
|
134
|
+
"instagram",
|
|
135
|
+
"bluesky",
|
|
136
|
+
])
|
|
137
|
+
|
|
138
|
+
function virtualConfigPlugin(data: VirtualConfig) {
|
|
139
|
+
const id = "virtual:hgdc-docs/config"
|
|
140
|
+
return {
|
|
141
|
+
name: "hgdc-docs-config",
|
|
142
|
+
resolveId: (source: string) => (source === id ? `\0${id}` : undefined),
|
|
143
|
+
load: (source: string) => (source === `\0${id}` ? `export default ${JSON.stringify(data)}` : undefined),
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function machineReadableOutputs(data: VirtualConfig, options: SiteOptions): AstroIntegration {
|
|
148
|
+
return {
|
|
149
|
+
name: "hgdc-docs-outputs",
|
|
150
|
+
hooks: {
|
|
151
|
+
"astro:build:done": async ({ dir, pages, logger }) => {
|
|
152
|
+
const written = await writeMachineReadable({
|
|
153
|
+
outDir: fileURLToPath(dir),
|
|
154
|
+
pathnames: pages.map((page) => page.pathname),
|
|
155
|
+
config: data.config,
|
|
156
|
+
tree: data.tree,
|
|
157
|
+
site: options.site,
|
|
158
|
+
})
|
|
159
|
+
logger.info(`llms.txt, llms-full.txt, ${written.pages} pages Markdown et le corpus de l'assistant écrits.`)
|
|
160
|
+
// Le build tourne dans le paquet, où ses dépendances se résolvent quel que soit le
|
|
161
|
+
// gestionnaire du client (npm, pnpm, bun isolé), puis le site seul est copié chez lui.
|
|
162
|
+
rmSync(options.outDir, { recursive: true, force: true })
|
|
163
|
+
cpSync(fileURLToPath(dir), options.outDir, { recursive: true, filter: (path) => !path.includes(".prerender") })
|
|
164
|
+
logger.info(`Site écrit dans ${options.outDir}`)
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export function createAstroConfig(options: SiteOptions = readSiteOptions()): AstroUserConfig {
|
|
171
|
+
const config = loadDocsConfig(options.contentDir, options.project)
|
|
172
|
+
const tree = normalizeNavigation(config)
|
|
173
|
+
const pages = scanPages(options.contentDir)
|
|
174
|
+
const missing = missingPages(tree, new Set(pages.keys()))
|
|
175
|
+
if (missing.length)
|
|
176
|
+
throw new DocsConfigError(
|
|
177
|
+
`docs.json cite des pages absentes du dossier :\n- ${missing.map((page) => `${page}.mdx`).join("\n- ")}`,
|
|
178
|
+
)
|
|
179
|
+
const data: VirtualConfig = { config, tree, apiEndpoint: options.apiEndpoint, site: options.site }
|
|
180
|
+
const theme = themeFor(config.colors?.primary, config.colors?.dark)
|
|
181
|
+
const logo = typeof config.logo === "string" ? { light: config.logo } : config.logo
|
|
182
|
+
const asset = (path: string) => join(options.contentDir, path.replace(/^\//, ""))
|
|
183
|
+
const social = Object.entries(config.footer?.socials ?? {})
|
|
184
|
+
.filter(([icon]) => SOCIAL_ICONS.has(icon))
|
|
185
|
+
.map(([icon, href]) => ({ icon: (icon === "x.com" ? "x.com" : icon) as "github", label: icon, href }))
|
|
186
|
+
|
|
187
|
+
return {
|
|
188
|
+
root: file("../template"),
|
|
189
|
+
srcDir: file("../template/src"),
|
|
190
|
+
publicDir: collectPublic(
|
|
191
|
+
options.contentDir,
|
|
192
|
+
[logo?.light, logo?.dark, config.favicon].filter((p): p is string => Boolean(p)),
|
|
193
|
+
),
|
|
194
|
+
outDir: file("../template/.build"),
|
|
195
|
+
cacheDir: join(options.contentDir, ".hgdc-docs", "cache"),
|
|
196
|
+
site: options.site ?? undefined,
|
|
197
|
+
trailingSlash: "always",
|
|
198
|
+
markdown: { processor: unified({ remarkPlugins: [remarkAutoImport] }) },
|
|
199
|
+
vite: {
|
|
200
|
+
plugins: [virtualConfigPlugin(data)],
|
|
201
|
+
server: { fs: { allow: [options.contentDir, file("..")] } },
|
|
202
|
+
},
|
|
203
|
+
integrations: [
|
|
204
|
+
starlight({
|
|
205
|
+
title: config.name,
|
|
206
|
+
description: config.description,
|
|
207
|
+
locales: { root: { label: "Français", lang: config.language === "fr" ? "fr-CH" : config.language } },
|
|
208
|
+
logo: logo
|
|
209
|
+
? { light: asset(logo.light), dark: asset(logo.dark ?? logo.light), replacesTitle: false }
|
|
210
|
+
: undefined,
|
|
211
|
+
favicon: config.favicon ?? "/favicon.svg",
|
|
212
|
+
social,
|
|
213
|
+
sidebar: starlightSidebar(tree, pages),
|
|
214
|
+
customCss: [file("./styles/theme.css")],
|
|
215
|
+
head: [{ tag: "style", content: themeCss(theme) }],
|
|
216
|
+
routeMiddleware: file("./overrides/route-data.ts"),
|
|
217
|
+
components: {
|
|
218
|
+
Head: file("./overrides/Head.astro"),
|
|
219
|
+
Sidebar: file("./overrides/Sidebar.astro"),
|
|
220
|
+
PageTitle: file("./overrides/PageTitle.astro"),
|
|
221
|
+
PageFrame: file("./overrides/PageFrame.astro"),
|
|
222
|
+
},
|
|
223
|
+
expressiveCode: { themes: ["github-dark-default", "github-light-default"] },
|
|
224
|
+
pagination: true,
|
|
225
|
+
}),
|
|
226
|
+
machineReadableOutputs(data, options),
|
|
227
|
+
],
|
|
228
|
+
}
|
|
229
|
+
}
|
package/src/locals.d.ts
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
---
|
|
2
|
+
import data from "virtual:hgdc-docs/config"
|
|
3
|
+
|
|
4
|
+
// Widget de l'assistant : panneau latéral non modal (la page reste lisible), plein écran
|
|
5
|
+
// sur téléphone. Logique dans `../widget/client.ts`.
|
|
6
|
+
const endpoint = new URL("/api/v1/ask", data.config.assistant?.endpoint ?? data.apiEndpoint).toString()
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
<hgdc-assistant data-endpoint={endpoint} data-project={data.config.project} data-name={data.config.name}>
|
|
10
|
+
<button type="button" class="hgdc-launcher" data-open aria-haspopup="dialog" aria-expanded="false">
|
|
11
|
+
<svg aria-hidden="true" viewBox="0 0 24 24" width="18" height="18">
|
|
12
|
+
<path d="M12 3l1.9 4.6L18.5 9.5l-4.6 1.9L12 16l-1.9-4.6L5.5 9.5l4.6-1.9z" fill="currentColor"></path>
|
|
13
|
+
<path d="M18.5 15l.8 1.9 1.9.8-1.9.8-.8 1.9-.8-1.9-1.9-.8 1.9-.8z" fill="currentColor"></path>
|
|
14
|
+
</svg>
|
|
15
|
+
<span>Demander à l'IA</span>
|
|
16
|
+
<kbd class="hgdc-kbd" data-shortcut aria-hidden="true">Ctrl I</kbd>
|
|
17
|
+
</button>
|
|
18
|
+
|
|
19
|
+
<section class="hgdc-panel" role="dialog" aria-modal="false" aria-labelledby="hgdc-assistant-title" hidden>
|
|
20
|
+
<header class="hgdc-panel__header">
|
|
21
|
+
<div>
|
|
22
|
+
<h2 id="hgdc-assistant-title">Assistant</h2>
|
|
23
|
+
<p>Répond à partir de cette documentation, avec ses sources.</p>
|
|
24
|
+
</div>
|
|
25
|
+
<div class="hgdc-panel__tools">
|
|
26
|
+
<button type="button" class="hgdc-icon-button" data-reset aria-label="Nouvelle conversation" hidden>
|
|
27
|
+
<svg aria-hidden="true" viewBox="0 0 24 24" width="18" height="18"
|
|
28
|
+
><path d="M4 12a8 8 0 1 0 2.3-5.7M4 4v4h4" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"
|
|
29
|
+
></path></svg
|
|
30
|
+
>
|
|
31
|
+
</button>
|
|
32
|
+
<button type="button" class="hgdc-icon-button" data-close aria-label="Fermer l'assistant">
|
|
33
|
+
<svg aria-hidden="true" viewBox="0 0 24 24" width="18" height="18"
|
|
34
|
+
><path d="M6 6l12 12M18 6 6 18" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"
|
|
35
|
+
></path></svg
|
|
36
|
+
>
|
|
37
|
+
</button>
|
|
38
|
+
</div>
|
|
39
|
+
</header>
|
|
40
|
+
|
|
41
|
+
<div class="hgdc-panel__body" data-log aria-live="polite">
|
|
42
|
+
<div class="hgdc-empty" data-empty>
|
|
43
|
+
<p class="hgdc-empty__title">Pose ta question sur {data.config.name}.</p>
|
|
44
|
+
<p>
|
|
45
|
+
Je cherche dans les pages de cette documentation et je cite mes sources. Si la réponse n'y est pas, je te le
|
|
46
|
+
dis.
|
|
47
|
+
</p>
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
<form class="hgdc-panel__form" data-form>
|
|
52
|
+
<label class="sr-only" for="hgdc-question">Ta question</label>
|
|
53
|
+
<textarea id="hgdc-question" data-input rows="1" maxlength="1000" placeholder="Comment publier ma doc ?" required
|
|
54
|
+
></textarea>
|
|
55
|
+
<button type="submit" class="hgdc-send" data-send aria-label="Envoyer la question">
|
|
56
|
+
<svg aria-hidden="true" viewBox="0 0 24 24" width="18" height="18"
|
|
57
|
+
><path d="M12 19V5M5 12l7-7 7 7" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
|
|
58
|
+
></path></svg
|
|
59
|
+
>
|
|
60
|
+
</button>
|
|
61
|
+
</form>
|
|
62
|
+
<p class="hgdc-panel__note">
|
|
63
|
+
Réponses générées par IA (Claude, d'Anthropic), à vérifier. Les questions sont conservées 90 jours pour améliorer cette documentation.
|
|
64
|
+
</p>
|
|
65
|
+
</section>
|
|
66
|
+
</hgdc-assistant>
|
|
67
|
+
|
|
68
|
+
<script>
|
|
69
|
+
import { defineAssistant } from "../widget/client.ts"
|
|
70
|
+
|
|
71
|
+
defineAssistant()
|
|
72
|
+
</script>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
import Default from "@astrojs/starlight/components/Head.astro"
|
|
3
|
+
|
|
4
|
+
import { markdownPath } from "../core/llms.ts"
|
|
5
|
+
|
|
6
|
+
// llmstxt.org : chaque page annonce sa version Markdown et l'index qui la décrit.
|
|
7
|
+
const markdown = markdownPath(Astro.url.pathname)
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
<Default><slot /></Default>
|
|
11
|
+
<link rel="alternate" type="text/markdown" href={markdown} />
|
|
12
|
+
<link rel="describedby" type="text/markdown" href="/llms.txt" />
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
import Default from "@astrojs/starlight/components/PageFrame.astro"
|
|
3
|
+
import data from "virtual:hgdc-docs/config"
|
|
4
|
+
|
|
5
|
+
import Assistant from "./Assistant.astro"
|
|
6
|
+
|
|
7
|
+
const assistant = data.config.project && data.config.assistant?.enabled !== false
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
<Default>
|
|
11
|
+
<slot name="header" slot="header" />
|
|
12
|
+
<slot name="sidebar" slot="sidebar" />
|
|
13
|
+
<slot />
|
|
14
|
+
</Default>
|
|
15
|
+
{assistant && <Assistant />}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
---
|
|
2
|
+
import Default from "@astrojs/starlight/components/PageTitle.astro"
|
|
3
|
+
import data from "virtual:hgdc-docs/config"
|
|
4
|
+
|
|
5
|
+
import { markdownPath } from "../core/llms.ts"
|
|
6
|
+
|
|
7
|
+
const markdown = markdownPath(Astro.url.pathname)
|
|
8
|
+
const assistant = Boolean(data.config.project) && data.config.assistant?.enabled !== false
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
<div class="hgdc-title">
|
|
12
|
+
<Default><slot /></Default>
|
|
13
|
+
<div class="hgdc-page-actions not-content" data-hgdc-skip data-pagefind-ignore>
|
|
14
|
+
<button type="button" class="hgdc-action" data-hgdc-copy={markdown}>
|
|
15
|
+
<svg aria-hidden="true" viewBox="0 0 24 24" width="16" height="16"
|
|
16
|
+
><rect x="8" y="8" width="12" height="12" rx="3" fill="none" stroke="currentColor" stroke-width="1.8"
|
|
17
|
+
></rect><path d="M16 8V6a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h2" fill="none" stroke="currentColor" stroke-width="1.8"
|
|
18
|
+
></path></svg
|
|
19
|
+
>
|
|
20
|
+
<span data-label>Copier pour un LLM</span>
|
|
21
|
+
</button>
|
|
22
|
+
<details class="hgdc-menu">
|
|
23
|
+
<summary class="hgdc-action hgdc-action--icon" aria-label="Autres formats de la page">
|
|
24
|
+
<svg aria-hidden="true" viewBox="0 0 24 24" width="16" height="16"
|
|
25
|
+
><path d="m6 9 6 6 6-6" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"></path></svg
|
|
26
|
+
>
|
|
27
|
+
</summary>
|
|
28
|
+
<ul class="hgdc-menu__list">
|
|
29
|
+
<li><a href={markdown}>Voir en Markdown</a></li>
|
|
30
|
+
<li><a data-hgdc-open="claude" href="https://claude.ai/new" target="_blank" rel="noopener">Ouvrir dans Claude</a></li>
|
|
31
|
+
<li><a data-hgdc-open="chatgpt" href="https://chatgpt.com/" target="_blank" rel="noopener">Ouvrir dans ChatGPT</a></li>
|
|
32
|
+
<li><a href="/llms.txt">Index pour les agents (llms.txt)</a></li>
|
|
33
|
+
</ul>
|
|
34
|
+
</details>
|
|
35
|
+
{
|
|
36
|
+
assistant && (
|
|
37
|
+
<button type="button" class="hgdc-action" data-hgdc-ask>
|
|
38
|
+
<svg aria-hidden="true" viewBox="0 0 24 24" width="16" height="16">
|
|
39
|
+
<path d="M12 3l1.9 4.6L18.5 9.5l-4.6 1.9L12 16l-1.9-4.6L5.5 9.5l4.6-1.9z" fill="currentColor" />
|
|
40
|
+
</svg>
|
|
41
|
+
<span>Demander à l'assistant</span>
|
|
42
|
+
</button>
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
<span class="sr-only" role="status" aria-live="polite" data-hgdc-copy-status></span>
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
|
|
49
|
+
<script>
|
|
50
|
+
const status = document.querySelector<HTMLElement>("[data-hgdc-copy-status]")
|
|
51
|
+
|
|
52
|
+
for (const button of document.querySelectorAll<HTMLButtonElement>("[data-hgdc-copy]")) {
|
|
53
|
+
const label = button.querySelector<HTMLElement>("[data-label]")!
|
|
54
|
+
button.addEventListener("click", async () => {
|
|
55
|
+
const done = (text: string) => {
|
|
56
|
+
label.textContent = text
|
|
57
|
+
if (status) status.textContent = text
|
|
58
|
+
setTimeout(() => (label.textContent = "Copier pour un LLM"), 2000)
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
const response = await fetch(button.dataset.hgdcCopy!)
|
|
62
|
+
if (!response.ok) throw new Error(String(response.status))
|
|
63
|
+
await navigator.clipboard.writeText(await response.text())
|
|
64
|
+
done("Page copiée")
|
|
65
|
+
} catch {
|
|
66
|
+
done("Copie impossible")
|
|
67
|
+
}
|
|
68
|
+
})
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// « Ouvrir dans… » : l'assistant externe lit la version Markdown de cette page.
|
|
72
|
+
const markdownUrl = new URL(document.querySelector<HTMLElement>("[data-hgdc-copy]")!.dataset.hgdcCopy!, location.href)
|
|
73
|
+
const prompt = `Lis ${markdownUrl.href} et réponds à mes questions sur cette page.`
|
|
74
|
+
for (const link of document.querySelectorAll<HTMLAnchorElement>("[data-hgdc-open]")) {
|
|
75
|
+
const base = link.dataset.hgdcOpen === "claude" ? "https://claude.ai/new" : "https://chatgpt.com/"
|
|
76
|
+
link.href = `${base}?q=${encodeURIComponent(prompt)}`
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Le menu se ferme au clic extérieur et à Échap, comme un menu natif.
|
|
80
|
+
for (const menu of document.querySelectorAll<HTMLDetailsElement>(".hgdc-menu")) {
|
|
81
|
+
document.addEventListener("click", (event) => {
|
|
82
|
+
if (menu.open && !menu.contains(event.target as Node)) menu.open = false
|
|
83
|
+
})
|
|
84
|
+
menu.addEventListener("keydown", (event) => {
|
|
85
|
+
if (event.key === "Escape" && menu.open) {
|
|
86
|
+
menu.open = false
|
|
87
|
+
menu.querySelector("summary")?.focus()
|
|
88
|
+
}
|
|
89
|
+
})
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
for (const button of document.querySelectorAll("[data-hgdc-ask]"))
|
|
93
|
+
button.addEventListener("click", () => document.dispatchEvent(new CustomEvent("hgdc:assistant-open")))
|
|
94
|
+
</script>
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
---
|
|
2
|
+
import Default from "@astrojs/starlight/components/Sidebar.astro"
|
|
3
|
+
import data from "virtual:hgdc-docs/config"
|
|
4
|
+
|
|
5
|
+
import { pagesOf, pageToPath } from "../core/navigation.ts"
|
|
6
|
+
|
|
7
|
+
// Onglets et versions de docs.json, en tête de la sidebar : visibles sur ordinateur et dans
|
|
8
|
+
// le menu mobile, sans toucher à la hauteur de l'en-tête de Starlight.
|
|
9
|
+
const sections = data.tree.versions.flatMap((version) => version.tabs.map((tab) => ({ version, tab })))
|
|
10
|
+
const index = Astro.locals.hgdcSection ?? 0
|
|
11
|
+
const current = sections[index] ?? sections[0]!
|
|
12
|
+
const firstPath = (items: Parameters<typeof pagesOf>[0]) => {
|
|
13
|
+
const first = pagesOf(items)[0]
|
|
14
|
+
return first ? pageToPath(first) : "/"
|
|
15
|
+
}
|
|
16
|
+
const tabs = current.version.tabs.filter((tab) => tab.label)
|
|
17
|
+
const versions = data.tree.versions.filter((version) => version.label)
|
|
18
|
+
const tabPosition = current.version.tabs.indexOf(current.tab)
|
|
19
|
+
const versionHref = (version: (typeof versions)[number]) =>
|
|
20
|
+
firstPath((version.tabs[tabPosition] ?? version.tabs[0]!).items)
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
{
|
|
24
|
+
(tabs.length > 1 || versions.length > 1) && (
|
|
25
|
+
<div class="hgdc-sections">
|
|
26
|
+
{versions.length > 1 && (
|
|
27
|
+
<label class="hgdc-version">
|
|
28
|
+
<span class="sr-only">Version de la documentation</span>
|
|
29
|
+
<select data-hgdc-version>
|
|
30
|
+
{versions.map((version) => (
|
|
31
|
+
<option value={versionHref(version)} selected={version === current.version}>
|
|
32
|
+
{version.label}
|
|
33
|
+
{version.isDefault ? " (actuelle)" : ""}
|
|
34
|
+
</option>
|
|
35
|
+
))}
|
|
36
|
+
</select>
|
|
37
|
+
</label>
|
|
38
|
+
)}
|
|
39
|
+
{tabs.length > 1 && (
|
|
40
|
+
<nav aria-label="Sections de la documentation">
|
|
41
|
+
<ul class="hgdc-tabs">
|
|
42
|
+
{tabs.map((tab) => (
|
|
43
|
+
<li>
|
|
44
|
+
<a href={firstPath(tab.items)} aria-current={tab === current.tab ? "page" : undefined}>
|
|
45
|
+
{tab.label}
|
|
46
|
+
</a>
|
|
47
|
+
</li>
|
|
48
|
+
))}
|
|
49
|
+
</ul>
|
|
50
|
+
</nav>
|
|
51
|
+
)}
|
|
52
|
+
</div>
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
<Default><slot /></Default>
|
|
56
|
+
|
|
57
|
+
<script>
|
|
58
|
+
for (const select of document.querySelectorAll<HTMLSelectElement>("[data-hgdc-version]"))
|
|
59
|
+
select.addEventListener("change", () => window.location.assign(select.value))
|
|
60
|
+
</script>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { defineRouteMiddleware, type StarlightRouteData } from "@astrojs/starlight/route-data"
|
|
2
|
+
import data from "virtual:hgdc-docs/config"
|
|
3
|
+
|
|
4
|
+
type Entry = StarlightRouteData["sidebar"][number]
|
|
5
|
+
type Link = Extract<Entry, { type: "link" }>
|
|
6
|
+
|
|
7
|
+
const containsCurrent = (entry: Entry): boolean =>
|
|
8
|
+
entry.type === "link" ? entry.isCurrent : entry.entries.some(containsCurrent)
|
|
9
|
+
|
|
10
|
+
const links = (entries: Array<Entry>): Array<Link> =>
|
|
11
|
+
entries.flatMap((entry) => (entry.type === "link" ? [entry] : links(entry.entries)))
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Plusieurs versions ou onglets : la sidebar de Starlight en contient une entrée par
|
|
15
|
+
* couple (version, onglet). On ne garde que celle de la page affichée, et la pagination
|
|
16
|
+
* précédent / suivant reste dans cette section.
|
|
17
|
+
*/
|
|
18
|
+
export const onRequest = defineRouteMiddleware((context) => {
|
|
19
|
+
const route = context.locals.starlightRoute
|
|
20
|
+
const sections = data.tree.versions.flatMap((version) => version.tabs)
|
|
21
|
+
if (sections.length > 1) {
|
|
22
|
+
const index = Math.max(route.sidebar.findIndex(containsCurrent), 0)
|
|
23
|
+
const section = route.sidebar[index]
|
|
24
|
+
if (section?.type === "group") route.sidebar = section.entries
|
|
25
|
+
context.locals.hgdcSection = index
|
|
26
|
+
}
|
|
27
|
+
const all = links(route.sidebar)
|
|
28
|
+
const current = all.findIndex((link) => link.isCurrent)
|
|
29
|
+
if (current !== -1 && route.pagination) {
|
|
30
|
+
route.pagination.prev = all[current - 1]
|
|
31
|
+
route.pagination.next = all[current + 1]
|
|
32
|
+
}
|
|
33
|
+
})
|