@hgdc/docs 0.2.0 → 0.4.0

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.
Files changed (233) hide show
  1. package/docs.schema.json +21 -0
  2. package/package.json +3 -1
  3. package/src/build/auto-import.ts +5 -24
  4. package/src/build/content.ts +82 -0
  5. package/src/build/markdown.ts +10 -5
  6. package/src/components/Accordion.astro +3 -1
  7. package/src/components/Callout.astro +31 -0
  8. package/src/components/Card.astro +3 -1
  9. package/src/components/Check.astro +2 -3
  10. package/src/components/CodeGroup.astro +9 -1
  11. package/src/components/Danger.astro +2 -3
  12. package/src/components/Info.astro +2 -3
  13. package/src/components/Note.astro +2 -3
  14. package/src/components/Tip.astro +2 -3
  15. package/src/components/Warning.astro +2 -3
  16. package/src/core/components.ts +166 -0
  17. package/src/core/config.ts +42 -0
  18. package/src/core/feedback-api.ts +27 -0
  19. package/src/core/theme.ts +6 -3
  20. package/src/core/validate.ts +276 -0
  21. package/src/integration.ts +102 -30
  22. package/src/overrides/Assistant.astro +17 -39
  23. package/src/overrides/Footer.astro +107 -0
  24. package/src/overrides/Head.astro +14 -14
  25. package/src/overrides/Header.astro +92 -0
  26. package/src/overrides/Icon.astro +65 -0
  27. package/src/overrides/PageFrame.astro +55 -8
  28. package/src/overrides/PageSidebar.astro +7 -0
  29. package/src/overrides/PageTitle.astro +126 -95
  30. package/src/overrides/Search.astro +54 -0
  31. package/src/overrides/Sidebar.astro +105 -60
  32. package/src/overrides/SidebarList.astro +52 -0
  33. package/src/overrides/SiteFooter.astro +51 -0
  34. package/src/overrides/ThemeProvider.astro +23 -0
  35. package/src/overrides/ThemeToggle.astro +90 -0
  36. package/src/overrides/TwoColumnContent.astro +45 -0
  37. package/src/overrides/sections.ts +29 -0
  38. package/src/styles/theme.css +2137 -511
  39. package/src/widget/client.ts +51 -19
  40. package/src/widget/code.ts +218 -0
  41. package/src/widget/excerpt.ts +5 -0
  42. package/src/widget/search.ts +222 -0
  43. package/template/.astro/collections/docs.schema.json +3 -0
  44. package/template/.astro/content-modules.mjs +35 -15
  45. package/template/.astro/data-store.json +1 -0
  46. package/template/.astro/settings.json +5 -0
  47. package/template/.build/.well-known/llms.txt +51 -34
  48. package/template/.build/404.html +16 -42
  49. package/template/.build/_astro/Assistant.astro_astro_type_script_index_0_lang.C1pz7yK1.js +4 -0
  50. package/template/.build/_astro/CodeGroup.astro_astro_type_script_index_0_lang.-eM8maAX.js +1 -0
  51. package/template/.build/_astro/Footer.astro_astro_type_script_index_0_lang.DzCnn3OT.js +1 -0
  52. package/template/.build/_astro/Search.astro_astro_type_script_index_0_lang.D7EcezBU.js +1 -0
  53. package/template/.build/_astro/code.smqv0zhe.js +9 -0
  54. package/template/.build/_astro/common.Dx3aBIS0.css +3 -0
  55. package/template/.build/_astro/{ec.8nqtp.css → ec.t9qpe.css} +1 -1
  56. package/template/.build/_astro/logo.BPu921Tp.svg +1 -0
  57. package/template/.build/_hgdc/corpus.json +1 -1
  58. package/template/.build/concepts/confidentialite/index.html +50 -0
  59. package/template/.build/concepts/confidentialite.md +38 -0
  60. package/template/.build/concepts/deploiements/index.html +106 -0
  61. package/template/.build/concepts/deploiements.md +70 -0
  62. package/template/.build/concepts/organisations-et-roles/index.html +83 -0
  63. package/template/.build/concepts/organisations-et-roles.md +39 -0
  64. package/template/.build/concepts/preversion/index.html +49 -0
  65. package/template/.build/concepts/preversion.md +52 -0
  66. package/template/.build/concepts/secrets-et-coffre/index.html +70 -0
  67. package/template/.build/concepts/secrets-et-coffre.md +54 -0
  68. package/template/.build/demarrer/depuis-github/index.html +34 -0
  69. package/template/.build/demarrer/depuis-github.md +68 -0
  70. package/template/.build/demarrer/premier-site/index.html +28 -0
  71. package/template/.build/demarrer/premier-site.md +94 -0
  72. package/template/.build/guides/brancher-un-domaine/index.html +91 -0
  73. package/template/.build/guides/brancher-un-domaine.md +81 -0
  74. package/template/.build/guides/cle-d-api/index.html +62 -0
  75. package/template/.build/guides/cle-d-api.md +78 -0
  76. package/template/.build/guides/depannage/index.html +64 -0
  77. package/template/.build/guides/depannage.md +69 -0
  78. package/template/.build/guides/deployer-depuis-github/index.html +45 -0
  79. package/template/.build/guides/deployer-depuis-github.md +70 -0
  80. package/template/.build/guides/deployer-un-framework/index.html +81 -0
  81. package/template/.build/guides/deployer-un-framework.md +74 -0
  82. package/template/.build/guides/github-actions-sans-application/index.html +53 -0
  83. package/template/.build/guides/github-actions-sans-application.md +95 -0
  84. package/template/.build/guides/inviter-une-personne/index.html +28 -0
  85. package/template/.build/guides/inviter-une-personne.md +38 -0
  86. package/template/.build/guides/lire-les-logs/index.html +37 -0
  87. package/template/.build/guides/lire-les-logs.md +48 -0
  88. package/template/.build/guides/migrer-depuis-vercel/index.html +25 -0
  89. package/template/.build/guides/migrer-depuis-vercel.md +51 -0
  90. package/template/.build/guides/monorepo/index.html +40 -0
  91. package/template/.build/guides/monorepo.md +42 -0
  92. package/template/.build/guides/revenir-en-arriere/index.html +39 -0
  93. package/template/.build/guides/revenir-en-arriere.md +50 -0
  94. package/template/.build/guides/site-statique/index.html +41 -0
  95. package/template/.build/guides/site-statique.md +59 -0
  96. package/template/.build/guides/synchroniser-doppler/index.html +46 -0
  97. package/template/.build/guides/synchroniser-doppler.md +63 -0
  98. package/template/.build/guides/variables-d-environnement/index.html +73 -0
  99. package/template/.build/guides/variables-d-environnement.md +92 -0
  100. package/template/.build/images/famille/account.svg +1 -0
  101. package/template/.build/images/famille/analytics.svg +1 -0
  102. package/template/.build/images/famille/docs.svg +1 -0
  103. package/template/.build/images/famille/host.svg +1 -0
  104. package/template/.build/images/famille/mail.svg +1 -0
  105. package/template/.build/images/famille/portal.svg +1 -0
  106. package/template/.build/images/famille/status.svg +1 -0
  107. package/template/.build/images/logo.svg +1 -0
  108. package/template/.build/index.html +30 -86
  109. package/template/.build/index.md +59 -21
  110. package/template/.build/llms-full.txt +2330 -518
  111. package/template/.build/llms.txt +51 -34
  112. package/template/.build/pagefind/fragment/fr-ch_16987e2.pf_fragment +0 -0
  113. package/template/.build/pagefind/fragment/fr-ch_2b4c97b.pf_fragment +0 -0
  114. package/template/.build/pagefind/fragment/fr-ch_3294a5c.pf_fragment +0 -0
  115. package/template/.build/pagefind/fragment/fr-ch_34858c9.pf_fragment +0 -0
  116. package/template/.build/pagefind/fragment/fr-ch_39ddba1.pf_fragment +0 -0
  117. package/template/.build/pagefind/fragment/fr-ch_4ab1899.pf_fragment +0 -0
  118. package/template/.build/pagefind/fragment/fr-ch_4d3a52a.pf_fragment +0 -0
  119. package/template/.build/pagefind/fragment/fr-ch_506c187.pf_fragment +0 -0
  120. package/template/.build/pagefind/fragment/fr-ch_5aa255a.pf_fragment +0 -0
  121. package/template/.build/pagefind/fragment/fr-ch_6841c7d.pf_fragment +0 -0
  122. package/template/.build/pagefind/fragment/fr-ch_6dc7a2d.pf_fragment +0 -0
  123. package/template/.build/pagefind/fragment/fr-ch_847edff.pf_fragment +0 -0
  124. package/template/.build/pagefind/fragment/fr-ch_8745af7.pf_fragment +0 -0
  125. package/template/.build/pagefind/fragment/fr-ch_8aac5e6.pf_fragment +0 -0
  126. package/template/.build/pagefind/fragment/fr-ch_916b233.pf_fragment +0 -0
  127. package/template/.build/pagefind/fragment/fr-ch_9fd014a.pf_fragment +0 -0
  128. package/template/.build/pagefind/fragment/fr-ch_a0808f8.pf_fragment +0 -0
  129. package/template/.build/pagefind/fragment/fr-ch_a4e1f69.pf_fragment +0 -0
  130. package/template/.build/pagefind/fragment/fr-ch_a80a53d.pf_fragment +0 -0
  131. package/template/.build/pagefind/fragment/fr-ch_a9e725b.pf_fragment +0 -0
  132. package/template/.build/pagefind/fragment/fr-ch_ac3f64c.pf_fragment +0 -0
  133. package/template/.build/pagefind/fragment/fr-ch_af292f1.pf_fragment +0 -0
  134. package/template/.build/pagefind/fragment/fr-ch_af3e716.pf_fragment +0 -0
  135. package/template/.build/pagefind/fragment/fr-ch_b0689a4.pf_fragment +0 -0
  136. package/template/.build/pagefind/fragment/fr-ch_baed996.pf_fragment +0 -0
  137. package/template/.build/pagefind/fragment/fr-ch_c89e27a.pf_fragment +0 -0
  138. package/template/.build/pagefind/fragment/fr-ch_cd6ca13.pf_fragment +0 -0
  139. package/template/.build/pagefind/fragment/fr-ch_d09af09.pf_fragment +0 -0
  140. package/template/.build/pagefind/fragment/fr-ch_d4bcea8.pf_fragment +0 -0
  141. package/template/.build/pagefind/fragment/fr-ch_dc2a134.pf_fragment +0 -0
  142. package/template/.build/pagefind/fragment/fr-ch_ded7ddc.pf_fragment +0 -0
  143. package/template/.build/pagefind/fragment/fr-ch_e2528cb.pf_fragment +0 -0
  144. package/template/.build/pagefind/fragment/fr-ch_e3cbb4e.pf_fragment +0 -0
  145. package/template/.build/pagefind/fragment/fr-ch_e7902d9.pf_fragment +0 -0
  146. package/template/.build/pagefind/fragment/fr-ch_f48ca47.pf_fragment +0 -0
  147. package/template/.build/pagefind/index/fr-ch_8a5f905.pf_index +0 -0
  148. package/template/.build/pagefind/index/fr-ch_fbd9c96.pf_index +0 -0
  149. package/template/.build/pagefind/pagefind-entry.json +1 -1
  150. package/template/.build/pagefind/pagefind.fr-ch_211aacbfa7f0e.pf_meta +0 -0
  151. package/template/.build/reference/api/deploiements/index.html +326 -0
  152. package/template/.build/reference/api/deploiements.md +170 -0
  153. package/template/.build/reference/api/erreurs/index.html +102 -0
  154. package/template/.build/reference/api/erreurs.md +50 -0
  155. package/template/.build/reference/api/fichiers/index.html +82 -0
  156. package/template/.build/reference/api/fichiers.md +62 -0
  157. package/template/.build/reference/api/introduction/index.html +126 -0
  158. package/template/.build/reference/api/introduction.md +84 -0
  159. package/template/.build/reference/api/projets/index.html +184 -0
  160. package/template/.build/reference/api/projets.md +102 -0
  161. package/template/.build/reference/api/utilisateur/index.html +96 -0
  162. package/template/.build/reference/api/utilisateur.md +89 -0
  163. package/template/.build/reference/api/variables/index.html +75 -0
  164. package/template/.build/reference/api/variables.md +58 -0
  165. package/template/.build/reference/cli/index.html +165 -82
  166. package/template/.build/reference/cli.md +159 -23
  167. package/template/.build/reference/compatibilite/index.html +196 -0
  168. package/template/.build/reference/compatibilite.md +64 -0
  169. package/template/.build/reference/domaines/index.html +171 -0
  170. package/template/.build/reference/domaines.md +76 -0
  171. package/template/.build/reference/limites/index.html +159 -0
  172. package/template/.build/reference/limites.md +56 -0
  173. package/template/.build/reference/variables/index.html +164 -0
  174. package/template/.build/reference/variables.md +63 -0
  175. package/template/.build/reference/vercel-json/index.html +65 -0
  176. package/template/.build/reference/vercel-json.md +51 -0
  177. package/template/node_modules/.vite/deps/_metadata.json +8 -8
  178. package/template/src/content.config.ts +8 -2
  179. package/template/.build/_astro/Assistant.astro_astro_type_script_index_0_lang.DWgmlr85.js +0 -4
  180. package/template/.build/_astro/MobileTableOfContents.astro_astro_type_script_index_0_lang.C3fM4Kra.js +0 -1
  181. package/template/.build/_astro/Search.astro_astro_type_script_index_0_lang.Cn3-p701.js +0 -1
  182. package/template/.build/_astro/TableOfContents.astro_astro_type_script_index_0_lang.BpJaGz7G.js +0 -1
  183. package/template/.build/_astro/common.C-MpX45O.css +0 -3
  184. package/template/.build/_astro/logo-clair.BqNh6JNi.svg +0 -1
  185. package/template/.build/_astro/logo-sombre.BkgsFXxh.svg +0 -1
  186. package/template/.build/_astro/starlight-toc.D4UDIxQL.js +0 -1
  187. package/template/.build/_astro/ui-core.CrG20ya_.js +0 -2
  188. package/template/.build/demarrer/premiere-doc/index.html +0 -84
  189. package/template/.build/demarrer/premiere-doc.md +0 -65
  190. package/template/.build/explications/donnees/index.html +0 -114
  191. package/template/.build/explications/donnees.md +0 -28
  192. package/template/.build/explications/fonctionnement/index.html +0 -93
  193. package/template/.build/explications/fonctionnement.md +0 -32
  194. package/template/.build/guides/agents/index.html +0 -118
  195. package/template/.build/guides/agents.md +0 -54
  196. package/template/.build/guides/assistant/index.html +0 -119
  197. package/template/.build/guides/assistant.md +0 -46
  198. package/template/.build/guides/composants/index.html +0 -97
  199. package/template/.build/guides/composants.md +0 -101
  200. package/template/.build/guides/ecrire/index.html +0 -116
  201. package/template/.build/guides/ecrire.md +0 -63
  202. package/template/.build/guides/navigation/index.html +0 -90
  203. package/template/.build/guides/navigation.md +0 -63
  204. package/template/.build/guides/publier/index.html +0 -95
  205. package/template/.build/guides/publier.md +0 -65
  206. package/template/.build/guides/theme/index.html +0 -87
  207. package/template/.build/guides/theme.md +0 -33
  208. package/template/.build/images/favicon.svg +0 -1
  209. package/template/.build/images/logo-clair.svg +0 -1
  210. package/template/.build/images/logo-sombre.svg +0 -1
  211. package/template/.build/pagefind/fragment/fr-ch_2a431a9.pf_fragment +0 -0
  212. package/template/.build/pagefind/fragment/fr-ch_456586a.pf_fragment +0 -0
  213. package/template/.build/pagefind/fragment/fr-ch_4f8575c.pf_fragment +0 -0
  214. package/template/.build/pagefind/fragment/fr-ch_68e734b.pf_fragment +0 -0
  215. package/template/.build/pagefind/fragment/fr-ch_72b91f4.pf_fragment +0 -0
  216. package/template/.build/pagefind/fragment/fr-ch_8f256dd.pf_fragment +0 -0
  217. package/template/.build/pagefind/fragment/fr-ch_95a15cc.pf_fragment +0 -0
  218. package/template/.build/pagefind/fragment/fr-ch_aa80c55.pf_fragment +0 -0
  219. package/template/.build/pagefind/fragment/fr-ch_ab79738.pf_fragment +0 -0
  220. package/template/.build/pagefind/fragment/fr-ch_cfa4336.pf_fragment +0 -0
  221. package/template/.build/pagefind/fragment/fr-ch_dab8f5e.pf_fragment +0 -0
  222. package/template/.build/pagefind/fragment/fr-ch_de60b43.pf_fragment +0 -0
  223. package/template/.build/pagefind/fragment/fr-ch_e02aa49.pf_fragment +0 -0
  224. package/template/.build/pagefind/fragment/fr-ch_e254a54.pf_fragment +0 -0
  225. package/template/.build/pagefind/fragment/fr-ch_e2c46e3.pf_fragment +0 -0
  226. package/template/.build/pagefind/index/fr-ch_a71411b.pf_index +0 -0
  227. package/template/.build/pagefind/pagefind.fr-ch_2f3ea8fccf961.pf_meta +0 -0
  228. package/template/.build/reference/api-assistant/index.html +0 -161
  229. package/template/.build/reference/api-assistant.md +0 -70
  230. package/template/.build/reference/docs-json/index.html +0 -192
  231. package/template/.build/reference/docs-json.md +0 -43
  232. package/template/.build/reference/mcp/index.html +0 -151
  233. package/template/.build/reference/mcp.md +0 -49
package/docs.schema.json CHANGED
@@ -248,6 +248,27 @@
248
248
  }
249
249
  }
250
250
  },
251
+ "redirects": {
252
+ "maxItems": 500,
253
+ "type": "array",
254
+ "items": {
255
+ "type": "object",
256
+ "properties": {
257
+ "source": {
258
+ "type": "string",
259
+ "pattern": "^\\/(?!\\/)[A-Za-z0-9._~\\-/%]*$"
260
+ },
261
+ "destination": {
262
+ "type": "string",
263
+ "minLength": 1
264
+ },
265
+ "permanent": {
266
+ "type": "boolean"
267
+ }
268
+ },
269
+ "required": ["source", "destination"]
270
+ }
271
+ },
251
272
  "assistant": {
252
273
  "type": "object",
253
274
  "properties": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hgdc/docs",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "Modèle de site de HGDC Docs : documentation MDX (docs.json) construite avec Astro Starlight, recherche, versions, llms.txt et assistant.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
@@ -31,6 +31,7 @@
31
31
  "@astrojs/starlight": "0.42.6",
32
32
  "@fontsource-variable/geist": "5.3.0",
33
33
  "@fontsource-variable/inter": "5.3.0",
34
+ "@mdx-js/mdx": "3.1.1",
34
35
  "astro": "7.3.8",
35
36
  "github-slugger": "2.0.0",
36
37
  "hast-util-select": "6.0.4",
@@ -41,6 +42,7 @@
41
42
  "remark-stringify": "11.0.0",
42
43
  "unified": "11.0.5",
43
44
  "vfile": "6.0.3",
45
+ "yaml": "2.9.1",
44
46
  "zod": "4.6.5"
45
47
  },
46
48
  "devDependencies": {
@@ -1,30 +1,7 @@
1
1
  import type { Root } from "mdast"
2
2
  import type { VFile } from "vfile"
3
3
 
4
- /**
5
- * Composants disponibles dans chaque page MDX sans `import`, comme chez Mintlify
6
- * (`<Note>`, `<Steps>`, `<Card>`…). Le plugin ajoute en tête du fichier l'import des
7
- * composants que la page n'importe pas déjà elle-même.
8
- */
9
- export const MDX_COMPONENTS = [
10
- "Accordion",
11
- "AccordionGroup",
12
- "Card",
13
- "CardGroup",
14
- "Check",
15
- "CodeGroup",
16
- "Columns",
17
- "Danger",
18
- "Frame",
19
- "Info",
20
- "Note",
21
- "Step",
22
- "Steps",
23
- "Tab",
24
- "Tabs",
25
- "Tip",
26
- "Warning",
27
- ] as const
4
+ import { MDX_COMPONENTS } from "../core/components.ts"
28
5
 
29
6
  const SOURCE = "@hgdc/docs/components"
30
7
 
@@ -64,6 +41,10 @@ function importNode(name: string): EsmNode {
64
41
  }
65
42
  }
66
43
 
44
+ /**
45
+ * Ajoute en tête de chaque page MDX l'import des composants (`core/components.ts`) que la
46
+ * page n'importe pas déjà elle-même : `<Note>`, `<Steps>`… s'écrivent sans `import`.
47
+ */
67
48
  export function remarkAutoImport() {
68
49
  return (tree: Root, file: VFile) => {
69
50
  if (!String(file.path ?? file.history.at(-1) ?? "").endsWith(".mdx")) return
@@ -0,0 +1,82 @@
1
+ import type { Element, ElementContent, Root, RootContent } from "hast"
2
+
3
+ /**
4
+ * Mise en forme du contenu, au build.
5
+ *
6
+ * Titre d'endpoint (`## GET /v9/projects`) en barre de méthode : la méthode en pastille
7
+ * colorée, le chemin en police mono, ses paramètres (`:id`, `{id}`) soulignés. Le texte du
8
+ * titre ne change pas : l'ancre, la table des matières et la recherche restent les mêmes.
9
+ * Tableau : enveloppé dans `.hd-table`, qui défile en largeur sans déborder de la colonne.
10
+ */
11
+ const ENDPOINT = /^(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)\s+(\/\S*)$/
12
+ const PARAM = /(:[A-Za-z_][\w]*|\{[^}/]+\})/
13
+
14
+ export function parseEndpoint(text: string): { method: string; path: string } | null {
15
+ const match = ENDPOINT.exec(text.trim())
16
+ return match ? { method: match[1]!, path: match[2]! } : null
17
+ }
18
+
19
+ function textOf(node: Element): string | null {
20
+ let text = ""
21
+ for (const child of node.children) {
22
+ if (child.type === "text") text += child.value
23
+ else if (child.type === "element" && child.tagName === "code") {
24
+ for (const inner of child.children) if (inner.type === "text") text += inner.value
25
+ } else return null
26
+ }
27
+ return text
28
+ }
29
+
30
+ function pathChildren(path: string): Array<ElementContent> {
31
+ return path
32
+ .split(PARAM)
33
+ .filter(Boolean)
34
+ .map((part) =>
35
+ PARAM.test(part)
36
+ ? {
37
+ type: "element",
38
+ tagName: "span",
39
+ properties: { className: ["hd-path__param"] },
40
+ children: [{ type: "text", value: part }],
41
+ }
42
+ : { type: "text", value: part },
43
+ )
44
+ }
45
+
46
+ export function rehypeDocsContent() {
47
+ const walk = (nodes: Array<RootContent | ElementContent>) => {
48
+ for (const [index, node] of nodes.entries()) {
49
+ if (node.type !== "element") continue
50
+ // Tableau : un cadre qui défile seul en largeur, le tableau garde toute la largeur.
51
+ if (node.tagName === "table") {
52
+ nodes[index] = { type: "element", tagName: "div", properties: { className: ["hd-table"] }, children: [node] }
53
+ continue
54
+ }
55
+ if (node.tagName !== "h2" && node.tagName !== "h3") {
56
+ walk(node.children)
57
+ continue
58
+ }
59
+ const text = textOf(node)
60
+ const endpoint = text ? parseEndpoint(text) : null
61
+ if (!endpoint) continue
62
+ node.properties.className = [...((node.properties.className as Array<string>) ?? []), "hd-endpoint"]
63
+ node.children = [
64
+ {
65
+ type: "element",
66
+ tagName: "span",
67
+ properties: { className: ["hd-method"], dataMethod: endpoint.method.toLowerCase() },
68
+ children: [{ type: "text", value: endpoint.method }],
69
+ },
70
+ { type: "text", value: " " },
71
+ {
72
+ type: "element",
73
+ tagName: "code",
74
+ properties: { className: ["hd-path"] },
75
+ children: pathChildren(endpoint.path),
76
+ },
77
+ ]
78
+ }
79
+ }
80
+ // Typé en `unknown` : Astro et ce paquet ne résolvent pas toujours le même @types/hast.
81
+ return (tree: unknown) => walk((tree as Root).children)
82
+ }
@@ -70,12 +70,17 @@ function replaceCodeBlocks(content: Element): void {
70
70
  }
71
71
  }
72
72
 
73
- /** Encadré → citation dont la première ligne porte le titre en gras : `> **Note**`. */
73
+ /**
74
+ * Encadré → citation dont la première ligne porte le titre en gras : `> **Note**`. Le titre
75
+ * est celui de la page, sinon le nom du type (`aria-label` de `.hd-callout`).
76
+ */
74
77
  function replaceAsides(content: Element): void {
75
- for (const aside of selectAll("aside.starlight-aside", content)) {
76
- const title = select(".starlight-aside__title", aside)
77
- const body = select(".starlight-aside__content", aside)
78
- const label = title ? toString(title as Parameters<typeof toString>[0]).trim() : ""
78
+ for (const aside of selectAll("aside.starlight-aside, aside.hd-callout", content)) {
79
+ const title = select(".starlight-aside__title, .hd-callout__title", aside)
80
+ const body = select(".starlight-aside__content, .hd-callout__body", aside)
81
+ if (body && title) body.children = body.children.filter((child) => child !== title)
82
+ const named = typeof aside.properties["ariaLabel"] === "string" ? aside.properties["ariaLabel"] : ""
83
+ const label = title ? toString(title as Parameters<typeof toString>[0]).trim() : named
79
84
  const quote: Element = {
80
85
  type: "element",
81
86
  tagName: "blockquote",
@@ -1,4 +1,6 @@
1
1
  ---
2
+ import Icon from "../overrides/Icon.astro"
3
+
2
4
  interface Props {
3
5
  title: string
4
6
  defaultOpen?: boolean
@@ -7,6 +9,6 @@ const { title, defaultOpen = false } = Astro.props
7
9
  ---
8
10
 
9
11
  <details class="hgdc-accordion" open={defaultOpen}>
10
- <summary>{title}</summary>
12
+ <summary><Icon name="chevron-right" size={16} class="hgdc-accordion__chevron" /><span>{title}</span></summary>
11
13
  <div class="hgdc-accordion__body"><slot /></div>
12
14
  </details>
@@ -0,0 +1,31 @@
1
+ ---
2
+ import Icon from "../overrides/Icon.astro"
3
+
4
+ /**
5
+ * Encadré commun à Note, Info, Tip, Check, Warning et Danger : un lavis de sa couleur, un
6
+ * filet de la même teinte et son icône. Le type est dit au lecteur d'écran.
7
+ */
8
+ export type CalloutTone = "note" | "info" | "tip" | "check" | "warning" | "danger"
9
+ interface Props {
10
+ tone: CalloutTone
11
+ title?: string | undefined
12
+ }
13
+ const LABELS: Record<CalloutTone, string> = {
14
+ note: "Note",
15
+ info: "Info",
16
+ tip: "Astuce",
17
+ check: "Validé",
18
+ warning: "Attention",
19
+ danger: "Danger",
20
+ }
21
+ const ICONS = { note: "note", info: "info", tip: "tip", check: "circle-check", warning: "warning", danger: "danger" } as const
22
+ const { tone, title } = Astro.props
23
+ ---
24
+
25
+ <aside class="hd-callout" data-tone={tone} aria-label={title ?? LABELS[tone]}>
26
+ <Icon name={ICONS[tone]} class="hd-callout__icon" />
27
+ <div class="hd-callout__body">
28
+ {title && <p class="hd-callout__title">{title}</p>}
29
+ <slot />
30
+ </div>
31
+ </aside>
@@ -1,4 +1,6 @@
1
1
  ---
2
+ import Icon from "../overrides/Icon.astro"
3
+
2
4
  interface Props {
3
5
  title: string
4
6
  href?: string
@@ -10,6 +12,6 @@ const Tag = href ? "a" : "div"
10
12
  ---
11
13
 
12
14
  <Tag class:list={["hgdc-card", { "hgdc-card--link": href }]} href={href}>
13
- <span class="hgdc-card__title">{title}</span>
15
+ <span class="hgdc-card__title">{title}{href && <Icon name="arrow-right" size={16} class="hgdc-card__arrow" />}</span>
14
16
  <div class="hgdc-card__body"><slot /></div>
15
17
  </Tag>
@@ -1,10 +1,9 @@
1
1
  ---
2
- import { Aside } from "@astrojs/starlight/components"
2
+ import Callout from "./Callout.astro"
3
3
 
4
4
  interface Props {
5
5
  title?: string
6
6
  }
7
- const { title } = Astro.props
8
7
  ---
9
8
 
10
- <Aside type="tip" title={title}><slot /></Aside>
9
+ <Callout tone="check" title={Astro.props.title}><slot /></Callout>
@@ -1,4 +1,12 @@
1
1
  ---
2
+ // Plusieurs blocs de code en onglets, nommés d'après le titre de chaque bloc (```bash npm).
3
+ // Sans JavaScript, seul le premier bloc s'affiche ; le script ajoute les onglets.
2
4
  ---
3
5
 
4
- <div class="hgdc-code-group"><slot /></div>
6
+ <hd-code-group class="hgdc-code-group not-content"><slot /></hd-code-group>
7
+
8
+ <script>
9
+ import { defineCodeGroup } from "../widget/code.ts"
10
+
11
+ defineCodeGroup()
12
+ </script>
@@ -1,10 +1,9 @@
1
1
  ---
2
- import { Aside } from "@astrojs/starlight/components"
2
+ import Callout from "./Callout.astro"
3
3
 
4
4
  interface Props {
5
5
  title?: string
6
6
  }
7
- const { title } = Astro.props
8
7
  ---
9
8
 
10
- <Aside type="danger" title={title}><slot /></Aside>
9
+ <Callout tone="danger" title={Astro.props.title}><slot /></Callout>
@@ -1,10 +1,9 @@
1
1
  ---
2
- import { Aside } from "@astrojs/starlight/components"
2
+ import Callout from "./Callout.astro"
3
3
 
4
4
  interface Props {
5
5
  title?: string
6
6
  }
7
- const { title = "Info" } = Astro.props
8
7
  ---
9
8
 
10
- <Aside type="note" title={title}><slot /></Aside>
9
+ <Callout tone="info" title={Astro.props.title}><slot /></Callout>
@@ -1,10 +1,9 @@
1
1
  ---
2
- import { Aside } from "@astrojs/starlight/components"
2
+ import Callout from "./Callout.astro"
3
3
 
4
4
  interface Props {
5
5
  title?: string
6
6
  }
7
- const { title } = Astro.props
8
7
  ---
9
8
 
10
- <Aside type="note" title={title}><slot /></Aside>
9
+ <Callout tone="note" title={Astro.props.title}><slot /></Callout>
@@ -1,10 +1,9 @@
1
1
  ---
2
- import { Aside } from "@astrojs/starlight/components"
2
+ import Callout from "./Callout.astro"
3
3
 
4
4
  interface Props {
5
5
  title?: string
6
6
  }
7
- const { title } = Astro.props
8
7
  ---
9
8
 
10
- <Aside type="tip" title={title}><slot /></Aside>
9
+ <Callout tone="tip" title={Astro.props.title}><slot /></Callout>
@@ -1,10 +1,9 @@
1
1
  ---
2
- import { Aside } from "@astrojs/starlight/components"
2
+ import Callout from "./Callout.astro"
3
3
 
4
4
  interface Props {
5
5
  title?: string
6
6
  }
7
- const { title } = Astro.props
8
7
  ---
9
8
 
10
- <Aside type="caution" title={title}><slot /></Aside>
9
+ <Callout tone="warning" title={Astro.props.title}><slot /></Callout>
@@ -0,0 +1,166 @@
1
+ /**
2
+ * Composants disponibles dans chaque page MDX sans `import`, comme chez Mintlify
3
+ * (`<Note>`, `<Steps>`, `<Card>`…). Lus par le build (`build/auto-import.ts`) et par
4
+ * l'éditeur de la console (validation, insertion par « / » ou « < », mode visuel).
5
+ * Les props décrites ici sont celles des fichiers `src/components/*.astro`.
6
+ */
7
+ export const MDX_COMPONENTS = [
8
+ "Accordion",
9
+ "AccordionGroup",
10
+ "Card",
11
+ "CardGroup",
12
+ "Check",
13
+ "CodeGroup",
14
+ "Columns",
15
+ "Danger",
16
+ "Frame",
17
+ "Info",
18
+ "Note",
19
+ "Step",
20
+ "Steps",
21
+ "Tab",
22
+ "Tabs",
23
+ "Tip",
24
+ "Warning",
25
+ ] as const
26
+
27
+ export type MdxComponent = (typeof MDX_COMPONENTS)[number]
28
+
29
+ export function isMdxComponent(name: string): name is MdxComponent {
30
+ return (MDX_COMPONENTS as ReadonlyArray<string>).includes(name)
31
+ }
32
+
33
+ export type ComponentProp = {
34
+ name: string
35
+ label: string
36
+ type: "string" | "number" | "boolean"
37
+ required?: boolean
38
+ }
39
+
40
+ /** Props de chaque composant, pour les formulaires du mode visuel. */
41
+ export const COMPONENT_PROPS: Record<MdxComponent, ReadonlyArray<ComponentProp>> = {
42
+ Accordion: [
43
+ { name: "title", label: "Titre", type: "string", required: true },
44
+ { name: "defaultOpen", label: "Ouvert au départ", type: "boolean" },
45
+ ],
46
+ AccordionGroup: [],
47
+ Card: [
48
+ { name: "title", label: "Titre", type: "string", required: true },
49
+ { name: "href", label: "Lien", type: "string" },
50
+ ],
51
+ CardGroup: [{ name: "cols", label: "Colonnes", type: "number" }],
52
+ Check: [{ name: "title", label: "Titre", type: "string" }],
53
+ CodeGroup: [],
54
+ Columns: [{ name: "cols", label: "Colonnes", type: "number" }],
55
+ Danger: [{ name: "title", label: "Titre", type: "string" }],
56
+ Frame: [{ name: "caption", label: "Légende", type: "string" }],
57
+ Info: [{ name: "title", label: "Titre", type: "string" }],
58
+ Note: [{ name: "title", label: "Titre", type: "string" }],
59
+ Step: [{ name: "title", label: "Titre", type: "string", required: true }],
60
+ Steps: [],
61
+ Tab: [{ name: "title", label: "Titre", type: "string", required: true }],
62
+ Tabs: [{ name: "syncKey", label: "Clé de synchronisation", type: "string" }],
63
+ Tip: [{ name: "title", label: "Titre", type: "string" }],
64
+ Warning: [{ name: "title", label: "Titre", type: "string" }],
65
+ }
66
+
67
+ export type BlockCategory = "Encadrés" | "Mise en page" | "Contenu"
68
+
69
+ /**
70
+ * Blocs proposés à l'insertion, dans l'ordre du menu. `snippet` est le MDX inséré ; `$|`
71
+ * marque l'endroit où placer le curseur. Plusieurs blocs peuvent produire le même
72
+ * composant (colonnes à 2, 3 ou 4).
73
+ */
74
+ export type Block = {
75
+ id: string
76
+ label: string
77
+ description: string
78
+ category: BlockCategory
79
+ keywords: ReadonlyArray<string>
80
+ snippet: string
81
+ }
82
+
83
+ const callout = (name: string, label: string, description: string, keywords: Array<string>): Block => ({
84
+ id: name.toLowerCase(),
85
+ label,
86
+ description,
87
+ category: "Encadrés",
88
+ keywords: [name.toLowerCase(), ...keywords],
89
+ snippet: `<${name}>\n$|\n</${name}>`,
90
+ })
91
+
92
+ const columns = (cols: number): Block => ({
93
+ id: `columns-${cols}`,
94
+ label: `${cols} colonnes`,
95
+ description: `Cartes côte à côte, sur ${cols} colonnes.`,
96
+ category: "Mise en page",
97
+ keywords: ["colonnes", "columns", "grille", String(cols)],
98
+ snippet: `<Columns cols={${cols}}>\n${Array.from({ length: cols }, (_, i) => ` <Card title="Carte ${i + 1}">${i === 0 ? "$|" : ""}</Card>`).join("\n")}\n</Columns>`,
99
+ })
100
+
101
+ export const BLOCKS: ReadonlyArray<Block> = [
102
+ callout("Note", "Note", "Une information utile à côté du texte.", ["information", "remarque"]),
103
+ callout("Tip", "Conseil", "Une astuce qui fait gagner du temps.", ["astuce", "conseil"]),
104
+ callout("Info", "Info", "Un contexte ou une précision.", ["information"]),
105
+ callout("Warning", "Attention", "Un point à ne pas manquer.", ["attention", "avertissement"]),
106
+ callout("Danger", "Danger", "Une action risquée ou irréversible.", ["danger", "risque"]),
107
+ callout("Check", "Validé", "Une étape réussie, un résultat attendu.", ["validé", "succès", "ok"]),
108
+ columns(2),
109
+ columns(3),
110
+ columns(4),
111
+ {
112
+ id: "steps",
113
+ label: "Étapes",
114
+ description: "Une marche à suivre numérotée.",
115
+ category: "Mise en page",
116
+ keywords: ["étapes", "steps", "procédure"],
117
+ snippet:
118
+ '<Steps>\n <Step title="Première étape">\n $|\n </Step>\n <Step title="Deuxième étape">\n </Step>\n</Steps>',
119
+ },
120
+ {
121
+ id: "tabs",
122
+ label: "Onglets",
123
+ description: "Plusieurs variantes d'un même contenu.",
124
+ category: "Mise en page",
125
+ keywords: ["onglets", "tabs"],
126
+ snippet: '<Tabs>\n <Tab title="Premier">\n $|\n </Tab>\n <Tab title="Second">\n </Tab>\n</Tabs>',
127
+ },
128
+ {
129
+ id: "accordion",
130
+ label: "Accordéon",
131
+ description: "Un détail replié, ouvert au clic.",
132
+ category: "Mise en page",
133
+ keywords: ["accordéon", "accordion", "replié", "faq"],
134
+ snippet: '<AccordionGroup>\n <Accordion title="Question">\n $|\n </Accordion>\n</AccordionGroup>',
135
+ },
136
+ {
137
+ id: "card",
138
+ label: "Carte",
139
+ description: "Un lien mis en avant, avec un titre.",
140
+ category: "Contenu",
141
+ keywords: ["carte", "card", "lien"],
142
+ snippet: '<Card title="Titre" href="/">\n $|\n</Card>',
143
+ },
144
+ {
145
+ id: "code-group",
146
+ label: "Groupe de code",
147
+ description: "Le même exemple en plusieurs langages.",
148
+ category: "Contenu",
149
+ keywords: ["code", "codegroup", "exemple"],
150
+ snippet: "<CodeGroup>\n\n```bash npm\n$|\n```\n\n```bash bun\n\n```\n\n</CodeGroup>",
151
+ },
152
+ {
153
+ id: "frame",
154
+ label: "Cadre",
155
+ description: "Une image ou une capture, avec sa légende.",
156
+ category: "Contenu",
157
+ keywords: ["cadre", "frame", "image", "capture"],
158
+ snippet: '<Frame caption="Légende">\n ![Description de l\'image]($|)\n</Frame>',
159
+ },
160
+ ]
161
+
162
+ /** Texte à insérer et position du curseur dans ce texte. */
163
+ export function expandSnippet(snippet: string): { text: string; cursor: number } {
164
+ const cursor = snippet.indexOf("$|")
165
+ return cursor === -1 ? { text: snippet, cursor: snippet.length } : { text: snippet.replace("$|", ""), cursor }
166
+ }
@@ -56,6 +56,47 @@ const logo = z.union([
56
56
  z.object({ light: z.string(), dark: z.string().optional(), href: z.string().optional() }),
57
57
  ])
58
58
 
59
+ /**
60
+ * Réseaux du pied de page : clé de `footer.socials` (celles de Mintlify), libellé, icône de
61
+ * Starlight. Lu par le build et par le formulaire des réglages de la console.
62
+ */
63
+ export const SOCIAL_NETWORKS = {
64
+ linkedin: { label: "LinkedIn", icon: "linkedin" },
65
+ github: { label: "GitHub", icon: "github" },
66
+ gitlab: { label: "GitLab", icon: "gitlab" },
67
+ x: { label: "X", icon: "x.com" },
68
+ youtube: { label: "YouTube", icon: "youtube" },
69
+ instagram: { label: "Instagram", icon: "instagram" },
70
+ facebook: { label: "Facebook", icon: "facebook" },
71
+ discord: { label: "Discord", icon: "discord" },
72
+ mastodon: { label: "Mastodon", icon: "mastodon" },
73
+ bluesky: { label: "Bluesky", icon: "blueSky" },
74
+ website: { label: "Site web", icon: "external" },
75
+ } as const
76
+
77
+ /** Ancienne écriture acceptée : `x.com` (Starlight) et `twitter` désignent X. */
78
+ const SOCIAL_ALIASES: Record<string, keyof typeof SOCIAL_NETWORKS> = { "x.com": "x", twitter: "x" }
79
+
80
+ export function socialNetwork(key: string): (typeof SOCIAL_NETWORKS)[keyof typeof SOCIAL_NETWORKS] | null {
81
+ const name = (SOCIAL_ALIASES[key] ?? key) as keyof typeof SOCIAL_NETWORKS
82
+ return SOCIAL_NETWORKS[name] ?? null
83
+ }
84
+
85
+ const REDIRECT_SOURCE = /^\/(?!\/)[A-Za-z0-9._~\-/%]*$/
86
+
87
+ /** Ancienne adresse vers la nouvelle (champ `redirects` de Mintlify), sans motif. */
88
+ const redirect = z.object({
89
+ source: z.string().regex(REDIRECT_SOURCE, "adresse source attendue, commençant par « / » (ex. /guides/ancien)"),
90
+ destination: z
91
+ .string()
92
+ .min(1)
93
+ .refine(
94
+ (value) => /^\/(?!\/)/.test(value) || value.startsWith("https://"),
95
+ "destination attendue : un chemin du site (« /… ») ou une adresse https://",
96
+ ),
97
+ permanent: z.boolean().optional(),
98
+ })
99
+
59
100
  export const docsConfigSchema = z.object({
60
101
  $schema: z.string().optional(),
61
102
  name: z.string().min(1).max(80),
@@ -86,6 +127,7 @@ export const docsConfigSchema = z.object({
86
127
  })
87
128
  .optional(),
88
129
  footer: z.object({ socials: z.record(z.string(), z.url()).optional() }).optional(),
130
+ redirects: z.array(redirect).max(500, "500 redirections au plus").optional(),
89
131
  assistant: z
90
132
  .object({
91
133
  enabled: z.boolean().default(true),
@@ -0,0 +1,27 @@
1
+ import { z } from "zod"
2
+
3
+ /**
4
+ * Contrat de `POST /api/v1/feedback` entre le bas de chaque page (« Cette page t'a aidé ? »)
5
+ * et la console. Aucune donnée personnelle : la page, la réponse, un commentaire facultatif.
6
+ */
7
+ export const MAX_FEEDBACK_COMMENT = 1000
8
+ /** Code joint à un signalement « Signaler un code faux », tronqué par le site. */
9
+ export const MAX_FEEDBACK_CODE = 2000
10
+
11
+ export const feedbackRequestSchema = z
12
+ .object({
13
+ project: z.string().regex(/^[a-z0-9]{8,64}$/),
14
+ /** Chemin de la page, `base` compris (`/docs/guides/publier/`). */
15
+ page: z.string().startsWith("/").max(512),
16
+ helpful: z.boolean(),
17
+ comment: z.string().trim().max(MAX_FEEDBACK_COMMENT).optional(),
18
+ /** `code` : « Signaler un code faux » sur un bloc de code ; absent, l'avis porte sur la page. */
19
+ kind: z.enum(["page", "code"]).optional(),
20
+ code: z.string().max(MAX_FEEDBACK_CODE).optional(),
21
+ })
22
+ .refine((value) => value.kind !== "code" || Boolean(value.comment), {
23
+ message: "un signalement de code dit ce qui ne va pas",
24
+ path: ["comment"],
25
+ })
26
+
27
+ export type FeedbackRequest = z.infer<typeof feedbackRequestSchema>
package/src/core/theme.ts CHANGED
@@ -12,8 +12,10 @@ export type Theme = { light: AccentPalette; dark: AccentPalette }
12
12
  /** Accent violet de la DA HGDC (`--brand-accent` du kit), si le client n'en donne pas. */
13
13
  export const HGDC_ACCENT = "#7c3aed"
14
14
 
15
- const LIGHT_BG = "#ffffff"
16
- const DARK_BG = "#0f0f10"
15
+ /** Fonds sur lesquels l'accent doit tenir 4,5:1 : la surface la plus proche de lui (élément
16
+ * courant teinté, encart `muted`), un cran plus foncé ou plus clair que la page. */
17
+ const LIGHT_BG = "#f2f2f2"
18
+ const DARK_BG = "#2a2a2a"
17
19
 
18
20
  export function themeFor(primary: string = HGDC_ACCENT, dark?: string): Theme {
19
21
  const base = hexToOklch(primary)
@@ -48,7 +50,8 @@ export function withContrast(color: Oklch, background: string, ratio: number): s
48
50
  export function themeCss(theme: Theme): string {
49
51
  const vars = (p: AccentPalette) =>
50
52
  `--sl-color-accent-low:${p.low};--sl-color-accent:${p.accent};--sl-color-accent-high:${p.high};--hgdc-on-accent:${p.onAccent};`
51
- return `:root{${vars(theme.dark)}}:root[data-theme="light"]{${vars(theme.light)}}`
53
+ // Clair par défaut : le sombre ne vaut que choisi (`data-hd-theme`, overrides/ThemeProvider.astro).
54
+ return `:root{${vars(theme.dark)}}:root:not([data-hd-theme="dark"]){${vars(theme.light)}}`
52
55
  }
53
56
 
54
57
  // --- Conversions sRGB ↔ OKLCH (Björn Ottosson) et contraste WCAG 2 ---