@hgdc/docs 0.0.0-stage → 0.1.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 (175) hide show
  1. package/bin/hgdc-docs.mjs +48 -0
  2. package/docs.schema.json +294 -0
  3. package/package.json +50 -4
  4. package/src/build/auto-import.ts +74 -0
  5. package/src/build/markdown.ts +114 -0
  6. package/src/build/outputs.ts +91 -0
  7. package/src/build/public-files.test.ts +29 -0
  8. package/src/build/public-files.ts +35 -0
  9. package/src/components/Accordion.astro +12 -0
  10. package/src/components/AccordionGroup.astro +4 -0
  11. package/src/components/Card.astro +15 -0
  12. package/src/components/CardGroup.astro +8 -0
  13. package/src/components/Check.astro +10 -0
  14. package/src/components/CodeGroup.astro +4 -0
  15. package/src/components/Columns.astro +8 -0
  16. package/src/components/Danger.astro +10 -0
  17. package/src/components/Frame.astro +11 -0
  18. package/src/components/Info.astro +10 -0
  19. package/src/components/Note.astro +10 -0
  20. package/src/components/Step.astro +11 -0
  21. package/src/components/Steps.astro +4 -0
  22. package/src/components/Tab.astro +9 -0
  23. package/src/components/Tabs.astro +9 -0
  24. package/src/components/Tip.astro +10 -0
  25. package/src/components/Warning.astro +10 -0
  26. package/src/core/answer-markdown.ts +109 -0
  27. package/src/core/assistant-api.ts +65 -0
  28. package/src/core/chunk.ts +134 -0
  29. package/src/core/config.ts +153 -0
  30. package/src/core/core.test.ts +253 -0
  31. package/src/core/corpus.ts +42 -0
  32. package/src/core/json-schema.ts +12 -0
  33. package/src/core/llms.ts +95 -0
  34. package/src/core/navigation.ts +88 -0
  35. package/src/core/theme.ts +111 -0
  36. package/src/integration.ts +229 -0
  37. package/src/locals.d.ts +6 -0
  38. package/src/overrides/Assistant.astro +72 -0
  39. package/src/overrides/Head.astro +12 -0
  40. package/src/overrides/PageFrame.astro +15 -0
  41. package/src/overrides/PageTitle.astro +94 -0
  42. package/src/overrides/Sidebar.astro +60 -0
  43. package/src/overrides/route-data.ts +33 -0
  44. package/src/styles/theme.css +773 -0
  45. package/src/virtual.d.ts +6 -0
  46. package/src/widget/client.ts +252 -0
  47. package/template/.astro/collections/docs.schema.json +651 -0
  48. package/template/.astro/content-assets.mjs +1 -0
  49. package/template/.astro/content-modules.mjs +18 -0
  50. package/template/.astro/content.d.ts +179 -0
  51. package/template/.astro/types.d.ts +2 -0
  52. package/template/.build/.well-known/llms.txt +38 -0
  53. package/template/.build/404.html +42 -0
  54. package/template/.build/_astro/Assistant.astro_astro_type_script_index_0_lang.DWgmlr85.js +4 -0
  55. package/template/.build/_astro/MobileTableOfContents.astro_astro_type_script_index_0_lang.C3fM4Kra.js +1 -0
  56. package/template/.build/_astro/Search.astro_astro_type_script_index_0_lang.g9hoxVJg.js +1 -0
  57. package/template/.build/_astro/TableOfContents.astro_astro_type_script_index_0_lang.BpJaGz7G.js +1 -0
  58. package/template/.build/_astro/common.8GHCkW7i.css +3 -0
  59. package/template/.build/_astro/ec.0vx5m.js +3 -0
  60. package/template/.build/_astro/ec.8nqtp.css +1 -0
  61. package/template/.build/_astro/geist-cyrillic-ext-wght-normal.DjL33-gN.woff2 +0 -0
  62. package/template/.build/_astro/geist-cyrillic-wght-normal.BEAKL7Jp.woff2 +0 -0
  63. package/template/.build/_astro/geist-latin-ext-wght-normal.DC-KSUi6.woff2 +0 -0
  64. package/template/.build/_astro/geist-latin-wght-normal.BgDaEnEv.woff2 +0 -0
  65. package/template/.build/_astro/geist-vietnamese-wght-normal.6IgcOCM7.woff2 +0 -0
  66. package/template/.build/_astro/inter-cyrillic-ext-wght-normal.BOeWTOD4.woff2 +0 -0
  67. package/template/.build/_astro/inter-cyrillic-wght-normal.DqGufNeO.woff2 +0 -0
  68. package/template/.build/_astro/inter-greek-ext-wght-normal.DlzME5K_.woff2 +0 -0
  69. package/template/.build/_astro/inter-greek-wght-normal.CkhJZR-_.woff2 +0 -0
  70. package/template/.build/_astro/inter-latin-ext-wght-normal.DO1Apj_S.woff2 +0 -0
  71. package/template/.build/_astro/inter-latin-wght-normal.Dx4kXJAl.woff2 +0 -0
  72. package/template/.build/_astro/inter-vietnamese-wght-normal.CBcvBZtf.woff2 +0 -0
  73. package/template/.build/_astro/logo-clair.BqNh6JNi.svg +1 -0
  74. package/template/.build/_astro/logo-sombre.BkgsFXxh.svg +1 -0
  75. package/template/.build/_astro/page.cNngjUgL.js +1 -0
  76. package/template/.build/_astro/print.ehPL0gv-.css +1 -0
  77. package/template/.build/_astro/starlight-toc.D4UDIxQL.js +1 -0
  78. package/template/.build/_astro/ui-core.Clh5jUUO.js +2 -0
  79. package/template/.build/_hgdc/corpus.json +1 -0
  80. package/template/.build/demarrer/premiere-doc/index.html +84 -0
  81. package/template/.build/demarrer/premiere-doc.md +65 -0
  82. package/template/.build/explications/donnees/index.html +114 -0
  83. package/template/.build/explications/donnees.md +28 -0
  84. package/template/.build/explications/fonctionnement/index.html +92 -0
  85. package/template/.build/explications/fonctionnement.md +32 -0
  86. package/template/.build/guides/agents/index.html +113 -0
  87. package/template/.build/guides/agents.md +40 -0
  88. package/template/.build/guides/assistant/index.html +117 -0
  89. package/template/.build/guides/assistant.md +42 -0
  90. package/template/.build/guides/composants/index.html +97 -0
  91. package/template/.build/guides/composants.md +103 -0
  92. package/template/.build/guides/ecrire/index.html +115 -0
  93. package/template/.build/guides/ecrire.md +63 -0
  94. package/template/.build/guides/navigation/index.html +89 -0
  95. package/template/.build/guides/navigation.md +63 -0
  96. package/template/.build/guides/publier/index.html +86 -0
  97. package/template/.build/guides/publier.md +65 -0
  98. package/template/.build/guides/theme/index.html +86 -0
  99. package/template/.build/guides/theme.md +33 -0
  100. package/template/.build/images/favicon.svg +1 -0
  101. package/template/.build/images/logo-clair.svg +1 -0
  102. package/template/.build/images/logo-sombre.svg +1 -0
  103. package/template/.build/index.html +87 -0
  104. package/template/.build/index.md +35 -0
  105. package/template/.build/llms-full.txt +830 -0
  106. package/template/.build/llms.txt +38 -0
  107. package/template/.build/pagefind/fragment/fr-ch_148313b.pf_fragment +0 -0
  108. package/template/.build/pagefind/fragment/fr-ch_424fd16.pf_fragment +0 -0
  109. package/template/.build/pagefind/fragment/fr-ch_4f8575c.pf_fragment +0 -0
  110. package/template/.build/pagefind/fragment/fr-ch_5a4d77d.pf_fragment +0 -0
  111. package/template/.build/pagefind/fragment/fr-ch_6c36edc.pf_fragment +0 -0
  112. package/template/.build/pagefind/fragment/fr-ch_73d7887.pf_fragment +0 -0
  113. package/template/.build/pagefind/fragment/fr-ch_8234ed3.pf_fragment +0 -0
  114. package/template/.build/pagefind/fragment/fr-ch_8f8a19e.pf_fragment +0 -0
  115. package/template/.build/pagefind/fragment/fr-ch_9f1b981.pf_fragment +0 -0
  116. package/template/.build/pagefind/fragment/fr-ch_a19039d.pf_fragment +0 -0
  117. package/template/.build/pagefind/fragment/fr-ch_d72cc1e.pf_fragment +0 -0
  118. package/template/.build/pagefind/fragment/fr-ch_d732215.pf_fragment +0 -0
  119. package/template/.build/pagefind/fragment/fr-ch_e2c46e3.pf_fragment +0 -0
  120. package/template/.build/pagefind/fragment/fr-ch_e78d58b.pf_fragment +0 -0
  121. package/template/.build/pagefind/fragment/fr-ch_e92f972.pf_fragment +0 -0
  122. package/template/.build/pagefind/index/fr-ch_f94c95d.pf_index +0 -0
  123. package/template/.build/pagefind/pagefind-component-ui.css +1509 -0
  124. package/template/.build/pagefind/pagefind-component-ui.js +56 -0
  125. package/template/.build/pagefind/pagefind-entry.json +1 -0
  126. package/template/.build/pagefind/pagefind-highlight.js +1070 -0
  127. package/template/.build/pagefind/pagefind-modular-ui.css +214 -0
  128. package/template/.build/pagefind/pagefind-modular-ui.js +8 -0
  129. package/template/.build/pagefind/pagefind-ui.css +1 -0
  130. package/template/.build/pagefind/pagefind-ui.js +2 -0
  131. package/template/.build/pagefind/pagefind-worker.js +6 -0
  132. package/template/.build/pagefind/pagefind.fr-ch_fb78ab42961f4.pf_meta +0 -0
  133. package/template/.build/pagefind/pagefind.js +6 -0
  134. package/template/.build/pagefind/wasm.fr-ch.pagefind +0 -0
  135. package/template/.build/pagefind/wasm.unknown.pagefind +0 -0
  136. package/template/.build/reference/api-assistant/index.html +161 -0
  137. package/template/.build/reference/api-assistant.md +60 -0
  138. package/template/.build/reference/cli/index.html +153 -0
  139. package/template/.build/reference/cli.md +31 -0
  140. package/template/.build/reference/docs-json/index.html +192 -0
  141. package/template/.build/reference/docs-json.md +43 -0
  142. package/template/.build/reference/mcp/index.html +151 -0
  143. package/template/.build/reference/mcp.md +49 -0
  144. package/template/astro.config.ts +6 -0
  145. package/template/node_modules/.vite/deps/_metadata.json +74 -0
  146. package/template/node_modules/.vite/deps/astro-B3JHJyiv.js +434 -0
  147. package/template/node_modules/.vite/deps/astro-B3JHJyiv.js.map +1 -0
  148. package/template/node_modules/.vite/deps/astro_n_aria-query.js +6390 -0
  149. package/template/node_modules/.vite/deps/astro_n_aria-query.js.map +1 -0
  150. package/template/node_modules/.vite/deps/astro_n_axobject-query.js +2705 -0
  151. package/template/node_modules/.vite/deps/astro_n_axobject-query.js.map +1 -0
  152. package/template/node_modules/.vite/deps/astro_n_html-escaper.js +65 -0
  153. package/template/node_modules/.vite/deps/astro_n_html-escaper.js.map +1 -0
  154. package/template/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js +455 -0
  155. package/template/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js.map +1 -0
  156. package/template/node_modules/.vite/deps/audit-Do8QH17W.js +1543 -0
  157. package/template/node_modules/.vite/deps/audit-Do8QH17W.js.map +1 -0
  158. package/template/node_modules/.vite/deps/highlight-BLtvqtzO.js +48 -0
  159. package/template/node_modules/.vite/deps/highlight-BLtvqtzO.js.map +1 -0
  160. package/template/node_modules/.vite/deps/icons-C92i61-2.js +42 -0
  161. package/template/node_modules/.vite/deps/icons-C92i61-2.js.map +1 -0
  162. package/template/node_modules/.vite/deps/package.json +3 -0
  163. package/template/node_modules/.vite/deps/rolldown-runtime-C3I60J0A.js +4 -0
  164. package/template/node_modules/.vite/deps/settings-BYmTKrLi.js +189 -0
  165. package/template/node_modules/.vite/deps/settings-BYmTKrLi.js.map +1 -0
  166. package/template/node_modules/.vite/deps/toolbar-CEKhukuK.js +461 -0
  167. package/template/node_modules/.vite/deps/toolbar-CEKhukuK.js.map +1 -0
  168. package/template/node_modules/.vite/deps/ui-library-BO4--ANp.js +1015 -0
  169. package/template/node_modules/.vite/deps/ui-library-BO4--ANp.js.map +1 -0
  170. package/template/node_modules/.vite/deps/window-vo3A6TMn.js +32 -0
  171. package/template/node_modules/.vite/deps/window-vo3A6TMn.js.map +1 -0
  172. package/template/node_modules/.vite/deps/xray-Lf9x6g8P.js +112 -0
  173. package/template/node_modules/.vite/deps/xray-Lf9x6g8P.js.map +1 -0
  174. package/template/src/content.config.ts +21 -0
  175. package/README.md +0 -3
@@ -0,0 +1,6 @@
1
+ declare module "virtual:hgdc-docs/config" {
2
+ import type { VirtualConfig } from "@hgdc/docs/integration"
3
+
4
+ const data: VirtualConfig
5
+ export default data
6
+ }
@@ -0,0 +1,252 @@
1
+ import type { AnswerSource, AskResponse } from "../core/assistant-api.ts"
2
+ import { parseMarkdown, type Block, type Inline } from "../core/answer-markdown.ts"
3
+
4
+ const FALLBACK_ERROR = "L'assistant ne répond pas pour l'instant. Réessaie dans un moment."
5
+
6
+ function isAnswer(value: unknown): value is AskResponse {
7
+ const v = value as Partial<AskResponse> | null
8
+ return Boolean(v && typeof v.answer === "string" && Array.isArray(v.sources) && typeof v.status === "string")
9
+ }
10
+
11
+ function errorMessage(value: unknown): string | null {
12
+ const message = (value as { error?: { message?: unknown } } | null)?.error?.message
13
+ return typeof message === "string" ? message : null
14
+ }
15
+
16
+ function el<K extends keyof HTMLElementTagNameMap>(
17
+ tag: K,
18
+ className?: string,
19
+ text?: string,
20
+ ): HTMLElementTagNameMap[K] {
21
+ const node = document.createElement(tag)
22
+ if (className) node.className = className
23
+ if (text !== undefined) node.textContent = text
24
+ return node
25
+ }
26
+
27
+ function renderInline(parent: HTMLElement, nodes: Array<Inline>, sources: Map<number, AnswerSource>): void {
28
+ for (const node of nodes) {
29
+ switch (node.type) {
30
+ case "text":
31
+ parent.append(node.value)
32
+ break
33
+ case "code":
34
+ parent.append(el("code", undefined, node.value))
35
+ break
36
+ case "strong":
37
+ case "em": {
38
+ const child = el(node.type)
39
+ renderInline(child, node.children, sources)
40
+ parent.append(child)
41
+ break
42
+ }
43
+ case "link": {
44
+ const link = el("a")
45
+ link.href = node.href
46
+ link.rel = "noopener noreferrer"
47
+ renderInline(link, node.children, sources)
48
+ parent.append(link)
49
+ break
50
+ }
51
+ case "ref": {
52
+ const source = sources.get(node.n)
53
+ if (!source) break
54
+ const ref = el("a", "hgdc-ref", String(node.n))
55
+ ref.href = source.url
56
+ ref.title = source.section ? `${source.title} › ${source.section}` : source.title
57
+ ref.setAttribute("aria-label", `Source ${node.n} : ${ref.title}`)
58
+ parent.append(ref)
59
+ }
60
+ }
61
+ }
62
+ }
63
+
64
+ function renderBlocks(blocks: Array<Block>, sources: Map<number, AnswerSource>): DocumentFragment {
65
+ const fragment = document.createDocumentFragment()
66
+ for (const block of blocks) {
67
+ if (block.type === "code") {
68
+ const pre = el("pre")
69
+ pre.append(el("code", undefined, block.value))
70
+ fragment.append(pre)
71
+ } else if (block.type === "list") {
72
+ const list = el(block.ordered ? "ol" : "ul")
73
+ for (const item of block.items) {
74
+ const li = el("li")
75
+ renderInline(li, item, sources)
76
+ list.append(li)
77
+ }
78
+ fragment.append(list)
79
+ } else {
80
+ const node = el(block.type === "heading" ? "h3" : "p")
81
+ renderInline(node, block.children, sources)
82
+ fragment.append(node)
83
+ }
84
+ }
85
+ return fragment
86
+ }
87
+
88
+ class Assistant extends HTMLElement {
89
+ /** Identifiants des réponses précédentes : le serveur relit lui-même la conversation. */
90
+ private previous: Array<string> = []
91
+ private pending = false
92
+ private opener: HTMLElement | null = null
93
+
94
+ private get panel() {
95
+ return this.querySelector<HTMLElement>(".hgdc-panel")!
96
+ }
97
+ private get launcher() {
98
+ return this.querySelector<HTMLButtonElement>("[data-open]")!
99
+ }
100
+ private get input() {
101
+ return this.querySelector<HTMLTextAreaElement>("[data-input]")!
102
+ }
103
+ private get log() {
104
+ return this.querySelector<HTMLElement>("[data-log]")!
105
+ }
106
+
107
+ connectedCallback(): void {
108
+ const mac = /Mac|iPhone|iPad/.test(navigator.platform)
109
+ const shortcut = this.querySelector("[data-shortcut]")
110
+ if (shortcut) shortcut.textContent = mac ? "⌘ I" : "Ctrl I"
111
+
112
+ this.launcher.addEventListener("click", () => this.toggle(true, this.launcher))
113
+ this.querySelector("[data-close]")!.addEventListener("click", () => this.toggle(false))
114
+ this.querySelector("[data-reset]")!.addEventListener("click", () => this.reset())
115
+ this.querySelector("[data-form]")!.addEventListener("submit", (event) => {
116
+ event.preventDefault()
117
+ void this.ask()
118
+ })
119
+ this.input.addEventListener("keydown", (event) => {
120
+ if (event.key === "Enter" && !event.shiftKey && !event.isComposing) {
121
+ event.preventDefault()
122
+ void this.ask()
123
+ }
124
+ })
125
+ this.input.addEventListener("input", () => this.autosize())
126
+ document.addEventListener("hgdc:assistant-open", () => this.toggle(true, document.activeElement as HTMLElement))
127
+ document.addEventListener("keydown", (event) => {
128
+ if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === "i") {
129
+ event.preventDefault()
130
+ this.toggle(Boolean(this.panel.hidden), document.activeElement as HTMLElement)
131
+ } else if (event.key === "Escape" && !this.panel.hidden) this.toggle(false)
132
+ })
133
+ }
134
+
135
+ private toggle(open: boolean, opener?: HTMLElement | null): void {
136
+ this.panel.hidden = !open
137
+ this.launcher.hidden = open
138
+ this.launcher.setAttribute("aria-expanded", String(open))
139
+ document.documentElement.toggleAttribute("data-hgdc-assistant", open)
140
+ // Plein écran sur téléphone : le panneau devient modal et le reste de la page inerte.
141
+ const fullscreen = open && matchMedia("(max-width: 50em)").matches
142
+ this.panel.setAttribute("aria-modal", String(fullscreen))
143
+ document.querySelector<HTMLElement>(".page")?.toggleAttribute("inert", fullscreen)
144
+ if (open) {
145
+ this.opener = opener ?? null
146
+ this.input.focus()
147
+ } else (this.opener ?? this.launcher).focus()
148
+ }
149
+
150
+ private autosize(): void {
151
+ this.input.style.height = "auto"
152
+ this.input.style.height = `${Math.min(this.input.scrollHeight, 160)}px`
153
+ }
154
+
155
+ private reset(): void {
156
+ this.previous = []
157
+ this.log.replaceChildren(this.querySelector("[data-empty]") ?? "")
158
+ this.querySelector<HTMLElement>("[data-empty]")!.hidden = false
159
+ this.querySelector<HTMLElement>("[data-reset]")!.hidden = true
160
+ this.input.focus()
161
+ }
162
+
163
+ private bubble(role: "user" | "assistant", className = ""): HTMLElement {
164
+ const node = el("div", `hgdc-msg hgdc-msg--${role} ${className}`.trim())
165
+ this.log.append(node)
166
+ node.scrollIntoView({
167
+ block: "end",
168
+ behavior: matchMedia("(prefers-reduced-motion: reduce)").matches ? "auto" : "smooth",
169
+ })
170
+ return node
171
+ }
172
+
173
+ private async ask(retry?: string): Promise<void> {
174
+ const question = (retry ?? this.input.value).trim()
175
+ if (this.pending || question.length < 2) return
176
+ this.pending = true
177
+ this.querySelector<HTMLElement>("[data-empty]")!.hidden = true
178
+ this.querySelector<HTMLElement>("[data-reset]")!.hidden = false
179
+ this.querySelector<HTMLButtonElement>("[data-send]")!.disabled = true
180
+ if (!retry) {
181
+ this.bubble("user").textContent = question
182
+ this.input.value = ""
183
+ this.autosize()
184
+ }
185
+ const thinking = this.bubble("assistant", "hgdc-msg--pending")
186
+ thinking.textContent = "Je cherche dans la documentation…"
187
+ thinking.setAttribute("aria-busy", "true")
188
+
189
+ try {
190
+ const response = await fetch(this.dataset.endpoint!, {
191
+ method: "POST",
192
+ headers: { "content-type": "application/json" },
193
+ body: JSON.stringify({
194
+ project: this.dataset.project,
195
+ question,
196
+ page: location.pathname,
197
+ previous: this.previous.slice(-3),
198
+ }),
199
+ })
200
+ const body: unknown = await response.json().catch(() => null)
201
+ if (!response.ok || !isAnswer(body)) throw new Error(errorMessage(body) ?? FALLBACK_ERROR)
202
+ thinking.remove()
203
+ this.showAnswer(body)
204
+ if (body.status === "answered") this.previous.push(body.id)
205
+ } catch (error) {
206
+ thinking.remove()
207
+ this.showError(
208
+ error instanceof Error && error.message !== "Failed to fetch" ? error.message : FALLBACK_ERROR,
209
+ question,
210
+ )
211
+ } finally {
212
+ this.pending = false
213
+ this.querySelector<HTMLButtonElement>("[data-send]")!.disabled = false
214
+ }
215
+ }
216
+
217
+ private showAnswer(answer: AskResponse): void {
218
+ const node = this.bubble("assistant", answer.status === "no_answer" ? "hgdc-msg--empty" : "")
219
+ const sources = new Map(answer.sources.map((source) => [source.n, source]))
220
+ node.append(renderBlocks(parseMarkdown(answer.answer, [location.origin]), sources))
221
+ if (answer.sources.length) {
222
+ const list = el("ol", "hgdc-sources")
223
+ list.setAttribute("aria-label", "Sources")
224
+ for (const source of answer.sources) {
225
+ const item = el("li")
226
+ const link = el("a")
227
+ link.href = source.url
228
+ link.append(el("span", "hgdc-sources__n", String(source.n)), el("span", undefined, source.title))
229
+ if (source.section) link.append(el("span", "hgdc-sources__section", source.section))
230
+ item.append(link)
231
+ list.append(item)
232
+ }
233
+ node.append(el("p", "hgdc-sources__label", "Sources"), list)
234
+ }
235
+ }
236
+
237
+ private showError(message: string, question: string): void {
238
+ const node = this.bubble("assistant", "hgdc-msg--error")
239
+ node.append(el("p", undefined, message))
240
+ const retry = el("button", "hgdc-retry", "Réessayer")
241
+ retry.type = "button"
242
+ retry.addEventListener("click", () => {
243
+ node.remove()
244
+ void this.ask(question)
245
+ })
246
+ node.append(retry)
247
+ }
248
+ }
249
+
250
+ export function defineAssistant(): void {
251
+ if (!customElements.get("hgdc-assistant")) customElements.define("hgdc-assistant", Assistant)
252
+ }