@gmickel/gno 2.3.3 → 2.5.1
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 +22 -3
- package/assets/skill/SKILL.md +53 -0
- package/assets/skill/cli-reference.md +29 -0
- package/assets/skill/examples.md +30 -0
- package/assets/skill/mcp-reference.md +26 -0
- package/assets/spa-production.json.gz +0 -0
- package/browser-extension/artifacts/{gno-browser-clipper-v2.3.3.zip → gno-browser-clipper-v2.5.1.zip} +0 -0
- package/browser-extension/artifacts/gno-browser-clipper-v2.5.1.zip.sha256 +1 -0
- package/browser-extension/dist/manifest.json +1 -1
- package/package.json +1 -1
- package/spec/cli.md +41 -0
- package/spec/compiled-context.md +68 -0
- package/spec/db/schema.sql +2 -0
- package/spec/evals.md +48 -0
- package/spec/mcp.md +37 -0
- package/spec/output-schemas/ask.schema.json +12 -0
- package/spec/output-schemas/compiled-context-check.schema.json +44 -0
- package/spec/output-schemas/compiled-context-file.schema.json +165 -0
- package/spec/output-schemas/compiled-context-preview.schema.json +142 -0
- package/spec/output-schemas/context-capsule-v1.schema.json +205 -32
- package/spec/output-schemas/context-capsule-verification.schema.json +32 -9
- package/spec/output-schemas/evidence-fixtures.schema.json +176 -0
- package/spec/output-schemas/evidence-report.schema.json +322 -0
- package/spec/output-schemas/evidence-run.schema.json +277 -0
- package/spec/output-schemas/metadata-predicate.schema.json +460 -0
- package/spec/output-schemas/retrieval-trace-filters.schema.json +13 -4
- package/spec/output-schemas/search-results.schema.json +12 -0
- package/spec/output-schemas/status.schema.json +37 -6
- package/src/app/compiled-context-files.ts +361 -0
- package/src/app/compiled-context.ts +240 -0
- package/src/app/context-runtime-contract.ts +33 -2
- package/src/app/context-runtime-input.ts +17 -0
- package/src/app/context-runtime-types.ts +2 -0
- package/src/app/context-runtime.ts +27 -3
- package/src/app/context-surface.ts +7 -0
- package/src/app/verified-ask.ts +1 -0
- package/src/cli/commands/ask.ts +1 -0
- package/src/cli/commands/audit.ts +4 -0
- package/src/cli/commands/context-build.ts +1 -0
- package/src/cli/commands/context-compiled.ts +136 -0
- package/src/cli/commands/search.ts +1 -0
- package/src/cli/commands/status.ts +10 -0
- package/src/cli/errors.ts +10 -3
- package/src/cli/options.ts +19 -0
- package/src/cli/program.ts +82 -0
- package/src/converters/types.ts +2 -0
- package/src/core/compiled-context.ts +254 -0
- package/src/core/context-budget.ts +2 -10
- package/src/core/context-capsule-retrieval-schema.ts +8 -0
- package/src/core/context-capsule-schema.ts +47 -3
- package/src/core/context-capsule-validation.ts +14 -2
- package/src/core/context-capsule-verification.ts +1 -0
- package/src/core/context-capsule.ts +16 -2
- package/src/core/context-compiler.ts +3 -0
- package/src/core/file-lock.ts +22 -5
- package/src/core/folder-setup-planning.ts +2 -1
- package/src/core/network-boundary-inventory.ts +8 -0
- package/src/core/record-metadata.ts +1 -1
- package/src/core/retrieval-replay-candidate.ts +5 -0
- package/src/core/retrieval-trace-filter-normalization.ts +4 -0
- package/src/core/retrieval-trace-request.ts +1 -0
- package/src/core/retrieval-trace-session.ts +5 -1
- package/src/core/retrieval-trace.ts +2 -0
- package/src/core/setup-receipt.ts +27 -20
- package/src/core/typed-metadata.ts +228 -0
- package/src/core/validation.ts +9 -2
- package/src/core/windows-private-path.ts +96 -0
- package/src/index.ts +2 -2
- package/src/ingestion/compiled-context.ts +15 -0
- package/src/ingestion/record-adapter-canonical.ts +13 -2
- package/src/ingestion/record-container.ts +9 -0
- package/src/ingestion/sync.ts +51 -9
- package/src/ingestion/typed-metadata.ts +55 -0
- package/src/ingestion/walker.ts +5 -4
- package/src/llm/nodeLlamaCpp/simulator-install.ts +6 -2
- package/src/mcp/http-egress.ts +11 -3
- package/src/mcp/retrieval-warnings.ts +24 -0
- package/src/mcp/tools/ask.ts +23 -2
- package/src/mcp/tools/context.ts +47 -2
- package/src/mcp/tools/index.ts +58 -1
- package/src/mcp/tools/query.ts +16 -1
- package/src/mcp/tools/search.ts +12 -1
- package/src/mcp/tools/status.ts +4 -0
- package/src/mcp/tools/vsearch.ts +12 -1
- package/src/pipeline/diagnose.ts +6 -0
- package/src/pipeline/filters.ts +65 -0
- package/src/pipeline/graph-retrieval.ts +9 -1
- package/src/pipeline/hybrid.ts +22 -2
- package/src/pipeline/search.ts +11 -0
- package/src/pipeline/types.ts +7 -1
- package/src/pipeline/vsearch.ts +21 -1
- package/src/sdk/client.ts +98 -3
- package/src/sdk/index.ts +11 -0
- package/src/sdk/types.ts +35 -1
- package/src/serve/compiled-context.ts +84 -0
- package/src/serve/context-capsule.ts +1 -0
- package/src/serve/public/app.tsx +12 -1
- package/src/serve/public/components/MetadataFilter.tsx +186 -0
- package/src/serve/public/globals.built.css +1 -1
- package/src/serve/public/lib/metadata-filter.ts +28 -0
- package/src/serve/public/lib/retrieval-filters.ts +3 -0
- package/src/serve/public/lib/workspace-tabs.ts +2 -0
- package/src/serve/public/pages/Ask.tsx +48 -3
- package/src/serve/public/pages/CompiledContext.tsx +364 -0
- package/src/serve/public/pages/Dashboard.tsx +7 -0
- package/src/serve/public/pages/DocView.tsx +33 -0
- package/src/serve/public/pages/Search.tsx +44 -1
- package/src/serve/routes/api.ts +50 -0
- package/src/serve/server.ts +43 -2
- package/src/serve/spa-production-build.ts +6 -5
- package/src/store/migrations/030-typed-metadata.ts +13 -0
- package/src/store/migrations/index.ts +2 -0
- package/src/store/sqlite/adapter.ts +69 -5
- package/src/store/sqlite/eligibility.ts +12 -0
- package/src/store/sqlite/metadata-predicate.ts +71 -0
- package/src/store/types.ts +11 -0
- package/browser-extension/artifacts/gno-browser-clipper-v2.3.3.zip.sha256 +0 -1
package/src/sdk/index.ts
CHANGED
|
@@ -80,6 +80,10 @@ export type {
|
|
|
80
80
|
SectionTargetCreateSelector,
|
|
81
81
|
SectionTargetResolveResult,
|
|
82
82
|
SectionTargetV1,
|
|
83
|
+
MetadataPredicate,
|
|
84
|
+
MetadataScalar,
|
|
85
|
+
TypedMetadata,
|
|
86
|
+
TypedValue,
|
|
83
87
|
MemoryCandidate,
|
|
84
88
|
MemoryFact,
|
|
85
89
|
MemoryRecallReceipt,
|
|
@@ -124,3 +128,10 @@ export type {
|
|
|
124
128
|
RetrievalTracePurgeResult,
|
|
125
129
|
RetrievalTraceSummary,
|
|
126
130
|
} from "../core/retrieval-trace-management";
|
|
131
|
+
|
|
132
|
+
export type {
|
|
133
|
+
CompiledContextPreviewInput,
|
|
134
|
+
CompiledContextCheckInput,
|
|
135
|
+
CompiledContextPreview,
|
|
136
|
+
CompiledContextCheck,
|
|
137
|
+
} from "../core/compiled-context";
|
package/src/sdk/types.ts
CHANGED
|
@@ -1,9 +1,17 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
previewCompiledContext,
|
|
3
|
+
checkCompiledContext,
|
|
4
|
+
} from "../app/compiled-context";
|
|
5
|
+
import type {
|
|
6
|
+
compileContextFile,
|
|
7
|
+
checkContextFile,
|
|
8
|
+
refreshContextFile,
|
|
9
|
+
} from "../app/compiled-context-files";
|
|
1
10
|
/**
|
|
2
11
|
* Public SDK types.
|
|
3
12
|
*
|
|
4
13
|
* @module src/sdk/types
|
|
5
14
|
*/
|
|
6
|
-
|
|
7
15
|
import type {
|
|
8
16
|
ContextCapsuleBuildInput,
|
|
9
17
|
ContextRuntimeErrorCode,
|
|
@@ -18,6 +26,10 @@ import type {
|
|
|
18
26
|
CollectionEgressPolicyState,
|
|
19
27
|
EgressRelaxationConfirmation,
|
|
20
28
|
} from "../core/collection-egress-policy-service";
|
|
29
|
+
import type {
|
|
30
|
+
CompiledContextPreviewInput,
|
|
31
|
+
CompiledContextCheckInput,
|
|
32
|
+
} from "../core/compiled-context";
|
|
21
33
|
import type {
|
|
22
34
|
ContextCapsuleErrorCode,
|
|
23
35
|
ContextCapsuleV1,
|
|
@@ -328,6 +340,21 @@ export interface GnoClient {
|
|
|
328
340
|
query(query: string, options?: GnoQueryOptions): Promise<SearchResults>;
|
|
329
341
|
ask(query: string, options?: GnoAskOptions): Promise<AskResult>;
|
|
330
342
|
context(input: GnoContextInput): Promise<GnoContextResult>;
|
|
343
|
+
compiledContextPreview(
|
|
344
|
+
input: CompiledContextPreviewInput
|
|
345
|
+
): ReturnType<typeof previewCompiledContext>;
|
|
346
|
+
compiledContextCheck(
|
|
347
|
+
input: CompiledContextCheckInput
|
|
348
|
+
): ReturnType<typeof checkCompiledContext>;
|
|
349
|
+
compileContextFile(
|
|
350
|
+
input: Parameters<typeof compileContextFile>[0]
|
|
351
|
+
): ReturnType<typeof compileContextFile>;
|
|
352
|
+
checkContextFile(
|
|
353
|
+
input: Parameters<typeof checkContextFile>[0]
|
|
354
|
+
): ReturnType<typeof checkContextFile>;
|
|
355
|
+
refreshContextFile(
|
|
356
|
+
input: Parameters<typeof refreshContextFile>[0]
|
|
357
|
+
): ReturnType<typeof refreshContextFile>;
|
|
331
358
|
verifyContext(
|
|
332
359
|
capsule: ContextCapsuleV1
|
|
333
360
|
): Promise<GnoContextVerificationResult>;
|
|
@@ -435,3 +462,10 @@ export interface GnoClient {
|
|
|
435
462
|
): Promise<SectionTargetResolveResult>;
|
|
436
463
|
close(): Promise<void>;
|
|
437
464
|
}
|
|
465
|
+
|
|
466
|
+
export type {
|
|
467
|
+
MetadataPredicate,
|
|
468
|
+
MetadataScalar,
|
|
469
|
+
TypedMetadata,
|
|
470
|
+
TypedValue,
|
|
471
|
+
} from "../core/typed-metadata";
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { ServerContext } from "./context";
|
|
2
|
+
|
|
3
|
+
/** Read-only compiled context adapters; caller authority comes from the socket. */
|
|
4
|
+
import {
|
|
5
|
+
previewCompiledContext,
|
|
6
|
+
checkCompiledContext,
|
|
7
|
+
} from "../app/compiled-context";
|
|
8
|
+
import {
|
|
9
|
+
compiledContextPreviewInputSchema,
|
|
10
|
+
compiledContextCheckInputSchema,
|
|
11
|
+
} from "../core/compiled-context";
|
|
12
|
+
import {
|
|
13
|
+
isLocalClientRequest,
|
|
14
|
+
type RequestPeerServer,
|
|
15
|
+
} from "./request-locality";
|
|
16
|
+
|
|
17
|
+
export async function handleCompiledContext(
|
|
18
|
+
context: ServerContext,
|
|
19
|
+
request: Request,
|
|
20
|
+
server: RequestPeerServer | undefined,
|
|
21
|
+
operation: "preview" | "check"
|
|
22
|
+
): Promise<Response> {
|
|
23
|
+
try {
|
|
24
|
+
// Bound bytes before JSON parsing, even when Content-Length is absent.
|
|
25
|
+
const reader = request.body?.getReader();
|
|
26
|
+
if (!reader)
|
|
27
|
+
return Response.json(
|
|
28
|
+
{ error: { code: "invalid_input", message: "JSON body required" } },
|
|
29
|
+
{ status: 400 }
|
|
30
|
+
);
|
|
31
|
+
const chunks: Uint8Array[] = [];
|
|
32
|
+
let size = 0;
|
|
33
|
+
while (true) {
|
|
34
|
+
const part = await reader.read();
|
|
35
|
+
if (part.done) break;
|
|
36
|
+
size += part.value.byteLength;
|
|
37
|
+
if (size > 8 * 1024 * 1024 + 65536) {
|
|
38
|
+
await reader.cancel();
|
|
39
|
+
return Response.json(
|
|
40
|
+
{
|
|
41
|
+
error: {
|
|
42
|
+
code: "invalid_input",
|
|
43
|
+
message: "Compiled context request exceeds byte limit",
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
{ status: 413 }
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
chunks.push(part.value);
|
|
50
|
+
}
|
|
51
|
+
const raw: unknown = JSON.parse(
|
|
52
|
+
await new Blob(chunks.map((chunk) => new Uint8Array(chunk))).text()
|
|
53
|
+
);
|
|
54
|
+
const input =
|
|
55
|
+
operation === "preview"
|
|
56
|
+
? compiledContextPreviewInputSchema.parse(raw)
|
|
57
|
+
: compiledContextCheckInputSchema.parse(raw);
|
|
58
|
+
const local = isLocalClientRequest(request, server);
|
|
59
|
+
const deps = {
|
|
60
|
+
store: context.store,
|
|
61
|
+
config: context.config,
|
|
62
|
+
indexName: context.indexName,
|
|
63
|
+
destinationZone: local ? ("local_process" as const) : ("remote" as const),
|
|
64
|
+
caller: { authenticated: local, operationAuthorized: true },
|
|
65
|
+
};
|
|
66
|
+
const result =
|
|
67
|
+
operation === "preview"
|
|
68
|
+
? await previewCompiledContext(input, deps)
|
|
69
|
+
: await checkCompiledContext(input, deps);
|
|
70
|
+
return Response.json(result);
|
|
71
|
+
} catch {
|
|
72
|
+
// Supplied Capsules may contain private metadata; errors never echo inputs.
|
|
73
|
+
return Response.json(
|
|
74
|
+
{
|
|
75
|
+
error: {
|
|
76
|
+
code: "compiled_context_unavailable",
|
|
77
|
+
message:
|
|
78
|
+
"Compiled context is invalid, stale, or unavailable for this caller",
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
{ status: 400 }
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
}
|
package/src/serve/public/app.tsx
CHANGED
|
@@ -47,6 +47,7 @@ const Collections = lazy(() => import("./pages/Collections"));
|
|
|
47
47
|
const Connectors = lazy(() => import("./pages/Connectors"));
|
|
48
48
|
const Ask = lazy(() => import("./pages/Ask"));
|
|
49
49
|
const GraphView = lazy(() => import("./pages/GraphView"));
|
|
50
|
+
const CompiledContext = lazy(() => import("./pages/CompiledContext"));
|
|
50
51
|
const TraceHistory = lazy(() => import("./pages/TraceHistory"));
|
|
51
52
|
|
|
52
53
|
type Route =
|
|
@@ -59,6 +60,7 @@ type Route =
|
|
|
59
60
|
| "/collections"
|
|
60
61
|
| "/graph"
|
|
61
62
|
| "/connectors"
|
|
63
|
+
| "/context/compiled"
|
|
62
64
|
| "/traces";
|
|
63
65
|
type Navigate = (to: string | number) => void;
|
|
64
66
|
|
|
@@ -78,6 +80,7 @@ const routes: Record<Route, React.ComponentType<RoutePageProps>> = {
|
|
|
78
80
|
"/ask": Ask,
|
|
79
81
|
"/graph": GraphView,
|
|
80
82
|
"/traces": TraceHistory,
|
|
83
|
+
"/context/compiled": CompiledContext,
|
|
81
84
|
};
|
|
82
85
|
|
|
83
86
|
interface AppContentProps {
|
|
@@ -179,7 +182,7 @@ function AppContent({
|
|
|
179
182
|
<div className="ornament mx-auto mb-4 max-w-[8rem] text-muted-foreground/20">
|
|
180
183
|
<span className="text-[10px]">◆</span>
|
|
181
184
|
</div>
|
|
182
|
-
<div className="flex items-center justify-center gap-
|
|
185
|
+
<div className="flex flex-wrap items-center justify-center gap-x-3 gap-y-2 px-4 text-muted-foreground/60 sm:gap-x-5">
|
|
183
186
|
<button
|
|
184
187
|
className="transition-colors duration-300 hover:text-primary"
|
|
185
188
|
onClick={() => navigate("/collections")}
|
|
@@ -196,6 +199,14 @@ function AppContent({
|
|
|
196
199
|
Trace history
|
|
197
200
|
</button>
|
|
198
201
|
<span className="text-border/30">—</span>
|
|
202
|
+
<button
|
|
203
|
+
className="transition-colors duration-300 hover:text-primary"
|
|
204
|
+
onClick={() => navigate("/context/compiled")}
|
|
205
|
+
type="button"
|
|
206
|
+
>
|
|
207
|
+
Compiled context
|
|
208
|
+
</button>
|
|
209
|
+
<span className="text-border/30">—</span>
|
|
199
210
|
<a
|
|
200
211
|
className="transition-colors duration-300 hover:text-primary"
|
|
201
212
|
href="https://github.com/gmickel/gno"
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { useId, useState } from "react";
|
|
2
|
+
|
|
3
|
+
import { parseMetadataFilter } from "../lib/metadata-filter";
|
|
4
|
+
import { Button } from "./ui/button";
|
|
5
|
+
import { Input } from "./ui/input";
|
|
6
|
+
import { Textarea } from "./ui/textarea";
|
|
7
|
+
|
|
8
|
+
const OPERATORS = [
|
|
9
|
+
"eq",
|
|
10
|
+
"ne",
|
|
11
|
+
"gt",
|
|
12
|
+
"gte",
|
|
13
|
+
"lt",
|
|
14
|
+
"lte",
|
|
15
|
+
"in",
|
|
16
|
+
"nin",
|
|
17
|
+
"all",
|
|
18
|
+
"exists",
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
/** The JSON text is the single source of truth, including invalid drafts. */
|
|
22
|
+
export function MetadataFilter({
|
|
23
|
+
value,
|
|
24
|
+
onChange,
|
|
25
|
+
}: {
|
|
26
|
+
value: string;
|
|
27
|
+
onChange: (value: string) => void;
|
|
28
|
+
}) {
|
|
29
|
+
const id = useId();
|
|
30
|
+
const [draftValue, setDraftValue] = useState<string | null>(null);
|
|
31
|
+
const [draftLeaf, setDraftLeaf] = useState<Record<string, unknown> | null>(
|
|
32
|
+
null
|
|
33
|
+
);
|
|
34
|
+
const { error } = parseMetadataFilter(value);
|
|
35
|
+
let leaf: Record<string, unknown> | null = null;
|
|
36
|
+
try {
|
|
37
|
+
const parsed: unknown = JSON.parse(value);
|
|
38
|
+
if (
|
|
39
|
+
parsed &&
|
|
40
|
+
typeof parsed === "object" &&
|
|
41
|
+
!Array.isArray(parsed) &&
|
|
42
|
+
"op" in parsed &&
|
|
43
|
+
OPERATORS.includes(String(parsed.op))
|
|
44
|
+
)
|
|
45
|
+
leaf = parsed as Record<string, unknown>;
|
|
46
|
+
} catch {
|
|
47
|
+
/* Invalid JSON stays editable below. */
|
|
48
|
+
}
|
|
49
|
+
if (value.trim()) leaf ??= draftLeaf;
|
|
50
|
+
const update = (patch: Record<string, unknown>) => {
|
|
51
|
+
const next = { ...leaf, ...patch };
|
|
52
|
+
setDraftLeaf(next);
|
|
53
|
+
if (draftValue !== null) {
|
|
54
|
+
const property = "values" in next ? "values" : "value";
|
|
55
|
+
onChange(
|
|
56
|
+
`{"op":${JSON.stringify(next.op)},"key":${JSON.stringify(next.key)},"${property}":${draftValue}}`
|
|
57
|
+
);
|
|
58
|
+
} else onChange(JSON.stringify(next));
|
|
59
|
+
};
|
|
60
|
+
return (
|
|
61
|
+
<fieldset className="min-w-0 space-y-3 border-border/40 border-t pt-3">
|
|
62
|
+
<legend className="px-1 font-mono text-muted-foreground text-xs">
|
|
63
|
+
Custom metadata
|
|
64
|
+
</legend>
|
|
65
|
+
<p className="text-muted-foreground text-xs">
|
|
66
|
+
Filter indexed gno.metadata fields. Text is case-sensitive. Values use
|
|
67
|
+
JSON: "approved", 0.8, true, or ["alpha"]. Filters are included in this
|
|
68
|
+
page’s URL.
|
|
69
|
+
</p>
|
|
70
|
+
{leaf ? (
|
|
71
|
+
<div className="grid min-w-0 gap-2 sm:grid-cols-3">
|
|
72
|
+
<label className="space-y-1 text-xs">
|
|
73
|
+
Field
|
|
74
|
+
<Input
|
|
75
|
+
onChange={(e) => update({ key: e.target.value })}
|
|
76
|
+
value={typeof leaf.key === "string" ? leaf.key : ""}
|
|
77
|
+
/>
|
|
78
|
+
</label>
|
|
79
|
+
<label className="space-y-1 text-xs">
|
|
80
|
+
Operator
|
|
81
|
+
<select
|
|
82
|
+
className="h-9 w-full cursor-pointer rounded-md border border-input bg-background px-2 focus-visible:ring-2 focus-visible:ring-primary/50"
|
|
83
|
+
onChange={(e) => {
|
|
84
|
+
setDraftValue(null);
|
|
85
|
+
setDraftLeaf(null);
|
|
86
|
+
const op = e.target.value;
|
|
87
|
+
onChange(
|
|
88
|
+
JSON.stringify({
|
|
89
|
+
op,
|
|
90
|
+
key: leaf?.key ?? "",
|
|
91
|
+
...(["in", "nin", "all"].includes(op)
|
|
92
|
+
? { values: [] }
|
|
93
|
+
: { value: op === "exists" ? true : "" }),
|
|
94
|
+
})
|
|
95
|
+
);
|
|
96
|
+
}}
|
|
97
|
+
value={String(leaf.op)}
|
|
98
|
+
>
|
|
99
|
+
{OPERATORS.map((op) => (
|
|
100
|
+
<option key={op} value={op}>
|
|
101
|
+
{op}
|
|
102
|
+
</option>
|
|
103
|
+
))}
|
|
104
|
+
</select>
|
|
105
|
+
</label>
|
|
106
|
+
<label className="space-y-1 text-xs">
|
|
107
|
+
Value (JSON)
|
|
108
|
+
<Input
|
|
109
|
+
onChange={(e) => {
|
|
110
|
+
const raw = e.target.value;
|
|
111
|
+
setDraftValue(raw);
|
|
112
|
+
setDraftLeaf(leaf);
|
|
113
|
+
const property = ["in", "nin", "all"].includes(String(leaf?.op))
|
|
114
|
+
? "values"
|
|
115
|
+
: "value";
|
|
116
|
+
// Preserve malformed JSON verbatim for correction; never drop the filter.
|
|
117
|
+
onChange(
|
|
118
|
+
`{"op":${JSON.stringify(leaf?.op)},"key":${JSON.stringify(leaf?.key ?? "")},"${property}":${raw}}`
|
|
119
|
+
);
|
|
120
|
+
}}
|
|
121
|
+
value={
|
|
122
|
+
draftValue ??
|
|
123
|
+
JSON.stringify("values" in leaf ? leaf.values : leaf.value) ??
|
|
124
|
+
""
|
|
125
|
+
}
|
|
126
|
+
/>
|
|
127
|
+
</label>
|
|
128
|
+
</div>
|
|
129
|
+
) : !value.trim() ? (
|
|
130
|
+
<Button
|
|
131
|
+
onClick={() => {
|
|
132
|
+
setDraftValue(null);
|
|
133
|
+
setDraftLeaf(null);
|
|
134
|
+
onChange(
|
|
135
|
+
JSON.stringify({ op: "eq", key: "status", value: "approved" })
|
|
136
|
+
);
|
|
137
|
+
}}
|
|
138
|
+
size="sm"
|
|
139
|
+
type="button"
|
|
140
|
+
variant="outline"
|
|
141
|
+
>
|
|
142
|
+
Add metadata filter
|
|
143
|
+
</Button>
|
|
144
|
+
) : null}
|
|
145
|
+
<label className="block space-y-1 text-xs" htmlFor={`${id}-json`}>
|
|
146
|
+
Advanced filter (JSON)
|
|
147
|
+
<Textarea
|
|
148
|
+
aria-describedby={error ? `${id}-error` : undefined}
|
|
149
|
+
aria-invalid={Boolean(error)}
|
|
150
|
+
className="min-h-20 font-mono text-xs"
|
|
151
|
+
id={`${id}-json`}
|
|
152
|
+
onChange={(e) => {
|
|
153
|
+
setDraftValue(null);
|
|
154
|
+
setDraftLeaf(null);
|
|
155
|
+
onChange(e.target.value);
|
|
156
|
+
}}
|
|
157
|
+
placeholder='{"op":"gte","key":"confidence","value":0.8}'
|
|
158
|
+
value={value}
|
|
159
|
+
/>
|
|
160
|
+
</label>
|
|
161
|
+
{error && (
|
|
162
|
+
<p
|
|
163
|
+
className="break-words text-destructive text-xs"
|
|
164
|
+
id={`${id}-error`}
|
|
165
|
+
role="alert"
|
|
166
|
+
>
|
|
167
|
+
{error}
|
|
168
|
+
</p>
|
|
169
|
+
)}
|
|
170
|
+
{value && (
|
|
171
|
+
<Button
|
|
172
|
+
onClick={() => {
|
|
173
|
+
setDraftValue(null);
|
|
174
|
+
setDraftLeaf(null);
|
|
175
|
+
onChange("");
|
|
176
|
+
}}
|
|
177
|
+
size="sm"
|
|
178
|
+
type="button"
|
|
179
|
+
variant="ghost"
|
|
180
|
+
>
|
|
181
|
+
Clear metadata filter
|
|
182
|
+
</Button>
|
|
183
|
+
)}
|
|
184
|
+
</fieldset>
|
|
185
|
+
);
|
|
186
|
+
}
|