Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.
| Docs: a workspace knowledge base people and agents write together | 1 | /** |
| 2 | * How Docs pages change things: a JSON request to `-/docs/api`, then the | |
| 3 | * sidebar and page load again so they show it. Returns the service's | |
| 4 | * `{ ok, value }` or `{ ok: false, error }`. | |
| 5 | */ | |
| 6 | import type { Result } from "@g1t/contracts"; | |
| 7 | import { useCallback, useState } from "react"; | |
| 8 | import { useRevalidator, useRouteLoaderData } from "react-router"; | |
| 9 | ||
| 10 | import type { DocsLayoutData } from "../../routes/workspace/docs/layout"; | |
| 11 | ||
| 12 | export function useDocsData(): DocsLayoutData | undefined { | |
| 13 | return useRouteLoaderData("routes/workspace/docs/layout") as DocsLayoutData | undefined; | |
| 14 | } | |
| 15 | ||
| 16 | export async function docsRequest<T>(slug: string, intent: string, body: Record<string, unknown> = {}): Promise<Result<T>> { | |
| 17 | try { | |
| 18 | const response = await fetch(`/${slug}/-/docs/api`, { | |
| 19 | method: "POST", | |
| 20 | headers: { "content-type": "application/json", accept: "application/json" }, | |
| 21 | body: JSON.stringify({ intent, ...body }), | |
| 22 | }); | |
| 23 | return (await response.json()) as Result<T>; | |
| 24 | } catch { | |
| 25 | return { ok: false, error: { code: "conflict", message: "Docs didn't answer. Check your connection and try again." } }; | |
| 26 | } | |
| 27 | } | |
| 28 | ||
| 29 | export async function docsQuery<T>(slug: string, query: Record<string, string>): Promise<Result<T>> { | |
| 30 | try { | |
| 31 | const response = await fetch(`/${slug}/-/docs/api?${new URLSearchParams(query)}`, { headers: { accept: "application/json" } }); | |
| 32 | return (await response.json()) as Result<T>; | |
| 33 | } catch { | |
| 34 | return { ok: false, error: { code: "conflict", message: "Docs didn't answer. Check your connection and try again." } }; | |
| 35 | } | |
| 36 | } | |
| 37 | ||
| 38 | /** `send(intent, body)`: makes the change, then reloads what shows it. `busy` while it works; `error` when it failed. */ | |
| 39 | export function useDocsAction(slug: string) { | |
| 40 | const revalidator = useRevalidator(); | |
| 41 | const [busy, setBusy] = useState(false); | |
| 42 | const [error, setError] = useState<string | null>(null); | |
| 43 | const send = useCallback( | |
| 44 | async <T>(intent: string, body: Record<string, unknown> = {}, options: { reload?: boolean } = {}): Promise<Result<T>> => { | |
| 45 | setBusy(true); | |
| 46 | setError(null); | |
| 47 | const result = await docsRequest<T>(slug, intent, body); | |
| 48 | setBusy(false); | |
| 49 | if (!result.ok) setError(result.error.message); | |
| 50 | else if (options.reload !== false) void revalidator.revalidate(); | |
| 51 | return result; | |
| 52 | }, | |
| 53 | [slug, revalidator], | |
| 54 | ); | |
| 55 | return { send, busy, error, setError }; | |
| 56 | } |