@gmickel/gno 0.16.0 → 0.17.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.
- package/README.md +36 -1
- package/package.json +4 -1
- package/src/cli/commands/ask.ts +9 -0
- package/src/cli/commands/query.ts +3 -2
- package/src/cli/pager.ts +1 -1
- package/src/cli/program.ts +89 -0
- package/src/core/links.ts +92 -20
- package/src/ingestion/sync.ts +267 -23
- package/src/ingestion/types.ts +2 -0
- package/src/ingestion/walker.ts +2 -1
- package/src/mcp/tools/index.ts +30 -1
- package/src/mcp/tools/query.ts +22 -2
- package/src/mcp/tools/search.ts +8 -0
- package/src/mcp/tools/vsearch.ts +8 -0
- package/src/pipeline/answer.ts +324 -7
- package/src/pipeline/expansion.ts +243 -7
- package/src/pipeline/explain.ts +93 -5
- package/src/pipeline/hybrid.ts +240 -57
- package/src/pipeline/query-modes.ts +125 -0
- package/src/pipeline/rerank.ts +34 -13
- package/src/pipeline/search.ts +41 -3
- package/src/pipeline/temporal.ts +257 -0
- package/src/pipeline/types.ts +58 -0
- package/src/pipeline/vsearch.ts +107 -9
- package/src/serve/public/app.tsx +1 -3
- package/src/serve/public/globals.built.css +2 -2
- package/src/serve/public/lib/retrieval-filters.ts +167 -0
- package/src/serve/public/pages/Ask.tsx +339 -109
- package/src/serve/public/pages/Browse.tsx +71 -5
- package/src/serve/public/pages/DocView.tsx +2 -21
- package/src/serve/public/pages/Search.tsx +507 -120
- package/src/serve/routes/api.ts +202 -2
- package/src/store/migrations/006-document-metadata.ts +104 -0
- package/src/store/migrations/007-document-date-fields.ts +24 -0
- package/src/store/migrations/index.ts +3 -1
- package/src/store/sqlite/adapter.ts +218 -5
- package/src/store/types.ts +46 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ArrowLeft, ChevronRight, FileText, FolderOpen } from "lucide-react";
|
|
2
|
-
import { useEffect, useState } from "react";
|
|
2
|
+
import { Fragment, useEffect, useState } from "react";
|
|
3
3
|
|
|
4
4
|
import { Loader } from "../components/ai-elements/loader";
|
|
5
5
|
import { Badge } from "../components/ui/badge";
|
|
@@ -44,6 +44,9 @@ interface DocsResponse {
|
|
|
44
44
|
total: number;
|
|
45
45
|
limit: number;
|
|
46
46
|
offset: number;
|
|
47
|
+
availableDateFields: string[];
|
|
48
|
+
sortField: string;
|
|
49
|
+
sortOrder: "asc" | "desc";
|
|
47
50
|
}
|
|
48
51
|
|
|
49
52
|
export default function Browse({ navigate }: PageProps) {
|
|
@@ -54,6 +57,9 @@ export default function Browse({ navigate }: PageProps) {
|
|
|
54
57
|
const [offset, setOffset] = useState(0);
|
|
55
58
|
const [loading, setLoading] = useState(false);
|
|
56
59
|
const [initialLoad, setInitialLoad] = useState(true);
|
|
60
|
+
const [availableDateFields, setAvailableDateFields] = useState<string[]>([]);
|
|
61
|
+
const [sortField, setSortField] = useState("modified");
|
|
62
|
+
const [sortOrder, setSortOrder] = useState<"asc" | "desc">("desc");
|
|
57
63
|
const limit = 25;
|
|
58
64
|
|
|
59
65
|
// Parse collection from URL on mount
|
|
@@ -75,21 +81,41 @@ export default function Browse({ navigate }: PageProps) {
|
|
|
75
81
|
|
|
76
82
|
useEffect(() => {
|
|
77
83
|
setLoading(true);
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
:
|
|
84
|
+
const params = new URLSearchParams({
|
|
85
|
+
limit: String(limit),
|
|
86
|
+
offset: String(offset),
|
|
87
|
+
sortField,
|
|
88
|
+
sortOrder,
|
|
89
|
+
});
|
|
90
|
+
if (selected) {
|
|
91
|
+
params.set("collection", selected);
|
|
92
|
+
}
|
|
93
|
+
const url = `/api/docs?${params.toString()}`;
|
|
81
94
|
|
|
82
95
|
void apiFetch<DocsResponse>(url).then(({ data }) => {
|
|
83
96
|
setLoading(false);
|
|
84
97
|
setInitialLoad(false);
|
|
85
98
|
if (data) {
|
|
99
|
+
setAvailableDateFields(data.availableDateFields ?? []);
|
|
100
|
+
setSortField(data.sortField);
|
|
101
|
+
setSortOrder(data.sortOrder);
|
|
86
102
|
setDocs((prev) =>
|
|
87
103
|
offset === 0 ? data.documents : [...prev, ...data.documents]
|
|
88
104
|
);
|
|
89
105
|
setTotal(data.total);
|
|
90
106
|
}
|
|
91
107
|
});
|
|
92
|
-
}, [selected, offset]);
|
|
108
|
+
}, [selected, offset, sortField, sortOrder]);
|
|
109
|
+
|
|
110
|
+
useEffect(() => {
|
|
111
|
+
if (sortField === "modified" || availableDateFields.includes(sortField)) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
setSortField("modified");
|
|
115
|
+
setSortOrder("desc");
|
|
116
|
+
setOffset(0);
|
|
117
|
+
setDocs([]);
|
|
118
|
+
}, [availableDateFields, sortField]);
|
|
93
119
|
|
|
94
120
|
const handleCollectionChange = (value: string) => {
|
|
95
121
|
const newSelected = value === "all" ? "" : value;
|
|
@@ -107,6 +133,24 @@ export default function Browse({ navigate }: PageProps) {
|
|
|
107
133
|
setOffset((prev) => prev + limit);
|
|
108
134
|
};
|
|
109
135
|
|
|
136
|
+
const handleSortChange = (value: string) => {
|
|
137
|
+
const [nextField, nextOrder] = value.split(":");
|
|
138
|
+
if (!nextField || (nextOrder !== "asc" && nextOrder !== "desc")) {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
setSortField(nextField);
|
|
142
|
+
setSortOrder(nextOrder);
|
|
143
|
+
setOffset(0);
|
|
144
|
+
setDocs([]);
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
const formatDateFieldLabel = (field: string) =>
|
|
148
|
+
field
|
|
149
|
+
.split("_")
|
|
150
|
+
.filter((token) => token.length > 0)
|
|
151
|
+
.map((token) => token.charAt(0).toUpperCase() + token.slice(1))
|
|
152
|
+
.join(" ");
|
|
153
|
+
|
|
110
154
|
const getExtBadgeVariant = (ext: string) => {
|
|
111
155
|
switch (ext.toLowerCase()) {
|
|
112
156
|
case ".md":
|
|
@@ -157,6 +201,28 @@ export default function Browse({ navigate }: PageProps) {
|
|
|
157
201
|
))}
|
|
158
202
|
</SelectContent>
|
|
159
203
|
</Select>
|
|
204
|
+
<Select
|
|
205
|
+
onValueChange={handleSortChange}
|
|
206
|
+
value={`${sortField}:${sortOrder}`}
|
|
207
|
+
>
|
|
208
|
+
<SelectTrigger className="w-[230px]">
|
|
209
|
+
<SelectValue placeholder="Sort" />
|
|
210
|
+
</SelectTrigger>
|
|
211
|
+
<SelectContent>
|
|
212
|
+
<SelectItem value="modified:desc">Newest Modified</SelectItem>
|
|
213
|
+
<SelectItem value="modified:asc">Oldest Modified</SelectItem>
|
|
214
|
+
{availableDateFields.map((field) => (
|
|
215
|
+
<Fragment key={field}>
|
|
216
|
+
<SelectItem value={`${field}:desc`}>
|
|
217
|
+
{`Newest by ${formatDateFieldLabel(field)}`}
|
|
218
|
+
</SelectItem>
|
|
219
|
+
<SelectItem value={`${field}:asc`}>
|
|
220
|
+
{`Oldest by ${formatDateFieldLabel(field)}`}
|
|
221
|
+
</SelectItem>
|
|
222
|
+
</Fragment>
|
|
223
|
+
))}
|
|
224
|
+
</SelectContent>
|
|
225
|
+
</Select>
|
|
160
226
|
<Badge className="font-mono" variant="outline">
|
|
161
227
|
{total.toLocaleString()} docs
|
|
162
228
|
</Badge>
|
|
@@ -192,29 +192,10 @@ export default function DocView({ navigate }: PageProps) {
|
|
|
192
192
|
// Request sequencing - ignore stale responses on rapid navigation
|
|
193
193
|
const requestIdRef = useRef(0);
|
|
194
194
|
|
|
195
|
-
//
|
|
196
|
-
const
|
|
195
|
+
// App remounts page on route/query changes, so URI is stable per render.
|
|
196
|
+
const currentUri = useMemo(() => {
|
|
197
197
|
const params = new URLSearchParams(window.location.search);
|
|
198
198
|
return params.get("uri") ?? "";
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
// Listen for URL changes (popstate for back/forward, locationchange for navigate())
|
|
202
|
-
useEffect(() => {
|
|
203
|
-
const updateUri = () => {
|
|
204
|
-
const params = new URLSearchParams(window.location.search);
|
|
205
|
-
const newUri = params.get("uri") ?? "";
|
|
206
|
-
setCurrentUri(newUri);
|
|
207
|
-
};
|
|
208
|
-
|
|
209
|
-
// Listen for browser back/forward
|
|
210
|
-
window.addEventListener("popstate", updateUri);
|
|
211
|
-
// Listen for programmatic navigation via navigate()
|
|
212
|
-
window.addEventListener("locationchange", updateUri);
|
|
213
|
-
|
|
214
|
-
return () => {
|
|
215
|
-
window.removeEventListener("popstate", updateUri);
|
|
216
|
-
window.removeEventListener("locationchange", updateUri);
|
|
217
|
-
};
|
|
218
199
|
}, []);
|
|
219
200
|
|
|
220
201
|
// Fetch document when URI changes
|