Skip to content
113 linesCodeBlameRaw

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 together1import type { DocSearchHit } from "@g1t/contracts";
Docs know what code they describe; a project's docs folder in Docs; Docs events; files on any S3 store2import { FolderGit2, Search } from "lucide-react";
Docs: a workspace knowledge base people and agents write together3import { Form, Link, data, useSubmit } from "react-router";
4
5import type { Route } from "./+types/search";
6import { useDocsData } from "../../../components/docs/actions";
7import { PageIcon } from "../../../components/docs/sidebar";
8import { EmptyState, TimeAgo } from "../../../components/ui";
9import { SelectField } from "../../../components/ui/select";
10import { snippetParts } from "../../../lib/docs";
11import { page } from "../../../lib/meta";
12import { docs } from "../../../lib/services.server";
13import { requireUser, roleIn } from "../../../lib/session.server";
14
15export function meta({ params, ...args }: Route.MetaArgs) {
16 return page(args, { title: `Search docs · ${params.owner} · g1t` });
17}
18
19export async function loader({ params, context, request }: Route.LoaderArgs) {
20 const viewer = requireUser(context, request);
21 if (!roleIn(viewer, params.owner)) throw data(null, { status: 404 });
22 const url = new URL(request.url);
23 const q = url.searchParams.get("q") ?? "";
24 const space = url.searchParams.get("space") || null;
25 const project = url.searchParams.get("project") || null;
26 const found = q.trim()
Docs index by meaning: passages of every page and project doc, embedded on save and recalled for agents; hybrid search for people27 ? await docs.search(params.owner.toLowerCase(), viewer, { query: q, space_id: space, project, limit: 50, mode: "hybrid" }).catch(() => null)
Docs: a workspace knowledge base people and agents write together28 : null;
29 return { q, space, project, hits: found?.ok ? found.value : q.trim() ? null : ([] as DocSearchHit[]) };
30}
31
Docs index by meaning: passages of every page and project doc, embedded on save and recalled for agents; hybrid search for people32/**
33 * Every page the viewer can read, and the projects' docs shown in Docs,
34 * by words and by meaning at once; by space and by project. Each hit shows
35 * the passage that matched, under its heading.
36 */
Docs: a workspace knowledge base people and agents write together37export default function DocsSearch({ loaderData, params }: Route.ComponentProps) {
38 const slug = params.owner.toLowerCase();
39 const { q, space, project, hits } = loaderData;
40 const layout = useDocsData();
41 const submit = useSubmit();
42 const spaces = layout?.sidebar?.spaces ?? [];
43 const projects = [...new Set(spaces.flatMap((s) => s.projects))].sort();
44 const refresh = () => submit(document.getElementById("docs-search") as HTMLFormElement);
45 return (
46 <div className="mx-auto max-w-3xl">
47 <h1 className="text-xl font-semibold tracking-tight">Search docs</h1>
48 <Form method="get" id="docs-search" className="mt-4 flex flex-wrap gap-2">
49 <label className="flex h-10 min-w-0 grow items-center gap-2 rounded-md border border-line bg-surface px-3 focus-within:border-accent/60">
50 <Search size={16} className="text-faint" aria-hidden="true" />
Docs index by meaning: passages of every page and project doc, embedded on save and recalled for agents; hybrid search for people51 <input name="q" defaultValue={q} autoFocus placeholder="Words, or a question" aria-label="Search" className="min-w-0 grow bg-transparent text-sm outline-none placeholder:text-faint" />
Docs: a workspace knowledge base people and agents write together52 </label>
53 <SelectField
54 name="space"
55 aria-label="Space"
56 value={space ?? ""}
57 afterChange={refresh}
58 options={[{ value: "", label: "Every space" }, ...spaces.map((s) => ({ value: s.id, label: s.name }))]}
59 className="h-10 min-w-40"
60 />
61 {projects.length > 0 && (
62 <SelectField name="project" aria-label="Project" value={project ?? ""} afterChange={refresh} options={[{ value: "", label: "Any project" }, ...projects.map((p) => ({ value: p, label: p }))]} className="h-10 min-w-40" />
63 )}
64 </Form>
65 <div className="mt-6">
66 {hits === null ? (
67 <EmptyState title="Search didn't answer">Try again in a moment.</EmptyState>
68 ) : !q.trim() ? (
Docs index by meaning: passages of every page and project doc, embedded on save and recalled for agents; hybrid search for people69 <p className="text-sm text-muted">
70 Search every space you can read, and the projects&apos; docs shown in Docs, by their words and by what they mean: ask a question and the passage that answers it comes up even when it words it differently. Agents recall from
71 Docs the same way, and only from what everyone they answer can read.
72 </p>
Docs: a workspace knowledge base people and agents write together73 ) : hits.length === 0 ? (
74 <EmptyState title="Nothing found">No page you can read matches &ldquo;{q}&rdquo;.</EmptyState>
75 ) : (
76 <ul className="divide-y divide-line overflow-hidden rounded-xl border border-line bg-surface">
77 {hits.map((hit) => (
78 <li key={hit.id}>
79 <Link to={hit.path} className="block px-4 py-3 transition-colors hover:bg-raised">
80 <span className="flex items-center gap-2">
Docs know what code they describe; a project's docs folder in Docs; Docs events; files on any S3 store81 {hit.repo_file ? <FolderGit2 size={15} className="shrink-0 text-faint" aria-hidden="true" /> : <PageIcon icon={hit.icon} />}
Docs: a workspace knowledge base people and agents write together82 <span className="truncate text-sm font-medium">{hit.title || "Untitled"}</span>
83 <span className="ml-auto shrink-0 text-xs text-faint">
84 {hit.space_name} · <TimeAgo at={hit.updated_at} />
85 </span>
86 </span>
Docs index by meaning: passages of every page and project doc, embedded on save and recalled for agents; hybrid search for people87 {hit.heading && <span className="mt-0.5 block truncate text-xs font-medium text-muted">{hit.heading}</span>}
Docs: a workspace knowledge base people and agents write together88 <span className="mt-1 line-clamp-2 block text-xs leading-relaxed text-muted">
89 {snippetParts(hit.snippet).map((part, i) =>
90 part.match ? (
91 <mark key={i} className="rounded bg-accent/20 px-0.5 text-fg">
92 {part.text}
93 </mark>
94 ) : (
95 <span key={i}>{part.text}</span>
96 ),
97 )}
98 </span>
99 {hit.projects.length > 0 && <span className="mt-1 block text-[0.6875rem] text-faint">{hit.projects.join(" · ")}</span>}
100 </Link>
101 </li>
102 ))}
103 </ul>
104 )}
105 </div>
106 <p className="mt-4 text-xs text-faint">
107 <Link to={`/${slug}/-/docs`} className="hover:text-fg">
108 Back to Docs
109 </Link>
110 </p>
111 </div>
112 );
113}